"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "svcb.go" between
dns-1.1.48.tar.gz and dns-1.1.49.tar.gz

About: GO DNS implements a DNS library in Go.

svcb.go  (dns-1.1.48):svcb.go  (dns-1.1.49)
package dns package dns
import ( import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt"
"net" "net"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
) )
// SVCBKey is the type of the keys used in the SVCB RR. // SVCBKey is the type of the keys used in the SVCB RR.
type SVCBKey uint16 type SVCBKey uint16
// Keys defined in draft-ietf-dnsop-svcb-https-08 Section 14.3.2. // Keys defined in draft-ietf-dnsop-svcb-https-08 Section 14.3.2.
const ( const (
SVCB_MANDATORY SVCBKey = iota SVCB_MANDATORY SVCBKey = iota
SVCB_ALPN SVCB_ALPN
SVCB_NO_DEFAULT_ALPN SVCB_NO_DEFAULT_ALPN
SVCB_PORT SVCB_PORT
SVCB_IPV4HINT SVCB_IPV4HINT
SVCB_ECHCONFIG SVCB_ECHCONFIG
SVCB_IPV6HINT SVCB_IPV6HINT
SVCB_DOHPATH // draft-ietf-add-svcb-dns-02 Section 9
svcb_RESERVED SVCBKey = 65535 svcb_RESERVED SVCBKey = 65535
) )
var svcbKeyToStringMap = map[SVCBKey]string{ var svcbKeyToStringMap = map[SVCBKey]string{
SVCB_MANDATORY: "mandatory", SVCB_MANDATORY: "mandatory",
SVCB_ALPN: "alpn", SVCB_ALPN: "alpn",
SVCB_NO_DEFAULT_ALPN: "no-default-alpn", SVCB_NO_DEFAULT_ALPN: "no-default-alpn",
SVCB_PORT: "port", SVCB_PORT: "port",
SVCB_IPV4HINT: "ipv4hint", SVCB_IPV4HINT: "ipv4hint",
SVCB_ECHCONFIG: "ech", SVCB_ECHCONFIG: "ech",
SVCB_IPV6HINT: "ipv6hint", SVCB_IPV6HINT: "ipv6hint",
SVCB_DOHPATH: "dohpath",
} }
var svcbStringToKeyMap = reverseSVCBKeyMap(svcbKeyToStringMap) var svcbStringToKeyMap = reverseSVCBKeyMap(svcbKeyToStringMap)
func reverseSVCBKeyMap(m map[SVCBKey]string) map[string]SVCBKey { func reverseSVCBKeyMap(m map[SVCBKey]string) map[string]SVCBKey {
n := make(map[string]SVCBKey, len(m)) n := make(map[string]SVCBKey, len(m))
for u, s := range m { for u, s := range m {
n[s] = u n[s] = u
} }
return n return n
skipping to change at line 199 skipping to change at line 202
case SVCB_NO_DEFAULT_ALPN: case SVCB_NO_DEFAULT_ALPN:
return new(SVCBNoDefaultAlpn) return new(SVCBNoDefaultAlpn)
case SVCB_PORT: case SVCB_PORT:
return new(SVCBPort) return new(SVCBPort)
case SVCB_IPV4HINT: case SVCB_IPV4HINT:
return new(SVCBIPv4Hint) return new(SVCBIPv4Hint)
case SVCB_ECHCONFIG: case SVCB_ECHCONFIG:
return new(SVCBECHConfig) return new(SVCBECHConfig)
case SVCB_IPV6HINT: case SVCB_IPV6HINT:
return new(SVCBIPv6Hint) return new(SVCBIPv6Hint)
case SVCB_DOHPATH:
return new(SVCBDoHPath)
case svcb_RESERVED: case svcb_RESERVED:
return nil return nil
default: default:
e := new(SVCBLocal) e := new(SVCBLocal)
e.KeyCode = key e.KeyCode = key
return e return e
} }
} }
// SVCB RR. See RFC xxxx (https://tools.ietf.org/html/draft-ietf-dnsop-svcb-http s-08). // SVCB RR. See RFC xxxx (https://tools.ietf.org/html/draft-ietf-dnsop-svcb-http s-08).
//
// NOTE: The HTTPS/SVCB RFCs are in the draft stage.
// The API, including constants and types related to SVCBKeyValues, may
// change in future versions in accordance with the latest drafts.
type SVCB struct { type SVCB struct {
Hdr RR_Header Hdr RR_Header
Priority uint16 // If zero, Value must be empty or discarded by t he user of this library Priority uint16 // If zero, Value must be empty or discarded by t he user of this library
Target string `dns:"domain-name"` Target string `dns:"domain-name"`
Value []SVCBKeyValue `dns:"pairs"` Value []SVCBKeyValue `dns:"pairs"`
} }
// HTTPS RR. Everything valid for SVCB applies to HTTPS as well. // HTTPS RR. Everything valid for SVCB applies to HTTPS as well.
// Except that the HTTPS record is intended for use with the HTTP and HTTPS prot ocols. // Except that the HTTPS record is intended for use with the HTTP and HTTPS prot ocols.
//
// NOTE: The HTTPS/SVCB RFCs are in the draft stage.
// The API, including constants and types related to SVCBKeyValues, may
// change in future versions in accordance with the latest drafts.
type HTTPS struct { type HTTPS struct {
SVCB SVCB
} }
func (rr *HTTPS) String() string { func (rr *HTTPS) String() string {
return rr.SVCB.String() return rr.SVCB.String()
} }
func (rr *HTTPS) parse(c *zlexer, o string) *ParseError { func (rr *HTTPS) parse(c *zlexer, o string) *ParseError {
return rr.SVCB.parse(c, o) return rr.SVCB.parse(c, o)
skipping to change at line 333 skipping to change at line 346
// SVCBAlpn pair is used to list supported connection protocols. // SVCBAlpn pair is used to list supported connection protocols.
// The user of this library must ensure that at least one protocol is listed whe n alpn is present. // The user of this library must ensure that at least one protocol is listed whe n alpn is present.
// Protocol IDs can be found at: // Protocol IDs can be found at:
// https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-v alues.xhtml#alpn-protocol-ids // https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-v alues.xhtml#alpn-protocol-ids
// Basic use pattern for creating an alpn option: // Basic use pattern for creating an alpn option:
// //
// h := new(dns.HTTPS) // h := new(dns.HTTPS)
// h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassI NET} // h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassI NET}
// e := new(dns.SVCBAlpn) // e := new(dns.SVCBAlpn)
// e.Alpn = []string{"h2", "http/1.1"} // e.Alpn = []string{"h2", "http/1.1"}
// h.Value = append(o.Value, e) // h.Value = append(h.Value, e)
type SVCBAlpn struct { type SVCBAlpn struct {
Alpn []string Alpn []string
} }
func (*SVCBAlpn) Key() SVCBKey { return SVCB_ALPN } func (*SVCBAlpn) Key() SVCBKey { return SVCB_ALPN }
func (s *SVCBAlpn) String() string { return strings.Join(s.Alpn, ",") }
func (s *SVCBAlpn) String() string {
// An ALPN value is a comma-separated list of values, each of which can b
e
// an arbitrary binary value. In order to allow parsing, the comma and
// backslash characters are themselves excaped.
//
// However, this escaping is done in addition to the normal escaping whic
h
// 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#a
ppendix-A.1
var str strings.Builder
for i, alpn := range s.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
str.Grow(4*len(alpn) + 1)
if i > 0 {
str.WriteByte(',')
}
for j := 0; j < len(alpn); j++ {
e := alpn[j]
if ' ' > e || e > '~' {
str.WriteString(escapeByte(e))
continue
}
switch e {
// We escape a few characters which may confuse humans or
parsers.
case '"', ';', ' ':
str.WriteByte('\\')
str.WriteByte(e)
// The comma and backslash characters themselves must be
// doubly-escaped. We use `\\` for the first backslash an
d
// the escaped numeric value for the other value. We espe
cially
// don't want a comma in the output.
case ',':
str.WriteString(`\\\044`)
case '\\':
str.WriteString(`\\\092`)
default:
str.WriteByte(e)
}
}
}
return str.String()
}
func (s *SVCBAlpn) pack() ([]byte, error) { func (s *SVCBAlpn) pack() ([]byte, error) {
// Liberally estimate the size of an alpn as 10 octets // Liberally estimate the size of an alpn as 10 octets
b := make([]byte, 0, 10*len(s.Alpn)) b := make([]byte, 0, 10*len(s.Alpn))
for _, e := range s.Alpn { for _, e := range s.Alpn {
if e == "" { if e == "" {
return nil, errors.New("dns: svcbalpn: empty alpn-id") return nil, errors.New("dns: svcbalpn: empty alpn-id")
} }
if len(e) > 255 { if len(e) > 255 {
return nil, errors.New("dns: svcbalpn: alpn-id too long") return nil, errors.New("dns: svcbalpn: alpn-id too long")
skipping to change at line 374 skipping to change at line 431
return errors.New("dns: svcbalpn: alpn array overflowing" ) return errors.New("dns: svcbalpn: alpn array overflowing" )
} }
alpn = append(alpn, string(b[i:i+length])) alpn = append(alpn, string(b[i:i+length]))
i += length i += length
} }
s.Alpn = alpn s.Alpn = alpn
return nil return nil
} }
func (s *SVCBAlpn) parse(b string) error { func (s *SVCBAlpn) parse(b string) error {
s.Alpn = strings.Split(b, ",") if len(b) == 0 {
s.Alpn = []string{}
return nil
}
alpn := []string{}
a := []byte{}
for p := 0; p < len(b); {
c, q := nextByte(b, p)
if q == 0 {
return errors.New("dns: svcbalpn: unterminated escape")
}
p += q
// If we find a comma, we have finished reading an alpn.
if c == ',' {
if len(a) == 0 {
return errors.New("dns: svcbalpn: empty protocol
identifier")
}
alpn = append(alpn, string(a))
a = []byte{}
continue
}
// If it's a backslash, we need to handle a comma-separated list.
if c == '\\' {
dc, dq := nextByte(b, p)
if dq == 0 {
return errors.New("dns: svcbalpn: unterminated es
cape decoding comma-separated list")
}
if dc != '\\' && dc != ',' {
return errors.New("dns: svcbalpn: bad escaped cha
racter decoding comma-separated list")
}
p += dq
c = dc
}
a = append(a, c)
}
// Add the final alpn.
if len(a) == 0 {
return errors.New("dns: svcbalpn: last protocol identifier empty"
)
}
s.Alpn = append(alpn, string(a))
return nil return nil
} }
func (s *SVCBAlpn) len() int { func (s *SVCBAlpn) len() int {
var l int var l int
for _, e := range s.Alpn { for _, e := range s.Alpn {
l += 1 + len(e) l += 1 + len(e)
} }
return l return l
} }
skipping to change at line 672 skipping to change at line 769
hint := make([]net.IP, len(s.Hint)) hint := make([]net.IP, len(s.Hint))
for i, ip := range s.Hint { for i, ip := range s.Hint {
hint[i] = copyIP(ip) hint[i] = copyIP(ip)
} }
return &SVCBIPv6Hint{ return &SVCBIPv6Hint{
Hint: hint, Hint: hint,
} }
} }
// SVCBDoHPath pair is used to indicate the URI template that the
// clients may use to construct a DNS over HTTPS URI.
//
// See RFC xxxx (https://datatracker.ietf.org/doc/html/draft-ietf-add-svcb-dns-0
2)
// and RFC yyyy (https://datatracker.ietf.org/doc/html/draft-ietf-add-ddr-06).
//
// 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.ClassIN
ET}
// 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) Key() SVCBKey { return SVCB_DOHPATH }
func (s *SVCBDoHPath) String() string { return svcbParamToStr([]byte(s.Te
mplate)) }
func (s *SVCBDoHPath) len() int { return len(s.Template) }
func (s *SVCBDoHPath) pack() ([]byte, error) { return []byte(s.Template), nil }
func (s *SVCBDoHPath) unpack(b []byte) error {
s.Template = string(b)
return nil
}
func (s *SVCBDoHPath) parse(b string) error {
template, err := svcbParseParam(b)
if err != nil {
return fmt.Errorf("dns: svcbdohpath: %w", err)
}
s.Template = string(template)
return nil
}
func (s *SVCBDoHPath) copy() SVCBKeyValue {
return &SVCBDoHPath{
Template: s.Template,
}
}
// SVCBLocal pair is intended for experimental/private use. The key is recommend ed // SVCBLocal pair is intended for experimental/private use. The key is recommend ed
// to be in the range [SVCB_PRIVATE_LOWER, SVCB_PRIVATE_UPPER]. // to be in the range [SVCB_PRIVATE_LOWER, SVCB_PRIVATE_UPPER].
// Basic use pattern for creating a keyNNNNN option: // Basic use pattern for creating a keyNNNNN option:
// //
// h := new(dns.HTTPS) // h := new(dns.HTTPS)
// h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassI NET} // h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassI NET}
// e := new(dns.SVCBLocal) // e := new(dns.SVCBLocal)
// e.KeyCode = 65400 // e.KeyCode = 65400
// e.Data = []byte("abc") // e.Data = []byte("abc")
// h.Value = append(h.Value, e) // h.Value = append(h.Value, e)
type SVCBLocal struct { type SVCBLocal struct {
KeyCode SVCBKey // Never 65535 or any assigned keys. KeyCode SVCBKey // Never 65535 or any assigned keys.
Data []byte // All byte sequences are allowed. Data []byte // All byte sequences are allowed.
} }
func (s *SVCBLocal) Key() SVCBKey { return s.KeyCode } func (s *SVCBLocal) Key() SVCBKey { return s.KeyCode }
func (s *SVCBLocal) String() string { return svcbParamToStr(s.Data) }
func (s *SVCBLocal) pack() ([]byte, error) { return append([]byte(nil), s.Data.. .), nil } func (s *SVCBLocal) pack() ([]byte, error) { return append([]byte(nil), s.Data.. .), nil }
func (s *SVCBLocal) len() int { return len(s.Data) } func (s *SVCBLocal) len() int { return len(s.Data) }
func (s *SVCBLocal) unpack(b []byte) error { func (s *SVCBLocal) unpack(b []byte) error {
s.Data = append([]byte(nil), b...) s.Data = append([]byte(nil), b...)
return nil return nil
} }
func (s *SVCBLocal) String() string {
var str strings.Builder
str.Grow(4 * len(s.Data))
for _, e := range s.Data {
if ' ' <= e && e <= '~' {
switch e {
case '"', ';', ' ', '\\':
str.WriteByte('\\')
str.WriteByte(e)
default:
str.WriteByte(e)
}
} else {
str.WriteString(escapeByte(e))
}
}
return str.String()
}
func (s *SVCBLocal) parse(b string) error { func (s *SVCBLocal) parse(b string) error {
data := make([]byte, 0, len(b)) data, err := svcbParseParam(b)
for i := 0; i < len(b); { if err != nil {
if b[i] != '\\' { return fmt.Errorf("dns: svcblocal: svcb private/experimental key
data = append(data, b[i]) %w", err)
i++
continue
}
if i+1 == len(b) {
return errors.New("dns: svcblocal: svcb private/experimen
tal key escape unterminated")
}
if isDigit(b[i+1]) {
if i+3 < len(b) && isDigit(b[i+2]) && isDigit(b[i+3]) {
a, err := strconv.ParseUint(b[i+1:i+4], 10, 8)
if err == nil {
i += 4
data = append(data, byte(a))
continue
}
}
return errors.New("dns: svcblocal: svcb private/experimen
tal key bad escaped octet")
} else {
data = append(data, b[i+1])
i += 2
}
} }
s.Data = data s.Data = data
return nil return nil
} }
func (s *SVCBLocal) copy() SVCBKeyValue { func (s *SVCBLocal) copy() SVCBKeyValue {
return &SVCBLocal{s.KeyCode, return &SVCBLocal{s.KeyCode,
append([]byte(nil), s.Data...), append([]byte(nil), s.Data...),
} }
} }
skipping to change at line 780 skipping to change at line 886
return false return false
} }
b1, err1 := e.pack() b1, err1 := e.pack()
b2, err2 := b[i].pack() b2, err2 := b[i].pack()
if err1 != nil || err2 != nil || !bytes.Equal(b1, b2) { if err1 != nil || err2 != nil || !bytes.Equal(b1, b2) {
return false return false
} }
} }
return true return true
} }
// svcbParamStr converts the value of an SVCB parameter into a DNS presentation-
format string.
func svcbParamToStr(s []byte) string {
var str strings.Builder
str.Grow(4 * len(s))
for _, e := range s {
if ' ' <= e && e <= '~' {
switch e {
case '"', ';', ' ', '\\':
str.WriteByte('\\')
str.WriteByte(e)
default:
str.WriteByte(e)
}
} else {
str.WriteString(escapeByte(e))
}
}
return str.String()
}
// svcbParseParam parses a DNS presentation-format string into an SVCB parameter
value.
func svcbParseParam(b string) ([]byte, error) {
data := make([]byte, 0, len(b))
for i := 0; i < len(b); {
if b[i] != '\\' {
data = append(data, b[i])
i++
continue
}
if i+1 == len(b) {
return nil, errors.New("escape unterminated")
}
if isDigit(b[i+1]) {
if i+3 < len(b) && isDigit(b[i+2]) && isDigit(b[i+3]) {
a, err := strconv.ParseUint(b[i+1:i+4], 10, 8)
if err == nil {
i += 4
data = append(data, byte(a))
continue
}
}
return nil, errors.New("bad escaped octet")
} else {
data = append(data, b[i+1])
i += 2
}
}
return data, nil
}
 End of changes. 14 change blocks. 
49 lines changed or deleted 168 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)