...

Source file src/github.com/miekg/dns/svcb.go

Documentation: github.com/miekg/dns

     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  	codes := make([]SVCBKey, 0, strings.Count(b, ",")+1)
   318  	for len(b) > 0 {
   319  		var key string
   320  		key, b, _ = strings.Cut(b, ",")
   321  		codes = append(codes, svcbStringToKey(key))
   322  	}
   323  	s.Code = codes
   324  	return nil
   325  }
   326  
   327  func (s *SVCBMandatory) len() int {
   328  	return 2 * len(s.Code)
   329  }
   330  
   331  func (s *SVCBMandatory) copy() SVCBKeyValue {
   332  	return &SVCBMandatory{cloneSlice(s.Code)}
   333  }
   334  
   335  // SVCBAlpn pair is used to list supported connection protocols.
   336  // The user of this library must ensure that at least one protocol is listed when alpn is present.
   337  // Protocol IDs can be found at:
   338  // https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
   339  // Basic use pattern for creating an alpn option:
   340  //
   341  //	h := new(dns.HTTPS)
   342  //	h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET}
   343  //	e := new(dns.SVCBAlpn)
   344  //	e.Alpn = []string{"h2", "http/1.1"}
   345  //	h.Value = append(h.Value, e)
   346  type SVCBAlpn struct {
   347  	Alpn []string
   348  }
   349  
   350  func (*SVCBAlpn) Key() SVCBKey { return SVCB_ALPN }
   351  
   352  func (s *SVCBAlpn) String() string {
   353  	// An ALPN value is a comma-separated list of values, each of which can be
   354  	// an arbitrary binary value. In order to allow parsing, the comma and
   355  	// backslash characters are themselves escaped.
   356  	//
   357  	// However, this escaping is done in addition to the normal escaping which
   358  	// happens in zone files, meaning that these values must be
   359  	// double-escaped. This looks terrible, so if you see a never-ending
   360  	// sequence of backslash in a zone file this may be why.
   361  	//
   362  	// https://datatracker.ietf.org/doc/html/draft-ietf-dnsop-svcb-https-08#appendix-A.1
   363  	var str strings.Builder
   364  	for i, alpn := range s.Alpn {
   365  		// 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
   366  		str.Grow(4*len(alpn) + 1)
   367  		if i > 0 {
   368  			str.WriteByte(',')
   369  		}
   370  		for j := 0; j < len(alpn); j++ {
   371  			e := alpn[j]
   372  			if ' ' > e || e > '~' {
   373  				str.WriteString(escapeByte(e))
   374  				continue
   375  			}
   376  			switch e {
   377  			// We escape a few characters which may confuse humans or parsers.
   378  			case '"', ';', ' ':
   379  				str.WriteByte('\\')
   380  				str.WriteByte(e)
   381  			// The comma and backslash characters themselves must be
   382  			// doubly-escaped. We use `\\` for the first backslash and
   383  			// the escaped numeric value for the other value. We especially
   384  			// don't want a comma in the output.
   385  			case ',':
   386  				str.WriteString(`\\\044`)
   387  			case '\\':
   388  				str.WriteString(`\\\092`)
   389  			default:
   390  				str.WriteByte(e)
   391  			}
   392  		}
   393  	}
   394  	return str.String()
   395  }
   396  
   397  func (s *SVCBAlpn) pack() ([]byte, error) {
   398  	// Liberally estimate the size of an alpn as 10 octets
   399  	b := make([]byte, 0, 10*len(s.Alpn))
   400  	for _, e := range s.Alpn {
   401  		if e == "" {
   402  			return nil, errors.New("dns: svcbalpn: empty alpn-id")
   403  		}
   404  		if len(e) > 255 {
   405  			return nil, errors.New("dns: svcbalpn: alpn-id too long")
   406  		}
   407  		b = append(b, byte(len(e)))
   408  		b = append(b, e...)
   409  	}
   410  	return b, nil
   411  }
   412  
   413  func (s *SVCBAlpn) unpack(b []byte) error {
   414  	// Estimate the size of the smallest alpn as 4 bytes
   415  	alpn := make([]string, 0, len(b)/4)
   416  	for i := 0; i < len(b); {
   417  		length := int(b[i])
   418  		i++
   419  		if i+length > len(b) {
   420  			return errors.New("dns: svcbalpn: alpn array overflowing")
   421  		}
   422  		alpn = append(alpn, string(b[i:i+length]))
   423  		i += length
   424  	}
   425  	s.Alpn = alpn
   426  	return nil
   427  }
   428  
   429  func (s *SVCBAlpn) parse(b string) error {
   430  	if len(b) == 0 {
   431  		s.Alpn = []string{}
   432  		return nil
   433  	}
   434  
   435  	alpn := []string{}
   436  	a := []byte{}
   437  	for p := 0; p < len(b); {
   438  		c, q := nextByte(b, p)
   439  		if q == 0 {
   440  			return errors.New("dns: svcbalpn: unterminated escape")
   441  		}
   442  		p += q
   443  		// If we find a comma, we have finished reading an alpn.
   444  		if c == ',' {
   445  			if len(a) == 0 {
   446  				return errors.New("dns: svcbalpn: empty protocol identifier")
   447  			}
   448  			alpn = append(alpn, string(a))
   449  			a = []byte{}
   450  			continue
   451  		}
   452  		// If it's a backslash, we need to handle a comma-separated list.
   453  		if c == '\\' {
   454  			dc, dq := nextByte(b, p)
   455  			if dq == 0 {
   456  				return errors.New("dns: svcbalpn: unterminated escape decoding comma-separated list")
   457  			}
   458  			if dc != '\\' && dc != ',' {
   459  				return errors.New("dns: svcbalpn: bad escaped character decoding comma-separated list")
   460  			}
   461  			p += dq
   462  			c = dc
   463  		}
   464  		a = append(a, c)
   465  	}
   466  	// Add the final alpn.
   467  	if len(a) == 0 {
   468  		return errors.New("dns: svcbalpn: last protocol identifier empty")
   469  	}
   470  	s.Alpn = append(alpn, string(a))
   471  	return nil
   472  }
   473  
   474  func (s *SVCBAlpn) len() int {
   475  	var l int
   476  	for _, e := range s.Alpn {
   477  		l += 1 + len(e)
   478  	}
   479  	return l
   480  }
   481  
   482  func (s *SVCBAlpn) copy() SVCBKeyValue {
   483  	return &SVCBAlpn{cloneSlice(s.Alpn)}
   484  }
   485  
   486  // SVCBNoDefaultAlpn pair signifies no support for default connection protocols.
   487  // Should be used in conjunction with alpn.
   488  // Basic use pattern for creating a no-default-alpn option:
   489  //
   490  //	s := &dns.SVCB{Hdr: dns.RR_Header{Name: ".", Rrtype: dns.TypeSVCB, Class: dns.ClassINET}}
   491  //	t := new(dns.SVCBAlpn)
   492  //	t.Alpn = []string{"xmpp-client"}
   493  //	s.Value = append(s.Value, t)
   494  //	e := new(dns.SVCBNoDefaultAlpn)
   495  //	s.Value = append(s.Value, e)
   496  type SVCBNoDefaultAlpn struct{}
   497  
   498  func (*SVCBNoDefaultAlpn) Key() SVCBKey          { return SVCB_NO_DEFAULT_ALPN }
   499  func (*SVCBNoDefaultAlpn) copy() SVCBKeyValue    { return &SVCBNoDefaultAlpn{} }
   500  func (*SVCBNoDefaultAlpn) pack() ([]byte, error) { return []byte{}, nil }
   501  func (*SVCBNoDefaultAlpn) String() string        { return "" }
   502  func (*SVCBNoDefaultAlpn) len() int              { return 0 }
   503  
   504  func (*SVCBNoDefaultAlpn) unpack(b []byte) error {
   505  	if len(b) != 0 {
   506  		return errors.New("dns: svcbnodefaultalpn: no-default-alpn must have no value")
   507  	}
   508  	return nil
   509  }
   510  
   511  func (*SVCBNoDefaultAlpn) parse(b string) error {
   512  	if b != "" {
   513  		return errors.New("dns: svcbnodefaultalpn: no-default-alpn must have no value")
   514  	}
   515  	return nil
   516  }
   517  
   518  // SVCBPort pair defines the port for connection.
   519  // Basic use pattern for creating a port option:
   520  //
   521  //	s := &dns.SVCB{Hdr: dns.RR_Header{Name: ".", Rrtype: dns.TypeSVCB, Class: dns.ClassINET}}
   522  //	e := new(dns.SVCBPort)
   523  //	e.Port = 80
   524  //	s.Value = append(s.Value, e)
   525  type SVCBPort struct {
   526  	Port uint16
   527  }
   528  
   529  func (*SVCBPort) Key() SVCBKey         { return SVCB_PORT }
   530  func (*SVCBPort) len() int             { return 2 }
   531  func (s *SVCBPort) String() string     { return strconv.FormatUint(uint64(s.Port), 10) }
   532  func (s *SVCBPort) copy() SVCBKeyValue { return &SVCBPort{s.Port} }
   533  
   534  func (s *SVCBPort) unpack(b []byte) error {
   535  	if len(b) != 2 {
   536  		return errors.New("dns: svcbport: port length is not exactly 2 octets")
   537  	}
   538  	s.Port = binary.BigEndian.Uint16(b)
   539  	return nil
   540  }
   541  
   542  func (s *SVCBPort) pack() ([]byte, error) {
   543  	b := make([]byte, 2)
   544  	binary.BigEndian.PutUint16(b, s.Port)
   545  	return b, nil
   546  }
   547  
   548  func (s *SVCBPort) parse(b string) error {
   549  	port, err := strconv.ParseUint(b, 10, 16)
   550  	if err != nil {
   551  		return errors.New("dns: svcbport: port out of range")
   552  	}
   553  	s.Port = uint16(port)
   554  	return nil
   555  }
   556  
   557  // SVCBIPv4Hint pair suggests an IPv4 address which may be used to open connections
   558  // if A and AAAA record responses for SVCB's Target domain haven't been received.
   559  // In that case, optionally, A and AAAA requests can be made, after which the connection
   560  // to the hinted IP address may be terminated and a new connection may be opened.
   561  // Basic use pattern for creating an ipv4hint option:
   562  //
   563  //		h := new(dns.HTTPS)
   564  //		h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET}
   565  //		e := new(dns.SVCBIPv4Hint)
   566  //		e.Hint = []net.IP{net.IPv4(1,1,1,1).To4()}
   567  //
   568  //	 Or
   569  //
   570  //		e.Hint = []net.IP{net.ParseIP("1.1.1.1").To4()}
   571  //		h.Value = append(h.Value, e)
   572  type SVCBIPv4Hint struct {
   573  	Hint []net.IP
   574  }
   575  
   576  func (*SVCBIPv4Hint) Key() SVCBKey { return SVCB_IPV4HINT }
   577  func (s *SVCBIPv4Hint) len() int   { return 4 * len(s.Hint) }
   578  
   579  func (s *SVCBIPv4Hint) pack() ([]byte, error) {
   580  	b := make([]byte, 0, 4*len(s.Hint))
   581  	for _, e := range s.Hint {
   582  		x := e.To4()
   583  		if x == nil {
   584  			return nil, errors.New("dns: svcbipv4hint: expected ipv4, hint is ipv6")
   585  		}
   586  		b = append(b, x...)
   587  	}
   588  	return b, nil
   589  }
   590  
   591  func (s *SVCBIPv4Hint) unpack(b []byte) error {
   592  	if len(b) == 0 || len(b)%4 != 0 {
   593  		return errors.New("dns: svcbipv4hint: ipv4 address byte array length is not a multiple of 4")
   594  	}
   595  	b = cloneSlice(b)
   596  	x := make([]net.IP, 0, len(b)/4)
   597  	for i := 0; i < len(b); i += 4 {
   598  		x = append(x, net.IP(b[i:i+4]))
   599  	}
   600  	s.Hint = x
   601  	return nil
   602  }
   603  
   604  func (s *SVCBIPv4Hint) String() string {
   605  	str := make([]string, len(s.Hint))
   606  	for i, e := range s.Hint {
   607  		x := e.To4()
   608  		if x == nil {
   609  			return "<nil>"
   610  		}
   611  		str[i] = x.String()
   612  	}
   613  	return strings.Join(str, ",")
   614  }
   615  
   616  func (s *SVCBIPv4Hint) parse(b string) error {
   617  	if b == "" {
   618  		return errors.New("dns: svcbipv4hint: empty hint")
   619  	}
   620  	if strings.Contains(b, ":") {
   621  		return errors.New("dns: svcbipv4hint: expected ipv4, got ipv6")
   622  	}
   623  
   624  	hint := make([]net.IP, 0, strings.Count(b, ",")+1)
   625  	for len(b) > 0 {
   626  		var e string
   627  		e, b, _ = strings.Cut(b, ",")
   628  		ip := net.ParseIP(e).To4()
   629  		if ip == nil {
   630  			return errors.New("dns: svcbipv4hint: bad ip")
   631  		}
   632  		hint = append(hint, ip)
   633  	}
   634  	s.Hint = hint
   635  	return nil
   636  }
   637  
   638  func (s *SVCBIPv4Hint) copy() SVCBKeyValue {
   639  	hint := make([]net.IP, len(s.Hint))
   640  	for i, ip := range s.Hint {
   641  		hint[i] = cloneSlice(ip)
   642  	}
   643  	return &SVCBIPv4Hint{Hint: hint}
   644  }
   645  
   646  // SVCBECHConfig pair contains the ECHConfig structure defined in draft-ietf-tls-esni [RFC xxxx].
   647  // Basic use pattern for creating an ech option:
   648  //
   649  //	h := new(dns.HTTPS)
   650  //	h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET}
   651  //	e := new(dns.SVCBECHConfig)
   652  //	e.ECH = []byte{0xfe, 0x08, ...}
   653  //	h.Value = append(h.Value, e)
   654  type SVCBECHConfig struct {
   655  	ECH []byte // Specifically ECHConfigList including the redundant length prefix
   656  }
   657  
   658  func (*SVCBECHConfig) Key() SVCBKey     { return SVCB_ECHCONFIG }
   659  func (s *SVCBECHConfig) String() string { return toBase64(s.ECH) }
   660  func (s *SVCBECHConfig) len() int       { return len(s.ECH) }
   661  
   662  func (s *SVCBECHConfig) pack() ([]byte, error) {
   663  	return cloneSlice(s.ECH), nil
   664  }
   665  
   666  func (s *SVCBECHConfig) copy() SVCBKeyValue {
   667  	return &SVCBECHConfig{cloneSlice(s.ECH)}
   668  }
   669  
   670  func (s *SVCBECHConfig) unpack(b []byte) error {
   671  	s.ECH = cloneSlice(b)
   672  	return nil
   673  }
   674  
   675  func (s *SVCBECHConfig) parse(b string) error {
   676  	x, err := fromBase64([]byte(b))
   677  	if err != nil {
   678  		return errors.New("dns: svcbech: bad base64 ech")
   679  	}
   680  	s.ECH = x
   681  	return nil
   682  }
   683  
   684  // SVCBIPv6Hint pair suggests an IPv6 address which may be used to open connections
   685  // if A and AAAA record responses for SVCB's Target domain haven't been received.
   686  // In that case, optionally, A and AAAA requests can be made, after which the
   687  // connection to the hinted IP address may be terminated and a new connection may be opened.
   688  // Basic use pattern for creating an ipv6hint option:
   689  //
   690  //	h := new(dns.HTTPS)
   691  //	h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET}
   692  //	e := new(dns.SVCBIPv6Hint)
   693  //	e.Hint = []net.IP{net.ParseIP("2001:db8::1")}
   694  //	h.Value = append(h.Value, e)
   695  type SVCBIPv6Hint struct {
   696  	Hint []net.IP
   697  }
   698  
   699  func (*SVCBIPv6Hint) Key() SVCBKey { return SVCB_IPV6HINT }
   700  func (s *SVCBIPv6Hint) len() int   { return 16 * len(s.Hint) }
   701  
   702  func (s *SVCBIPv6Hint) pack() ([]byte, error) {
   703  	b := make([]byte, 0, 16*len(s.Hint))
   704  	for _, e := range s.Hint {
   705  		if len(e) != net.IPv6len || e.To4() != nil {
   706  			return nil, errors.New("dns: svcbipv6hint: expected ipv6, hint is ipv4")
   707  		}
   708  		b = append(b, e...)
   709  	}
   710  	return b, nil
   711  }
   712  
   713  func (s *SVCBIPv6Hint) unpack(b []byte) error {
   714  	if len(b) == 0 || len(b)%16 != 0 {
   715  		return errors.New("dns: svcbipv6hint: ipv6 address byte array length not a multiple of 16")
   716  	}
   717  	b = cloneSlice(b)
   718  	x := make([]net.IP, 0, len(b)/16)
   719  	for i := 0; i < len(b); i += 16 {
   720  		ip := net.IP(b[i : i+16])
   721  		if ip.To4() != nil {
   722  			return errors.New("dns: svcbipv6hint: expected ipv6, got ipv4")
   723  		}
   724  		x = append(x, ip)
   725  	}
   726  	s.Hint = x
   727  	return nil
   728  }
   729  
   730  func (s *SVCBIPv6Hint) String() string {
   731  	str := make([]string, len(s.Hint))
   732  	for i, e := range s.Hint {
   733  		if x := e.To4(); x != nil {
   734  			return "<nil>"
   735  		}
   736  		str[i] = e.String()
   737  	}
   738  	return strings.Join(str, ",")
   739  }
   740  
   741  func (s *SVCBIPv6Hint) parse(b string) error {
   742  	if b == "" {
   743  		return errors.New("dns: svcbipv6hint: empty hint")
   744  	}
   745  
   746  	hint := make([]net.IP, 0, strings.Count(b, ",")+1)
   747  	for len(b) > 0 {
   748  		var e string
   749  		e, b, _ = strings.Cut(b, ",")
   750  		ip := net.ParseIP(e)
   751  		if ip == nil {
   752  			return errors.New("dns: svcbipv6hint: bad ip")
   753  		}
   754  		if ip.To4() != nil {
   755  			return errors.New("dns: svcbipv6hint: expected ipv6, got ipv4-mapped-ipv6")
   756  		}
   757  		hint = append(hint, ip)
   758  	}
   759  	s.Hint = hint
   760  	return nil
   761  }
   762  
   763  func (s *SVCBIPv6Hint) copy() SVCBKeyValue {
   764  	hint := make([]net.IP, len(s.Hint))
   765  	for i, ip := range s.Hint {
   766  		hint[i] = cloneSlice(ip)
   767  	}
   768  	return &SVCBIPv6Hint{Hint: hint}
   769  }
   770  
   771  // SVCBDoHPath pair is used to indicate the URI template that the
   772  // clients may use to construct a DNS over HTTPS URI.
   773  //
   774  // See RFC xxxx (https://datatracker.ietf.org/doc/html/draft-ietf-add-svcb-dns-02)
   775  // and RFC yyyy (https://datatracker.ietf.org/doc/html/draft-ietf-add-ddr-06).
   776  //
   777  // A basic example of using the dohpath option together with the alpn
   778  // option to indicate support for DNS over HTTPS on a certain path:
   779  //
   780  //	s := new(dns.SVCB)
   781  //	s.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeSVCB, Class: dns.ClassINET}
   782  //	e := new(dns.SVCBAlpn)
   783  //	e.Alpn = []string{"h2", "h3"}
   784  //	p := new(dns.SVCBDoHPath)
   785  //	p.Template = "/dns-query{?dns}"
   786  //	s.Value = append(s.Value, e, p)
   787  //
   788  // The parsing currently doesn't validate that Template is a valid
   789  // RFC 6570 URI template.
   790  type SVCBDoHPath struct {
   791  	Template string
   792  }
   793  
   794  func (*SVCBDoHPath) Key() SVCBKey            { return SVCB_DOHPATH }
   795  func (s *SVCBDoHPath) String() string        { return svcbParamToStr([]byte(s.Template)) }
   796  func (s *SVCBDoHPath) len() int              { return len(s.Template) }
   797  func (s *SVCBDoHPath) pack() ([]byte, error) { return []byte(s.Template), nil }
   798  
   799  func (s *SVCBDoHPath) unpack(b []byte) error {
   800  	s.Template = string(b)
   801  	return nil
   802  }
   803  
   804  func (s *SVCBDoHPath) parse(b string) error {
   805  	template, err := svcbParseParam(b)
   806  	if err != nil {
   807  		return fmt.Errorf("dns: svcbdohpath: %w", err)
   808  	}
   809  	s.Template = string(template)
   810  	return nil
   811  }
   812  
   813  func (s *SVCBDoHPath) copy() SVCBKeyValue {
   814  	return &SVCBDoHPath{
   815  		Template: s.Template,
   816  	}
   817  }
   818  
   819  // SVCBLocal pair is intended for experimental/private use. The key is recommended
   820  // to be in the range [SVCB_PRIVATE_LOWER, SVCB_PRIVATE_UPPER].
   821  // Basic use pattern for creating a keyNNNNN option:
   822  //
   823  //	h := new(dns.HTTPS)
   824  //	h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET}
   825  //	e := new(dns.SVCBLocal)
   826  //	e.KeyCode = 65400
   827  //	e.Data = []byte("abc")
   828  //	h.Value = append(h.Value, e)
   829  type SVCBLocal struct {
   830  	KeyCode SVCBKey // Never 65535 or any assigned keys.
   831  	Data    []byte  // All byte sequences are allowed.
   832  }
   833  
   834  func (s *SVCBLocal) Key() SVCBKey          { return s.KeyCode }
   835  func (s *SVCBLocal) String() string        { return svcbParamToStr(s.Data) }
   836  func (s *SVCBLocal) pack() ([]byte, error) { return cloneSlice(s.Data), nil }
   837  func (s *SVCBLocal) len() int              { return len(s.Data) }
   838  
   839  func (s *SVCBLocal) unpack(b []byte) error {
   840  	s.Data = cloneSlice(b)
   841  	return nil
   842  }
   843  
   844  func (s *SVCBLocal) parse(b string) error {
   845  	data, err := svcbParseParam(b)
   846  	if err != nil {
   847  		return fmt.Errorf("dns: svcblocal: svcb private/experimental key %w", err)
   848  	}
   849  	s.Data = data
   850  	return nil
   851  }
   852  
   853  func (s *SVCBLocal) copy() SVCBKeyValue {
   854  	return &SVCBLocal{s.KeyCode, cloneSlice(s.Data)}
   855  }
   856  
   857  func (rr *SVCB) String() string {
   858  	s := rr.Hdr.String() +
   859  		strconv.Itoa(int(rr.Priority)) + " " +
   860  		sprintName(rr.Target)
   861  	for _, e := range rr.Value {
   862  		s += " " + e.Key().String() + "=\"" + e.String() + "\""
   863  	}
   864  	return s
   865  }
   866  
   867  // areSVCBPairArraysEqual checks if SVCBKeyValue arrays are equal after sorting their
   868  // copies. arrA and arrB have equal lengths, otherwise zduplicate.go wouldn't call this function.
   869  func areSVCBPairArraysEqual(a []SVCBKeyValue, b []SVCBKeyValue) bool {
   870  	a = cloneSlice(a)
   871  	b = cloneSlice(b)
   872  	sort.Slice(a, func(i, j int) bool { return a[i].Key() < a[j].Key() })
   873  	sort.Slice(b, func(i, j int) bool { return b[i].Key() < b[j].Key() })
   874  	for i, e := range a {
   875  		if e.Key() != b[i].Key() {
   876  			return false
   877  		}
   878  		b1, err1 := e.pack()
   879  		b2, err2 := b[i].pack()
   880  		if err1 != nil || err2 != nil || !bytes.Equal(b1, b2) {
   881  			return false
   882  		}
   883  	}
   884  	return true
   885  }
   886  
   887  // svcbParamStr converts the value of an SVCB parameter into a DNS presentation-format string.
   888  func svcbParamToStr(s []byte) string {
   889  	var str strings.Builder
   890  	str.Grow(4 * len(s))
   891  	for _, e := range s {
   892  		if ' ' <= e && e <= '~' {
   893  			switch e {
   894  			case '"', ';', ' ', '\\':
   895  				str.WriteByte('\\')
   896  				str.WriteByte(e)
   897  			default:
   898  				str.WriteByte(e)
   899  			}
   900  		} else {
   901  			str.WriteString(escapeByte(e))
   902  		}
   903  	}
   904  	return str.String()
   905  }
   906  
   907  // svcbParseParam parses a DNS presentation-format string into an SVCB parameter value.
   908  func svcbParseParam(b string) ([]byte, error) {
   909  	data := make([]byte, 0, len(b))
   910  	for i := 0; i < len(b); {
   911  		if b[i] != '\\' {
   912  			data = append(data, b[i])
   913  			i++
   914  			continue
   915  		}
   916  		if i+1 == len(b) {
   917  			return nil, errors.New("escape unterminated")
   918  		}
   919  		if isDigit(b[i+1]) {
   920  			if i+3 < len(b) && isDigit(b[i+2]) && isDigit(b[i+3]) {
   921  				a, err := strconv.ParseUint(b[i+1:i+4], 10, 8)
   922  				if err == nil {
   923  					i += 4
   924  					data = append(data, byte(a))
   925  					continue
   926  				}
   927  			}
   928  			return nil, errors.New("bad escaped octet")
   929  		} else {
   930  			data = append(data, b[i+1])
   931  			i += 2
   932  		}
   933  	}
   934  	return data, nil
   935  }
   936  

View as plain text