1package dns 2 3import ( 4 "bytes" 5 "encoding/binary" 6 "errors" 7 "net" 8 "sort" 9 "strconv" 10 "strings" 11) 12 13type SVCBKey uint16 14 15// Keys defined in draft-ietf-dnsop-svcb-https-01 Section 12.3.2. 16const ( 17 SVCB_MANDATORY SVCBKey = 0 18 SVCB_ALPN SVCBKey = 1 19 SVCB_NO_DEFAULT_ALPN SVCBKey = 2 20 SVCB_PORT SVCBKey = 3 21 SVCB_IPV4HINT SVCBKey = 4 22 SVCB_ECHCONFIG SVCBKey = 5 23 SVCB_IPV6HINT SVCBKey = 6 24 svcb_RESERVED SVCBKey = 65535 25) 26 27var svcbKeyToStringMap = map[SVCBKey]string{ 28 SVCB_MANDATORY: "mandatory", 29 SVCB_ALPN: "alpn", 30 SVCB_NO_DEFAULT_ALPN: "no-default-alpn", 31 SVCB_PORT: "port", 32 SVCB_IPV4HINT: "ipv4hint", 33 SVCB_ECHCONFIG: "echconfig", 34 SVCB_IPV6HINT: "ipv6hint", 35} 36 37var svcbStringToKeyMap = reverseSVCBKeyMap(svcbKeyToStringMap) 38 39func reverseSVCBKeyMap(m map[SVCBKey]string) map[string]SVCBKey { 40 n := make(map[string]SVCBKey, len(m)) 41 for u, s := range m { 42 n[s] = u 43 } 44 return n 45} 46 47// String takes the numerical code of an SVCB key and returns its name. 48// Returns an empty string for reserved keys. 49// Accepts unassigned keys as well as experimental/private keys. 50func (key SVCBKey) String() string { 51 if x := svcbKeyToStringMap[key]; x != "" { 52 return x 53 } 54 if key == svcb_RESERVED { 55 return "" 56 } 57 return "key" + strconv.FormatUint(uint64(key), 10) 58} 59 60// svcbStringToKey returns the numerical code of an SVCB key. 61// Returns svcb_RESERVED for reserved/invalid keys. 62// Accepts unassigned keys as well as experimental/private keys. 63func svcbStringToKey(s string) SVCBKey { 64 if strings.HasPrefix(s, "key") { 65 a, err := strconv.ParseUint(s[3:], 10, 16) 66 // no leading zeros 67 // key shouldn't be registered 68 if err != nil || a == 65535 || s[3] == '0' || svcbKeyToStringMap[SVCBKey(a)] != "" { 69 return svcb_RESERVED 70 } 71 return SVCBKey(a) 72 } 73 if key, ok := svcbStringToKeyMap[s]; ok { 74 return key 75 } 76 return svcb_RESERVED 77} 78 79func (rr *SVCB) parse(c *zlexer, o string) *ParseError { 80 l, _ := c.Next() 81 i, e := strconv.ParseUint(l.token, 10, 16) 82 if e != nil || l.err { 83 return &ParseError{l.token, "bad SVCB priority", l} 84 } 85 rr.Priority = uint16(i) 86 87 c.Next() // zBlank 88 l, _ = c.Next() // zString 89 rr.Target = l.token 90 91 name, nameOk := toAbsoluteName(l.token, o) 92 if l.err || !nameOk { 93 return &ParseError{l.token, "bad SVCB Target", l} 94 } 95 rr.Target = name 96 97 // Values (if any) 98 l, _ = c.Next() 99 var xs []SVCBKeyValue 100 // Helps require whitespace between pairs. 101 // Prevents key1000="a"key1001=... 102 canHaveNextKey := true 103 for l.value != zNewline && l.value != zEOF { 104 switch l.value { 105 case zString: 106 if !canHaveNextKey { 107 // The key we can now read was probably meant to be 108 // a part of the last value. 109 return &ParseError{l.token, "bad SVCB value quotation", l} 110 } 111 112 // In key=value pairs, value does not have to be quoted unless value 113 // contains whitespace. And keys don't need to have values. 114 // Similarly, keys with an equality signs after them don't need values. 115 // l.token includes at least up to the first equality sign. 116 idx := strings.IndexByte(l.token, '=') 117 var key, value string 118 if idx < 0 { 119 // Key with no value and no equality sign 120 key = l.token 121 } else if idx == 0 { 122 return &ParseError{l.token, "bad SVCB key", l} 123 } else { 124 key, value = l.token[:idx], l.token[idx+1:] 125 126 if value == "" { 127 // We have a key and an equality sign. Maybe we have nothing 128 // after "=" or we have a double quote. 129 l, _ = c.Next() 130 if l.value == zQuote { 131 // Only needed when value ends with double quotes. 132 // Any value starting with zQuote ends with it. 133 canHaveNextKey = false 134 135 l, _ = c.Next() 136 switch l.value { 137 case zString: 138 // We have a value in double quotes. 139 value = l.token 140 l, _ = c.Next() 141 if l.value != zQuote { 142 return &ParseError{l.token, "SVCB unterminated value", l} 143 } 144 case zQuote: 145 // There's nothing in double quotes. 146 default: 147 return &ParseError{l.token, "bad SVCB value", l} 148 } 149 } 150 } 151 } 152 kv := makeSVCBKeyValue(svcbStringToKey(key)) 153 if kv == nil { 154 return &ParseError{l.token, "bad SVCB key", l} 155 } 156 if err := kv.parse(value); err != nil { 157 return &ParseError{l.token, err.Error(), l} 158 } 159 xs = append(xs, kv) 160 case zQuote: 161 return &ParseError{l.token, "SVCB key can't contain double quotes", l} 162 case zBlank: 163 canHaveNextKey = true 164 default: 165 return &ParseError{l.token, "bad SVCB values", l} 166 } 167 l, _ = c.Next() 168 } 169 rr.Value = xs 170 if rr.Priority == 0 && len(xs) > 0 { 171 return &ParseError{l.token, "SVCB aliasform can't have values", l} 172 } 173 return nil 174} 175 176// makeSVCBKeyValue returns an SVCBKeyValue struct with the key or nil for reserved keys. 177func makeSVCBKeyValue(key SVCBKey) SVCBKeyValue { 178 switch key { 179 case SVCB_MANDATORY: 180 return new(SVCBMandatory) 181 case SVCB_ALPN: 182 return new(SVCBAlpn) 183 case SVCB_NO_DEFAULT_ALPN: 184 return new(SVCBNoDefaultAlpn) 185 case SVCB_PORT: 186 return new(SVCBPort) 187 case SVCB_IPV4HINT: 188 return new(SVCBIPv4Hint) 189 case SVCB_ECHCONFIG: 190 return new(SVCBECHConfig) 191 case SVCB_IPV6HINT: 192 return new(SVCBIPv6Hint) 193 case svcb_RESERVED: 194 return nil 195 default: 196 e := new(SVCBLocal) 197 e.KeyCode = key 198 return e 199 } 200} 201 202// SVCB RR. See RFC xxxx (https://tools.ietf.org/html/draft-ietf-dnsop-svcb-https-01). 203type SVCB struct { 204 Hdr RR_Header 205 Priority uint16 206 Target string `dns:"domain-name"` 207 Value []SVCBKeyValue `dns:"pairs"` // Value must be empty if Priority is zero. 208} 209 210// HTTPS RR. Everything valid for SVCB applies to HTTPS as well. 211// Except that the HTTPS record is intended for use with the HTTP and HTTPS protocols. 212type HTTPS struct { 213 SVCB 214} 215 216func (rr *HTTPS) String() string { 217 return rr.SVCB.String() 218} 219 220func (rr *HTTPS) parse(c *zlexer, o string) *ParseError { 221 return rr.SVCB.parse(c, o) 222} 223 224// SVCBKeyValue defines a key=value pair for the SVCB RR type. 225// An SVCB RR can have multiple SVCBKeyValues appended to it. 226type SVCBKeyValue interface { 227 Key() SVCBKey // Key returns the numerical key code. 228 pack() ([]byte, error) // pack returns the encoded value. 229 unpack([]byte) error // unpack sets the value. 230 String() string // String returns the string representation of the value. 231 parse(string) error // parse sets the value to the given string representation of the value. 232 copy() SVCBKeyValue // copy returns a deep-copy of the pair. 233 len() int // len returns the length of value in the wire format. 234} 235 236// SVCBMandatory pair adds to required keys that must be interpreted for the RR 237// to be functional. 238// Basic use pattern for creating a mandatory option: 239// 240// s := &dns.SVCB{Hdr: dns.RR_Header{Name: ".", Rrtype: dns.TypeSVCB, Class: dns.ClassINET}} 241// e := new(dns.SVCBMandatory) 242// e.Code = []uint16{65403} 243// s.Value = append(s.Value, e) 244type SVCBMandatory struct { 245 Code []SVCBKey // Must not include mandatory 246} 247 248func (*SVCBMandatory) Key() SVCBKey { return SVCB_MANDATORY } 249 250func (s *SVCBMandatory) String() string { 251 str := make([]string, len(s.Code)) 252 for i, e := range s.Code { 253 str[i] = e.String() 254 } 255 return strings.Join(str, ",") 256} 257 258func (s *SVCBMandatory) pack() ([]byte, error) { 259 codes := append([]SVCBKey(nil), s.Code...) 260 sort.Slice(codes, func(i, j int) bool { 261 return codes[i] < codes[j] 262 }) 263 b := make([]byte, 2*len(codes)) 264 for i, e := range codes { 265 binary.BigEndian.PutUint16(b[2*i:], uint16(e)) 266 } 267 return b, nil 268} 269 270func (s *SVCBMandatory) unpack(b []byte) error { 271 if len(b)%2 != 0 { 272 return errors.New("dns: svcbmandatory: value length is not a multiple of 2") 273 } 274 codes := make([]SVCBKey, 0, len(b)/2) 275 for i := 0; i < len(b); i += 2 { 276 // We assume strictly increasing order. 277 codes = append(codes, SVCBKey(binary.BigEndian.Uint16(b[i:]))) 278 } 279 s.Code = codes 280 return nil 281} 282 283func (s *SVCBMandatory) parse(b string) error { 284 str := strings.Split(b, ",") 285 codes := make([]SVCBKey, 0, len(str)) 286 for _, e := range str { 287 codes = append(codes, svcbStringToKey(e)) 288 } 289 s.Code = codes 290 return nil 291} 292 293func (s *SVCBMandatory) len() int { 294 return 2 * len(s.Code) 295} 296 297func (s *SVCBMandatory) copy() SVCBKeyValue { 298 return &SVCBMandatory{ 299 append([]SVCBKey(nil), s.Code...), 300 } 301} 302 303// SVCBAlpn pair is used to list supported connection protocols. 304// Protocol ids can be found at: 305// https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids 306// Basic use pattern for creating an alpn option: 307// 308// h := new(dns.HTTPS) 309// h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET} 310// e := new(dns.SVCBAlpn) 311// e.Alpn = []string{"h2", "http/1.1"} 312// h.Value = append(o.Value, e) 313type SVCBAlpn struct { 314 Alpn []string 315} 316 317func (*SVCBAlpn) Key() SVCBKey { return SVCB_ALPN } 318func (s *SVCBAlpn) String() string { return strings.Join(s.Alpn, ",") } 319 320func (s *SVCBAlpn) pack() ([]byte, error) { 321 // Liberally estimate the size of an alpn as 10 octets 322 b := make([]byte, 0, 10*len(s.Alpn)) 323 for _, e := range s.Alpn { 324 if e == "" { 325 return nil, errors.New("dns: svcbalpn: empty alpn-id") 326 } 327 if len(e) > 255 { 328 return nil, errors.New("dns: svcbalpn: alpn-id too long") 329 } 330 b = append(b, byte(len(e))) 331 b = append(b, e...) 332 } 333 return b, nil 334} 335 336func (s *SVCBAlpn) unpack(b []byte) error { 337 // Estimate the size of the smallest alpn as 4 bytes 338 alpn := make([]string, 0, len(b)/4) 339 for i := 0; i < len(b); { 340 length := int(b[i]) 341 i++ 342 if i+length > len(b) { 343 return errors.New("dns: svcbalpn: alpn array overflowing") 344 } 345 alpn = append(alpn, string(b[i:i+length])) 346 i += length 347 } 348 s.Alpn = alpn 349 return nil 350} 351 352func (s *SVCBAlpn) parse(b string) error { 353 s.Alpn = strings.Split(b, ",") 354 return nil 355} 356 357func (s *SVCBAlpn) len() int { 358 var l int 359 for _, e := range s.Alpn { 360 l += 1 + len(e) 361 } 362 return l 363} 364 365func (s *SVCBAlpn) copy() SVCBKeyValue { 366 return &SVCBAlpn{ 367 append([]string(nil), s.Alpn...), 368 } 369} 370 371// SVCBNoDefaultAlpn pair signifies no support for default connection protocols. 372// Basic use pattern for creating a no-default-alpn option: 373// 374// s := &dns.SVCB{Hdr: dns.RR_Header{Name: ".", Rrtype: dns.TypeSVCB, Class: dns.ClassINET}} 375// e := new(dns.SVCBNoDefaultAlpn) 376// s.Value = append(s.Value, e) 377type SVCBNoDefaultAlpn struct{} 378 379func (*SVCBNoDefaultAlpn) Key() SVCBKey { return SVCB_NO_DEFAULT_ALPN } 380func (*SVCBNoDefaultAlpn) copy() SVCBKeyValue { return &SVCBNoDefaultAlpn{} } 381func (*SVCBNoDefaultAlpn) pack() ([]byte, error) { return []byte{}, nil } 382func (*SVCBNoDefaultAlpn) String() string { return "" } 383func (*SVCBNoDefaultAlpn) len() int { return 0 } 384 385func (*SVCBNoDefaultAlpn) unpack(b []byte) error { 386 if len(b) != 0 { 387 return errors.New("dns: svcbnodefaultalpn: no_default_alpn must have no value") 388 } 389 return nil 390} 391 392func (*SVCBNoDefaultAlpn) parse(b string) error { 393 if b != "" { 394 return errors.New("dns: svcbnodefaultalpn: no_default_alpn must have no value") 395 } 396 return nil 397} 398 399// SVCBPort pair defines the port for connection. 400// Basic use pattern for creating a port option: 401// 402// s := &dns.SVCB{Hdr: dns.RR_Header{Name: ".", Rrtype: dns.TypeSVCB, Class: dns.ClassINET}} 403// e := new(dns.SVCBPort) 404// e.Port = 80 405// s.Value = append(s.Value, e) 406type SVCBPort struct { 407 Port uint16 408} 409 410func (*SVCBPort) Key() SVCBKey { return SVCB_PORT } 411func (*SVCBPort) len() int { return 2 } 412func (s *SVCBPort) String() string { return strconv.FormatUint(uint64(s.Port), 10) } 413func (s *SVCBPort) copy() SVCBKeyValue { return &SVCBPort{s.Port} } 414 415func (s *SVCBPort) unpack(b []byte) error { 416 if len(b) != 2 { 417 return errors.New("dns: svcbport: port length is not exactly 2 octets") 418 } 419 s.Port = binary.BigEndian.Uint16(b) 420 return nil 421} 422 423func (s *SVCBPort) pack() ([]byte, error) { 424 b := make([]byte, 2) 425 binary.BigEndian.PutUint16(b, s.Port) 426 return b, nil 427} 428 429func (s *SVCBPort) parse(b string) error { 430 port, err := strconv.ParseUint(b, 10, 16) 431 if err != nil { 432 return errors.New("dns: svcbport: port out of range") 433 } 434 s.Port = uint16(port) 435 return nil 436} 437 438// SVCBIPv4Hint pair suggests an IPv4 address which may be used to open connections 439// if A and AAAA record responses for SVCB's Target domain haven't been received. 440// In that case, optionally, A and AAAA requests can be made, after which the connection 441// to the hinted IP address may be terminated and a new connection may be opened. 442// Basic use pattern for creating an ipv4hint option: 443// 444// h := new(dns.HTTPS) 445// h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET} 446// e := new(dns.SVCBIPv4Hint) 447// e.Hint = []net.IP{net.IPv4(1,1,1,1).To4()} 448// 449// Or 450// 451// e.Hint = []net.IP{net.ParseIP("1.1.1.1").To4()} 452// h.Value = append(h.Value, e) 453type SVCBIPv4Hint struct { 454 Hint []net.IP 455} 456 457func (*SVCBIPv4Hint) Key() SVCBKey { return SVCB_IPV4HINT } 458func (s *SVCBIPv4Hint) len() int { return 4 * len(s.Hint) } 459 460func (s *SVCBIPv4Hint) pack() ([]byte, error) { 461 b := make([]byte, 0, 4*len(s.Hint)) 462 for _, e := range s.Hint { 463 x := e.To4() 464 if x == nil { 465 return nil, errors.New("dns: svcbipv4hint: expected ipv4, hint is ipv6") 466 } 467 b = append(b, x...) 468 } 469 return b, nil 470} 471 472func (s *SVCBIPv4Hint) unpack(b []byte) error { 473 if len(b) == 0 || len(b)%4 != 0 { 474 return errors.New("dns: svcbipv4hint: ipv4 address byte array length is not a multiple of 4") 475 } 476 x := make([]net.IP, 0, len(b)/4) 477 for i := 0; i < len(b); i += 4 { 478 x = append(x, net.IP(b[i:i+4])) 479 } 480 s.Hint = x 481 return nil 482} 483 484func (s *SVCBIPv4Hint) String() string { 485 str := make([]string, len(s.Hint)) 486 for i, e := range s.Hint { 487 x := e.To4() 488 if x == nil { 489 return "<nil>" 490 } 491 str[i] = x.String() 492 } 493 return strings.Join(str, ",") 494} 495 496func (s *SVCBIPv4Hint) parse(b string) error { 497 if strings.Contains(b, ":") { 498 return errors.New("dns: svcbipv4hint: expected ipv4, got ipv6") 499 } 500 str := strings.Split(b, ",") 501 dst := make([]net.IP, len(str)) 502 for i, e := range str { 503 ip := net.ParseIP(e).To4() 504 if ip == nil { 505 return errors.New("dns: svcbipv4hint: bad ip") 506 } 507 dst[i] = ip 508 } 509 s.Hint = dst 510 return nil 511} 512 513func (s *SVCBIPv4Hint) copy() SVCBKeyValue { 514 hint := make([]net.IP, len(s.Hint)) 515 for i, ip := range s.Hint { 516 hint[i] = copyIP(ip) 517 } 518 519 return &SVCBIPv4Hint{ 520 Hint: hint, 521 } 522} 523 524// SVCBECHConfig pair contains the ECHConfig structure defined in draft-ietf-tls-esni [RFC xxxx]. 525// Basic use pattern for creating an echconfig option: 526// 527// h := new(dns.HTTPS) 528// h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET} 529// e := new(dns.SVCBECHConfig) 530// e.ECH = []byte{0xfe, 0x08, ...} 531// h.Value = append(h.Value, e) 532type SVCBECHConfig struct { 533 ECH []byte 534} 535 536func (*SVCBECHConfig) Key() SVCBKey { return SVCB_ECHCONFIG } 537func (s *SVCBECHConfig) String() string { return toBase64(s.ECH) } 538func (s *SVCBECHConfig) len() int { return len(s.ECH) } 539 540func (s *SVCBECHConfig) pack() ([]byte, error) { 541 return append([]byte(nil), s.ECH...), nil 542} 543 544func (s *SVCBECHConfig) copy() SVCBKeyValue { 545 return &SVCBECHConfig{ 546 append([]byte(nil), s.ECH...), 547 } 548} 549 550func (s *SVCBECHConfig) unpack(b []byte) error { 551 s.ECH = append([]byte(nil), b...) 552 return nil 553} 554func (s *SVCBECHConfig) parse(b string) error { 555 x, err := fromBase64([]byte(b)) 556 if err != nil { 557 return errors.New("dns: svcbechconfig: bad base64 echconfig") 558 } 559 s.ECH = x 560 return nil 561} 562 563// SVCBIPv6Hint pair suggests an IPv6 address which may be used to open connections 564// if A and AAAA record responses for SVCB's Target domain haven't been received. 565// In that case, optionally, A and AAAA requests can be made, after which the 566// connection to the hinted IP address may be terminated and a new connection may be opened. 567// Basic use pattern for creating an ipv6hint option: 568// 569// h := new(dns.HTTPS) 570// h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET} 571// e := new(dns.SVCBIPv6Hint) 572// e.Hint = []net.IP{net.ParseIP("2001:db8::1")} 573// h.Value = append(h.Value, e) 574type SVCBIPv6Hint struct { 575 Hint []net.IP 576} 577 578func (*SVCBIPv6Hint) Key() SVCBKey { return SVCB_IPV6HINT } 579func (s *SVCBIPv6Hint) len() int { return 16 * len(s.Hint) } 580 581func (s *SVCBIPv6Hint) pack() ([]byte, error) { 582 b := make([]byte, 0, 16*len(s.Hint)) 583 for _, e := range s.Hint { 584 if len(e) != net.IPv6len || e.To4() != nil { 585 return nil, errors.New("dns: svcbipv6hint: expected ipv6, hint is ipv4") 586 } 587 b = append(b, e...) 588 } 589 return b, nil 590} 591 592func (s *SVCBIPv6Hint) unpack(b []byte) error { 593 if len(b) == 0 || len(b)%16 != 0 { 594 return errors.New("dns: svcbipv6hint: ipv6 address byte array length not a multiple of 16") 595 } 596 x := make([]net.IP, 0, len(b)/16) 597 for i := 0; i < len(b); i += 16 { 598 ip := net.IP(b[i : i+16]) 599 if ip.To4() != nil { 600 return errors.New("dns: svcbipv6hint: expected ipv6, got ipv4") 601 } 602 x = append(x, ip) 603 } 604 s.Hint = x 605 return nil 606} 607 608func (s *SVCBIPv6Hint) String() string { 609 str := make([]string, len(s.Hint)) 610 for i, e := range s.Hint { 611 if x := e.To4(); x != nil { 612 return "<nil>" 613 } 614 str[i] = e.String() 615 } 616 return strings.Join(str, ",") 617} 618 619func (s *SVCBIPv6Hint) parse(b string) error { 620 if strings.Contains(b, ".") { 621 return errors.New("dns: svcbipv6hint: expected ipv6, got ipv4") 622 } 623 str := strings.Split(b, ",") 624 dst := make([]net.IP, len(str)) 625 for i, e := range str { 626 ip := net.ParseIP(e) 627 if ip == nil { 628 return errors.New("dns: svcbipv6hint: bad ip") 629 } 630 dst[i] = ip 631 } 632 s.Hint = dst 633 return nil 634} 635 636func (s *SVCBIPv6Hint) copy() SVCBKeyValue { 637 hint := make([]net.IP, len(s.Hint)) 638 for i, ip := range s.Hint { 639 hint[i] = copyIP(ip) 640 } 641 642 return &SVCBIPv6Hint{ 643 Hint: hint, 644 } 645} 646 647// SVCBLocal pair is intended for experimental/private use. The key is recommended 648// to be in the range [SVCB_PRIVATE_LOWER, SVCB_PRIVATE_UPPER]. 649// Basic use pattern for creating a keyNNNNN option: 650// 651// h := new(dns.HTTPS) 652// h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET} 653// e := new(dns.SVCBLocal) 654// e.KeyCode = 65400 655// e.Data = []byte("abc") 656// h.Value = append(h.Value, e) 657type SVCBLocal struct { 658 KeyCode SVCBKey // Never 65535 or any assigned keys. 659 Data []byte // All byte sequences are allowed. 660} 661 662func (s *SVCBLocal) Key() SVCBKey { return s.KeyCode } 663func (s *SVCBLocal) pack() ([]byte, error) { return append([]byte(nil), s.Data...), nil } 664func (s *SVCBLocal) len() int { return len(s.Data) } 665 666func (s *SVCBLocal) unpack(b []byte) error { 667 s.Data = append([]byte(nil), b...) 668 return nil 669} 670 671func (s *SVCBLocal) String() string { 672 var str strings.Builder 673 str.Grow(4 * len(s.Data)) 674 for _, e := range s.Data { 675 if ' ' <= e && e <= '~' { 676 switch e { 677 case '"', ';', ' ', '\\': 678 str.WriteByte('\\') 679 str.WriteByte(e) 680 default: 681 str.WriteByte(e) 682 } 683 } else { 684 str.WriteString(escapeByte(e)) 685 } 686 } 687 return str.String() 688} 689 690func (s *SVCBLocal) parse(b string) error { 691 data := make([]byte, 0, len(b)) 692 for i := 0; i < len(b); { 693 if b[i] != '\\' { 694 data = append(data, b[i]) 695 i++ 696 continue 697 } 698 if i+1 == len(b) { 699 return errors.New("dns: svcblocal: svcb private/experimental key escape unterminated") 700 } 701 if isDigit(b[i+1]) { 702 if i+3 < len(b) && isDigit(b[i+2]) && isDigit(b[i+3]) { 703 a, err := strconv.ParseUint(b[i+1:i+4], 10, 8) 704 if err == nil { 705 i += 4 706 data = append(data, byte(a)) 707 continue 708 } 709 } 710 return errors.New("dns: svcblocal: svcb private/experimental key bad escaped octet") 711 } else { 712 data = append(data, b[i+1]) 713 i += 2 714 } 715 } 716 s.Data = data 717 return nil 718} 719 720func (s *SVCBLocal) copy() SVCBKeyValue { 721 return &SVCBLocal{s.KeyCode, 722 append([]byte(nil), s.Data...), 723 } 724} 725 726func (rr *SVCB) String() string { 727 s := rr.Hdr.String() + 728 strconv.Itoa(int(rr.Priority)) + " " + 729 sprintName(rr.Target) 730 for _, e := range rr.Value { 731 s += " " + e.Key().String() + "=\"" + e.String() + "\"" 732 } 733 return s 734} 735 736// areSVCBPairArraysEqual checks if SVCBKeyValue arrays are equal after sorting their 737// copies. arrA and arrB have equal lengths, otherwise zduplicate.go wouldn't call this function. 738func areSVCBPairArraysEqual(a []SVCBKeyValue, b []SVCBKeyValue) bool { 739 a = append([]SVCBKeyValue(nil), a...) 740 b = append([]SVCBKeyValue(nil), b...) 741 sort.Slice(a, func(i, j int) bool { return a[i].Key() < a[j].Key() }) 742 sort.Slice(b, func(i, j int) bool { return b[i].Key() < b[j].Key() }) 743 for i, e := range a { 744 if e.Key() != b[i].Key() { 745 return false 746 } 747 b1, err1 := e.pack() 748 b2, err2 := b[i].pack() 749 if err1 != nil || err2 != nil || !bytes.Equal(b1, b2) { 750 return false 751 } 752 } 753 return true 754} 755