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 }