mirror of
https://github.com/miekg/dns.git
synced 2025-08-10 19:46:57 +02:00
* Allow use of fs.FS for $INCLUDE and wrap errors This adds ZoneParser.SetIncludeAllowedFS, to specify an fs.FS when enabling support for $INCLUDE, for reading included files from somewhere other than the local filesystem. I've also modified ParseError to support wrapping another error, such as errors encountered while opening the $INCLUDE target. This allows for much more robust handling, using errors.Is() instead of testing for particular strings (which may not be identical between fs.FS implementations). ParseError was being constructed in a lot of places using positional instead of named members. Updating ParseError initialization after the new member field was added makes this change seem a lot larger than it actually is. The changes here should be completely backwards compatible. The ParseError change should be invisible to anyone not trying to unwrap it, and ZoneParser will continue to use os.Open if the existing SetIncludeAllowed method is called instead of the new SetIncludeAllowedFS method. * Don't duplicate SetIncludeAllowed; clarify edge cases Rather than duplicate functionality between SetIncludeAllowed and SetIncludeAllowedFS, have a method SetIncludeFS, which only sets the fs.FS. I've improved the documentation to point out some considerations for users hoping to use fs.FS as a security boundary. Per the fs.ValidPath documentation, fs.FS implementations must use path (not filepath) semantics, with slash as a separator (even on Windows). Some, like os.DirFS, also require all paths to be relative. I've clarified this in the documentation, made the includePath manipulation more robust to edge cases, and added some additional tests for relative and absolute paths.
1898 lines
45 KiB
Go
1898 lines
45 KiB
Go
package dns
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// A remainder of the rdata with embedded spaces, return the parsed string (sans the spaces)
|
|
// or an error
|
|
func endingToString(c *zlexer, errstr string) (string, *ParseError) {
|
|
var s strings.Builder
|
|
l, _ := c.Next() // zString
|
|
for l.value != zNewline && l.value != zEOF {
|
|
if l.err {
|
|
return s.String(), &ParseError{err: errstr, lex: l}
|
|
}
|
|
switch l.value {
|
|
case zString:
|
|
s.WriteString(l.token)
|
|
case zBlank: // Ok
|
|
default:
|
|
return "", &ParseError{err: errstr, lex: l}
|
|
}
|
|
l, _ = c.Next()
|
|
}
|
|
|
|
return s.String(), nil
|
|
}
|
|
|
|
// A remainder of the rdata with embedded spaces, split on unquoted whitespace
|
|
// and return the parsed string slice or an error
|
|
func endingToTxtSlice(c *zlexer, errstr string) ([]string, *ParseError) {
|
|
// Get the remaining data until we see a zNewline
|
|
l, _ := c.Next()
|
|
if l.err {
|
|
return nil, &ParseError{err: errstr, lex: l}
|
|
}
|
|
|
|
// Build the slice
|
|
s := make([]string, 0)
|
|
quote := false
|
|
empty := false
|
|
for l.value != zNewline && l.value != zEOF {
|
|
if l.err {
|
|
return nil, &ParseError{err: errstr, lex: l}
|
|
}
|
|
switch l.value {
|
|
case zString:
|
|
empty = false
|
|
if len(l.token) > 255 {
|
|
// split up tokens that are larger than 255 into 255-chunks
|
|
sx := []string{}
|
|
p, i := 0, 255
|
|
for {
|
|
if i <= len(l.token) {
|
|
sx = append(sx, l.token[p:i])
|
|
} else {
|
|
sx = append(sx, l.token[p:])
|
|
break
|
|
|
|
}
|
|
p, i = p+255, i+255
|
|
}
|
|
s = append(s, sx...)
|
|
break
|
|
}
|
|
|
|
s = append(s, l.token)
|
|
case zBlank:
|
|
if quote {
|
|
// zBlank can only be seen in between txt parts.
|
|
return nil, &ParseError{err: errstr, lex: l}
|
|
}
|
|
case zQuote:
|
|
if empty && quote {
|
|
s = append(s, "")
|
|
}
|
|
quote = !quote
|
|
empty = true
|
|
default:
|
|
return nil, &ParseError{err: errstr, lex: l}
|
|
}
|
|
l, _ = c.Next()
|
|
}
|
|
|
|
if quote {
|
|
return nil, &ParseError{err: errstr, lex: l}
|
|
}
|
|
|
|
return s, nil
|
|
}
|
|
|
|
func (rr *A) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
rr.A = net.ParseIP(l.token)
|
|
// IPv4 addresses cannot include ":".
|
|
// We do this rather than use net.IP's To4() because
|
|
// To4() treats IPv4-mapped IPv6 addresses as being
|
|
// IPv4.
|
|
isIPv4 := !strings.Contains(l.token, ":")
|
|
if rr.A == nil || !isIPv4 || l.err {
|
|
return &ParseError{err: "bad A A", lex: l}
|
|
}
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *AAAA) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
rr.AAAA = net.ParseIP(l.token)
|
|
// IPv6 addresses must include ":", and IPv4
|
|
// addresses cannot include ":".
|
|
isIPv6 := strings.Contains(l.token, ":")
|
|
if rr.AAAA == nil || !isIPv6 || l.err {
|
|
return &ParseError{err: "bad AAAA AAAA", lex: l}
|
|
}
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *NS) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
name, nameOk := toAbsoluteName(l.token, o)
|
|
if l.err || !nameOk {
|
|
return &ParseError{err: "bad NS Ns", lex: l}
|
|
}
|
|
rr.Ns = name
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *PTR) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
name, nameOk := toAbsoluteName(l.token, o)
|
|
if l.err || !nameOk {
|
|
return &ParseError{err: "bad PTR Ptr", lex: l}
|
|
}
|
|
rr.Ptr = name
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *NSAPPTR) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
name, nameOk := toAbsoluteName(l.token, o)
|
|
if l.err || !nameOk {
|
|
return &ParseError{err: "bad NSAP-PTR Ptr", lex: l}
|
|
}
|
|
rr.Ptr = name
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *RP) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
mbox, mboxOk := toAbsoluteName(l.token, o)
|
|
if l.err || !mboxOk {
|
|
return &ParseError{err: "bad RP Mbox", lex: l}
|
|
}
|
|
rr.Mbox = mbox
|
|
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
rr.Txt = l.token
|
|
|
|
txt, txtOk := toAbsoluteName(l.token, o)
|
|
if l.err || !txtOk {
|
|
return &ParseError{err: "bad RP Txt", lex: l}
|
|
}
|
|
rr.Txt = txt
|
|
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *MR) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
name, nameOk := toAbsoluteName(l.token, o)
|
|
if l.err || !nameOk {
|
|
return &ParseError{err: "bad MR Mr", lex: l}
|
|
}
|
|
rr.Mr = name
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *MB) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
name, nameOk := toAbsoluteName(l.token, o)
|
|
if l.err || !nameOk {
|
|
return &ParseError{err: "bad MB Mb", lex: l}
|
|
}
|
|
rr.Mb = name
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *MG) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
name, nameOk := toAbsoluteName(l.token, o)
|
|
if l.err || !nameOk {
|
|
return &ParseError{err: "bad MG Mg", lex: l}
|
|
}
|
|
rr.Mg = name
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *HINFO) parse(c *zlexer, o string) *ParseError {
|
|
chunks, e := endingToTxtSlice(c, "bad HINFO Fields")
|
|
if e != nil {
|
|
return e
|
|
}
|
|
|
|
if ln := len(chunks); ln == 0 {
|
|
return nil
|
|
} else if ln == 1 {
|
|
// Can we split it?
|
|
if out := strings.Fields(chunks[0]); len(out) > 1 {
|
|
chunks = out
|
|
} else {
|
|
chunks = append(chunks, "")
|
|
}
|
|
}
|
|
|
|
rr.Cpu = chunks[0]
|
|
rr.Os = strings.Join(chunks[1:], " ")
|
|
|
|
return nil
|
|
}
|
|
|
|
func (rr *MINFO) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
rmail, rmailOk := toAbsoluteName(l.token, o)
|
|
if l.err || !rmailOk {
|
|
return &ParseError{err: "bad MINFO Rmail", lex: l}
|
|
}
|
|
rr.Rmail = rmail
|
|
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
rr.Email = l.token
|
|
|
|
email, emailOk := toAbsoluteName(l.token, o)
|
|
if l.err || !emailOk {
|
|
return &ParseError{err: "bad MINFO Email", lex: l}
|
|
}
|
|
rr.Email = email
|
|
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *MF) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
name, nameOk := toAbsoluteName(l.token, o)
|
|
if l.err || !nameOk {
|
|
return &ParseError{err: "bad MF Mf", lex: l}
|
|
}
|
|
rr.Mf = name
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *MD) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
name, nameOk := toAbsoluteName(l.token, o)
|
|
if l.err || !nameOk {
|
|
return &ParseError{err: "bad MD Md", lex: l}
|
|
}
|
|
rr.Md = name
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *MX) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
i, e := strconv.ParseUint(l.token, 10, 16)
|
|
if e != nil || l.err {
|
|
return &ParseError{err: "bad MX Pref", lex: l}
|
|
}
|
|
rr.Preference = uint16(i)
|
|
|
|
c.Next() // zBlank
|
|
l, _ = c.Next() // zString
|
|
rr.Mx = l.token
|
|
|
|
name, nameOk := toAbsoluteName(l.token, o)
|
|
if l.err || !nameOk {
|
|
return &ParseError{err: "bad MX Mx", lex: l}
|
|
}
|
|
rr.Mx = name
|
|
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *RT) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
i, e := strconv.ParseUint(l.token, 10, 16)
|
|
if e != nil {
|
|
return &ParseError{err: "bad RT Preference", lex: l}
|
|
}
|
|
rr.Preference = uint16(i)
|
|
|
|
c.Next() // zBlank
|
|
l, _ = c.Next() // zString
|
|
rr.Host = l.token
|
|
|
|
name, nameOk := toAbsoluteName(l.token, o)
|
|
if l.err || !nameOk {
|
|
return &ParseError{err: "bad RT Host", lex: l}
|
|
}
|
|
rr.Host = name
|
|
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *AFSDB) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
i, e := strconv.ParseUint(l.token, 10, 16)
|
|
if e != nil || l.err {
|
|
return &ParseError{err: "bad AFSDB Subtype", lex: l}
|
|
}
|
|
rr.Subtype = uint16(i)
|
|
|
|
c.Next() // zBlank
|
|
l, _ = c.Next() // zString
|
|
rr.Hostname = l.token
|
|
|
|
name, nameOk := toAbsoluteName(l.token, o)
|
|
if l.err || !nameOk {
|
|
return &ParseError{err: "bad AFSDB Hostname", lex: l}
|
|
}
|
|
rr.Hostname = name
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *X25) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
if l.err {
|
|
return &ParseError{err: "bad X25 PSDNAddress", lex: l}
|
|
}
|
|
rr.PSDNAddress = l.token
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *KX) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
i, e := strconv.ParseUint(l.token, 10, 16)
|
|
if e != nil || l.err {
|
|
return &ParseError{err: "bad KX Pref", lex: l}
|
|
}
|
|
rr.Preference = uint16(i)
|
|
|
|
c.Next() // zBlank
|
|
l, _ = c.Next() // zString
|
|
rr.Exchanger = l.token
|
|
|
|
name, nameOk := toAbsoluteName(l.token, o)
|
|
if l.err || !nameOk {
|
|
return &ParseError{err: "bad KX Exchanger", lex: l}
|
|
}
|
|
rr.Exchanger = name
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *CNAME) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
name, nameOk := toAbsoluteName(l.token, o)
|
|
if l.err || !nameOk {
|
|
return &ParseError{err: "bad CNAME Target", lex: l}
|
|
}
|
|
rr.Target = name
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *DNAME) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
name, nameOk := toAbsoluteName(l.token, o)
|
|
if l.err || !nameOk {
|
|
return &ParseError{err: "bad DNAME Target", lex: l}
|
|
}
|
|
rr.Target = name
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *SOA) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
ns, nsOk := toAbsoluteName(l.token, o)
|
|
if l.err || !nsOk {
|
|
return &ParseError{err: "bad SOA Ns", lex: l}
|
|
}
|
|
rr.Ns = ns
|
|
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
rr.Mbox = l.token
|
|
|
|
mbox, mboxOk := toAbsoluteName(l.token, o)
|
|
if l.err || !mboxOk {
|
|
return &ParseError{err: "bad SOA Mbox", lex: l}
|
|
}
|
|
rr.Mbox = mbox
|
|
|
|
c.Next() // zBlank
|
|
|
|
var (
|
|
v uint32
|
|
ok bool
|
|
)
|
|
for i := 0; i < 5; i++ {
|
|
l, _ = c.Next()
|
|
if l.err {
|
|
return &ParseError{err: "bad SOA zone parameter", lex: l}
|
|
}
|
|
if j, err := strconv.ParseUint(l.token, 10, 32); err != nil {
|
|
if i == 0 {
|
|
// Serial must be a number
|
|
return &ParseError{err: "bad SOA zone parameter", lex: l}
|
|
}
|
|
// We allow other fields to be unitful duration strings
|
|
if v, ok = stringToTTL(l.token); !ok {
|
|
return &ParseError{err: "bad SOA zone parameter", lex: l}
|
|
|
|
}
|
|
} else {
|
|
v = uint32(j)
|
|
}
|
|
switch i {
|
|
case 0:
|
|
rr.Serial = v
|
|
c.Next() // zBlank
|
|
case 1:
|
|
rr.Refresh = v
|
|
c.Next() // zBlank
|
|
case 2:
|
|
rr.Retry = v
|
|
c.Next() // zBlank
|
|
case 3:
|
|
rr.Expire = v
|
|
c.Next() // zBlank
|
|
case 4:
|
|
rr.Minttl = v
|
|
}
|
|
}
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *SRV) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
i, e := strconv.ParseUint(l.token, 10, 16)
|
|
if e != nil || l.err {
|
|
return &ParseError{err: "bad SRV Priority", lex: l}
|
|
}
|
|
rr.Priority = uint16(i)
|
|
|
|
c.Next() // zBlank
|
|
l, _ = c.Next() // zString
|
|
i, e1 := strconv.ParseUint(l.token, 10, 16)
|
|
if e1 != nil || l.err {
|
|
return &ParseError{err: "bad SRV Weight", lex: l}
|
|
}
|
|
rr.Weight = uint16(i)
|
|
|
|
c.Next() // zBlank
|
|
l, _ = c.Next() // zString
|
|
i, e2 := strconv.ParseUint(l.token, 10, 16)
|
|
if e2 != nil || l.err {
|
|
return &ParseError{err: "bad SRV Port", lex: l}
|
|
}
|
|
rr.Port = uint16(i)
|
|
|
|
c.Next() // zBlank
|
|
l, _ = c.Next() // zString
|
|
rr.Target = l.token
|
|
|
|
name, nameOk := toAbsoluteName(l.token, o)
|
|
if l.err || !nameOk {
|
|
return &ParseError{err: "bad SRV Target", lex: l}
|
|
}
|
|
rr.Target = name
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *NAPTR) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
i, e := strconv.ParseUint(l.token, 10, 16)
|
|
if e != nil || l.err {
|
|
return &ParseError{err: "bad NAPTR Order", lex: l}
|
|
}
|
|
rr.Order = uint16(i)
|
|
|
|
c.Next() // zBlank
|
|
l, _ = c.Next() // zString
|
|
i, e1 := strconv.ParseUint(l.token, 10, 16)
|
|
if e1 != nil || l.err {
|
|
return &ParseError{err: "bad NAPTR Preference", lex: l}
|
|
}
|
|
rr.Preference = uint16(i)
|
|
|
|
// Flags
|
|
c.Next() // zBlank
|
|
l, _ = c.Next() // _QUOTE
|
|
if l.value != zQuote {
|
|
return &ParseError{err: "bad NAPTR Flags", lex: l}
|
|
}
|
|
l, _ = c.Next() // Either String or Quote
|
|
if l.value == zString {
|
|
rr.Flags = l.token
|
|
l, _ = c.Next() // _QUOTE
|
|
if l.value != zQuote {
|
|
return &ParseError{err: "bad NAPTR Flags", lex: l}
|
|
}
|
|
} else if l.value == zQuote {
|
|
rr.Flags = ""
|
|
} else {
|
|
return &ParseError{err: "bad NAPTR Flags", lex: l}
|
|
}
|
|
|
|
// Service
|
|
c.Next() // zBlank
|
|
l, _ = c.Next() // _QUOTE
|
|
if l.value != zQuote {
|
|
return &ParseError{err: "bad NAPTR Service", lex: l}
|
|
}
|
|
l, _ = c.Next() // Either String or Quote
|
|
if l.value == zString {
|
|
rr.Service = l.token
|
|
l, _ = c.Next() // _QUOTE
|
|
if l.value != zQuote {
|
|
return &ParseError{err: "bad NAPTR Service", lex: l}
|
|
}
|
|
} else if l.value == zQuote {
|
|
rr.Service = ""
|
|
} else {
|
|
return &ParseError{err: "bad NAPTR Service", lex: l}
|
|
}
|
|
|
|
// Regexp
|
|
c.Next() // zBlank
|
|
l, _ = c.Next() // _QUOTE
|
|
if l.value != zQuote {
|
|
return &ParseError{err: "bad NAPTR Regexp", lex: l}
|
|
}
|
|
l, _ = c.Next() // Either String or Quote
|
|
if l.value == zString {
|
|
rr.Regexp = l.token
|
|
l, _ = c.Next() // _QUOTE
|
|
if l.value != zQuote {
|
|
return &ParseError{err: "bad NAPTR Regexp", lex: l}
|
|
}
|
|
} else if l.value == zQuote {
|
|
rr.Regexp = ""
|
|
} else {
|
|
return &ParseError{err: "bad NAPTR Regexp", lex: l}
|
|
}
|
|
|
|
// After quote no space??
|
|
c.Next() // zBlank
|
|
l, _ = c.Next() // zString
|
|
rr.Replacement = l.token
|
|
|
|
name, nameOk := toAbsoluteName(l.token, o)
|
|
if l.err || !nameOk {
|
|
return &ParseError{err: "bad NAPTR Replacement", lex: l}
|
|
}
|
|
rr.Replacement = name
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *TALINK) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
previousName, previousNameOk := toAbsoluteName(l.token, o)
|
|
if l.err || !previousNameOk {
|
|
return &ParseError{err: "bad TALINK PreviousName", lex: l}
|
|
}
|
|
rr.PreviousName = previousName
|
|
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
rr.NextName = l.token
|
|
|
|
nextName, nextNameOk := toAbsoluteName(l.token, o)
|
|
if l.err || !nextNameOk {
|
|
return &ParseError{err: "bad TALINK NextName", lex: l}
|
|
}
|
|
rr.NextName = nextName
|
|
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *LOC) parse(c *zlexer, o string) *ParseError {
|
|
// Non zero defaults for LOC record, see RFC 1876, Section 3.
|
|
rr.Size = 0x12 // 1e2 cm (1m)
|
|
rr.HorizPre = 0x16 // 1e6 cm (10000m)
|
|
rr.VertPre = 0x13 // 1e3 cm (10m)
|
|
ok := false
|
|
|
|
// North
|
|
l, _ := c.Next()
|
|
i, e := strconv.ParseUint(l.token, 10, 32)
|
|
if e != nil || l.err || i > 90 {
|
|
return &ParseError{err: "bad LOC Latitude", lex: l}
|
|
}
|
|
rr.Latitude = 1000 * 60 * 60 * uint32(i)
|
|
|
|
c.Next() // zBlank
|
|
// Either number, 'N' or 'S'
|
|
l, _ = c.Next()
|
|
if rr.Latitude, ok = locCheckNorth(l.token, rr.Latitude); ok {
|
|
goto East
|
|
}
|
|
if i, err := strconv.ParseUint(l.token, 10, 32); err != nil || l.err || i > 59 {
|
|
return &ParseError{err: "bad LOC Latitude minutes", lex: l}
|
|
} else {
|
|
rr.Latitude += 1000 * 60 * uint32(i)
|
|
}
|
|
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
if i, err := strconv.ParseFloat(l.token, 64); err != nil || l.err || i < 0 || i >= 60 {
|
|
return &ParseError{err: "bad LOC Latitude seconds", lex: l}
|
|
} else {
|
|
rr.Latitude += uint32(1000 * i)
|
|
}
|
|
c.Next() // zBlank
|
|
// Either number, 'N' or 'S'
|
|
l, _ = c.Next()
|
|
if rr.Latitude, ok = locCheckNorth(l.token, rr.Latitude); ok {
|
|
goto East
|
|
}
|
|
// If still alive, flag an error
|
|
return &ParseError{err: "bad LOC Latitude North/South", lex: l}
|
|
|
|
East:
|
|
// East
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
if i, err := strconv.ParseUint(l.token, 10, 32); err != nil || l.err || i > 180 {
|
|
return &ParseError{err: "bad LOC Longitude", lex: l}
|
|
} else {
|
|
rr.Longitude = 1000 * 60 * 60 * uint32(i)
|
|
}
|
|
c.Next() // zBlank
|
|
// Either number, 'E' or 'W'
|
|
l, _ = c.Next()
|
|
if rr.Longitude, ok = locCheckEast(l.token, rr.Longitude); ok {
|
|
goto Altitude
|
|
}
|
|
if i, err := strconv.ParseUint(l.token, 10, 32); err != nil || l.err || i > 59 {
|
|
return &ParseError{err: "bad LOC Longitude minutes", lex: l}
|
|
} else {
|
|
rr.Longitude += 1000 * 60 * uint32(i)
|
|
}
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
if i, err := strconv.ParseFloat(l.token, 64); err != nil || l.err || i < 0 || i >= 60 {
|
|
return &ParseError{err: "bad LOC Longitude seconds", lex: l}
|
|
} else {
|
|
rr.Longitude += uint32(1000 * i)
|
|
}
|
|
c.Next() // zBlank
|
|
// Either number, 'E' or 'W'
|
|
l, _ = c.Next()
|
|
if rr.Longitude, ok = locCheckEast(l.token, rr.Longitude); ok {
|
|
goto Altitude
|
|
}
|
|
// If still alive, flag an error
|
|
return &ParseError{err: "bad LOC Longitude East/West", lex: l}
|
|
|
|
Altitude:
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
if l.token == "" || l.err {
|
|
return &ParseError{err: "bad LOC Altitude", lex: l}
|
|
}
|
|
if l.token[len(l.token)-1] == 'M' || l.token[len(l.token)-1] == 'm' {
|
|
l.token = l.token[0 : len(l.token)-1]
|
|
}
|
|
if i, err := strconv.ParseFloat(l.token, 64); err != nil {
|
|
return &ParseError{err: "bad LOC Altitude", lex: l}
|
|
} else {
|
|
rr.Altitude = uint32(i*100.0 + 10000000.0 + 0.5)
|
|
}
|
|
|
|
// And now optionally the other values
|
|
l, _ = c.Next()
|
|
count := 0
|
|
for l.value != zNewline && l.value != zEOF {
|
|
switch l.value {
|
|
case zString:
|
|
switch count {
|
|
case 0: // Size
|
|
exp, m, ok := stringToCm(l.token)
|
|
if !ok {
|
|
return &ParseError{err: "bad LOC Size", lex: l}
|
|
}
|
|
rr.Size = exp&0x0f | m<<4&0xf0
|
|
case 1: // HorizPre
|
|
exp, m, ok := stringToCm(l.token)
|
|
if !ok {
|
|
return &ParseError{err: "bad LOC HorizPre", lex: l}
|
|
}
|
|
rr.HorizPre = exp&0x0f | m<<4&0xf0
|
|
case 2: // VertPre
|
|
exp, m, ok := stringToCm(l.token)
|
|
if !ok {
|
|
return &ParseError{err: "bad LOC VertPre", lex: l}
|
|
}
|
|
rr.VertPre = exp&0x0f | m<<4&0xf0
|
|
}
|
|
count++
|
|
case zBlank:
|
|
// Ok
|
|
default:
|
|
return &ParseError{err: "bad LOC Size, HorizPre or VertPre", lex: l}
|
|
}
|
|
l, _ = c.Next()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (rr *HIP) parse(c *zlexer, o string) *ParseError {
|
|
// HitLength is not represented
|
|
l, _ := c.Next()
|
|
i, e := strconv.ParseUint(l.token, 10, 8)
|
|
if e != nil || l.err {
|
|
return &ParseError{err: "bad HIP PublicKeyAlgorithm", lex: l}
|
|
}
|
|
rr.PublicKeyAlgorithm = uint8(i)
|
|
|
|
c.Next() // zBlank
|
|
l, _ = c.Next() // zString
|
|
if l.token == "" || l.err {
|
|
return &ParseError{err: "bad HIP Hit", lex: l}
|
|
}
|
|
rr.Hit = l.token // This can not contain spaces, see RFC 5205 Section 6.
|
|
rr.HitLength = uint8(len(rr.Hit)) / 2
|
|
|
|
c.Next() // zBlank
|
|
l, _ = c.Next() // zString
|
|
if l.token == "" || l.err {
|
|
return &ParseError{err: "bad HIP PublicKey", lex: l}
|
|
}
|
|
rr.PublicKey = l.token // This cannot contain spaces
|
|
decodedPK, decodedPKerr := base64.StdEncoding.DecodeString(rr.PublicKey)
|
|
if decodedPKerr != nil {
|
|
return &ParseError{err: "bad HIP PublicKey", lex: l}
|
|
}
|
|
rr.PublicKeyLength = uint16(len(decodedPK))
|
|
|
|
// RendezvousServers (if any)
|
|
l, _ = c.Next()
|
|
var xs []string
|
|
for l.value != zNewline && l.value != zEOF {
|
|
switch l.value {
|
|
case zString:
|
|
name, nameOk := toAbsoluteName(l.token, o)
|
|
if l.err || !nameOk {
|
|
return &ParseError{err: "bad HIP RendezvousServers", lex: l}
|
|
}
|
|
xs = append(xs, name)
|
|
case zBlank:
|
|
// Ok
|
|
default:
|
|
return &ParseError{err: "bad HIP RendezvousServers", lex: l}
|
|
}
|
|
l, _ = c.Next()
|
|
}
|
|
|
|
rr.RendezvousServers = xs
|
|
return nil
|
|
}
|
|
|
|
func (rr *CERT) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
if v, ok := StringToCertType[l.token]; ok {
|
|
rr.Type = v
|
|
} else if i, err := strconv.ParseUint(l.token, 10, 16); err != nil {
|
|
return &ParseError{err: "bad CERT Type", lex: l}
|
|
} else {
|
|
rr.Type = uint16(i)
|
|
}
|
|
c.Next() // zBlank
|
|
l, _ = c.Next() // zString
|
|
i, e := strconv.ParseUint(l.token, 10, 16)
|
|
if e != nil || l.err {
|
|
return &ParseError{err: "bad CERT KeyTag", lex: l}
|
|
}
|
|
rr.KeyTag = uint16(i)
|
|
c.Next() // zBlank
|
|
l, _ = c.Next() // zString
|
|
if v, ok := StringToAlgorithm[l.token]; ok {
|
|
rr.Algorithm = v
|
|
} else if i, err := strconv.ParseUint(l.token, 10, 8); err != nil {
|
|
return &ParseError{err: "bad CERT Algorithm", lex: l}
|
|
} else {
|
|
rr.Algorithm = uint8(i)
|
|
}
|
|
s, e1 := endingToString(c, "bad CERT Certificate")
|
|
if e1 != nil {
|
|
return e1
|
|
}
|
|
rr.Certificate = s
|
|
return nil
|
|
}
|
|
|
|
func (rr *OPENPGPKEY) parse(c *zlexer, o string) *ParseError {
|
|
s, e := endingToString(c, "bad OPENPGPKEY PublicKey")
|
|
if e != nil {
|
|
return e
|
|
}
|
|
rr.PublicKey = s
|
|
return nil
|
|
}
|
|
|
|
func (rr *CSYNC) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
j, e := strconv.ParseUint(l.token, 10, 32)
|
|
if e != nil {
|
|
// Serial must be a number
|
|
return &ParseError{err: "bad CSYNC serial", lex: l}
|
|
}
|
|
rr.Serial = uint32(j)
|
|
|
|
c.Next() // zBlank
|
|
|
|
l, _ = c.Next()
|
|
j, e1 := strconv.ParseUint(l.token, 10, 16)
|
|
if e1 != nil {
|
|
// Serial must be a number
|
|
return &ParseError{err: "bad CSYNC flags", lex: l}
|
|
}
|
|
rr.Flags = uint16(j)
|
|
|
|
rr.TypeBitMap = make([]uint16, 0)
|
|
var (
|
|
k uint16
|
|
ok bool
|
|
)
|
|
l, _ = c.Next()
|
|
for l.value != zNewline && l.value != zEOF {
|
|
switch l.value {
|
|
case zBlank:
|
|
// Ok
|
|
case zString:
|
|
tokenUpper := strings.ToUpper(l.token)
|
|
if k, ok = StringToType[tokenUpper]; !ok {
|
|
if k, ok = typeToInt(l.token); !ok {
|
|
return &ParseError{err: "bad CSYNC TypeBitMap", lex: l}
|
|
}
|
|
}
|
|
rr.TypeBitMap = append(rr.TypeBitMap, k)
|
|
default:
|
|
return &ParseError{err: "bad CSYNC TypeBitMap", lex: l}
|
|
}
|
|
l, _ = c.Next()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (rr *ZONEMD) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
i, e := strconv.ParseUint(l.token, 10, 32)
|
|
if e != nil || l.err {
|
|
return &ParseError{err: "bad ZONEMD Serial", lex: l}
|
|
}
|
|
rr.Serial = uint32(i)
|
|
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
i, e1 := strconv.ParseUint(l.token, 10, 8)
|
|
if e1 != nil || l.err {
|
|
return &ParseError{err: "bad ZONEMD Scheme", lex: l}
|
|
}
|
|
rr.Scheme = uint8(i)
|
|
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
i, err := strconv.ParseUint(l.token, 10, 8)
|
|
if err != nil || l.err {
|
|
return &ParseError{err: "bad ZONEMD Hash Algorithm", lex: l}
|
|
}
|
|
rr.Hash = uint8(i)
|
|
|
|
s, e2 := endingToString(c, "bad ZONEMD Digest")
|
|
if e2 != nil {
|
|
return e2
|
|
}
|
|
rr.Digest = s
|
|
return nil
|
|
}
|
|
|
|
func (rr *SIG) parse(c *zlexer, o string) *ParseError { return rr.RRSIG.parse(c, o) }
|
|
|
|
func (rr *RRSIG) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
tokenUpper := strings.ToUpper(l.token)
|
|
if t, ok := StringToType[tokenUpper]; !ok {
|
|
if strings.HasPrefix(tokenUpper, "TYPE") {
|
|
t, ok = typeToInt(l.token)
|
|
if !ok {
|
|
return &ParseError{err: "bad RRSIG Typecovered", lex: l}
|
|
}
|
|
rr.TypeCovered = t
|
|
} else {
|
|
return &ParseError{err: "bad RRSIG Typecovered", lex: l}
|
|
}
|
|
} else {
|
|
rr.TypeCovered = t
|
|
}
|
|
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
if l.err {
|
|
return &ParseError{err: "bad RRSIG Algorithm", lex: l}
|
|
}
|
|
i, e := strconv.ParseUint(l.token, 10, 8)
|
|
rr.Algorithm = uint8(i) // if 0 we'll check the mnemonic in the if
|
|
if e != nil {
|
|
v, ok := StringToAlgorithm[l.token]
|
|
if !ok {
|
|
return &ParseError{err: "bad RRSIG Algorithm", lex: l}
|
|
}
|
|
rr.Algorithm = v
|
|
}
|
|
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
i, e1 := strconv.ParseUint(l.token, 10, 8)
|
|
if e1 != nil || l.err {
|
|
return &ParseError{err: "bad RRSIG Labels", lex: l}
|
|
}
|
|
rr.Labels = uint8(i)
|
|
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
i, e2 := strconv.ParseUint(l.token, 10, 32)
|
|
if e2 != nil || l.err {
|
|
return &ParseError{err: "bad RRSIG OrigTtl", lex: l}
|
|
}
|
|
rr.OrigTtl = uint32(i)
|
|
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
if i, err := StringToTime(l.token); err != nil {
|
|
// Try to see if all numeric and use it as epoch
|
|
if i, err := strconv.ParseUint(l.token, 10, 32); err == nil {
|
|
rr.Expiration = uint32(i)
|
|
} else {
|
|
return &ParseError{err: "bad RRSIG Expiration", lex: l}
|
|
}
|
|
} else {
|
|
rr.Expiration = i
|
|
}
|
|
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
if i, err := StringToTime(l.token); err != nil {
|
|
if i, err := strconv.ParseUint(l.token, 10, 32); err == nil {
|
|
rr.Inception = uint32(i)
|
|
} else {
|
|
return &ParseError{err: "bad RRSIG Inception", lex: l}
|
|
}
|
|
} else {
|
|
rr.Inception = i
|
|
}
|
|
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
i, e3 := strconv.ParseUint(l.token, 10, 16)
|
|
if e3 != nil || l.err {
|
|
return &ParseError{err: "bad RRSIG KeyTag", lex: l}
|
|
}
|
|
rr.KeyTag = uint16(i)
|
|
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
rr.SignerName = l.token
|
|
name, nameOk := toAbsoluteName(l.token, o)
|
|
if l.err || !nameOk {
|
|
return &ParseError{err: "bad RRSIG SignerName", lex: l}
|
|
}
|
|
rr.SignerName = name
|
|
|
|
s, e4 := endingToString(c, "bad RRSIG Signature")
|
|
if e4 != nil {
|
|
return e4
|
|
}
|
|
rr.Signature = s
|
|
|
|
return nil
|
|
}
|
|
|
|
func (rr *NSEC) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
name, nameOk := toAbsoluteName(l.token, o)
|
|
if l.err || !nameOk {
|
|
return &ParseError{err: "bad NSEC NextDomain", lex: l}
|
|
}
|
|
rr.NextDomain = name
|
|
|
|
rr.TypeBitMap = make([]uint16, 0)
|
|
var (
|
|
k uint16
|
|
ok bool
|
|
)
|
|
l, _ = c.Next()
|
|
for l.value != zNewline && l.value != zEOF {
|
|
switch l.value {
|
|
case zBlank:
|
|
// Ok
|
|
case zString:
|
|
tokenUpper := strings.ToUpper(l.token)
|
|
if k, ok = StringToType[tokenUpper]; !ok {
|
|
if k, ok = typeToInt(l.token); !ok {
|
|
return &ParseError{err: "bad NSEC TypeBitMap", lex: l}
|
|
}
|
|
}
|
|
rr.TypeBitMap = append(rr.TypeBitMap, k)
|
|
default:
|
|
return &ParseError{err: "bad NSEC TypeBitMap", lex: l}
|
|
}
|
|
l, _ = c.Next()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (rr *NSEC3) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
i, e := strconv.ParseUint(l.token, 10, 8)
|
|
if e != nil || l.err {
|
|
return &ParseError{err: "bad NSEC3 Hash", lex: l}
|
|
}
|
|
rr.Hash = uint8(i)
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
i, e1 := strconv.ParseUint(l.token, 10, 8)
|
|
if e1 != nil || l.err {
|
|
return &ParseError{err: "bad NSEC3 Flags", lex: l}
|
|
}
|
|
rr.Flags = uint8(i)
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
i, e2 := strconv.ParseUint(l.token, 10, 16)
|
|
if e2 != nil || l.err {
|
|
return &ParseError{err: "bad NSEC3 Iterations", lex: l}
|
|
}
|
|
rr.Iterations = uint16(i)
|
|
c.Next()
|
|
l, _ = c.Next()
|
|
if l.token == "" || l.err {
|
|
return &ParseError{err: "bad NSEC3 Salt", lex: l}
|
|
}
|
|
if l.token != "-" {
|
|
rr.SaltLength = uint8(len(l.token)) / 2
|
|
rr.Salt = l.token
|
|
}
|
|
|
|
c.Next()
|
|
l, _ = c.Next()
|
|
if l.token == "" || l.err {
|
|
return &ParseError{err: "bad NSEC3 NextDomain", lex: l}
|
|
}
|
|
rr.HashLength = 20 // Fix for NSEC3 (sha1 160 bits)
|
|
rr.NextDomain = l.token
|
|
|
|
rr.TypeBitMap = make([]uint16, 0)
|
|
var (
|
|
k uint16
|
|
ok bool
|
|
)
|
|
l, _ = c.Next()
|
|
for l.value != zNewline && l.value != zEOF {
|
|
switch l.value {
|
|
case zBlank:
|
|
// Ok
|
|
case zString:
|
|
tokenUpper := strings.ToUpper(l.token)
|
|
if k, ok = StringToType[tokenUpper]; !ok {
|
|
if k, ok = typeToInt(l.token); !ok {
|
|
return &ParseError{err: "bad NSEC3 TypeBitMap", lex: l}
|
|
}
|
|
}
|
|
rr.TypeBitMap = append(rr.TypeBitMap, k)
|
|
default:
|
|
return &ParseError{err: "bad NSEC3 TypeBitMap", lex: l}
|
|
}
|
|
l, _ = c.Next()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (rr *NSEC3PARAM) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
i, e := strconv.ParseUint(l.token, 10, 8)
|
|
if e != nil || l.err {
|
|
return &ParseError{err: "bad NSEC3PARAM Hash", lex: l}
|
|
}
|
|
rr.Hash = uint8(i)
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
i, e1 := strconv.ParseUint(l.token, 10, 8)
|
|
if e1 != nil || l.err {
|
|
return &ParseError{err: "bad NSEC3PARAM Flags", lex: l}
|
|
}
|
|
rr.Flags = uint8(i)
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
i, e2 := strconv.ParseUint(l.token, 10, 16)
|
|
if e2 != nil || l.err {
|
|
return &ParseError{err: "bad NSEC3PARAM Iterations", lex: l}
|
|
}
|
|
rr.Iterations = uint16(i)
|
|
c.Next()
|
|
l, _ = c.Next()
|
|
if l.token != "-" {
|
|
rr.SaltLength = uint8(len(l.token) / 2)
|
|
rr.Salt = l.token
|
|
}
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *EUI48) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
if len(l.token) != 17 || l.err {
|
|
return &ParseError{err: "bad EUI48 Address", lex: l}
|
|
}
|
|
addr := make([]byte, 12)
|
|
dash := 0
|
|
for i := 0; i < 10; i += 2 {
|
|
addr[i] = l.token[i+dash]
|
|
addr[i+1] = l.token[i+1+dash]
|
|
dash++
|
|
if l.token[i+1+dash] != '-' {
|
|
return &ParseError{err: "bad EUI48 Address", lex: l}
|
|
}
|
|
}
|
|
addr[10] = l.token[15]
|
|
addr[11] = l.token[16]
|
|
|
|
i, e := strconv.ParseUint(string(addr), 16, 48)
|
|
if e != nil {
|
|
return &ParseError{err: "bad EUI48 Address", lex: l}
|
|
}
|
|
rr.Address = i
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *EUI64) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
if len(l.token) != 23 || l.err {
|
|
return &ParseError{err: "bad EUI64 Address", lex: l}
|
|
}
|
|
addr := make([]byte, 16)
|
|
dash := 0
|
|
for i := 0; i < 14; i += 2 {
|
|
addr[i] = l.token[i+dash]
|
|
addr[i+1] = l.token[i+1+dash]
|
|
dash++
|
|
if l.token[i+1+dash] != '-' {
|
|
return &ParseError{err: "bad EUI64 Address", lex: l}
|
|
}
|
|
}
|
|
addr[14] = l.token[21]
|
|
addr[15] = l.token[22]
|
|
|
|
i, e := strconv.ParseUint(string(addr), 16, 64)
|
|
if e != nil {
|
|
return &ParseError{err: "bad EUI68 Address", lex: l}
|
|
}
|
|
rr.Address = i
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *SSHFP) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
i, e := strconv.ParseUint(l.token, 10, 8)
|
|
if e != nil || l.err {
|
|
return &ParseError{err: "bad SSHFP Algorithm", lex: l}
|
|
}
|
|
rr.Algorithm = uint8(i)
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
i, e1 := strconv.ParseUint(l.token, 10, 8)
|
|
if e1 != nil || l.err {
|
|
return &ParseError{err: "bad SSHFP Type", lex: l}
|
|
}
|
|
rr.Type = uint8(i)
|
|
c.Next() // zBlank
|
|
s, e2 := endingToString(c, "bad SSHFP Fingerprint")
|
|
if e2 != nil {
|
|
return e2
|
|
}
|
|
rr.FingerPrint = s
|
|
return nil
|
|
}
|
|
|
|
func (rr *DNSKEY) parseDNSKEY(c *zlexer, o, typ string) *ParseError {
|
|
l, _ := c.Next()
|
|
i, e := strconv.ParseUint(l.token, 10, 16)
|
|
if e != nil || l.err {
|
|
return &ParseError{err: "bad " + typ + " Flags", lex: l}
|
|
}
|
|
rr.Flags = uint16(i)
|
|
c.Next() // zBlank
|
|
l, _ = c.Next() // zString
|
|
i, e1 := strconv.ParseUint(l.token, 10, 8)
|
|
if e1 != nil || l.err {
|
|
return &ParseError{err: "bad " + typ + " Protocol", lex: l}
|
|
}
|
|
rr.Protocol = uint8(i)
|
|
c.Next() // zBlank
|
|
l, _ = c.Next() // zString
|
|
i, e2 := strconv.ParseUint(l.token, 10, 8)
|
|
if e2 != nil || l.err {
|
|
return &ParseError{err: "bad " + typ + " Algorithm", lex: l}
|
|
}
|
|
rr.Algorithm = uint8(i)
|
|
s, e3 := endingToString(c, "bad "+typ+" PublicKey")
|
|
if e3 != nil {
|
|
return e3
|
|
}
|
|
rr.PublicKey = s
|
|
return nil
|
|
}
|
|
|
|
func (rr *DNSKEY) parse(c *zlexer, o string) *ParseError { return rr.parseDNSKEY(c, o, "DNSKEY") }
|
|
func (rr *KEY) parse(c *zlexer, o string) *ParseError { return rr.parseDNSKEY(c, o, "KEY") }
|
|
func (rr *CDNSKEY) parse(c *zlexer, o string) *ParseError { return rr.parseDNSKEY(c, o, "CDNSKEY") }
|
|
func (rr *DS) parse(c *zlexer, o string) *ParseError { return rr.parseDS(c, o, "DS") }
|
|
func (rr *DLV) parse(c *zlexer, o string) *ParseError { return rr.parseDS(c, o, "DLV") }
|
|
func (rr *CDS) parse(c *zlexer, o string) *ParseError { return rr.parseDS(c, o, "CDS") }
|
|
|
|
func (rr *IPSECKEY) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
num, err := strconv.ParseUint(l.token, 10, 8)
|
|
if err != nil || l.err {
|
|
return &ParseError{err: "bad IPSECKEY value", lex: l}
|
|
}
|
|
rr.Precedence = uint8(num)
|
|
c.Next() // zBlank
|
|
|
|
l, _ = c.Next()
|
|
num, err = strconv.ParseUint(l.token, 10, 8)
|
|
if err != nil || l.err {
|
|
return &ParseError{err: "bad IPSECKEY value", lex: l}
|
|
}
|
|
rr.GatewayType = uint8(num)
|
|
c.Next() // zBlank
|
|
|
|
l, _ = c.Next()
|
|
num, err = strconv.ParseUint(l.token, 10, 8)
|
|
if err != nil || l.err {
|
|
return &ParseError{err: "bad IPSECKEY value", lex: l}
|
|
}
|
|
rr.Algorithm = uint8(num)
|
|
c.Next() // zBlank
|
|
|
|
l, _ = c.Next()
|
|
if l.err {
|
|
return &ParseError{err: "bad IPSECKEY gateway", lex: l}
|
|
}
|
|
|
|
rr.GatewayAddr, rr.GatewayHost, err = parseAddrHostUnion(l.token, o, rr.GatewayType)
|
|
if err != nil {
|
|
return &ParseError{wrappedErr: fmt.Errorf("IPSECKEY %w", err), lex: l}
|
|
}
|
|
|
|
c.Next() // zBlank
|
|
|
|
s, pErr := endingToString(c, "bad IPSECKEY PublicKey")
|
|
if pErr != nil {
|
|
return pErr
|
|
}
|
|
rr.PublicKey = s
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *AMTRELAY) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
num, err := strconv.ParseUint(l.token, 10, 8)
|
|
if err != nil || l.err {
|
|
return &ParseError{err: "bad AMTRELAY value", lex: l}
|
|
}
|
|
rr.Precedence = uint8(num)
|
|
c.Next() // zBlank
|
|
|
|
l, _ = c.Next()
|
|
if l.err || !(l.token == "0" || l.token == "1") {
|
|
return &ParseError{err: "bad discovery value", lex: l}
|
|
}
|
|
if l.token == "1" {
|
|
rr.GatewayType = 0x80
|
|
}
|
|
|
|
c.Next() // zBlank
|
|
|
|
l, _ = c.Next()
|
|
num, err = strconv.ParseUint(l.token, 10, 8)
|
|
if err != nil || l.err {
|
|
return &ParseError{err: "bad AMTRELAY value", lex: l}
|
|
}
|
|
rr.GatewayType |= uint8(num)
|
|
c.Next() // zBlank
|
|
|
|
l, _ = c.Next()
|
|
if l.err {
|
|
return &ParseError{err: "bad AMTRELAY gateway", lex: l}
|
|
}
|
|
|
|
rr.GatewayAddr, rr.GatewayHost, err = parseAddrHostUnion(l.token, o, rr.GatewayType&0x7f)
|
|
if err != nil {
|
|
return &ParseError{wrappedErr: fmt.Errorf("AMTRELAY %w", err), lex: l}
|
|
}
|
|
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
// same constants and parsing between IPSECKEY and AMTRELAY
|
|
func parseAddrHostUnion(token, o string, gatewayType uint8) (addr net.IP, host string, err error) {
|
|
switch gatewayType {
|
|
case IPSECGatewayNone:
|
|
if token != "." {
|
|
return addr, host, errors.New("gateway type none with gateway set")
|
|
}
|
|
case IPSECGatewayIPv4, IPSECGatewayIPv6:
|
|
addr = net.ParseIP(token)
|
|
if addr == nil {
|
|
return addr, host, errors.New("gateway IP invalid")
|
|
}
|
|
if (addr.To4() == nil) == (gatewayType == IPSECGatewayIPv4) {
|
|
return addr, host, errors.New("gateway IP family mismatch")
|
|
}
|
|
case IPSECGatewayHost:
|
|
var ok bool
|
|
host, ok = toAbsoluteName(token, o)
|
|
if !ok {
|
|
return addr, host, errors.New("invalid gateway host")
|
|
}
|
|
}
|
|
|
|
return addr, host, nil
|
|
}
|
|
|
|
func (rr *RKEY) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
i, e := strconv.ParseUint(l.token, 10, 16)
|
|
if e != nil || l.err {
|
|
return &ParseError{err: "bad RKEY Flags", lex: l}
|
|
}
|
|
rr.Flags = uint16(i)
|
|
c.Next() // zBlank
|
|
l, _ = c.Next() // zString
|
|
i, e1 := strconv.ParseUint(l.token, 10, 8)
|
|
if e1 != nil || l.err {
|
|
return &ParseError{err: "bad RKEY Protocol", lex: l}
|
|
}
|
|
rr.Protocol = uint8(i)
|
|
c.Next() // zBlank
|
|
l, _ = c.Next() // zString
|
|
i, e2 := strconv.ParseUint(l.token, 10, 8)
|
|
if e2 != nil || l.err {
|
|
return &ParseError{err: "bad RKEY Algorithm", lex: l}
|
|
}
|
|
rr.Algorithm = uint8(i)
|
|
s, e3 := endingToString(c, "bad RKEY PublicKey")
|
|
if e3 != nil {
|
|
return e3
|
|
}
|
|
rr.PublicKey = s
|
|
return nil
|
|
}
|
|
|
|
func (rr *EID) parse(c *zlexer, o string) *ParseError {
|
|
s, e := endingToString(c, "bad EID Endpoint")
|
|
if e != nil {
|
|
return e
|
|
}
|
|
rr.Endpoint = s
|
|
return nil
|
|
}
|
|
|
|
func (rr *NIMLOC) parse(c *zlexer, o string) *ParseError {
|
|
s, e := endingToString(c, "bad NIMLOC Locator")
|
|
if e != nil {
|
|
return e
|
|
}
|
|
rr.Locator = s
|
|
return nil
|
|
}
|
|
|
|
func (rr *GPOS) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
_, e := strconv.ParseFloat(l.token, 64)
|
|
if e != nil || l.err {
|
|
return &ParseError{err: "bad GPOS Longitude", lex: l}
|
|
}
|
|
rr.Longitude = l.token
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
_, e1 := strconv.ParseFloat(l.token, 64)
|
|
if e1 != nil || l.err {
|
|
return &ParseError{err: "bad GPOS Latitude", lex: l}
|
|
}
|
|
rr.Latitude = l.token
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
_, e2 := strconv.ParseFloat(l.token, 64)
|
|
if e2 != nil || l.err {
|
|
return &ParseError{err: "bad GPOS Altitude", lex: l}
|
|
}
|
|
rr.Altitude = l.token
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *DS) parseDS(c *zlexer, o, typ string) *ParseError {
|
|
l, _ := c.Next()
|
|
i, e := strconv.ParseUint(l.token, 10, 16)
|
|
if e != nil || l.err {
|
|
return &ParseError{err: "bad " + typ + " KeyTag", lex: l}
|
|
}
|
|
rr.KeyTag = uint16(i)
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
if i, err := strconv.ParseUint(l.token, 10, 8); err != nil {
|
|
tokenUpper := strings.ToUpper(l.token)
|
|
i, ok := StringToAlgorithm[tokenUpper]
|
|
if !ok || l.err {
|
|
return &ParseError{err: "bad " + typ + " Algorithm", lex: l}
|
|
}
|
|
rr.Algorithm = i
|
|
} else {
|
|
rr.Algorithm = uint8(i)
|
|
}
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
i, e1 := strconv.ParseUint(l.token, 10, 8)
|
|
if e1 != nil || l.err {
|
|
return &ParseError{err: "bad " + typ + " DigestType", lex: l}
|
|
}
|
|
rr.DigestType = uint8(i)
|
|
s, e2 := endingToString(c, "bad "+typ+" Digest")
|
|
if e2 != nil {
|
|
return e2
|
|
}
|
|
rr.Digest = s
|
|
return nil
|
|
}
|
|
|
|
func (rr *TA) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
i, e := strconv.ParseUint(l.token, 10, 16)
|
|
if e != nil || l.err {
|
|
return &ParseError{err: "bad TA KeyTag", lex: l}
|
|
}
|
|
rr.KeyTag = uint16(i)
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
if i, err := strconv.ParseUint(l.token, 10, 8); err != nil {
|
|
tokenUpper := strings.ToUpper(l.token)
|
|
i, ok := StringToAlgorithm[tokenUpper]
|
|
if !ok || l.err {
|
|
return &ParseError{err: "bad TA Algorithm", lex: l}
|
|
}
|
|
rr.Algorithm = i
|
|
} else {
|
|
rr.Algorithm = uint8(i)
|
|
}
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
i, e1 := strconv.ParseUint(l.token, 10, 8)
|
|
if e1 != nil || l.err {
|
|
return &ParseError{err: "bad TA DigestType", lex: l}
|
|
}
|
|
rr.DigestType = uint8(i)
|
|
s, e2 := endingToString(c, "bad TA Digest")
|
|
if e2 != nil {
|
|
return e2
|
|
}
|
|
rr.Digest = s
|
|
return nil
|
|
}
|
|
|
|
func (rr *TLSA) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
i, e := strconv.ParseUint(l.token, 10, 8)
|
|
if e != nil || l.err {
|
|
return &ParseError{err: "bad TLSA Usage", lex: l}
|
|
}
|
|
rr.Usage = uint8(i)
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
i, e1 := strconv.ParseUint(l.token, 10, 8)
|
|
if e1 != nil || l.err {
|
|
return &ParseError{err: "bad TLSA Selector", lex: l}
|
|
}
|
|
rr.Selector = uint8(i)
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
i, e2 := strconv.ParseUint(l.token, 10, 8)
|
|
if e2 != nil || l.err {
|
|
return &ParseError{err: "bad TLSA MatchingType", lex: l}
|
|
}
|
|
rr.MatchingType = uint8(i)
|
|
// So this needs be e2 (i.e. different than e), because...??t
|
|
s, e3 := endingToString(c, "bad TLSA Certificate")
|
|
if e3 != nil {
|
|
return e3
|
|
}
|
|
rr.Certificate = s
|
|
return nil
|
|
}
|
|
|
|
func (rr *SMIMEA) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
i, e := strconv.ParseUint(l.token, 10, 8)
|
|
if e != nil || l.err {
|
|
return &ParseError{err: "bad SMIMEA Usage", lex: l}
|
|
}
|
|
rr.Usage = uint8(i)
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
i, e1 := strconv.ParseUint(l.token, 10, 8)
|
|
if e1 != nil || l.err {
|
|
return &ParseError{err: "bad SMIMEA Selector", lex: l}
|
|
}
|
|
rr.Selector = uint8(i)
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
i, e2 := strconv.ParseUint(l.token, 10, 8)
|
|
if e2 != nil || l.err {
|
|
return &ParseError{err: "bad SMIMEA MatchingType", lex: l}
|
|
}
|
|
rr.MatchingType = uint8(i)
|
|
// So this needs be e2 (i.e. different than e), because...??t
|
|
s, e3 := endingToString(c, "bad SMIMEA Certificate")
|
|
if e3 != nil {
|
|
return e3
|
|
}
|
|
rr.Certificate = s
|
|
return nil
|
|
}
|
|
|
|
func (rr *RFC3597) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
if l.token != "\\#" {
|
|
return &ParseError{err: "bad RFC3597 Rdata", lex: l}
|
|
}
|
|
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
rdlength, e := strconv.ParseUint(l.token, 10, 16)
|
|
if e != nil || l.err {
|
|
return &ParseError{err: "bad RFC3597 Rdata ", lex: l}
|
|
}
|
|
|
|
s, e1 := endingToString(c, "bad RFC3597 Rdata")
|
|
if e1 != nil {
|
|
return e1
|
|
}
|
|
if int(rdlength)*2 != len(s) {
|
|
return &ParseError{err: "bad RFC3597 Rdata", lex: l}
|
|
}
|
|
rr.Rdata = s
|
|
return nil
|
|
}
|
|
|
|
func (rr *SPF) parse(c *zlexer, o string) *ParseError {
|
|
s, e := endingToTxtSlice(c, "bad SPF Txt")
|
|
if e != nil {
|
|
return e
|
|
}
|
|
rr.Txt = s
|
|
return nil
|
|
}
|
|
|
|
func (rr *AVC) parse(c *zlexer, o string) *ParseError {
|
|
s, e := endingToTxtSlice(c, "bad AVC Txt")
|
|
if e != nil {
|
|
return e
|
|
}
|
|
rr.Txt = s
|
|
return nil
|
|
}
|
|
|
|
func (rr *TXT) parse(c *zlexer, o string) *ParseError {
|
|
// no zBlank reading here, because all this rdata is TXT
|
|
s, e := endingToTxtSlice(c, "bad TXT Txt")
|
|
if e != nil {
|
|
return e
|
|
}
|
|
rr.Txt = s
|
|
return nil
|
|
}
|
|
|
|
// identical to setTXT
|
|
func (rr *NINFO) parse(c *zlexer, o string) *ParseError {
|
|
s, e := endingToTxtSlice(c, "bad NINFO ZSData")
|
|
if e != nil {
|
|
return e
|
|
}
|
|
rr.ZSData = s
|
|
return nil
|
|
}
|
|
|
|
func (rr *URI) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
i, e := strconv.ParseUint(l.token, 10, 16)
|
|
if e != nil || l.err {
|
|
return &ParseError{err: "bad URI Priority", lex: l}
|
|
}
|
|
rr.Priority = uint16(i)
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
i, e1 := strconv.ParseUint(l.token, 10, 16)
|
|
if e1 != nil || l.err {
|
|
return &ParseError{err: "bad URI Weight", lex: l}
|
|
}
|
|
rr.Weight = uint16(i)
|
|
|
|
c.Next() // zBlank
|
|
s, e2 := endingToTxtSlice(c, "bad URI Target")
|
|
if e2 != nil {
|
|
return e2
|
|
}
|
|
if len(s) != 1 {
|
|
return &ParseError{err: "bad URI Target", lex: l}
|
|
}
|
|
rr.Target = s[0]
|
|
return nil
|
|
}
|
|
|
|
func (rr *DHCID) parse(c *zlexer, o string) *ParseError {
|
|
// awesome record to parse!
|
|
s, e := endingToString(c, "bad DHCID Digest")
|
|
if e != nil {
|
|
return e
|
|
}
|
|
rr.Digest = s
|
|
return nil
|
|
}
|
|
|
|
func (rr *NID) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
i, e := strconv.ParseUint(l.token, 10, 16)
|
|
if e != nil || l.err {
|
|
return &ParseError{err: "bad NID Preference", lex: l}
|
|
}
|
|
rr.Preference = uint16(i)
|
|
c.Next() // zBlank
|
|
l, _ = c.Next() // zString
|
|
u, e1 := stringToNodeID(l)
|
|
if e1 != nil || l.err {
|
|
return e1
|
|
}
|
|
rr.NodeID = u
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *L32) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
i, e := strconv.ParseUint(l.token, 10, 16)
|
|
if e != nil || l.err {
|
|
return &ParseError{err: "bad L32 Preference", lex: l}
|
|
}
|
|
rr.Preference = uint16(i)
|
|
c.Next() // zBlank
|
|
l, _ = c.Next() // zString
|
|
rr.Locator32 = net.ParseIP(l.token)
|
|
if rr.Locator32 == nil || l.err {
|
|
return &ParseError{err: "bad L32 Locator", lex: l}
|
|
}
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *LP) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
i, e := strconv.ParseUint(l.token, 10, 16)
|
|
if e != nil || l.err {
|
|
return &ParseError{err: "bad LP Preference", lex: l}
|
|
}
|
|
rr.Preference = uint16(i)
|
|
|
|
c.Next() // zBlank
|
|
l, _ = c.Next() // zString
|
|
rr.Fqdn = l.token
|
|
name, nameOk := toAbsoluteName(l.token, o)
|
|
if l.err || !nameOk {
|
|
return &ParseError{err: "bad LP Fqdn", lex: l}
|
|
}
|
|
rr.Fqdn = name
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *L64) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
i, e := strconv.ParseUint(l.token, 10, 16)
|
|
if e != nil || l.err {
|
|
return &ParseError{err: "bad L64 Preference", lex: l}
|
|
}
|
|
rr.Preference = uint16(i)
|
|
c.Next() // zBlank
|
|
l, _ = c.Next() // zString
|
|
u, e1 := stringToNodeID(l)
|
|
if e1 != nil || l.err {
|
|
return e1
|
|
}
|
|
rr.Locator64 = u
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *UID) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
i, e := strconv.ParseUint(l.token, 10, 32)
|
|
if e != nil || l.err {
|
|
return &ParseError{err: "bad UID Uid", lex: l}
|
|
}
|
|
rr.Uid = uint32(i)
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *GID) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
i, e := strconv.ParseUint(l.token, 10, 32)
|
|
if e != nil || l.err {
|
|
return &ParseError{err: "bad GID Gid", lex: l}
|
|
}
|
|
rr.Gid = uint32(i)
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *UINFO) parse(c *zlexer, o string) *ParseError {
|
|
s, e := endingToTxtSlice(c, "bad UINFO Uinfo")
|
|
if e != nil {
|
|
return e
|
|
}
|
|
if ln := len(s); ln == 0 {
|
|
return nil
|
|
}
|
|
rr.Uinfo = s[0] // silently discard anything after the first character-string
|
|
return nil
|
|
}
|
|
|
|
func (rr *PX) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
i, e := strconv.ParseUint(l.token, 10, 16)
|
|
if e != nil || l.err {
|
|
return &ParseError{err: "bad PX Preference", lex: l}
|
|
}
|
|
rr.Preference = uint16(i)
|
|
|
|
c.Next() // zBlank
|
|
l, _ = c.Next() // zString
|
|
rr.Map822 = l.token
|
|
map822, map822Ok := toAbsoluteName(l.token, o)
|
|
if l.err || !map822Ok {
|
|
return &ParseError{err: "bad PX Map822", lex: l}
|
|
}
|
|
rr.Map822 = map822
|
|
|
|
c.Next() // zBlank
|
|
l, _ = c.Next() // zString
|
|
rr.Mapx400 = l.token
|
|
mapx400, mapx400Ok := toAbsoluteName(l.token, o)
|
|
if l.err || !mapx400Ok {
|
|
return &ParseError{err: "bad PX Mapx400", lex: l}
|
|
}
|
|
rr.Mapx400 = mapx400
|
|
return slurpRemainder(c)
|
|
}
|
|
|
|
func (rr *CAA) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
i, e := strconv.ParseUint(l.token, 10, 8)
|
|
if e != nil || l.err {
|
|
return &ParseError{err: "bad CAA Flag", lex: l}
|
|
}
|
|
rr.Flag = uint8(i)
|
|
|
|
c.Next() // zBlank
|
|
l, _ = c.Next() // zString
|
|
if l.value != zString {
|
|
return &ParseError{err: "bad CAA Tag", lex: l}
|
|
}
|
|
rr.Tag = l.token
|
|
|
|
c.Next() // zBlank
|
|
s, e1 := endingToTxtSlice(c, "bad CAA Value")
|
|
if e1 != nil {
|
|
return e1
|
|
}
|
|
if len(s) != 1 {
|
|
return &ParseError{err: "bad CAA Value", lex: l}
|
|
}
|
|
rr.Value = s[0]
|
|
return nil
|
|
}
|
|
|
|
func (rr *TKEY) parse(c *zlexer, o string) *ParseError {
|
|
l, _ := c.Next()
|
|
|
|
// Algorithm
|
|
if l.value != zString {
|
|
return &ParseError{err: "bad TKEY algorithm", lex: l}
|
|
}
|
|
rr.Algorithm = l.token
|
|
c.Next() // zBlank
|
|
|
|
// Get the key length and key values
|
|
l, _ = c.Next()
|
|
i, e := strconv.ParseUint(l.token, 10, 8)
|
|
if e != nil || l.err {
|
|
return &ParseError{err: "bad TKEY key length", lex: l}
|
|
}
|
|
rr.KeySize = uint16(i)
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
if l.value != zString {
|
|
return &ParseError{err: "bad TKEY key", lex: l}
|
|
}
|
|
rr.Key = l.token
|
|
c.Next() // zBlank
|
|
|
|
// Get the otherdata length and string data
|
|
l, _ = c.Next()
|
|
i, e1 := strconv.ParseUint(l.token, 10, 8)
|
|
if e1 != nil || l.err {
|
|
return &ParseError{err: "bad TKEY otherdata length", lex: l}
|
|
}
|
|
rr.OtherLen = uint16(i)
|
|
c.Next() // zBlank
|
|
l, _ = c.Next()
|
|
if l.value != zString {
|
|
return &ParseError{err: "bad TKEY otherday", lex: l}
|
|
}
|
|
rr.OtherData = l.token
|
|
return nil
|
|
}
|
|
|
|
func (rr *APL) parse(c *zlexer, o string) *ParseError {
|
|
var prefixes []APLPrefix
|
|
|
|
for {
|
|
l, _ := c.Next()
|
|
if l.value == zNewline || l.value == zEOF {
|
|
break
|
|
}
|
|
if l.value == zBlank && prefixes != nil {
|
|
continue
|
|
}
|
|
if l.value != zString {
|
|
return &ParseError{err: "unexpected APL field", lex: l}
|
|
}
|
|
|
|
// Expected format: [!]afi:address/prefix
|
|
|
|
colon := strings.IndexByte(l.token, ':')
|
|
if colon == -1 {
|
|
return &ParseError{err: "missing colon in APL field", lex: l}
|
|
}
|
|
|
|
family, cidr := l.token[:colon], l.token[colon+1:]
|
|
|
|
var negation bool
|
|
if family != "" && family[0] == '!' {
|
|
negation = true
|
|
family = family[1:]
|
|
}
|
|
|
|
afi, e := strconv.ParseUint(family, 10, 16)
|
|
if e != nil {
|
|
return &ParseError{wrappedErr: fmt.Errorf("failed to parse APL family: %w", e), lex: l}
|
|
}
|
|
var addrLen int
|
|
switch afi {
|
|
case 1:
|
|
addrLen = net.IPv4len
|
|
case 2:
|
|
addrLen = net.IPv6len
|
|
default:
|
|
return &ParseError{err: "unrecognized APL family", lex: l}
|
|
}
|
|
|
|
ip, subnet, e1 := net.ParseCIDR(cidr)
|
|
if e1 != nil {
|
|
return &ParseError{wrappedErr: fmt.Errorf("failed to parse APL address: %w", e1), lex: l}
|
|
}
|
|
if !ip.Equal(subnet.IP) {
|
|
return &ParseError{err: "extra bits in APL address", lex: l}
|
|
}
|
|
|
|
if len(subnet.IP) != addrLen {
|
|
return &ParseError{err: "address mismatch with the APL family", lex: l}
|
|
}
|
|
|
|
prefixes = append(prefixes, APLPrefix{
|
|
Negation: negation,
|
|
Network: *subnet,
|
|
})
|
|
}
|
|
|
|
rr.Prefixes = prefixes
|
|
return nil
|
|
}
|