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