summaryrefslogtreecommitdiff
path: root/vendor/github.com/miekg/dns/svcb.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/miekg/dns/svcb.go')
-rw-r--r--vendor/github.com/miekg/dns/svcb.go935
1 files changed, 0 insertions, 935 deletions
diff --git a/vendor/github.com/miekg/dns/svcb.go b/vendor/github.com/miekg/dns/svcb.go
deleted file mode 100644
index ea58710da..000000000
--- a/vendor/github.com/miekg/dns/svcb.go
+++ /dev/null
@@ -1,935 +0,0 @@
-package dns
-
-import (
- "bytes"
- "encoding/binary"
- "errors"
- "fmt"
- "net"
- "sort"
- "strconv"
- "strings"
-)
-
-// SVCBKey is the type of the keys used in the SVCB RR.
-type SVCBKey uint16
-
-// Keys defined in draft-ietf-dnsop-svcb-https-08 Section 14.3.2.
-const (
- SVCB_MANDATORY SVCBKey = iota
- SVCB_ALPN
- SVCB_NO_DEFAULT_ALPN
- SVCB_PORT
- SVCB_IPV4HINT
- SVCB_ECHCONFIG
- SVCB_IPV6HINT
- SVCB_DOHPATH // draft-ietf-add-svcb-dns-02 Section 9
-
- svcb_RESERVED SVCBKey = 65535
-)
-
-var svcbKeyToStringMap = map[SVCBKey]string{
- SVCB_MANDATORY: "mandatory",
- SVCB_ALPN: "alpn",
- SVCB_NO_DEFAULT_ALPN: "no-default-alpn",
- SVCB_PORT: "port",
- SVCB_IPV4HINT: "ipv4hint",
- SVCB_ECHCONFIG: "ech",
- SVCB_IPV6HINT: "ipv6hint",
- SVCB_DOHPATH: "dohpath",
-}
-
-var svcbStringToKeyMap = reverseSVCBKeyMap(svcbKeyToStringMap)
-
-func reverseSVCBKeyMap(m map[SVCBKey]string) map[string]SVCBKey {
- n := make(map[string]SVCBKey, len(m))
- for u, s := range m {
- n[s] = u
- }
- return n
-}
-
-// String takes the numerical code of an SVCB key and returns its name.
-// Returns an empty string for reserved keys.
-// Accepts unassigned keys as well as experimental/private keys.
-func (key SVCBKey) String() string {
- if x := svcbKeyToStringMap[key]; x != "" {
- return x
- }
- if key == svcb_RESERVED {
- return ""
- }
- return "key" + strconv.FormatUint(uint64(key), 10)
-}
-
-// svcbStringToKey returns the numerical code of an SVCB key.
-// Returns svcb_RESERVED for reserved/invalid keys.
-// Accepts unassigned keys as well as experimental/private keys.
-func svcbStringToKey(s string) SVCBKey {
- if strings.HasPrefix(s, "key") {
- a, err := strconv.ParseUint(s[3:], 10, 16)
- // no leading zeros
- // key shouldn't be registered
- if err != nil || a == 65535 || s[3] == '0' || svcbKeyToStringMap[SVCBKey(a)] != "" {
- return svcb_RESERVED
- }
- return SVCBKey(a)
- }
- if key, ok := svcbStringToKeyMap[s]; ok {
- return key
- }
- return svcb_RESERVED
-}
-
-func (rr *SVCB) parse(c *zlexer, o string) *ParseError {
- l, _ := c.Next()
- i, e := strconv.ParseUint(l.token, 10, 16)
- if e != nil || l.err {
- return &ParseError{l.token, "bad SVCB priority", l}
- }
- rr.Priority = uint16(i)
-
- c.Next() // zBlank
- l, _ = c.Next() // zString
- rr.Target = l.token
-
- name, nameOk := toAbsoluteName(l.token, o)
- if l.err || !nameOk {
- return &ParseError{l.token, "bad SVCB Target", l}
- }
- rr.Target = name
-
- // Values (if any)
- l, _ = c.Next()
- var xs []SVCBKeyValue
- // Helps require whitespace between pairs.
- // Prevents key1000="a"key1001=...
- canHaveNextKey := true
- for l.value != zNewline && l.value != zEOF {
- switch l.value {
- case zString:
- if !canHaveNextKey {
- // The key we can now read was probably meant to be
- // a part of the last value.
- return &ParseError{l.token, "bad SVCB value quotation", l}
- }
-
- // In key=value pairs, value does not have to be quoted unless value
- // contains whitespace. And keys don't need to have values.
- // Similarly, keys with an equality signs after them don't need values.
- // l.token includes at least up to the first equality sign.
- idx := strings.IndexByte(l.token, '=')
- var key, value string
- if idx < 0 {
- // Key with no value and no equality sign
- key = l.token
- } else if idx == 0 {
- return &ParseError{l.token, "bad SVCB key", l}
- } else {
- key, value = l.token[:idx], l.token[idx+1:]
-
- if value == "" {
- // We have a key and an equality sign. Maybe we have nothing
- // after "=" or we have a double quote.
- l, _ = c.Next()
- if l.value == zQuote {
- // Only needed when value ends with double quotes.
- // Any value starting with zQuote ends with it.
- canHaveNextKey = false
-
- l, _ = c.Next()
- switch l.value {
- case zString:
- // We have a value in double quotes.
- value = l.token
- l, _ = c.Next()
- if l.value != zQuote {
- return &ParseError{l.token, "SVCB unterminated value", l}
- }
- case zQuote:
- // There's nothing in double quotes.
- default:
- return &ParseError{l.token, "bad SVCB value", l}
- }
- }
- }
- }
- kv := makeSVCBKeyValue(svcbStringToKey(key))
- if kv == nil {
- return &ParseError{l.token, "bad SVCB key", l}
- }
- if err := kv.parse(value); err != nil {
- return &ParseError{l.token, err.Error(), l}
- }
- xs = append(xs, kv)
- case zQuote:
- return &ParseError{l.token, "SVCB key can't contain double quotes", l}
- case zBlank:
- canHaveNextKey = true
- default:
- return &ParseError{l.token, "bad SVCB values", l}
- }
- l, _ = c.Next()
- }
-
- // "In AliasMode, records SHOULD NOT include any SvcParams, and recipients MUST
- // ignore any SvcParams that are present."
- // However, we don't check rr.Priority == 0 && len(xs) > 0 here
- // It is the responsibility of the user of the library to check this.
- // This is to encourage the fixing of the source of this error.
-
- rr.Value = xs
- return nil
-}
-
-// makeSVCBKeyValue returns an SVCBKeyValue struct with the key or nil for reserved keys.
-func makeSVCBKeyValue(key SVCBKey) SVCBKeyValue {
- switch key {
- case SVCB_MANDATORY:
- return new(SVCBMandatory)
- case SVCB_ALPN:
- return new(SVCBAlpn)
- case SVCB_NO_DEFAULT_ALPN:
- return new(SVCBNoDefaultAlpn)
- case SVCB_PORT:
- return new(SVCBPort)
- case SVCB_IPV4HINT:
- return new(SVCBIPv4Hint)
- case SVCB_ECHCONFIG:
- return new(SVCBECHConfig)
- case SVCB_IPV6HINT:
- return new(SVCBIPv6Hint)
- case SVCB_DOHPATH:
- return new(SVCBDoHPath)
- case svcb_RESERVED:
- return nil
- default:
- e := new(SVCBLocal)
- e.KeyCode = key
- return e
- }
-}
-
-// SVCB RR. See RFC xxxx (https://tools.ietf.org/html/draft-ietf-dnsop-svcb-https-08).
-//
-// NOTE: The HTTPS/SVCB RFCs are in the draft stage.
-// The API, including constants and types related to SVCBKeyValues, may
-// change in future versions in accordance with the latest drafts.
-type SVCB struct {
- Hdr RR_Header
- Priority uint16 // If zero, Value must be empty or discarded by the user of this library
- Target string `dns:"domain-name"`
- Value []SVCBKeyValue `dns:"pairs"`
-}
-
-// HTTPS RR. Everything valid for SVCB applies to HTTPS as well.
-// Except that the HTTPS record is intended for use with the HTTP and HTTPS protocols.
-//
-// NOTE: The HTTPS/SVCB RFCs are in the draft stage.
-// The API, including constants and types related to SVCBKeyValues, may
-// change in future versions in accordance with the latest drafts.
-type HTTPS struct {
- SVCB
-}
-
-func (rr *HTTPS) String() string {
- return rr.SVCB.String()
-}
-
-func (rr *HTTPS) parse(c *zlexer, o string) *ParseError {
- return rr.SVCB.parse(c, o)
-}
-
-// SVCBKeyValue defines a key=value pair for the SVCB RR type.
-// An SVCB RR can have multiple SVCBKeyValues appended to it.
-type SVCBKeyValue interface {
- Key() SVCBKey // Key returns the numerical key code.
- pack() ([]byte, error) // pack returns the encoded value.
- unpack([]byte) error // unpack sets the value.
- String() string // String returns the string representation of the value.
- parse(string) error // parse sets the value to the given string representation of the value.
- copy() SVCBKeyValue // copy returns a deep-copy of the pair.
- len() int // len returns the length of value in the wire format.
-}
-
-// SVCBMandatory pair adds to required keys that must be interpreted for the RR
-// to be functional. If ignored, the whole RRSet must be ignored.
-// "port" and "no-default-alpn" are mandatory by default if present,
-// so they shouldn't be included here.
-//
-// It is incumbent upon the user of this library to reject the RRSet if
-// or avoid constructing such an RRSet that:
-// - "mandatory" is included as one of the keys of mandatory
-// - no key is listed multiple times in mandatory
-// - all keys listed in mandatory are present
-// - escape sequences are not used in mandatory
-// - mandatory, when present, lists at least one key
-//
-// Basic use pattern for creating a mandatory option:
-//
-// s := &dns.SVCB{Hdr: dns.RR_Header{Name: ".", Rrtype: dns.TypeSVCB, Class: dns.ClassINET}}
-// e := new(dns.SVCBMandatory)
-// e.Code = []uint16{dns.SVCB_ALPN}
-// s.Value = append(s.Value, e)
-// t := new(dns.SVCBAlpn)
-// t.Alpn = []string{"xmpp-client"}
-// s.Value = append(s.Value, t)
-type SVCBMandatory struct {
- Code []SVCBKey
-}
-
-func (*SVCBMandatory) Key() SVCBKey { return SVCB_MANDATORY }
-
-func (s *SVCBMandatory) String() string {
- str := make([]string, len(s.Code))
- for i, e := range s.Code {
- str[i] = e.String()
- }
- return strings.Join(str, ",")
-}
-
-func (s *SVCBMandatory) pack() ([]byte, error) {
- codes := append([]SVCBKey(nil), s.Code...)
- sort.Slice(codes, func(i, j int) bool {
- return codes[i] < codes[j]
- })
- b := make([]byte, 2*len(codes))
- for i, e := range codes {
- binary.BigEndian.PutUint16(b[2*i:], uint16(e))
- }
- return b, nil
-}
-
-func (s *SVCBMandatory) unpack(b []byte) error {
- if len(b)%2 != 0 {
- return errors.New("dns: svcbmandatory: value length is not a multiple of 2")
- }
- codes := make([]SVCBKey, 0, len(b)/2)
- for i := 0; i < len(b); i += 2 {
- // We assume strictly increasing order.
- codes = append(codes, SVCBKey(binary.BigEndian.Uint16(b[i:])))
- }
- s.Code = codes
- return nil
-}
-
-func (s *SVCBMandatory) parse(b string) error {
- str := strings.Split(b, ",")
- codes := make([]SVCBKey, 0, len(str))
- for _, e := range str {
- codes = append(codes, svcbStringToKey(e))
- }
- s.Code = codes
- return nil
-}
-
-func (s *SVCBMandatory) len() int {
- return 2 * len(s.Code)
-}
-
-func (s *SVCBMandatory) copy() SVCBKeyValue {
- return &SVCBMandatory{
- append([]SVCBKey(nil), s.Code...),
- }
-}
-
-// SVCBAlpn pair is used to list supported connection protocols.
-// The user of this library must ensure that at least one protocol is listed when alpn is present.
-// Protocol IDs can be found at:
-// https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
-// Basic use pattern for creating an alpn option:
-//
-// h := new(dns.HTTPS)
-// h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET}
-// e := new(dns.SVCBAlpn)
-// e.Alpn = []string{"h2", "http/1.1"}
-// h.Value = append(h.Value, e)
-type SVCBAlpn struct {
- Alpn []string
-}
-
-func (*SVCBAlpn) Key() SVCBKey { return SVCB_ALPN }
-
-func (s *SVCBAlpn) String() string {
- // An ALPN value is a comma-separated list of values, each of which can be
- // an arbitrary binary value. In order to allow parsing, the comma and
- // backslash characters are themselves excaped.
- //
- // However, this escaping is done in addition to the normal escaping which
- // happens in zone files, meaning that these values must be
- // double-escaped. This looks terrible, so if you see a never-ending
- // sequence of backslash in a zone file this may be why.
- //
- // https://datatracker.ietf.org/doc/html/draft-ietf-dnsop-svcb-https-08#appendix-A.1
- var str strings.Builder
- for i, alpn := range s.Alpn {
- // 4*len(alpn) is the worst case where we escape every character in the alpn as \123, plus 1 byte for the ',' separating the alpn from others
- str.Grow(4*len(alpn) + 1)
- if i > 0 {
- str.WriteByte(',')
- }
- for j := 0; j < len(alpn); j++ {
- e := alpn[j]
- if ' ' > e || e > '~' {
- str.WriteString(escapeByte(e))
- continue
- }
- switch e {
- // We escape a few characters which may confuse humans or parsers.
- case '"', ';', ' ':
- str.WriteByte('\\')
- str.WriteByte(e)
- // The comma and backslash characters themselves must be
- // doubly-escaped. We use `\\` for the first backslash and
- // the escaped numeric value for the other value. We especially
- // don't want a comma in the output.
- case ',':
- str.WriteString(`\\\044`)
- case '\\':
- str.WriteString(`\\\092`)
- default:
- str.WriteByte(e)
- }
- }
- }
- return str.String()
-}
-
-func (s *SVCBAlpn) pack() ([]byte, error) {
- // Liberally estimate the size of an alpn as 10 octets
- b := make([]byte, 0, 10*len(s.Alpn))
- for _, e := range s.Alpn {
- if e == "" {
- return nil, errors.New("dns: svcbalpn: empty alpn-id")
- }
- if len(e) > 255 {
- return nil, errors.New("dns: svcbalpn: alpn-id too long")
- }
- b = append(b, byte(len(e)))
- b = append(b, e...)
- }
- return b, nil
-}
-
-func (s *SVCBAlpn) unpack(b []byte) error {
- // Estimate the size of the smallest alpn as 4 bytes
- alpn := make([]string, 0, len(b)/4)
- for i := 0; i < len(b); {
- length := int(b[i])
- i++
- if i+length > len(b) {
- return errors.New("dns: svcbalpn: alpn array overflowing")
- }
- alpn = append(alpn, string(b[i:i+length]))
- i += length
- }
- s.Alpn = alpn
- return nil
-}
-
-func (s *SVCBAlpn) parse(b string) error {
- if len(b) == 0 {
- s.Alpn = []string{}
- return nil
- }
-
- alpn := []string{}
- a := []byte{}
- for p := 0; p < len(b); {
- c, q := nextByte(b, p)
- if q == 0 {
- return errors.New("dns: svcbalpn: unterminated escape")
- }
- p += q
- // If we find a comma, we have finished reading an alpn.
- if c == ',' {
- if len(a) == 0 {
- return errors.New("dns: svcbalpn: empty protocol identifier")
- }
- alpn = append(alpn, string(a))
- a = []byte{}
- continue
- }
- // If it's a backslash, we need to handle a comma-separated list.
- if c == '\\' {
- dc, dq := nextByte(b, p)
- if dq == 0 {
- return errors.New("dns: svcbalpn: unterminated escape decoding comma-separated list")
- }
- if dc != '\\' && dc != ',' {
- return errors.New("dns: svcbalpn: bad escaped character decoding comma-separated list")
- }
- p += dq
- c = dc
- }
- a = append(a, c)
- }
- // Add the final alpn.
- if len(a) == 0 {
- return errors.New("dns: svcbalpn: last protocol identifier empty")
- }
- s.Alpn = append(alpn, string(a))
- return nil
-}
-
-func (s *SVCBAlpn) len() int {
- var l int
- for _, e := range s.Alpn {
- l += 1 + len(e)
- }
- return l
-}
-
-func (s *SVCBAlpn) copy() SVCBKeyValue {
- return &SVCBAlpn{
- append([]string(nil), s.Alpn...),
- }
-}
-
-// SVCBNoDefaultAlpn pair signifies no support for default connection protocols.
-// Should be used in conjunction with alpn.
-// Basic use pattern for creating a no-default-alpn option:
-//
-// s := &dns.SVCB{Hdr: dns.RR_Header{Name: ".", Rrtype: dns.TypeSVCB, Class: dns.ClassINET}}
-// t := new(dns.SVCBAlpn)
-// t.Alpn = []string{"xmpp-client"}
-// s.Value = append(s.Value, t)
-// e := new(dns.SVCBNoDefaultAlpn)
-// s.Value = append(s.Value, e)
-type SVCBNoDefaultAlpn struct{}
-
-func (*SVCBNoDefaultAlpn) Key() SVCBKey { return SVCB_NO_DEFAULT_ALPN }
-func (*SVCBNoDefaultAlpn) copy() SVCBKeyValue { return &SVCBNoDefaultAlpn{} }
-func (*SVCBNoDefaultAlpn) pack() ([]byte, error) { return []byte{}, nil }
-func (*SVCBNoDefaultAlpn) String() string { return "" }
-func (*SVCBNoDefaultAlpn) len() int { return 0 }
-
-func (*SVCBNoDefaultAlpn) unpack(b []byte) error {
- if len(b) != 0 {
- return errors.New("dns: svcbnodefaultalpn: no-default-alpn must have no value")
- }
- return nil
-}
-
-func (*SVCBNoDefaultAlpn) parse(b string) error {
- if b != "" {
- return errors.New("dns: svcbnodefaultalpn: no-default-alpn must have no value")
- }
- return nil
-}
-
-// SVCBPort pair defines the port for connection.
-// Basic use pattern for creating a port option:
-//
-// s := &dns.SVCB{Hdr: dns.RR_Header{Name: ".", Rrtype: dns.TypeSVCB, Class: dns.ClassINET}}
-// e := new(dns.SVCBPort)
-// e.Port = 80
-// s.Value = append(s.Value, e)
-type SVCBPort struct {
- Port uint16
-}
-
-func (*SVCBPort) Key() SVCBKey { return SVCB_PORT }
-func (*SVCBPort) len() int { return 2 }
-func (s *SVCBPort) String() string { return strconv.FormatUint(uint64(s.Port), 10) }
-func (s *SVCBPort) copy() SVCBKeyValue { return &SVCBPort{s.Port} }
-
-func (s *SVCBPort) unpack(b []byte) error {
- if len(b) != 2 {
- return errors.New("dns: svcbport: port length is not exactly 2 octets")
- }
- s.Port = binary.BigEndian.Uint16(b)
- return nil
-}
-
-func (s *SVCBPort) pack() ([]byte, error) {
- b := make([]byte, 2)
- binary.BigEndian.PutUint16(b, s.Port)
- return b, nil
-}
-
-func (s *SVCBPort) parse(b string) error {
- port, err := strconv.ParseUint(b, 10, 16)
- if err != nil {
- return errors.New("dns: svcbport: port out of range")
- }
- s.Port = uint16(port)
- return nil
-}
-
-// SVCBIPv4Hint pair suggests an IPv4 address which may be used to open connections
-// if A and AAAA record responses for SVCB's Target domain haven't been received.
-// In that case, optionally, A and AAAA requests can be made, after which the connection
-// to the hinted IP address may be terminated and a new connection may be opened.
-// Basic use pattern for creating an ipv4hint option:
-//
-// h := new(dns.HTTPS)
-// h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET}
-// e := new(dns.SVCBIPv4Hint)
-// e.Hint = []net.IP{net.IPv4(1,1,1,1).To4()}
-//
-// Or
-//
-// e.Hint = []net.IP{net.ParseIP("1.1.1.1").To4()}
-// h.Value = append(h.Value, e)
-type SVCBIPv4Hint struct {
- Hint []net.IP
-}
-
-func (*SVCBIPv4Hint) Key() SVCBKey { return SVCB_IPV4HINT }
-func (s *SVCBIPv4Hint) len() int { return 4 * len(s.Hint) }
-
-func (s *SVCBIPv4Hint) pack() ([]byte, error) {
- b := make([]byte, 0, 4*len(s.Hint))
- for _, e := range s.Hint {
- x := e.To4()
- if x == nil {
- return nil, errors.New("dns: svcbipv4hint: expected ipv4, hint is ipv6")
- }
- b = append(b, x...)
- }
- return b, nil
-}
-
-func (s *SVCBIPv4Hint) unpack(b []byte) error {
- if len(b) == 0 || len(b)%4 != 0 {
- return errors.New("dns: svcbipv4hint: ipv4 address byte array length is not a multiple of 4")
- }
- x := make([]net.IP, 0, len(b)/4)
- for i := 0; i < len(b); i += 4 {
- x = append(x, net.IP(b[i:i+4]))
- }
- s.Hint = x
- return nil
-}
-
-func (s *SVCBIPv4Hint) String() string {
- str := make([]string, len(s.Hint))
- for i, e := range s.Hint {
- x := e.To4()
- if x == nil {
- return "<nil>"
- }
- str[i] = x.String()
- }
- return strings.Join(str, ",")
-}
-
-func (s *SVCBIPv4Hint) parse(b string) error {
- if strings.Contains(b, ":") {
- return errors.New("dns: svcbipv4hint: expected ipv4, got ipv6")
- }
- str := strings.Split(b, ",")
- dst := make([]net.IP, len(str))
- for i, e := range str {
- ip := net.ParseIP(e).To4()
- if ip == nil {
- return errors.New("dns: svcbipv4hint: bad ip")
- }
- dst[i] = ip
- }
- s.Hint = dst
- return nil
-}
-
-func (s *SVCBIPv4Hint) copy() SVCBKeyValue {
- hint := make([]net.IP, len(s.Hint))
- for i, ip := range s.Hint {
- hint[i] = copyIP(ip)
- }
-
- return &SVCBIPv4Hint{
- Hint: hint,
- }
-}
-
-// SVCBECHConfig pair contains the ECHConfig structure defined in draft-ietf-tls-esni [RFC xxxx].
-// Basic use pattern for creating an ech option:
-//
-// h := new(dns.HTTPS)
-// h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET}
-// e := new(dns.SVCBECHConfig)
-// e.ECH = []byte{0xfe, 0x08, ...}
-// h.Value = append(h.Value, e)
-type SVCBECHConfig struct {
- ECH []byte // Specifically ECHConfigList including the redundant length prefix
-}
-
-func (*SVCBECHConfig) Key() SVCBKey { return SVCB_ECHCONFIG }
-func (s *SVCBECHConfig) String() string { return toBase64(s.ECH) }
-func (s *SVCBECHConfig) len() int { return len(s.ECH) }
-
-func (s *SVCBECHConfig) pack() ([]byte, error) {
- return append([]byte(nil), s.ECH...), nil
-}
-
-func (s *SVCBECHConfig) copy() SVCBKeyValue {
- return &SVCBECHConfig{
- append([]byte(nil), s.ECH...),
- }
-}
-
-func (s *SVCBECHConfig) unpack(b []byte) error {
- s.ECH = append([]byte(nil), b...)
- return nil
-}
-func (s *SVCBECHConfig) parse(b string) error {
- x, err := fromBase64([]byte(b))
- if err != nil {
- return errors.New("dns: svcbech: bad base64 ech")
- }
- s.ECH = x
- return nil
-}
-
-// SVCBIPv6Hint pair suggests an IPv6 address which may be used to open connections
-// if A and AAAA record responses for SVCB's Target domain haven't been received.
-// In that case, optionally, A and AAAA requests can be made, after which the
-// connection to the hinted IP address may be terminated and a new connection may be opened.
-// Basic use pattern for creating an ipv6hint option:
-//
-// h := new(dns.HTTPS)
-// h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET}
-// e := new(dns.SVCBIPv6Hint)
-// e.Hint = []net.IP{net.ParseIP("2001:db8::1")}
-// h.Value = append(h.Value, e)
-type SVCBIPv6Hint struct {
- Hint []net.IP
-}
-
-func (*SVCBIPv6Hint) Key() SVCBKey { return SVCB_IPV6HINT }
-func (s *SVCBIPv6Hint) len() int { return 16 * len(s.Hint) }
-
-func (s *SVCBIPv6Hint) pack() ([]byte, error) {
- b := make([]byte, 0, 16*len(s.Hint))
- for _, e := range s.Hint {
- if len(e) != net.IPv6len || e.To4() != nil {
- return nil, errors.New("dns: svcbipv6hint: expected ipv6, hint is ipv4")
- }
- b = append(b, e...)
- }
- return b, nil
-}
-
-func (s *SVCBIPv6Hint) unpack(b []byte) error {
- if len(b) == 0 || len(b)%16 != 0 {
- return errors.New("dns: svcbipv6hint: ipv6 address byte array length not a multiple of 16")
- }
- x := make([]net.IP, 0, len(b)/16)
- for i := 0; i < len(b); i += 16 {
- ip := net.IP(b[i : i+16])
- if ip.To4() != nil {
- return errors.New("dns: svcbipv6hint: expected ipv6, got ipv4")
- }
- x = append(x, ip)
- }
- s.Hint = x
- return nil
-}
-
-func (s *SVCBIPv6Hint) String() string {
- str := make([]string, len(s.Hint))
- for i, e := range s.Hint {
- if x := e.To4(); x != nil {
- return "<nil>"
- }
- str[i] = e.String()
- }
- return strings.Join(str, ",")
-}
-
-func (s *SVCBIPv6Hint) parse(b string) error {
- str := strings.Split(b, ",")
- dst := make([]net.IP, len(str))
- for i, e := range str {
- ip := net.ParseIP(e)
- if ip == nil {
- return errors.New("dns: svcbipv6hint: bad ip")
- }
- if ip.To4() != nil {
- return errors.New("dns: svcbipv6hint: expected ipv6, got ipv4-mapped-ipv6")
- }
- dst[i] = ip
- }
- s.Hint = dst
- return nil
-}
-
-func (s *SVCBIPv6Hint) copy() SVCBKeyValue {
- hint := make([]net.IP, len(s.Hint))
- for i, ip := range s.Hint {
- hint[i] = copyIP(ip)
- }
-
- return &SVCBIPv6Hint{
- Hint: hint,
- }
-}
-
-// 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 svcbParamToStr([]byte(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 {
- template, err := svcbParseParam(b)
- if err != nil {
- return fmt.Errorf("dns: svcbdohpath: %w", err)
- }
- s.Template = string(template)
- 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:
-//
-// h := new(dns.HTTPS)
-// h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET}
-// e := new(dns.SVCBLocal)
-// e.KeyCode = 65400
-// e.Data = []byte("abc")
-// h.Value = append(h.Value, e)
-type SVCBLocal struct {
- KeyCode SVCBKey // Never 65535 or any assigned keys.
- Data []byte // All byte sequences are allowed.
-}
-
-func (s *SVCBLocal) Key() SVCBKey { return s.KeyCode }
-func (s *SVCBLocal) String() string { return svcbParamToStr(s.Data) }
-func (s *SVCBLocal) pack() ([]byte, error) { return append([]byte(nil), s.Data...), nil }
-func (s *SVCBLocal) len() int { return len(s.Data) }
-
-func (s *SVCBLocal) unpack(b []byte) error {
- s.Data = append([]byte(nil), b...)
- return nil
-}
-
-func (s *SVCBLocal) parse(b string) error {
- data, err := svcbParseParam(b)
- if err != nil {
- return fmt.Errorf("dns: svcblocal: svcb private/experimental key %w", err)
- }
- s.Data = data
- return nil
-}
-
-func (s *SVCBLocal) copy() SVCBKeyValue {
- return &SVCBLocal{s.KeyCode,
- append([]byte(nil), s.Data...),
- }
-}
-
-func (rr *SVCB) String() string {
- s := rr.Hdr.String() +
- strconv.Itoa(int(rr.Priority)) + " " +
- sprintName(rr.Target)
- for _, e := range rr.Value {
- s += " " + e.Key().String() + "=\"" + e.String() + "\""
- }
- return s
-}
-
-// areSVCBPairArraysEqual checks if SVCBKeyValue arrays are equal after sorting their
-// copies. arrA and arrB have equal lengths, otherwise zduplicate.go wouldn't call this function.
-func areSVCBPairArraysEqual(a []SVCBKeyValue, b []SVCBKeyValue) bool {
- a = append([]SVCBKeyValue(nil), a...)
- b = append([]SVCBKeyValue(nil), b...)
- sort.Slice(a, func(i, j int) bool { return a[i].Key() < a[j].Key() })
- sort.Slice(b, func(i, j int) bool { return b[i].Key() < b[j].Key() })
- for i, e := range a {
- if e.Key() != b[i].Key() {
- return false
- }
- b1, err1 := e.pack()
- b2, err2 := b[i].pack()
- if err1 != nil || err2 != nil || !bytes.Equal(b1, b2) {
- return false
- }
- }
- return true
-}
-
-// svcbParamStr converts the value of an SVCB parameter into a DNS presentation-format string.
-func svcbParamToStr(s []byte) string {
- var str strings.Builder
- str.Grow(4 * len(s))
- for _, e := range s {
- if ' ' <= e && e <= '~' {
- switch e {
- case '"', ';', ' ', '\\':
- str.WriteByte('\\')
- str.WriteByte(e)
- default:
- str.WriteByte(e)
- }
- } else {
- str.WriteString(escapeByte(e))
- }
- }
- return str.String()
-}
-
-// svcbParseParam parses a DNS presentation-format string into an SVCB parameter value.
-func svcbParseParam(b string) ([]byte, error) {
- data := make([]byte, 0, len(b))
- for i := 0; i < len(b); {
- if b[i] != '\\' {
- data = append(data, b[i])
- i++
- continue
- }
- if i+1 == len(b) {
- return nil, errors.New("escape unterminated")
- }
- if isDigit(b[i+1]) {
- if i+3 < len(b) && isDigit(b[i+2]) && isDigit(b[i+3]) {
- a, err := strconv.ParseUint(b[i+1:i+4], 10, 8)
- if err == nil {
- i += 4
- data = append(data, byte(a))
- continue
- }
- }
- return nil, errors.New("bad escaped octet")
- } else {
- data = append(data, b[i+1])
- i += 2
- }
- }
- return data, nil
-}