package dns

import (
	
	
	
	
	
	
	
	
)

// SVCBKey is the type of the keys used in the SVCB RR.
type SVCBKey uint16

// Keys defined in rfc9460
const (
	SVCB_MANDATORY SVCBKey = iota
	SVCB_ALPN
	SVCB_NO_DEFAULT_ALPN
	SVCB_PORT
	SVCB_IPV4HINT
	SVCB_ECHCONFIG
	SVCB_IPV6HINT
	SVCB_DOHPATH // rfc9461 Section 5
	SVCB_OHTTP   // rfc9540 Section 8

	svcb_RESERVED SVCBKey = 65535
)

var svcbKeyToStringMap = map[SVCBKey]string{
	SVCB_MANDATORY:       "mandatory",
	SVCB_ALPN:            "alpn",
	SVCB_NO_DEFAULT_ALPN: "no-default-alpn",
	SVCB_PORT:            "port",
	SVCB_IPV4HINT:        "ipv4hint",
	SVCB_ECHCONFIG:       "ech",
	SVCB_IPV6HINT:        "ipv6hint",
	SVCB_DOHPATH:         "dohpath",
	SVCB_OHTTP:           "ohttp",
}

var svcbStringToKeyMap = reverseSVCBKeyMap(svcbKeyToStringMap)

func reverseSVCBKeyMap( map[SVCBKey]string) map[string]SVCBKey {
	 := make(map[string]SVCBKey, len())
	for ,  := range  {
		[] = 
	}
	return 
}

// String takes the numerical code of an SVCB key and returns its name.
// Returns an empty string for reserved keys.
// Accepts unassigned keys as well as experimental/private keys.
func ( SVCBKey) () string {
	if  := svcbKeyToStringMap[];  != "" {
		return 
	}
	if  == svcb_RESERVED {
		return ""
	}
	return "key" + strconv.FormatUint(uint64(), 10)
}

// svcbStringToKey returns the numerical code of an SVCB key.
// Returns svcb_RESERVED for reserved/invalid keys.
// Accepts unassigned keys as well as experimental/private keys.
func svcbStringToKey( string) SVCBKey {
	if strings.HasPrefix(, "key") {
		,  := strconv.ParseUint([3:], 10, 16)
		// no leading zeros
		// key shouldn't be registered
		if  != nil ||  == 65535 || [3] == '0' || svcbKeyToStringMap[SVCBKey()] != "" {
			return svcb_RESERVED
		}
		return SVCBKey()
	}
	if ,  := svcbStringToKeyMap[];  {
		return 
	}
	return svcb_RESERVED
}

func ( *SVCB) ( *zlexer,  string) *ParseError {
	,  := .Next()
	,  := strconv.ParseUint(.token, 10, 16)
	if  != nil || .err {
		return &ParseError{file: .token, err: "bad SVCB priority", lex: }
	}
	.Priority = uint16()

	.Next()        // zBlank
	, _ = .Next() // zString
	.Target = .token

	,  := toAbsoluteName(.token, )
	if .err || ! {
		return &ParseError{file: .token, err: "bad SVCB Target", lex: }
	}
	.Target = 

	// Values (if any)
	, _ = .Next()
	var  []SVCBKeyValue
	// Helps require whitespace between pairs.
	// Prevents key1000="a"key1001=...
	 := true
	for .value != zNewline && .value != zEOF {
		switch .value {
		case zString:
			if ! {
				// The key we can now read was probably meant to be
				// a part of the last value.
				return &ParseError{file: .token, err: "bad SVCB value quotation", lex: }
			}

			// In key=value pairs, value does not have to be quoted unless value
			// contains whitespace. And keys don't need to have values.
			// Similarly, keys with an equality signs after them don't need values.
			// l.token includes at least up to the first equality sign.
			 := strings.IndexByte(.token, '=')
			var ,  string
			if  < 0 {
				// Key with no value and no equality sign
				 = .token
			} else if  == 0 {
				return &ParseError{file: .token, err: "bad SVCB key", lex: }
			} else {
				,  = .token[:], .token[+1:]

				if  == "" {
					// We have a key and an equality sign. Maybe we have nothing
					// after "=" or we have a double quote.
					, _ = .Next()
					if .value == zQuote {
						// Only needed when value ends with double quotes.
						// Any value starting with zQuote ends with it.
						 = false

						, _ = .Next()
						switch .value {
						case zString:
							// We have a value in double quotes.
							 = .token
							, _ = .Next()
							if .value != zQuote {
								return &ParseError{file: .token, err: "SVCB unterminated value", lex: }
							}
						case zQuote:
							// There's nothing in double quotes.
						default:
							return &ParseError{file: .token, err: "bad SVCB value", lex: }
						}
					}
				}
			}
			 := makeSVCBKeyValue(svcbStringToKey())
			if  == nil {
				return &ParseError{file: .token, err: "bad SVCB key", lex: }
			}
			if  := .parse();  != nil {
				return &ParseError{file: .token, wrappedErr: , lex: }
			}
			 = append(, )
		case zQuote:
			return &ParseError{file: .token, err: "SVCB key can't contain double quotes", lex: }
		case zBlank:
			 = true
		default:
			return &ParseError{file: .token, err: "bad SVCB values", lex: }
		}
		, _ = .Next()
	}

	// "In AliasMode, records SHOULD NOT include any SvcParams, and recipients MUST
	// ignore any SvcParams that are present."
	// However, we don't check rr.Priority == 0 && len(xs) > 0 here
	// It is the responsibility of the user of the library to check this.
	// This is to encourage the fixing of the source of this error.

	.Value = 
	return nil
}

// makeSVCBKeyValue returns an SVCBKeyValue struct with the key or nil for reserved keys.
func makeSVCBKeyValue( SVCBKey) SVCBKeyValue {
	switch  {
	case SVCB_MANDATORY:
		return new(SVCBMandatory)
	case SVCB_ALPN:
		return new(SVCBAlpn)
	case SVCB_NO_DEFAULT_ALPN:
		return new(SVCBNoDefaultAlpn)
	case SVCB_PORT:
		return new(SVCBPort)
	case SVCB_IPV4HINT:
		return new(SVCBIPv4Hint)
	case SVCB_ECHCONFIG:
		return new(SVCBECHConfig)
	case SVCB_IPV6HINT:
		return new(SVCBIPv6Hint)
	case SVCB_DOHPATH:
		return new(SVCBDoHPath)
	case SVCB_OHTTP:
		return new(SVCBOhttp)
	case svcb_RESERVED:
		return nil
	default:
		 := new(SVCBLocal)
		.KeyCode = 
		return 
	}
}

// SVCB RR. See RFC 9460.
type SVCB struct {
	Hdr      RR_Header
	Priority uint16         // If zero, Value must be empty or discarded by the user of this library
	Target   string         `dns:"domain-name"`
	Value    []SVCBKeyValue `dns:"pairs"`
}

// HTTPS RR. See RFC 9460. Everything valid for SVCB applies to HTTPS as well.
// Except that the HTTPS record is intended for use with the HTTP and HTTPS protocols.
type HTTPS struct {
	SVCB
}

func ( *HTTPS) () string {
	return .SVCB.String()
}

func ( *HTTPS) ( *zlexer,  string) *ParseError {
	return .SVCB.parse(, )
}

// SVCBKeyValue defines a key=value pair for the SVCB RR type.
// An SVCB RR can have multiple SVCBKeyValues appended to it.
type SVCBKeyValue interface {
	Key() SVCBKey          // Key returns the numerical key code.
	pack() ([]byte, error) // pack returns the encoded value.
	unpack([]byte) error   // unpack sets the value.
	String() string        // String returns the string representation of the value.
	parse(string) error    // parse sets the value to the given string representation of the value.
	copy() SVCBKeyValue    // copy returns a deep-copy of the pair.
	len() int              // len returns the length of value in the wire format.
}

// SVCBMandatory pair adds to required keys that must be interpreted for the RR
// to be functional. If ignored, the whole RRSet must be ignored.
// "port" and "no-default-alpn" are mandatory by default if present,
// so they shouldn't be included here.
//
// It is incumbent upon the user of this library to reject the RRSet if
// or avoid constructing such an RRSet that:
// - "mandatory" is included as one of the keys of mandatory
// - no key is listed multiple times in mandatory
// - all keys listed in mandatory are present
// - escape sequences are not used in mandatory
// - mandatory, when present, lists at least one key
//
// Basic use pattern for creating a mandatory option:
//
//	s := &dns.SVCB{Hdr: dns.RR_Header{Name: ".", Rrtype: dns.TypeSVCB, Class: dns.ClassINET}}
//	e := new(dns.SVCBMandatory)
//	e.Code = []uint16{dns.SVCB_ALPN}
//	s.Value = append(s.Value, e)
//	t := new(dns.SVCBAlpn)
//	t.Alpn = []string{"xmpp-client"}
//	s.Value = append(s.Value, t)
type SVCBMandatory struct {
	Code []SVCBKey
}

func (*SVCBMandatory) () SVCBKey { return SVCB_MANDATORY }

func ( *SVCBMandatory) () string {
	 := make([]string, len(.Code))
	for ,  := range .Code {
		[] = .String()
	}
	return strings.Join(, ",")
}

func ( *SVCBMandatory) () ([]byte, error) {
	 := cloneSlice(.Code)
	sort.Slice(, func(,  int) bool {
		return [] < []
	})
	 := make([]byte, 2*len())
	for ,  := range  {
		binary.BigEndian.PutUint16([2*:], uint16())
	}
	return , nil
}

func ( *SVCBMandatory) ( []byte) error {
	if len()%2 != 0 {
		return errors.New("dns: svcbmandatory: value length is not a multiple of 2")
	}
	 := make([]SVCBKey, 0, len()/2)
	for  := 0;  < len();  += 2 {
		// We assume strictly increasing order.
		 = append(, SVCBKey(binary.BigEndian.Uint16([:])))
	}
	.Code = 
	return nil
}

func ( *SVCBMandatory) ( string) error {
	 := make([]SVCBKey, 0, strings.Count(, ",")+1)
	for len() > 0 {
		var  string
		, , _ = strings.Cut(, ",")
		 = append(, svcbStringToKey())
	}
	.Code = 
	return nil
}

func ( *SVCBMandatory) () int {
	return 2 * len(.Code)
}

func ( *SVCBMandatory) () SVCBKeyValue {
	return &SVCBMandatory{cloneSlice(.Code)}
}

// SVCBAlpn pair is used to list supported connection protocols.
// The user of this library must ensure that at least one protocol is listed when alpn is present.
// Protocol IDs can be found at:
// https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
// Basic use pattern for creating an alpn option:
//
//	h := new(dns.HTTPS)
//	h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET}
//	e := new(dns.SVCBAlpn)
//	e.Alpn = []string{"h2", "http/1.1"}
//	h.Value = append(h.Value, e)
type SVCBAlpn struct {
	Alpn []string
}

func (*SVCBAlpn) () SVCBKey { return SVCB_ALPN }

func ( *SVCBAlpn) () string {
	// An ALPN value is a comma-separated list of values, each of which can be
	// an arbitrary binary value. In order to allow parsing, the comma and
	// backslash characters are themselves escaped.
	//
	// However, this escaping is done in addition to the normal escaping which
	// happens in zone files, meaning that these values must be
	// double-escaped. This looks terrible, so if you see a never-ending
	// sequence of backslash in a zone file this may be why.
	//
	// https://datatracker.ietf.org/doc/html/draft-ietf-dnsop-svcb-https-08#appendix-A.1
	var  strings.Builder
	for ,  := range .Alpn {
		// 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
		.Grow(4*len() + 1)
		if  > 0 {
			.WriteByte(',')
		}
		for  := 0;  < len(); ++ {
			 := []
			if ' ' >  ||  > '~' {
				.WriteString(escapeByte())
				continue
			}
			switch  {
			// We escape a few characters which may confuse humans or parsers.
			case '"', ';', ' ':
				.WriteByte('\\')
				.WriteByte()
			// The comma and backslash characters themselves must be
			// doubly-escaped. We use `\\` for the first backslash and
			// the escaped numeric value for the other value. We especially
			// don't want a comma in the output.
			case ',':
				.WriteString(`\\\044`)
			case '\\':
				.WriteString(`\\\092`)
			default:
				.WriteByte()
			}
		}
	}
	return .String()
}

func ( *SVCBAlpn) () ([]byte, error) {
	// Liberally estimate the size of an alpn as 10 octets
	 := make([]byte, 0, 10*len(.Alpn))
	for ,  := range .Alpn {
		if  == "" {
			return nil, errors.New("dns: svcbalpn: empty alpn-id")
		}
		if len() > 255 {
			return nil, errors.New("dns: svcbalpn: alpn-id too long")
		}
		 = append(, byte(len()))
		 = append(, ...)
	}
	return , nil
}

func ( *SVCBAlpn) ( []byte) error {
	// Estimate the size of the smallest alpn as 4 bytes
	 := make([]string, 0, len()/4)
	for  := 0;  < len(); {
		 := int([])
		++
		if + > len() {
			return errors.New("dns: svcbalpn: alpn array overflowing")
		}
		 = append(, string([:+]))
		 += 
	}
	.Alpn = 
	return nil
}

func ( *SVCBAlpn) ( string) error {
	if len() == 0 {
		.Alpn = []string{}
		return nil
	}

	 := []string{}
	 := []byte{}
	for  := 0;  < len(); {
		,  := nextByte(, )
		if  == 0 {
			return errors.New("dns: svcbalpn: unterminated escape")
		}
		 += 
		// If we find a comma, we have finished reading an alpn.
		if  == ',' {
			if len() == 0 {
				return errors.New("dns: svcbalpn: empty protocol identifier")
			}
			 = append(, string())
			 = []byte{}
			continue
		}
		// If it's a backslash, we need to handle a comma-separated list.
		if  == '\\' {
			,  := nextByte(, )
			if  == 0 {
				return errors.New("dns: svcbalpn: unterminated escape decoding comma-separated list")
			}
			if  != '\\' &&  != ',' {
				return errors.New("dns: svcbalpn: bad escaped character decoding comma-separated list")
			}
			 += 
			 = 
		}
		 = append(, )
	}
	// Add the final alpn.
	if len() == 0 {
		return errors.New("dns: svcbalpn: last protocol identifier empty")
	}
	.Alpn = append(, string())
	return nil
}

func ( *SVCBAlpn) () int {
	var  int
	for ,  := range .Alpn {
		 += 1 + len()
	}
	return 
}

func ( *SVCBAlpn) () SVCBKeyValue {
	return &SVCBAlpn{cloneSlice(.Alpn)}
}

// SVCBNoDefaultAlpn pair signifies no support for default connection protocols.
// Should be used in conjunction with alpn.
// Basic use pattern for creating a no-default-alpn option:
//
//	s := &dns.SVCB{Hdr: dns.RR_Header{Name: ".", Rrtype: dns.TypeSVCB, Class: dns.ClassINET}}
//	t := new(dns.SVCBAlpn)
//	t.Alpn = []string{"xmpp-client"}
//	s.Value = append(s.Value, t)
//	e := new(dns.SVCBNoDefaultAlpn)
//	s.Value = append(s.Value, e)
type SVCBNoDefaultAlpn struct{}

func (*SVCBNoDefaultAlpn) () SVCBKey          { return SVCB_NO_DEFAULT_ALPN }
func (*SVCBNoDefaultAlpn) () SVCBKeyValue    { return &SVCBNoDefaultAlpn{} }
func (*SVCBNoDefaultAlpn) () ([]byte, error) { return []byte{}, nil }
func (*SVCBNoDefaultAlpn) () string        { return "" }
func (*SVCBNoDefaultAlpn) () int              { return 0 }

func (*SVCBNoDefaultAlpn) ( []byte) error {
	if len() != 0 {
		return errors.New("dns: svcbnodefaultalpn: no-default-alpn must have no value")
	}
	return nil
}

func (*SVCBNoDefaultAlpn) ( string) error {
	if  != "" {
		return errors.New("dns: svcbnodefaultalpn: no-default-alpn must have no value")
	}
	return nil
}

// SVCBPort pair defines the port for connection.
// Basic use pattern for creating a port option:
//
//	s := &dns.SVCB{Hdr: dns.RR_Header{Name: ".", Rrtype: dns.TypeSVCB, Class: dns.ClassINET}}
//	e := new(dns.SVCBPort)
//	e.Port = 80
//	s.Value = append(s.Value, e)
type SVCBPort struct {
	Port uint16
}

func (*SVCBPort) () SVCBKey         { return SVCB_PORT }
func (*SVCBPort) () int             { return 2 }
func ( *SVCBPort) () string     { return strconv.FormatUint(uint64(.Port), 10) }
func ( *SVCBPort) () SVCBKeyValue { return &SVCBPort{.Port} }

func ( *SVCBPort) ( []byte) error {
	if len() != 2 {
		return errors.New("dns: svcbport: port length is not exactly 2 octets")
	}
	.Port = binary.BigEndian.Uint16()
	return nil
}

func ( *SVCBPort) () ([]byte, error) {
	 := make([]byte, 2)
	binary.BigEndian.PutUint16(, .Port)
	return , nil
}

func ( *SVCBPort) ( string) error {
	,  := strconv.ParseUint(, 10, 16)
	if  != nil {
		return errors.New("dns: svcbport: port out of range")
	}
	.Port = uint16()
	return nil
}

// SVCBIPv4Hint pair suggests an IPv4 address which may be used to open connections
// if A and AAAA record responses for SVCB's Target domain haven't been received.
// In that case, optionally, A and AAAA requests can be made, after which the connection
// to the hinted IP address may be terminated and a new connection may be opened.
// Basic use pattern for creating an ipv4hint option:
//
//		h := new(dns.HTTPS)
//		h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET}
//		e := new(dns.SVCBIPv4Hint)
//		e.Hint = []net.IP{net.IPv4(1,1,1,1).To4()}
//
//	 Or
//
//		e.Hint = []net.IP{net.ParseIP("1.1.1.1").To4()}
//		h.Value = append(h.Value, e)
type SVCBIPv4Hint struct {
	Hint []net.IP
}

func (*SVCBIPv4Hint) () SVCBKey { return SVCB_IPV4HINT }
func ( *SVCBIPv4Hint) () int   { return 4 * len(.Hint) }

func ( *SVCBIPv4Hint) () ([]byte, error) {
	 := make([]byte, 0, 4*len(.Hint))
	for ,  := range .Hint {
		 := .To4()
		if  == nil {
			return nil, errors.New("dns: svcbipv4hint: expected ipv4, hint is ipv6")
		}
		 = append(, ...)
	}
	return , nil
}

func ( *SVCBIPv4Hint) ( []byte) error {
	if len() == 0 || len()%4 != 0 {
		return errors.New("dns: svcbipv4hint: ipv4 address byte array length is not a multiple of 4")
	}
	 = cloneSlice()
	 := make([]net.IP, 0, len()/4)
	for  := 0;  < len();  += 4 {
		 = append(, net.IP([:+4]))
	}
	.Hint = 
	return nil
}

func ( *SVCBIPv4Hint) () string {
	 := make([]string, len(.Hint))
	for ,  := range .Hint {
		 := .To4()
		if  == nil {
			return "<nil>"
		}
		[] = .String()
	}
	return strings.Join(, ",")
}

func ( *SVCBIPv4Hint) ( string) error {
	if  == "" {
		return errors.New("dns: svcbipv4hint: empty hint")
	}
	if strings.Contains(, ":") {
		return errors.New("dns: svcbipv4hint: expected ipv4, got ipv6")
	}

	 := make([]net.IP, 0, strings.Count(, ",")+1)
	for len() > 0 {
		var  string
		, , _ = strings.Cut(, ",")
		 := net.ParseIP().To4()
		if  == nil {
			return errors.New("dns: svcbipv4hint: bad ip")
		}
		 = append(, )
	}
	.Hint = 
	return nil
}

func ( *SVCBIPv4Hint) () SVCBKeyValue {
	 := make([]net.IP, len(.Hint))
	for ,  := range .Hint {
		[] = cloneSlice()
	}
	return &SVCBIPv4Hint{Hint: }
}

// SVCBECHConfig pair contains the ECHConfig structure defined in draft-ietf-tls-esni [RFC xxxx].
// Basic use pattern for creating an ech option:
//
//	h := new(dns.HTTPS)
//	h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET}
//	e := new(dns.SVCBECHConfig)
//	e.ECH = []byte{0xfe, 0x08, ...}
//	h.Value = append(h.Value, e)
type SVCBECHConfig struct {
	ECH []byte // Specifically ECHConfigList including the redundant length prefix
}

func (*SVCBECHConfig) () SVCBKey     { return SVCB_ECHCONFIG }
func ( *SVCBECHConfig) () string { return toBase64(.ECH) }
func ( *SVCBECHConfig) () int       { return len(.ECH) }

func ( *SVCBECHConfig) () ([]byte, error) {
	return cloneSlice(.ECH), nil
}

func ( *SVCBECHConfig) () SVCBKeyValue {
	return &SVCBECHConfig{cloneSlice(.ECH)}
}

func ( *SVCBECHConfig) ( []byte) error {
	.ECH = cloneSlice()
	return nil
}

func ( *SVCBECHConfig) ( string) error {
	,  := fromBase64([]byte())
	if  != nil {
		return errors.New("dns: svcbech: bad base64 ech")
	}
	.ECH = 
	return nil
}

// SVCBIPv6Hint pair suggests an IPv6 address which may be used to open connections
// if A and AAAA record responses for SVCB's Target domain haven't been received.
// In that case, optionally, A and AAAA requests can be made, after which the
// connection to the hinted IP address may be terminated and a new connection may be opened.
// Basic use pattern for creating an ipv6hint option:
//
//	h := new(dns.HTTPS)
//	h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET}
//	e := new(dns.SVCBIPv6Hint)
//	e.Hint = []net.IP{net.ParseIP("2001:db8::1")}
//	h.Value = append(h.Value, e)
type SVCBIPv6Hint struct {
	Hint []net.IP
}

func (*SVCBIPv6Hint) () SVCBKey { return SVCB_IPV6HINT }
func ( *SVCBIPv6Hint) () int   { return 16 * len(.Hint) }

func ( *SVCBIPv6Hint) () ([]byte, error) {
	 := make([]byte, 0, 16*len(.Hint))
	for ,  := range .Hint {
		if len() != net.IPv6len || .To4() != nil {
			return nil, errors.New("dns: svcbipv6hint: expected ipv6, hint is ipv4")
		}
		 = append(, ...)
	}
	return , nil
}

func ( *SVCBIPv6Hint) ( []byte) error {
	if len() == 0 || len()%16 != 0 {
		return errors.New("dns: svcbipv6hint: ipv6 address byte array length not a multiple of 16")
	}
	 = cloneSlice()
	 := make([]net.IP, 0, len()/16)
	for  := 0;  < len();  += 16 {
		 := net.IP([ : +16])
		if .To4() != nil {
			return errors.New("dns: svcbipv6hint: expected ipv6, got ipv4")
		}
		 = append(, )
	}
	.Hint = 
	return nil
}

func ( *SVCBIPv6Hint) () string {
	 := make([]string, len(.Hint))
	for ,  := range .Hint {
		if  := .To4();  != nil {
			return "<nil>"
		}
		[] = .String()
	}
	return strings.Join(, ",")
}

func ( *SVCBIPv6Hint) ( string) error {
	if  == "" {
		return errors.New("dns: svcbipv6hint: empty hint")
	}

	 := make([]net.IP, 0, strings.Count(, ",")+1)
	for len() > 0 {
		var  string
		, , _ = strings.Cut(, ",")
		 := net.ParseIP()
		if  == nil {
			return errors.New("dns: svcbipv6hint: bad ip")
		}
		if .To4() != nil {
			return errors.New("dns: svcbipv6hint: expected ipv6, got ipv4-mapped-ipv6")
		}
		 = append(, )
	}
	.Hint = 
	return nil
}

func ( *SVCBIPv6Hint) () SVCBKeyValue {
	 := make([]net.IP, len(.Hint))
	for ,  := range .Hint {
		[] = cloneSlice()
	}
	return &SVCBIPv6Hint{Hint: }
}

// SVCBDoHPath pair is used to indicate the URI template that the
// clients may use to construct a DNS over HTTPS URI.
//
// See RFC 9461 (https://datatracker.ietf.org/doc/html/rfc9461)
// and RFC 9462 (https://datatracker.ietf.org/doc/html/rfc9462).
//
// A basic example of using the dohpath option together with the alpn
// option to indicate support for DNS over HTTPS on a certain path:
//
//	s := new(dns.SVCB)
//	s.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeSVCB, Class: dns.ClassINET}
//	e := new(dns.SVCBAlpn)
//	e.Alpn = []string{"h2", "h3"}
//	p := new(dns.SVCBDoHPath)
//	p.Template = "/dns-query{?dns}"
//	s.Value = append(s.Value, e, p)
//
// The parsing currently doesn't validate that Template is a valid
// RFC 6570 URI template.
type SVCBDoHPath struct {
	Template string
}

func (*SVCBDoHPath) () SVCBKey            { return SVCB_DOHPATH }
func ( *SVCBDoHPath) () string        { return svcbParamToStr([]byte(.Template)) }
func ( *SVCBDoHPath) () int              { return len(.Template) }
func ( *SVCBDoHPath) () ([]byte, error) { return []byte(.Template), nil }

func ( *SVCBDoHPath) ( []byte) error {
	.Template = string()
	return nil
}

func ( *SVCBDoHPath) ( string) error {
	,  := svcbParseParam()
	if  != nil {
		return fmt.Errorf("dns: svcbdohpath: %w", )
	}
	.Template = string()
	return nil
}

func ( *SVCBDoHPath) () SVCBKeyValue {
	return &SVCBDoHPath{
		Template: .Template,
	}
}

// The "ohttp" SvcParamKey is used to indicate that a service described in a SVCB RR
// can be accessed as a target using an associated gateway.
// Both the presentation and wire-format values for the "ohttp" parameter MUST be empty.
//
// See RFC 9460 (https://datatracker.ietf.org/doc/html/rfc9460/)
// and RFC 9230 (https://datatracker.ietf.org/doc/html/rfc9230/)
//
// A basic example of using the dohpath option together with the alpn
// option to indicate support for DNS over HTTPS on a certain path:
//
//	s := new(dns.SVCB)
//	s.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeSVCB, Class: dns.ClassINET}
//	e := new(dns.SVCBAlpn)
//	e.Alpn = []string{"h2", "h3"}
//	p := new(dns.SVCBOhttp)
//	s.Value = append(s.Value, e, p)
type SVCBOhttp struct{}

func (*SVCBOhttp) () SVCBKey          { return SVCB_OHTTP }
func (*SVCBOhttp) () SVCBKeyValue    { return &SVCBOhttp{} }
func (*SVCBOhttp) () ([]byte, error) { return []byte{}, nil }
func (*SVCBOhttp) () string        { return "" }
func (*SVCBOhttp) () int              { return 0 }

func (*SVCBOhttp) ( []byte) error {
	if len() != 0 {
		return errors.New("dns: svcbotthp: svcbotthp must have no value")
	}
	return nil
}

func (*SVCBOhttp) ( string) error {
	if  != "" {
		return errors.New("dns: svcbotthp: svcbotthp must have no value")
	}
	return nil
}

// SVCBLocal pair is intended for experimental/private use. The key is recommended
// to be in the range [SVCB_PRIVATE_LOWER, SVCB_PRIVATE_UPPER].
// Basic use pattern for creating a keyNNNNN option:
//
//	h := new(dns.HTTPS)
//	h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET}
//	e := new(dns.SVCBLocal)
//	e.KeyCode = 65400
//	e.Data = []byte("abc")
//	h.Value = append(h.Value, e)
type SVCBLocal struct {
	KeyCode SVCBKey // Never 65535 or any assigned keys.
	Data    []byte  // All byte sequences are allowed.
}

func ( *SVCBLocal) () SVCBKey          { return .KeyCode }
func ( *SVCBLocal) () string        { return svcbParamToStr(.Data) }
func ( *SVCBLocal) () ([]byte, error) { return cloneSlice(.Data), nil }
func ( *SVCBLocal) () int              { return len(.Data) }

func ( *SVCBLocal) ( []byte) error {
	.Data = cloneSlice()
	return nil
}

func ( *SVCBLocal) ( string) error {
	,  := svcbParseParam()
	if  != nil {
		return fmt.Errorf("dns: svcblocal: svcb private/experimental key %w", )
	}
	.Data = 
	return nil
}

func ( *SVCBLocal) () SVCBKeyValue {
	return &SVCBLocal{.KeyCode, cloneSlice(.Data)}
}

func ( *SVCB) () string {
	 := .Hdr.String() +
		strconv.Itoa(int(.Priority)) + " " +
		sprintName(.Target)
	for ,  := range .Value {
		 += " " + .Key().String() + "=\"" + .String() + "\""
	}
	return 
}

// areSVCBPairArraysEqual checks if SVCBKeyValue arrays are equal after sorting their
// copies. arrA and arrB have equal lengths, otherwise zduplicate.go wouldn't call this function.
func areSVCBPairArraysEqual( []SVCBKeyValue,  []SVCBKeyValue) bool {
	 = cloneSlice()
	 = cloneSlice()
	sort.Slice(, func(,  int) bool { return [].Key() < [].Key() })
	sort.Slice(, func(,  int) bool { return [].Key() < [].Key() })
	for ,  := range  {
		if .Key() != [].Key() {
			return false
		}
		,  := .pack()
		,  := [].pack()
		if  != nil ||  != nil || !bytes.Equal(, ) {
			return false
		}
	}
	return true
}

// svcbParamStr converts the value of an SVCB parameter into a DNS presentation-format string.
func svcbParamToStr( []byte) string {
	var  strings.Builder
	.Grow(4 * len())
	for ,  := range  {
		if ' ' <=  &&  <= '~' {
			switch  {
			case '"', ';', ' ', '\\':
				.WriteByte('\\')
				.WriteByte()
			default:
				.WriteByte()
			}
		} else {
			.WriteString(escapeByte())
		}
	}
	return .String()
}

// svcbParseParam parses a DNS presentation-format string into an SVCB parameter value.
func svcbParseParam( string) ([]byte, error) {
	 := make([]byte, 0, len())
	for  := 0;  < len(); {
		if [] != '\\' {
			 = append(, [])
			++
			continue
		}
		if +1 == len() {
			return nil, errors.New("escape unterminated")
		}
		if isDigit([+1]) {
			if +3 < len() && isDigit([+2]) && isDigit([+3]) {
				,  := strconv.ParseUint([+1:+4], 10, 8)
				if  == nil {
					 += 4
					 = append(, byte())
					continue
				}
			}
			return nil, errors.New("bad escaped octet")
		} else {
			 = append(, [+1])
			 += 2
		}
	}
	return , nil
}