diff --git a/parse_test.go b/parse_test.go index 444ac1e5a8..07d254bd6f 100644 --- a/parse_test.go +++ b/parse_test.go @@ -1652,6 +1652,8 @@ func TestParseSVCB(t *testing.T) { `example.com. SVCB 16 foo.example.org. alpn=h2,h3-19 mandatory=ipv4hint,alpn ipv4hint=192.0.2.1`: `example.com. 3600 IN SVCB 16 foo.example.org. alpn="h2,h3-19" mandatory="ipv4hint,alpn" ipv4hint="192.0.2.1"`, `example.com. SVCB 16 foo.example.org. alpn="f\\\\oo\\,bar,h2"`: `example.com. 3600 IN SVCB 16 foo.example.org. alpn="f\\\\oo\\,bar,h2"`, `example.com. SVCB 16 foo.example.org. alpn=f\\\092oo\092,bar,h2`: `example.com. 3600 IN SVCB 16 foo.example.org. alpn="f\\\092oo\092,bar,h2"`, + // From draft-ietf-add-ddr-06 + `_dns.example.net. SVCB 1 example.net. alpn=h2 dohpath=/dns-query{?dns}`: `_dns.example.net. 3600 IN SVCB 1 example.net. alpn="h2" dohpath="/dns-query{?dns}"`, } for s, o := range svcbs { rr, err := NewRR(s) diff --git a/svcb.go b/svcb.go index ff5e01086d..68075e4b53 100644 --- a/svcb.go +++ b/svcb.go @@ -22,6 +22,7 @@ const ( SVCB_IPV4HINT SVCB_ECHCONFIG SVCB_IPV6HINT + SVCB_DOHPATH // draft-ietf-add-svcb-dns-02 Section 9 svcb_RESERVED SVCBKey = 65535 ) @@ -34,6 +35,7 @@ var svcbKeyToStringMap = map[SVCBKey]string{ SVCB_IPV4HINT: "ipv4hint", SVCB_ECHCONFIG: "ech", SVCB_IPV6HINT: "ipv6hint", + SVCB_DOHPATH: "dohpath", } var svcbStringToKeyMap = reverseSVCBKeyMap(svcbKeyToStringMap) @@ -196,6 +198,8 @@ func makeSVCBKeyValue(key SVCBKey) SVCBKeyValue { return new(SVCBECHConfig) case SVCB_IPV6HINT: return new(SVCBIPv6Hint) + case SVCB_DOHPATH: + return new(SVCBDoHPath) case svcb_RESERVED: return nil default: @@ -669,6 +673,50 @@ func (s *SVCBIPv6Hint) copy() SVCBKeyValue { } } +// SVCBDoHPath pair is used to indicate the URI template that the +// clients may use to construct a DNS over HTTPS URI. +// +// See RFC xxxx (https://datatracker.ietf.org/doc/html/draft-ietf-add-svcb-dns-02) +// and RFC yyyy (https://datatracker.ietf.org/doc/html/draft-ietf-add-ddr-06). +// +// A basic example of using the dohpath option together with the alpn +// option to indicate support for DNS over HTTPS on a certain path: +// +// s := new(dns.SVCB) +// s.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeSVCB, Class: dns.ClassINET} +// e := new(dns.SVCBAlpn) +// e.Alpn = []string{"h2", "h3"} +// p := new(dns.SVCBDoHPath) +// p.Template = "/dns-query{?dns}" +// s.Value = append(s.Value, e, p) +// +// The parsing currently doesn't validate that Template is a valid +// RFC 6570 URI template. +type SVCBDoHPath struct { + Template string +} + +func (*SVCBDoHPath) Key() SVCBKey { return SVCB_DOHPATH } +func (s *SVCBDoHPath) String() string { return s.Template } +func (s *SVCBDoHPath) len() int { return len(s.Template) } +func (s *SVCBDoHPath) pack() ([]byte, error) { return []byte(s.Template), nil } + +func (s *SVCBDoHPath) unpack(b []byte) error { + s.Template = string(b) + return nil +} + +func (s *SVCBDoHPath) parse(b string) error { + s.Template = b + return nil +} + +func (s *SVCBDoHPath) copy() SVCBKeyValue { + return &SVCBDoHPath{ + Template: s.Template, + } +} + // SVCBLocal pair is intended for experimental/private use. The key is recommended // to be in the range [SVCB_PRIVATE_LOWER, SVCB_PRIVATE_UPPER]. // Basic use pattern for creating a keyNNNNN option: diff --git a/svcb_test.go b/svcb_test.go index 254949954f..0f940e7e5a 100644 --- a/svcb_test.go +++ b/svcb_test.go @@ -18,6 +18,7 @@ func TestSVCB(t *testing.T) { {`no-default-alpn`, ``}, {`ipv6hint`, `1::4:4:4:4,1::3:3:3:3`}, {`ech`, `YUdWc2JHOD0=`}, + {`dohpath`, `/dns-query{?dns}`}, {`key65000`, `4\ 3`}, {`key65001`, `\"\ `}, {`key65002`, ``},