gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

svcb.go (26358B)


      1 package dns
      2 
      3 import (
      4 	"bytes"
      5 	"encoding/binary"
      6 	"errors"
      7 	"fmt"
      8 	"net"
      9 	"sort"
     10 	"strconv"
     11 	"strings"
     12 )
     13 
     14 // SVCBKey is the type of the keys used in the SVCB RR.
     15 type SVCBKey uint16
     16 
     17 // Keys defined in draft-ietf-dnsop-svcb-https-08 Section 14.3.2.
     18 const (
     19 	SVCB_MANDATORY SVCBKey = iota
     20 	SVCB_ALPN
     21 	SVCB_NO_DEFAULT_ALPN
     22 	SVCB_PORT
     23 	SVCB_IPV4HINT
     24 	SVCB_ECHCONFIG
     25 	SVCB_IPV6HINT
     26 	SVCB_DOHPATH // draft-ietf-add-svcb-dns-02 Section 9
     27 
     28 	svcb_RESERVED SVCBKey = 65535
     29 )
     30 
     31 var svcbKeyToStringMap = map[SVCBKey]string{
     32 	SVCB_MANDATORY:       "mandatory",
     33 	SVCB_ALPN:            "alpn",
     34 	SVCB_NO_DEFAULT_ALPN: "no-default-alpn",
     35 	SVCB_PORT:            "port",
     36 	SVCB_IPV4HINT:        "ipv4hint",
     37 	SVCB_ECHCONFIG:       "ech",
     38 	SVCB_IPV6HINT:        "ipv6hint",
     39 	SVCB_DOHPATH:         "dohpath",
     40 }
     41 
     42 var svcbStringToKeyMap = reverseSVCBKeyMap(svcbKeyToStringMap)
     43 
     44 func reverseSVCBKeyMap(m map[SVCBKey]string) map[string]SVCBKey {
     45 	n := make(map[string]SVCBKey, len(m))
     46 	for u, s := range m {
     47 		n[s] = u
     48 	}
     49 	return n
     50 }
     51 
     52 // String takes the numerical code of an SVCB key and returns its name.
     53 // Returns an empty string for reserved keys.
     54 // Accepts unassigned keys as well as experimental/private keys.
     55 func (key SVCBKey) String() string {
     56 	if x := svcbKeyToStringMap[key]; x != "" {
     57 		return x
     58 	}
     59 	if key == svcb_RESERVED {
     60 		return ""
     61 	}
     62 	return "key" + strconv.FormatUint(uint64(key), 10)
     63 }
     64 
     65 // svcbStringToKey returns the numerical code of an SVCB key.
     66 // Returns svcb_RESERVED for reserved/invalid keys.
     67 // Accepts unassigned keys as well as experimental/private keys.
     68 func svcbStringToKey(s string) SVCBKey {
     69 	if strings.HasPrefix(s, "key") {
     70 		a, err := strconv.ParseUint(s[3:], 10, 16)
     71 		// no leading zeros
     72 		// key shouldn't be registered
     73 		if err != nil || a == 65535 || s[3] == '0' || svcbKeyToStringMap[SVCBKey(a)] != "" {
     74 			return svcb_RESERVED
     75 		}
     76 		return SVCBKey(a)
     77 	}
     78 	if key, ok := svcbStringToKeyMap[s]; ok {
     79 		return key
     80 	}
     81 	return svcb_RESERVED
     82 }
     83 
     84 func (rr *SVCB) parse(c *zlexer, o string) *ParseError {
     85 	l, _ := c.Next()
     86 	i, e := strconv.ParseUint(l.token, 10, 16)
     87 	if e != nil || l.err {
     88 		return &ParseError{l.token, "bad SVCB priority", l}
     89 	}
     90 	rr.Priority = uint16(i)
     91 
     92 	c.Next()        // zBlank
     93 	l, _ = c.Next() // zString
     94 	rr.Target = l.token
     95 
     96 	name, nameOk := toAbsoluteName(l.token, o)
     97 	if l.err || !nameOk {
     98 		return &ParseError{l.token, "bad SVCB Target", l}
     99 	}
    100 	rr.Target = name
    101 
    102 	// Values (if any)
    103 	l, _ = c.Next()
    104 	var xs []SVCBKeyValue
    105 	// Helps require whitespace between pairs.
    106 	// Prevents key1000="a"key1001=...
    107 	canHaveNextKey := true
    108 	for l.value != zNewline && l.value != zEOF {
    109 		switch l.value {
    110 		case zString:
    111 			if !canHaveNextKey {
    112 				// The key we can now read was probably meant to be
    113 				// a part of the last value.
    114 				return &ParseError{l.token, "bad SVCB value quotation", l}
    115 			}
    116 
    117 			// In key=value pairs, value does not have to be quoted unless value
    118 			// contains whitespace. And keys don't need to have values.
    119 			// Similarly, keys with an equality signs after them don't need values.
    120 			// l.token includes at least up to the first equality sign.
    121 			idx := strings.IndexByte(l.token, '=')
    122 			var key, value string
    123 			if idx < 0 {
    124 				// Key with no value and no equality sign
    125 				key = l.token
    126 			} else if idx == 0 {
    127 				return &ParseError{l.token, "bad SVCB key", l}
    128 			} else {
    129 				key, value = l.token[:idx], l.token[idx+1:]
    130 
    131 				if value == "" {
    132 					// We have a key and an equality sign. Maybe we have nothing
    133 					// after "=" or we have a double quote.
    134 					l, _ = c.Next()
    135 					if l.value == zQuote {
    136 						// Only needed when value ends with double quotes.
    137 						// Any value starting with zQuote ends with it.
    138 						canHaveNextKey = false
    139 
    140 						l, _ = c.Next()
    141 						switch l.value {
    142 						case zString:
    143 							// We have a value in double quotes.
    144 							value = l.token
    145 							l, _ = c.Next()
    146 							if l.value != zQuote {
    147 								return &ParseError{l.token, "SVCB unterminated value", l}
    148 							}
    149 						case zQuote:
    150 							// There's nothing in double quotes.
    151 						default:
    152 							return &ParseError{l.token, "bad SVCB value", l}
    153 						}
    154 					}
    155 				}
    156 			}
    157 			kv := makeSVCBKeyValue(svcbStringToKey(key))
    158 			if kv == nil {
    159 				return &ParseError{l.token, "bad SVCB key", l}
    160 			}
    161 			if err := kv.parse(value); err != nil {
    162 				return &ParseError{l.token, err.Error(), l}
    163 			}
    164 			xs = append(xs, kv)
    165 		case zQuote:
    166 			return &ParseError{l.token, "SVCB key can't contain double quotes", l}
    167 		case zBlank:
    168 			canHaveNextKey = true
    169 		default:
    170 			return &ParseError{l.token, "bad SVCB values", l}
    171 		}
    172 		l, _ = c.Next()
    173 	}
    174 
    175 	// "In AliasMode, records SHOULD NOT include any SvcParams, and recipients MUST
    176 	// ignore any SvcParams that are present."
    177 	// However, we don't check rr.Priority == 0 && len(xs) > 0 here
    178 	// It is the responsibility of the user of the library to check this.
    179 	// This is to encourage the fixing of the source of this error.
    180 
    181 	rr.Value = xs
    182 	return nil
    183 }
    184 
    185 // makeSVCBKeyValue returns an SVCBKeyValue struct with the key or nil for reserved keys.
    186 func makeSVCBKeyValue(key SVCBKey) SVCBKeyValue {
    187 	switch key {
    188 	case SVCB_MANDATORY:
    189 		return new(SVCBMandatory)
    190 	case SVCB_ALPN:
    191 		return new(SVCBAlpn)
    192 	case SVCB_NO_DEFAULT_ALPN:
    193 		return new(SVCBNoDefaultAlpn)
    194 	case SVCB_PORT:
    195 		return new(SVCBPort)
    196 	case SVCB_IPV4HINT:
    197 		return new(SVCBIPv4Hint)
    198 	case SVCB_ECHCONFIG:
    199 		return new(SVCBECHConfig)
    200 	case SVCB_IPV6HINT:
    201 		return new(SVCBIPv6Hint)
    202 	case SVCB_DOHPATH:
    203 		return new(SVCBDoHPath)
    204 	case svcb_RESERVED:
    205 		return nil
    206 	default:
    207 		e := new(SVCBLocal)
    208 		e.KeyCode = key
    209 		return e
    210 	}
    211 }
    212 
    213 // SVCB RR. See RFC xxxx (https://tools.ietf.org/html/draft-ietf-dnsop-svcb-https-08).
    214 //
    215 // NOTE: The HTTPS/SVCB RFCs are in the draft stage.
    216 // The API, including constants and types related to SVCBKeyValues, may
    217 // change in future versions in accordance with the latest drafts.
    218 type SVCB struct {
    219 	Hdr      RR_Header
    220 	Priority uint16         // If zero, Value must be empty or discarded by the user of this library
    221 	Target   string         `dns:"domain-name"`
    222 	Value    []SVCBKeyValue `dns:"pairs"`
    223 }
    224 
    225 // HTTPS RR. Everything valid for SVCB applies to HTTPS as well.
    226 // Except that the HTTPS record is intended for use with the HTTP and HTTPS protocols.
    227 //
    228 // NOTE: The HTTPS/SVCB RFCs are in the draft stage.
    229 // The API, including constants and types related to SVCBKeyValues, may
    230 // change in future versions in accordance with the latest drafts.
    231 type HTTPS struct {
    232 	SVCB
    233 }
    234 
    235 func (rr *HTTPS) String() string {
    236 	return rr.SVCB.String()
    237 }
    238 
    239 func (rr *HTTPS) parse(c *zlexer, o string) *ParseError {
    240 	return rr.SVCB.parse(c, o)
    241 }
    242 
    243 // SVCBKeyValue defines a key=value pair for the SVCB RR type.
    244 // An SVCB RR can have multiple SVCBKeyValues appended to it.
    245 type SVCBKeyValue interface {
    246 	Key() SVCBKey          // Key returns the numerical key code.
    247 	pack() ([]byte, error) // pack returns the encoded value.
    248 	unpack([]byte) error   // unpack sets the value.
    249 	String() string        // String returns the string representation of the value.
    250 	parse(string) error    // parse sets the value to the given string representation of the value.
    251 	copy() SVCBKeyValue    // copy returns a deep-copy of the pair.
    252 	len() int              // len returns the length of value in the wire format.
    253 }
    254 
    255 // SVCBMandatory pair adds to required keys that must be interpreted for the RR
    256 // to be functional. If ignored, the whole RRSet must be ignored.
    257 // "port" and "no-default-alpn" are mandatory by default if present,
    258 // so they shouldn't be included here.
    259 //
    260 // It is incumbent upon the user of this library to reject the RRSet if
    261 // or avoid constructing such an RRSet that:
    262 // - "mandatory" is included as one of the keys of mandatory
    263 // - no key is listed multiple times in mandatory
    264 // - all keys listed in mandatory are present
    265 // - escape sequences are not used in mandatory
    266 // - mandatory, when present, lists at least one key
    267 //
    268 // Basic use pattern for creating a mandatory option:
    269 //
    270 //	s := &dns.SVCB{Hdr: dns.RR_Header{Name: ".", Rrtype: dns.TypeSVCB, Class: dns.ClassINET}}
    271 //	e := new(dns.SVCBMandatory)
    272 //	e.Code = []uint16{dns.SVCB_ALPN}
    273 //	s.Value = append(s.Value, e)
    274 //	t := new(dns.SVCBAlpn)
    275 //	t.Alpn = []string{"xmpp-client"}
    276 //	s.Value = append(s.Value, t)
    277 type SVCBMandatory struct {
    278 	Code []SVCBKey
    279 }
    280 
    281 func (*SVCBMandatory) Key() SVCBKey { return SVCB_MANDATORY }
    282 
    283 func (s *SVCBMandatory) String() string {
    284 	str := make([]string, len(s.Code))
    285 	for i, e := range s.Code {
    286 		str[i] = e.String()
    287 	}
    288 	return strings.Join(str, ",")
    289 }
    290 
    291 func (s *SVCBMandatory) pack() ([]byte, error) {
    292 	codes := cloneSlice(s.Code)
    293 	sort.Slice(codes, func(i, j int) bool {
    294 		return codes[i] < codes[j]
    295 	})
    296 	b := make([]byte, 2*len(codes))
    297 	for i, e := range codes {
    298 		binary.BigEndian.PutUint16(b[2*i:], uint16(e))
    299 	}
    300 	return b, nil
    301 }
    302 
    303 func (s *SVCBMandatory) unpack(b []byte) error {
    304 	if len(b)%2 != 0 {
    305 		return errors.New("dns: svcbmandatory: value length is not a multiple of 2")
    306 	}
    307 	codes := make([]SVCBKey, 0, len(b)/2)
    308 	for i := 0; i < len(b); i += 2 {
    309 		// We assume strictly increasing order.
    310 		codes = append(codes, SVCBKey(binary.BigEndian.Uint16(b[i:])))
    311 	}
    312 	s.Code = codes
    313 	return nil
    314 }
    315 
    316 func (s *SVCBMandatory) parse(b string) error {
    317 	str := strings.Split(b, ",")
    318 	codes := make([]SVCBKey, 0, len(str))
    319 	for _, e := range str {
    320 		codes = append(codes, svcbStringToKey(e))
    321 	}
    322 	s.Code = codes
    323 	return nil
    324 }
    325 
    326 func (s *SVCBMandatory) len() int {
    327 	return 2 * len(s.Code)
    328 }
    329 
    330 func (s *SVCBMandatory) copy() SVCBKeyValue {
    331 	return &SVCBMandatory{cloneSlice(s.Code)}
    332 }
    333 
    334 // SVCBAlpn pair is used to list supported connection protocols.
    335 // The user of this library must ensure that at least one protocol is listed when alpn is present.
    336 // Protocol IDs can be found at:
    337 // https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
    338 // Basic use pattern for creating an alpn option:
    339 //
    340 //	h := new(dns.HTTPS)
    341 //	h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET}
    342 //	e := new(dns.SVCBAlpn)
    343 //	e.Alpn = []string{"h2", "http/1.1"}
    344 //	h.Value = append(h.Value, e)
    345 type SVCBAlpn struct {
    346 	Alpn []string
    347 }
    348 
    349 func (*SVCBAlpn) Key() SVCBKey { return SVCB_ALPN }
    350 
    351 func (s *SVCBAlpn) String() string {
    352 	// An ALPN value is a comma-separated list of values, each of which can be
    353 	// an arbitrary binary value. In order to allow parsing, the comma and
    354 	// backslash characters are themselves escaped.
    355 	//
    356 	// However, this escaping is done in addition to the normal escaping which
    357 	// happens in zone files, meaning that these values must be
    358 	// double-escaped. This looks terrible, so if you see a never-ending
    359 	// sequence of backslash in a zone file this may be why.
    360 	//
    361 	// https://datatracker.ietf.org/doc/html/draft-ietf-dnsop-svcb-https-08#appendix-A.1
    362 	var str strings.Builder
    363 	for i, alpn := range s.Alpn {
    364 		// 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
    365 		str.Grow(4*len(alpn) + 1)
    366 		if i > 0 {
    367 			str.WriteByte(',')
    368 		}
    369 		for j := 0; j < len(alpn); j++ {
    370 			e := alpn[j]
    371 			if ' ' > e || e > '~' {
    372 				str.WriteString(escapeByte(e))
    373 				continue
    374 			}
    375 			switch e {
    376 			// We escape a few characters which may confuse humans or parsers.
    377 			case '"', ';', ' ':
    378 				str.WriteByte('\\')
    379 				str.WriteByte(e)
    380 			// The comma and backslash characters themselves must be
    381 			// doubly-escaped. We use `\\` for the first backslash and
    382 			// the escaped numeric value for the other value. We especially
    383 			// don't want a comma in the output.
    384 			case ',':
    385 				str.WriteString(`\\\044`)
    386 			case '\\':
    387 				str.WriteString(`\\\092`)
    388 			default:
    389 				str.WriteByte(e)
    390 			}
    391 		}
    392 	}
    393 	return str.String()
    394 }
    395 
    396 func (s *SVCBAlpn) pack() ([]byte, error) {
    397 	// Liberally estimate the size of an alpn as 10 octets
    398 	b := make([]byte, 0, 10*len(s.Alpn))
    399 	for _, e := range s.Alpn {
    400 		if e == "" {
    401 			return nil, errors.New("dns: svcbalpn: empty alpn-id")
    402 		}
    403 		if len(e) > 255 {
    404 			return nil, errors.New("dns: svcbalpn: alpn-id too long")
    405 		}
    406 		b = append(b, byte(len(e)))
    407 		b = append(b, e...)
    408 	}
    409 	return b, nil
    410 }
    411 
    412 func (s *SVCBAlpn) unpack(b []byte) error {
    413 	// Estimate the size of the smallest alpn as 4 bytes
    414 	alpn := make([]string, 0, len(b)/4)
    415 	for i := 0; i < len(b); {
    416 		length := int(b[i])
    417 		i++
    418 		if i+length > len(b) {
    419 			return errors.New("dns: svcbalpn: alpn array overflowing")
    420 		}
    421 		alpn = append(alpn, string(b[i:i+length]))
    422 		i += length
    423 	}
    424 	s.Alpn = alpn
    425 	return nil
    426 }
    427 
    428 func (s *SVCBAlpn) parse(b string) error {
    429 	if len(b) == 0 {
    430 		s.Alpn = []string{}
    431 		return nil
    432 	}
    433 
    434 	alpn := []string{}
    435 	a := []byte{}
    436 	for p := 0; p < len(b); {
    437 		c, q := nextByte(b, p)
    438 		if q == 0 {
    439 			return errors.New("dns: svcbalpn: unterminated escape")
    440 		}
    441 		p += q
    442 		// If we find a comma, we have finished reading an alpn.
    443 		if c == ',' {
    444 			if len(a) == 0 {
    445 				return errors.New("dns: svcbalpn: empty protocol identifier")
    446 			}
    447 			alpn = append(alpn, string(a))
    448 			a = []byte{}
    449 			continue
    450 		}
    451 		// If it's a backslash, we need to handle a comma-separated list.
    452 		if c == '\\' {
    453 			dc, dq := nextByte(b, p)
    454 			if dq == 0 {
    455 				return errors.New("dns: svcbalpn: unterminated escape decoding comma-separated list")
    456 			}
    457 			if dc != '\\' && dc != ',' {
    458 				return errors.New("dns: svcbalpn: bad escaped character decoding comma-separated list")
    459 			}
    460 			p += dq
    461 			c = dc
    462 		}
    463 		a = append(a, c)
    464 	}
    465 	// Add the final alpn.
    466 	if len(a) == 0 {
    467 		return errors.New("dns: svcbalpn: last protocol identifier empty")
    468 	}
    469 	s.Alpn = append(alpn, string(a))
    470 	return nil
    471 }
    472 
    473 func (s *SVCBAlpn) len() int {
    474 	var l int
    475 	for _, e := range s.Alpn {
    476 		l += 1 + len(e)
    477 	}
    478 	return l
    479 }
    480 
    481 func (s *SVCBAlpn) copy() SVCBKeyValue {
    482 	return &SVCBAlpn{cloneSlice(s.Alpn)}
    483 }
    484 
    485 // SVCBNoDefaultAlpn pair signifies no support for default connection protocols.
    486 // Should be used in conjunction with alpn.
    487 // Basic use pattern for creating a no-default-alpn option:
    488 //
    489 //	s := &dns.SVCB{Hdr: dns.RR_Header{Name: ".", Rrtype: dns.TypeSVCB, Class: dns.ClassINET}}
    490 //	t := new(dns.SVCBAlpn)
    491 //	t.Alpn = []string{"xmpp-client"}
    492 //	s.Value = append(s.Value, t)
    493 //	e := new(dns.SVCBNoDefaultAlpn)
    494 //	s.Value = append(s.Value, e)
    495 type SVCBNoDefaultAlpn struct{}
    496 
    497 func (*SVCBNoDefaultAlpn) Key() SVCBKey          { return SVCB_NO_DEFAULT_ALPN }
    498 func (*SVCBNoDefaultAlpn) copy() SVCBKeyValue    { return &SVCBNoDefaultAlpn{} }
    499 func (*SVCBNoDefaultAlpn) pack() ([]byte, error) { return []byte{}, nil }
    500 func (*SVCBNoDefaultAlpn) String() string        { return "" }
    501 func (*SVCBNoDefaultAlpn) len() int              { return 0 }
    502 
    503 func (*SVCBNoDefaultAlpn) unpack(b []byte) error {
    504 	if len(b) != 0 {
    505 		return errors.New("dns: svcbnodefaultalpn: no-default-alpn must have no value")
    506 	}
    507 	return nil
    508 }
    509 
    510 func (*SVCBNoDefaultAlpn) parse(b string) error {
    511 	if b != "" {
    512 		return errors.New("dns: svcbnodefaultalpn: no-default-alpn must have no value")
    513 	}
    514 	return nil
    515 }
    516 
    517 // SVCBPort pair defines the port for connection.
    518 // Basic use pattern for creating a port option:
    519 //
    520 //	s := &dns.SVCB{Hdr: dns.RR_Header{Name: ".", Rrtype: dns.TypeSVCB, Class: dns.ClassINET}}
    521 //	e := new(dns.SVCBPort)
    522 //	e.Port = 80
    523 //	s.Value = append(s.Value, e)
    524 type SVCBPort struct {
    525 	Port uint16
    526 }
    527 
    528 func (*SVCBPort) Key() SVCBKey         { return SVCB_PORT }
    529 func (*SVCBPort) len() int             { return 2 }
    530 func (s *SVCBPort) String() string     { return strconv.FormatUint(uint64(s.Port), 10) }
    531 func (s *SVCBPort) copy() SVCBKeyValue { return &SVCBPort{s.Port} }
    532 
    533 func (s *SVCBPort) unpack(b []byte) error {
    534 	if len(b) != 2 {
    535 		return errors.New("dns: svcbport: port length is not exactly 2 octets")
    536 	}
    537 	s.Port = binary.BigEndian.Uint16(b)
    538 	return nil
    539 }
    540 
    541 func (s *SVCBPort) pack() ([]byte, error) {
    542 	b := make([]byte, 2)
    543 	binary.BigEndian.PutUint16(b, s.Port)
    544 	return b, nil
    545 }
    546 
    547 func (s *SVCBPort) parse(b string) error {
    548 	port, err := strconv.ParseUint(b, 10, 16)
    549 	if err != nil {
    550 		return errors.New("dns: svcbport: port out of range")
    551 	}
    552 	s.Port = uint16(port)
    553 	return nil
    554 }
    555 
    556 // SVCBIPv4Hint pair suggests an IPv4 address which may be used to open connections
    557 // if A and AAAA record responses for SVCB's Target domain haven't been received.
    558 // In that case, optionally, A and AAAA requests can be made, after which the connection
    559 // to the hinted IP address may be terminated and a new connection may be opened.
    560 // Basic use pattern for creating an ipv4hint option:
    561 //
    562 //		h := new(dns.HTTPS)
    563 //		h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET}
    564 //		e := new(dns.SVCBIPv4Hint)
    565 //		e.Hint = []net.IP{net.IPv4(1,1,1,1).To4()}
    566 //
    567 //	 Or
    568 //
    569 //		e.Hint = []net.IP{net.ParseIP("1.1.1.1").To4()}
    570 //		h.Value = append(h.Value, e)
    571 type SVCBIPv4Hint struct {
    572 	Hint []net.IP
    573 }
    574 
    575 func (*SVCBIPv4Hint) Key() SVCBKey { return SVCB_IPV4HINT }
    576 func (s *SVCBIPv4Hint) len() int   { return 4 * len(s.Hint) }
    577 
    578 func (s *SVCBIPv4Hint) pack() ([]byte, error) {
    579 	b := make([]byte, 0, 4*len(s.Hint))
    580 	for _, e := range s.Hint {
    581 		x := e.To4()
    582 		if x == nil {
    583 			return nil, errors.New("dns: svcbipv4hint: expected ipv4, hint is ipv6")
    584 		}
    585 		b = append(b, x...)
    586 	}
    587 	return b, nil
    588 }
    589 
    590 func (s *SVCBIPv4Hint) unpack(b []byte) error {
    591 	if len(b) == 0 || len(b)%4 != 0 {
    592 		return errors.New("dns: svcbipv4hint: ipv4 address byte array length is not a multiple of 4")
    593 	}
    594 	b = cloneSlice(b)
    595 	x := make([]net.IP, 0, len(b)/4)
    596 	for i := 0; i < len(b); i += 4 {
    597 		x = append(x, net.IP(b[i:i+4]))
    598 	}
    599 	s.Hint = x
    600 	return nil
    601 }
    602 
    603 func (s *SVCBIPv4Hint) String() string {
    604 	str := make([]string, len(s.Hint))
    605 	for i, e := range s.Hint {
    606 		x := e.To4()
    607 		if x == nil {
    608 			return "<nil>"
    609 		}
    610 		str[i] = x.String()
    611 	}
    612 	return strings.Join(str, ",")
    613 }
    614 
    615 func (s *SVCBIPv4Hint) parse(b string) error {
    616 	if strings.Contains(b, ":") {
    617 		return errors.New("dns: svcbipv4hint: expected ipv4, got ipv6")
    618 	}
    619 	str := strings.Split(b, ",")
    620 	dst := make([]net.IP, len(str))
    621 	for i, e := range str {
    622 		ip := net.ParseIP(e).To4()
    623 		if ip == nil {
    624 			return errors.New("dns: svcbipv4hint: bad ip")
    625 		}
    626 		dst[i] = ip
    627 	}
    628 	s.Hint = dst
    629 	return nil
    630 }
    631 
    632 func (s *SVCBIPv4Hint) copy() SVCBKeyValue {
    633 	hint := make([]net.IP, len(s.Hint))
    634 	for i, ip := range s.Hint {
    635 		hint[i] = cloneSlice(ip)
    636 	}
    637 	return &SVCBIPv4Hint{Hint: hint}
    638 }
    639 
    640 // SVCBECHConfig pair contains the ECHConfig structure defined in draft-ietf-tls-esni [RFC xxxx].
    641 // Basic use pattern for creating an ech option:
    642 //
    643 //	h := new(dns.HTTPS)
    644 //	h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET}
    645 //	e := new(dns.SVCBECHConfig)
    646 //	e.ECH = []byte{0xfe, 0x08, ...}
    647 //	h.Value = append(h.Value, e)
    648 type SVCBECHConfig struct {
    649 	ECH []byte // Specifically ECHConfigList including the redundant length prefix
    650 }
    651 
    652 func (*SVCBECHConfig) Key() SVCBKey     { return SVCB_ECHCONFIG }
    653 func (s *SVCBECHConfig) String() string { return toBase64(s.ECH) }
    654 func (s *SVCBECHConfig) len() int       { return len(s.ECH) }
    655 
    656 func (s *SVCBECHConfig) pack() ([]byte, error) {
    657 	return cloneSlice(s.ECH), nil
    658 }
    659 
    660 func (s *SVCBECHConfig) copy() SVCBKeyValue {
    661 	return &SVCBECHConfig{cloneSlice(s.ECH)}
    662 }
    663 
    664 func (s *SVCBECHConfig) unpack(b []byte) error {
    665 	s.ECH = cloneSlice(b)
    666 	return nil
    667 }
    668 
    669 func (s *SVCBECHConfig) parse(b string) error {
    670 	x, err := fromBase64([]byte(b))
    671 	if err != nil {
    672 		return errors.New("dns: svcbech: bad base64 ech")
    673 	}
    674 	s.ECH = x
    675 	return nil
    676 }
    677 
    678 // SVCBIPv6Hint pair suggests an IPv6 address which may be used to open connections
    679 // if A and AAAA record responses for SVCB's Target domain haven't been received.
    680 // In that case, optionally, A and AAAA requests can be made, after which the
    681 // connection to the hinted IP address may be terminated and a new connection may be opened.
    682 // Basic use pattern for creating an ipv6hint option:
    683 //
    684 //	h := new(dns.HTTPS)
    685 //	h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET}
    686 //	e := new(dns.SVCBIPv6Hint)
    687 //	e.Hint = []net.IP{net.ParseIP("2001:db8::1")}
    688 //	h.Value = append(h.Value, e)
    689 type SVCBIPv6Hint struct {
    690 	Hint []net.IP
    691 }
    692 
    693 func (*SVCBIPv6Hint) Key() SVCBKey { return SVCB_IPV6HINT }
    694 func (s *SVCBIPv6Hint) len() int   { return 16 * len(s.Hint) }
    695 
    696 func (s *SVCBIPv6Hint) pack() ([]byte, error) {
    697 	b := make([]byte, 0, 16*len(s.Hint))
    698 	for _, e := range s.Hint {
    699 		if len(e) != net.IPv6len || e.To4() != nil {
    700 			return nil, errors.New("dns: svcbipv6hint: expected ipv6, hint is ipv4")
    701 		}
    702 		b = append(b, e...)
    703 	}
    704 	return b, nil
    705 }
    706 
    707 func (s *SVCBIPv6Hint) unpack(b []byte) error {
    708 	if len(b) == 0 || len(b)%16 != 0 {
    709 		return errors.New("dns: svcbipv6hint: ipv6 address byte array length not a multiple of 16")
    710 	}
    711 	b = cloneSlice(b)
    712 	x := make([]net.IP, 0, len(b)/16)
    713 	for i := 0; i < len(b); i += 16 {
    714 		ip := net.IP(b[i : i+16])
    715 		if ip.To4() != nil {
    716 			return errors.New("dns: svcbipv6hint: expected ipv6, got ipv4")
    717 		}
    718 		x = append(x, ip)
    719 	}
    720 	s.Hint = x
    721 	return nil
    722 }
    723 
    724 func (s *SVCBIPv6Hint) String() string {
    725 	str := make([]string, len(s.Hint))
    726 	for i, e := range s.Hint {
    727 		if x := e.To4(); x != nil {
    728 			return "<nil>"
    729 		}
    730 		str[i] = e.String()
    731 	}
    732 	return strings.Join(str, ",")
    733 }
    734 
    735 func (s *SVCBIPv6Hint) parse(b string) error {
    736 	str := strings.Split(b, ",")
    737 	dst := make([]net.IP, len(str))
    738 	for i, e := range str {
    739 		ip := net.ParseIP(e)
    740 		if ip == nil {
    741 			return errors.New("dns: svcbipv6hint: bad ip")
    742 		}
    743 		if ip.To4() != nil {
    744 			return errors.New("dns: svcbipv6hint: expected ipv6, got ipv4-mapped-ipv6")
    745 		}
    746 		dst[i] = ip
    747 	}
    748 	s.Hint = dst
    749 	return nil
    750 }
    751 
    752 func (s *SVCBIPv6Hint) copy() SVCBKeyValue {
    753 	hint := make([]net.IP, len(s.Hint))
    754 	for i, ip := range s.Hint {
    755 		hint[i] = cloneSlice(ip)
    756 	}
    757 	return &SVCBIPv6Hint{Hint: hint}
    758 }
    759 
    760 // SVCBDoHPath pair is used to indicate the URI template that the
    761 // clients may use to construct a DNS over HTTPS URI.
    762 //
    763 // See RFC xxxx (https://datatracker.ietf.org/doc/html/draft-ietf-add-svcb-dns-02)
    764 // and RFC yyyy (https://datatracker.ietf.org/doc/html/draft-ietf-add-ddr-06).
    765 //
    766 // A basic example of using the dohpath option together with the alpn
    767 // option to indicate support for DNS over HTTPS on a certain path:
    768 //
    769 //	s := new(dns.SVCB)
    770 //	s.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeSVCB, Class: dns.ClassINET}
    771 //	e := new(dns.SVCBAlpn)
    772 //	e.Alpn = []string{"h2", "h3"}
    773 //	p := new(dns.SVCBDoHPath)
    774 //	p.Template = "/dns-query{?dns}"
    775 //	s.Value = append(s.Value, e, p)
    776 //
    777 // The parsing currently doesn't validate that Template is a valid
    778 // RFC 6570 URI template.
    779 type SVCBDoHPath struct {
    780 	Template string
    781 }
    782 
    783 func (*SVCBDoHPath) Key() SVCBKey            { return SVCB_DOHPATH }
    784 func (s *SVCBDoHPath) String() string        { return svcbParamToStr([]byte(s.Template)) }
    785 func (s *SVCBDoHPath) len() int              { return len(s.Template) }
    786 func (s *SVCBDoHPath) pack() ([]byte, error) { return []byte(s.Template), nil }
    787 
    788 func (s *SVCBDoHPath) unpack(b []byte) error {
    789 	s.Template = string(b)
    790 	return nil
    791 }
    792 
    793 func (s *SVCBDoHPath) parse(b string) error {
    794 	template, err := svcbParseParam(b)
    795 	if err != nil {
    796 		return fmt.Errorf("dns: svcbdohpath: %w", err)
    797 	}
    798 	s.Template = string(template)
    799 	return nil
    800 }
    801 
    802 func (s *SVCBDoHPath) copy() SVCBKeyValue {
    803 	return &SVCBDoHPath{
    804 		Template: s.Template,
    805 	}
    806 }
    807 
    808 // SVCBLocal pair is intended for experimental/private use. The key is recommended
    809 // to be in the range [SVCB_PRIVATE_LOWER, SVCB_PRIVATE_UPPER].
    810 // Basic use pattern for creating a keyNNNNN option:
    811 //
    812 //	h := new(dns.HTTPS)
    813 //	h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET}
    814 //	e := new(dns.SVCBLocal)
    815 //	e.KeyCode = 65400
    816 //	e.Data = []byte("abc")
    817 //	h.Value = append(h.Value, e)
    818 type SVCBLocal struct {
    819 	KeyCode SVCBKey // Never 65535 or any assigned keys.
    820 	Data    []byte  // All byte sequences are allowed.
    821 }
    822 
    823 func (s *SVCBLocal) Key() SVCBKey          { return s.KeyCode }
    824 func (s *SVCBLocal) String() string        { return svcbParamToStr(s.Data) }
    825 func (s *SVCBLocal) pack() ([]byte, error) { return cloneSlice(s.Data), nil }
    826 func (s *SVCBLocal) len() int              { return len(s.Data) }
    827 
    828 func (s *SVCBLocal) unpack(b []byte) error {
    829 	s.Data = cloneSlice(b)
    830 	return nil
    831 }
    832 
    833 func (s *SVCBLocal) parse(b string) error {
    834 	data, err := svcbParseParam(b)
    835 	if err != nil {
    836 		return fmt.Errorf("dns: svcblocal: svcb private/experimental key %w", err)
    837 	}
    838 	s.Data = data
    839 	return nil
    840 }
    841 
    842 func (s *SVCBLocal) copy() SVCBKeyValue {
    843 	return &SVCBLocal{s.KeyCode, cloneSlice(s.Data)}
    844 }
    845 
    846 func (rr *SVCB) String() string {
    847 	s := rr.Hdr.String() +
    848 		strconv.Itoa(int(rr.Priority)) + " " +
    849 		sprintName(rr.Target)
    850 	for _, e := range rr.Value {
    851 		s += " " + e.Key().String() + "=\"" + e.String() + "\""
    852 	}
    853 	return s
    854 }
    855 
    856 // areSVCBPairArraysEqual checks if SVCBKeyValue arrays are equal after sorting their
    857 // copies. arrA and arrB have equal lengths, otherwise zduplicate.go wouldn't call this function.
    858 func areSVCBPairArraysEqual(a []SVCBKeyValue, b []SVCBKeyValue) bool {
    859 	a = cloneSlice(a)
    860 	b = cloneSlice(b)
    861 	sort.Slice(a, func(i, j int) bool { return a[i].Key() < a[j].Key() })
    862 	sort.Slice(b, func(i, j int) bool { return b[i].Key() < b[j].Key() })
    863 	for i, e := range a {
    864 		if e.Key() != b[i].Key() {
    865 			return false
    866 		}
    867 		b1, err1 := e.pack()
    868 		b2, err2 := b[i].pack()
    869 		if err1 != nil || err2 != nil || !bytes.Equal(b1, b2) {
    870 			return false
    871 		}
    872 	}
    873 	return true
    874 }
    875 
    876 // svcbParamStr converts the value of an SVCB parameter into a DNS presentation-format string.
    877 func svcbParamToStr(s []byte) string {
    878 	var str strings.Builder
    879 	str.Grow(4 * len(s))
    880 	for _, e := range s {
    881 		if ' ' <= e && e <= '~' {
    882 			switch e {
    883 			case '"', ';', ' ', '\\':
    884 				str.WriteByte('\\')
    885 				str.WriteByte(e)
    886 			default:
    887 				str.WriteByte(e)
    888 			}
    889 		} else {
    890 			str.WriteString(escapeByte(e))
    891 		}
    892 	}
    893 	return str.String()
    894 }
    895 
    896 // svcbParseParam parses a DNS presentation-format string into an SVCB parameter value.
    897 func svcbParseParam(b string) ([]byte, error) {
    898 	data := make([]byte, 0, len(b))
    899 	for i := 0; i < len(b); {
    900 		if b[i] != '\\' {
    901 			data = append(data, b[i])
    902 			i++
    903 			continue
    904 		}
    905 		if i+1 == len(b) {
    906 			return nil, errors.New("escape unterminated")
    907 		}
    908 		if isDigit(b[i+1]) {
    909 			if i+3 < len(b) && isDigit(b[i+2]) && isDigit(b[i+3]) {
    910 				a, err := strconv.ParseUint(b[i+1:i+4], 10, 8)
    911 				if err == nil {
    912 					i += 4
    913 					data = append(data, byte(a))
    914 					continue
    915 				}
    916 			}
    917 			return nil, errors.New("bad escaped octet")
    918 		} else {
    919 			data = append(data, b[i+1])
    920 			i += 2
    921 		}
    922 	}
    923 	return data, nil
    924 }