I went through the list and cleaned things up here and there. Signed-off-by: Miek Gieben <miek@miek.nl>
1560 lines
36 KiB
Go
1560 lines
36 KiB
Go
package dns
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"net"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type (
|
|
// Type is a DNS type.
|
|
Type uint16
|
|
// Class is a DNS class.
|
|
Class uint16
|
|
// Name is a DNS domain name.
|
|
Name string
|
|
)
|
|
|
|
// Packet formats
|
|
|
|
// Wire constants and supported types.
|
|
const (
|
|
// valid RR_Header.Rrtype and Question.qtype
|
|
|
|
TypeNone uint16 = 0
|
|
TypeA uint16 = 1
|
|
TypeNS uint16 = 2
|
|
TypeMD uint16 = 3
|
|
TypeMF uint16 = 4
|
|
TypeCNAME uint16 = 5
|
|
TypeSOA uint16 = 6
|
|
TypeMB uint16 = 7
|
|
TypeMG uint16 = 8
|
|
TypeMR uint16 = 9
|
|
TypeNULL uint16 = 10
|
|
TypePTR uint16 = 12
|
|
TypeHINFO uint16 = 13
|
|
TypeMINFO uint16 = 14
|
|
TypeMX uint16 = 15
|
|
TypeTXT uint16 = 16
|
|
TypeRP uint16 = 17
|
|
TypeAFSDB uint16 = 18
|
|
TypeX25 uint16 = 19
|
|
TypeISDN uint16 = 20
|
|
TypeRT uint16 = 21
|
|
TypeNSAPPTR uint16 = 23
|
|
TypeSIG uint16 = 24
|
|
TypeKEY uint16 = 25
|
|
TypePX uint16 = 26
|
|
TypeGPOS uint16 = 27
|
|
TypeAAAA uint16 = 28
|
|
TypeLOC uint16 = 29
|
|
TypeNXT uint16 = 30
|
|
TypeEID uint16 = 31
|
|
TypeNIMLOC uint16 = 32
|
|
TypeSRV uint16 = 33
|
|
TypeATMA uint16 = 34
|
|
TypeNAPTR uint16 = 35
|
|
TypeKX uint16 = 36
|
|
TypeCERT uint16 = 37
|
|
TypeDNAME uint16 = 39
|
|
TypeOPT uint16 = 41 // EDNS
|
|
TypeAPL uint16 = 42
|
|
TypeDS uint16 = 43
|
|
TypeSSHFP uint16 = 44
|
|
TypeRRSIG uint16 = 46
|
|
TypeNSEC uint16 = 47
|
|
TypeDNSKEY uint16 = 48
|
|
TypeDHCID uint16 = 49
|
|
TypeNSEC3 uint16 = 50
|
|
TypeNSEC3PARAM uint16 = 51
|
|
TypeTLSA uint16 = 52
|
|
TypeSMIMEA uint16 = 53
|
|
TypeHIP uint16 = 55
|
|
TypeNINFO uint16 = 56
|
|
TypeRKEY uint16 = 57
|
|
TypeTALINK uint16 = 58
|
|
TypeCDS uint16 = 59
|
|
TypeCDNSKEY uint16 = 60
|
|
TypeOPENPGPKEY uint16 = 61
|
|
TypeCSYNC uint16 = 62
|
|
TypeZONEMD uint16 = 63
|
|
TypeSVCB uint16 = 64
|
|
TypeHTTPS uint16 = 65
|
|
TypeSPF uint16 = 99
|
|
TypeUINFO uint16 = 100
|
|
TypeUID uint16 = 101
|
|
TypeGID uint16 = 102
|
|
TypeUNSPEC uint16 = 103
|
|
TypeNID uint16 = 104
|
|
TypeL32 uint16 = 105
|
|
TypeL64 uint16 = 106
|
|
TypeLP uint16 = 107
|
|
TypeEUI48 uint16 = 108
|
|
TypeEUI64 uint16 = 109
|
|
TypeURI uint16 = 256
|
|
TypeCAA uint16 = 257
|
|
TypeAVC uint16 = 258
|
|
|
|
TypeTKEY uint16 = 249
|
|
TypeTSIG uint16 = 250
|
|
|
|
// valid Question.Qtype only
|
|
TypeIXFR uint16 = 251
|
|
TypeAXFR uint16 = 252
|
|
TypeMAILB uint16 = 253
|
|
TypeMAILA uint16 = 254
|
|
TypeANY uint16 = 255
|
|
|
|
TypeTA uint16 = 32768
|
|
TypeDLV uint16 = 32769
|
|
TypeReserved uint16 = 65535
|
|
|
|
// valid Question.Qclass
|
|
ClassINET = 1
|
|
ClassCSNET = 2
|
|
ClassCHAOS = 3
|
|
ClassHESIOD = 4
|
|
ClassNONE = 254
|
|
ClassANY = 255
|
|
|
|
// Message Response Codes, see https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml
|
|
RcodeSuccess = 0 // NoError - No Error [DNS]
|
|
RcodeFormatError = 1 // FormErr - Format Error [DNS]
|
|
RcodeServerFailure = 2 // ServFail - Server Failure [DNS]
|
|
RcodeNameError = 3 // NXDomain - Non-Existent Domain [DNS]
|
|
RcodeNotImplemented = 4 // NotImp - Not Implemented [DNS]
|
|
RcodeRefused = 5 // Refused - Query Refused [DNS]
|
|
RcodeYXDomain = 6 // YXDomain - Name Exists when it should not [DNS Update]
|
|
RcodeYXRrset = 7 // YXRRSet - RR Set Exists when it should not [DNS Update]
|
|
RcodeNXRrset = 8 // NXRRSet - RR Set that should exist does not [DNS Update]
|
|
RcodeNotAuth = 9 // NotAuth - Server Not Authoritative for zone [DNS Update]
|
|
RcodeNotZone = 10 // NotZone - Name not contained in zone [DNS Update/TSIG]
|
|
RcodeBadSig = 16 // BADSIG - TSIG Signature Failure [TSIG]
|
|
RcodeBadVers = 16 // BADVERS - Bad OPT Version [EDNS0]
|
|
RcodeBadKey = 17 // BADKEY - Key not recognized [TSIG]
|
|
RcodeBadTime = 18 // BADTIME - Signature out of time window [TSIG]
|
|
RcodeBadMode = 19 // BADMODE - Bad TKEY Mode [TKEY]
|
|
RcodeBadName = 20 // BADNAME - Duplicate key name [TKEY]
|
|
RcodeBadAlg = 21 // BADALG - Algorithm not supported [TKEY]
|
|
RcodeBadTrunc = 22 // BADTRUNC - Bad Truncation [TSIG]
|
|
RcodeBadCookie = 23 // BADCOOKIE - Bad/missing Server Cookie [DNS Cookies]
|
|
|
|
// Message Opcodes. There is no 3.
|
|
OpcodeQuery = 0
|
|
OpcodeIQuery = 1
|
|
OpcodeStatus = 2
|
|
OpcodeNotify = 4
|
|
OpcodeUpdate = 5
|
|
)
|
|
|
|
// Used in ZONEMD https://tools.ietf.org/html/rfc8976
|
|
const (
|
|
ZoneMDSchemeSimple = 1
|
|
|
|
ZoneMDHashAlgSHA384 = 1
|
|
ZoneMDHashAlgSHA512 = 2
|
|
)
|
|
|
|
// Header is the wire format for the DNS packet header.
|
|
type Header struct {
|
|
Id uint16
|
|
Bits uint16
|
|
Qdcount, Ancount, Nscount, Arcount uint16
|
|
}
|
|
|
|
const (
|
|
headerSize = 12
|
|
|
|
// Header.Bits
|
|
_QR = 1 << 15 // query/response (response=1)
|
|
_AA = 1 << 10 // authoritative
|
|
_TC = 1 << 9 // truncated
|
|
_RD = 1 << 8 // recursion desired
|
|
_RA = 1 << 7 // recursion available
|
|
_Z = 1 << 6 // Z
|
|
_AD = 1 << 5 // authenticated data
|
|
_CD = 1 << 4 // checking disabled
|
|
)
|
|
|
|
// Various constants used in the LOC RR. See RFC 1887.
|
|
const (
|
|
LOC_EQUATOR = 1 << 31 // RFC 1876, Section 2.
|
|
LOC_PRIMEMERIDIAN = 1 << 31 // RFC 1876, Section 2.
|
|
LOC_HOURS = 60 * 1000
|
|
LOC_DEGREES = 60 * LOC_HOURS
|
|
LOC_ALTITUDEBASE = 100000
|
|
)
|
|
|
|
// Different Certificate Types, see RFC 4398, Section 2.1
|
|
const (
|
|
CertPKIX = 1 + iota
|
|
CertSPKI
|
|
CertPGP
|
|
CertIPIX
|
|
CertISPKI
|
|
CertIPGP
|
|
CertACPKIX
|
|
CertIACPKIX
|
|
CertURI = 253
|
|
CertOID = 254
|
|
)
|
|
|
|
// CertTypeToString converts the Cert Type to its string representation.
|
|
// See RFC 4398 and RFC 6944.
|
|
var CertTypeToString = map[uint16]string{
|
|
CertPKIX: "PKIX",
|
|
CertSPKI: "SPKI",
|
|
CertPGP: "PGP",
|
|
CertIPIX: "IPIX",
|
|
CertISPKI: "ISPKI",
|
|
CertIPGP: "IPGP",
|
|
CertACPKIX: "ACPKIX",
|
|
CertIACPKIX: "IACPKIX",
|
|
CertURI: "URI",
|
|
CertOID: "OID",
|
|
}
|
|
|
|
//go:generate go run types_generate.go
|
|
|
|
// Question holds a DNS question. Usually there is just one. While the
|
|
// original DNS RFCs allow multiple questions in the question section of a
|
|
// message, in practice it never works. Because most DNS servers see multiple
|
|
// questions as an error, it is recommended to only have one question per
|
|
// message.
|
|
type Question struct {
|
|
Name string `dns:"cdomain-name"` // "cdomain-name" specifies encoding (and may be compressed)
|
|
Qtype uint16
|
|
Qclass uint16
|
|
}
|
|
|
|
func (q *Question) len(off int, compression map[string]struct{}) int {
|
|
l := domainNameLen(q.Name, off, compression, true)
|
|
l += 2 + 2
|
|
return l
|
|
}
|
|
|
|
func (q *Question) String() (s string) {
|
|
// prefix with ; (as in dig)
|
|
s = ";" + sprintName(q.Name) + "\t"
|
|
s += Class(q.Qclass).String() + "\t"
|
|
s += " " + Type(q.Qtype).String()
|
|
return s
|
|
}
|
|
|
|
// ANY is a wild card record. See RFC 1035, Section 3.2.3. ANY
|
|
// is named "*" there.
|
|
type ANY struct {
|
|
Hdr RR_Header
|
|
// Does not have any rdata
|
|
}
|
|
|
|
func (rr *ANY) String() string { return rr.Hdr.String() }
|
|
|
|
func (*ANY) parse(c *zlexer, origin string) *ParseError {
|
|
return &ParseError{err: "ANY records do not have a presentation format"}
|
|
}
|
|
|
|
// NULL RR. See RFC 1035.
|
|
type NULL struct {
|
|
Hdr RR_Header
|
|
Data string `dns:"any"`
|
|
}
|
|
|
|
func (rr *NULL) String() string {
|
|
// There is no presentation format; prefix string with a comment.
|
|
return ";" + rr.Hdr.String() + rr.Data
|
|
}
|
|
|
|
func (*NULL) parse(c *zlexer, origin string) *ParseError {
|
|
return &ParseError{err: "NULL records do not have a presentation format"}
|
|
}
|
|
|
|
// CNAME RR. See RFC 1034.
|
|
type CNAME struct {
|
|
Hdr RR_Header
|
|
Target string `dns:"cdomain-name"`
|
|
}
|
|
|
|
func (rr *CNAME) String() string { return rr.Hdr.String() + sprintName(rr.Target) }
|
|
|
|
// HINFO RR. See RFC 1034.
|
|
type HINFO struct {
|
|
Hdr RR_Header
|
|
Cpu string
|
|
Os string
|
|
}
|
|
|
|
func (rr *HINFO) String() string {
|
|
return rr.Hdr.String() + sprintTxt([]string{rr.Cpu, rr.Os})
|
|
}
|
|
|
|
// MB RR. See RFC 1035.
|
|
type MB struct {
|
|
Hdr RR_Header
|
|
Mb string `dns:"cdomain-name"`
|
|
}
|
|
|
|
func (rr *MB) String() string { return rr.Hdr.String() + sprintName(rr.Mb) }
|
|
|
|
// MG RR. See RFC 1035.
|
|
type MG struct {
|
|
Hdr RR_Header
|
|
Mg string `dns:"cdomain-name"`
|
|
}
|
|
|
|
func (rr *MG) String() string { return rr.Hdr.String() + sprintName(rr.Mg) }
|
|
|
|
// MINFO RR. See RFC 1035.
|
|
type MINFO struct {
|
|
Hdr RR_Header
|
|
Rmail string `dns:"cdomain-name"`
|
|
Email string `dns:"cdomain-name"`
|
|
}
|
|
|
|
func (rr *MINFO) String() string {
|
|
return rr.Hdr.String() + sprintName(rr.Rmail) + " " + sprintName(rr.Email)
|
|
}
|
|
|
|
// MR RR. See RFC 1035.
|
|
type MR struct {
|
|
Hdr RR_Header
|
|
Mr string `dns:"cdomain-name"`
|
|
}
|
|
|
|
func (rr *MR) String() string {
|
|
return rr.Hdr.String() + sprintName(rr.Mr)
|
|
}
|
|
|
|
// MF RR. See RFC 1035.
|
|
type MF struct {
|
|
Hdr RR_Header
|
|
Mf string `dns:"cdomain-name"`
|
|
}
|
|
|
|
func (rr *MF) String() string {
|
|
return rr.Hdr.String() + sprintName(rr.Mf)
|
|
}
|
|
|
|
// MD RR. See RFC 1035.
|
|
type MD struct {
|
|
Hdr RR_Header
|
|
Md string `dns:"cdomain-name"`
|
|
}
|
|
|
|
func (rr *MD) String() string {
|
|
return rr.Hdr.String() + sprintName(rr.Md)
|
|
}
|
|
|
|
// MX RR. See RFC 1035.
|
|
type MX struct {
|
|
Hdr RR_Header
|
|
Preference uint16
|
|
Mx string `dns:"cdomain-name"`
|
|
}
|
|
|
|
func (rr *MX) String() string {
|
|
return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Mx)
|
|
}
|
|
|
|
// AFSDB RR. See RFC 1183.
|
|
type AFSDB struct {
|
|
Hdr RR_Header
|
|
Subtype uint16
|
|
Hostname string `dns:"domain-name"`
|
|
}
|
|
|
|
func (rr *AFSDB) String() string {
|
|
return rr.Hdr.String() + strconv.Itoa(int(rr.Subtype)) + " " + sprintName(rr.Hostname)
|
|
}
|
|
|
|
// X25 RR. See RFC 1183, Section 3.1.
|
|
type X25 struct {
|
|
Hdr RR_Header
|
|
PSDNAddress string
|
|
}
|
|
|
|
func (rr *X25) String() string {
|
|
return rr.Hdr.String() + rr.PSDNAddress
|
|
}
|
|
|
|
// RT RR. See RFC 1183, Section 3.3.
|
|
type RT struct {
|
|
Hdr RR_Header
|
|
Preference uint16
|
|
Host string `dns:"domain-name"` // RFC 3597 prohibits compressing records not defined in RFC 1035.
|
|
}
|
|
|
|
func (rr *RT) String() string {
|
|
return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Host)
|
|
}
|
|
|
|
// NS RR. See RFC 1035.
|
|
type NS struct {
|
|
Hdr RR_Header
|
|
Ns string `dns:"cdomain-name"`
|
|
}
|
|
|
|
func (rr *NS) String() string {
|
|
return rr.Hdr.String() + sprintName(rr.Ns)
|
|
}
|
|
|
|
// PTR RR. See RFC 1035.
|
|
type PTR struct {
|
|
Hdr RR_Header
|
|
Ptr string `dns:"cdomain-name"`
|
|
}
|
|
|
|
func (rr *PTR) String() string {
|
|
return rr.Hdr.String() + sprintName(rr.Ptr)
|
|
}
|
|
|
|
// RP RR. See RFC 1138, Section 2.2.
|
|
type RP struct {
|
|
Hdr RR_Header
|
|
Mbox string `dns:"domain-name"`
|
|
Txt string `dns:"domain-name"`
|
|
}
|
|
|
|
func (rr *RP) String() string {
|
|
return rr.Hdr.String() + sprintName(rr.Mbox) + " " + sprintName(rr.Txt)
|
|
}
|
|
|
|
// SOA RR. See RFC 1035.
|
|
type SOA struct {
|
|
Hdr RR_Header
|
|
Ns string `dns:"cdomain-name"`
|
|
Mbox string `dns:"cdomain-name"`
|
|
Serial uint32
|
|
Refresh uint32
|
|
Retry uint32
|
|
Expire uint32
|
|
Minttl uint32
|
|
}
|
|
|
|
func (rr *SOA) String() string {
|
|
return rr.Hdr.String() + sprintName(rr.Ns) + " " + sprintName(rr.Mbox) +
|
|
" " + strconv.FormatInt(int64(rr.Serial), 10) +
|
|
" " + strconv.FormatInt(int64(rr.Refresh), 10) +
|
|
" " + strconv.FormatInt(int64(rr.Retry), 10) +
|
|
" " + strconv.FormatInt(int64(rr.Expire), 10) +
|
|
" " + strconv.FormatInt(int64(rr.Minttl), 10)
|
|
}
|
|
|
|
// TXT RR. See RFC 1035.
|
|
type TXT struct {
|
|
Hdr RR_Header
|
|
Txt []string `dns:"txt"`
|
|
}
|
|
|
|
func (rr *TXT) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) }
|
|
|
|
func sprintName(s string) string {
|
|
var dst strings.Builder
|
|
|
|
for i := 0; i < len(s); {
|
|
if s[i] == '.' {
|
|
if dst.Len() != 0 {
|
|
dst.WriteByte('.')
|
|
}
|
|
i++
|
|
continue
|
|
}
|
|
|
|
b, n := nextByte(s, i)
|
|
if n == 0 {
|
|
// Drop "dangling" incomplete escapes.
|
|
if dst.Len() == 0 {
|
|
return s[:i]
|
|
}
|
|
break
|
|
}
|
|
if isDomainNameLabelSpecial(b) {
|
|
if dst.Len() == 0 {
|
|
dst.Grow(len(s) * 2)
|
|
dst.WriteString(s[:i])
|
|
}
|
|
dst.WriteByte('\\')
|
|
dst.WriteByte(b)
|
|
} else if b < ' ' || b > '~' { // unprintable, use \DDD
|
|
if dst.Len() == 0 {
|
|
dst.Grow(len(s) * 2)
|
|
dst.WriteString(s[:i])
|
|
}
|
|
dst.WriteString(escapeByte(b))
|
|
} else {
|
|
if dst.Len() != 0 {
|
|
dst.WriteByte(b)
|
|
}
|
|
}
|
|
i += n
|
|
}
|
|
if dst.Len() == 0 {
|
|
return s
|
|
}
|
|
return dst.String()
|
|
}
|
|
|
|
func sprintTxtOctet(s string) string {
|
|
var dst strings.Builder
|
|
dst.Grow(2 + len(s))
|
|
dst.WriteByte('"')
|
|
for i := 0; i < len(s); {
|
|
if i+1 < len(s) && s[i] == '\\' && s[i+1] == '.' {
|
|
dst.WriteString(s[i : i+2])
|
|
i += 2
|
|
continue
|
|
}
|
|
|
|
b, n := nextByte(s, i)
|
|
if n == 0 {
|
|
i++ // dangling back slash
|
|
} else {
|
|
writeTXTStringByte(&dst, b)
|
|
}
|
|
i += n
|
|
}
|
|
dst.WriteByte('"')
|
|
return dst.String()
|
|
}
|
|
|
|
func sprintTxt(txt []string) string {
|
|
var out strings.Builder
|
|
for i, s := range txt {
|
|
out.Grow(3 + len(s))
|
|
if i > 0 {
|
|
out.WriteString(` "`)
|
|
} else {
|
|
out.WriteByte('"')
|
|
}
|
|
for j := 0; j < len(s); {
|
|
b, n := nextByte(s, j)
|
|
if n == 0 {
|
|
break
|
|
}
|
|
writeTXTStringByte(&out, b)
|
|
j += n
|
|
}
|
|
out.WriteByte('"')
|
|
}
|
|
return out.String()
|
|
}
|
|
|
|
func writeTXTStringByte(s *strings.Builder, b byte) {
|
|
switch {
|
|
case b == '"' || b == '\\':
|
|
s.WriteByte('\\')
|
|
s.WriteByte(b)
|
|
case b < ' ' || b > '~':
|
|
s.WriteString(escapeByte(b))
|
|
default:
|
|
s.WriteByte(b)
|
|
}
|
|
}
|
|
|
|
const (
|
|
escapedByteSmall = "" +
|
|
`\000\001\002\003\004\005\006\007\008\009` +
|
|
`\010\011\012\013\014\015\016\017\018\019` +
|
|
`\020\021\022\023\024\025\026\027\028\029` +
|
|
`\030\031`
|
|
escapedByteLarge = `\127\128\129` +
|
|
`\130\131\132\133\134\135\136\137\138\139` +
|
|
`\140\141\142\143\144\145\146\147\148\149` +
|
|
`\150\151\152\153\154\155\156\157\158\159` +
|
|
`\160\161\162\163\164\165\166\167\168\169` +
|
|
`\170\171\172\173\174\175\176\177\178\179` +
|
|
`\180\181\182\183\184\185\186\187\188\189` +
|
|
`\190\191\192\193\194\195\196\197\198\199` +
|
|
`\200\201\202\203\204\205\206\207\208\209` +
|
|
`\210\211\212\213\214\215\216\217\218\219` +
|
|
`\220\221\222\223\224\225\226\227\228\229` +
|
|
`\230\231\232\233\234\235\236\237\238\239` +
|
|
`\240\241\242\243\244\245\246\247\248\249` +
|
|
`\250\251\252\253\254\255`
|
|
)
|
|
|
|
// escapeByte returns the \DDD escaping of b which must
|
|
// satisfy b < ' ' || b > '~'.
|
|
func escapeByte(b byte) string {
|
|
if b < ' ' {
|
|
return escapedByteSmall[b*4 : b*4+4]
|
|
}
|
|
|
|
b -= '~' + 1
|
|
// The cast here is needed as b*4 may overflow byte.
|
|
return escapedByteLarge[int(b)*4 : int(b)*4+4]
|
|
}
|
|
|
|
// isDomainNameLabelSpecial returns true if
|
|
// a domain name label byte should be prefixed
|
|
// with an escaping backslash.
|
|
func isDomainNameLabelSpecial(b byte) bool {
|
|
switch b {
|
|
case '.', ' ', '\'', '@', ';', '(', ')', '"', '\\':
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func nextByte(s string, offset int) (byte, int) {
|
|
if offset >= len(s) {
|
|
return 0, 0
|
|
}
|
|
if s[offset] != '\\' {
|
|
// not an escape sequence
|
|
return s[offset], 1
|
|
}
|
|
switch len(s) - offset {
|
|
case 1: // dangling escape
|
|
return 0, 0
|
|
case 2, 3: // too short to be \ddd
|
|
default: // maybe \ddd
|
|
if isDigit(s[offset+1]) && isDigit(s[offset+2]) && isDigit(s[offset+3]) {
|
|
return dddStringToByte(s[offset+1:]), 4
|
|
}
|
|
}
|
|
// not \ddd, just an RFC 1035 "quoted" character
|
|
return s[offset+1], 2
|
|
}
|
|
|
|
// SPF RR. See RFC 4408, Section 3.1.1.
|
|
type SPF struct {
|
|
Hdr RR_Header
|
|
Txt []string `dns:"txt"`
|
|
}
|
|
|
|
func (rr *SPF) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) }
|
|
|
|
// AVC RR. See https://www.iana.org/assignments/dns-parameters/AVC/avc-completed-template.
|
|
type AVC struct {
|
|
Hdr RR_Header
|
|
Txt []string `dns:"txt"`
|
|
}
|
|
|
|
func (rr *AVC) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) }
|
|
|
|
// SRV RR. See RFC 2782.
|
|
type SRV struct {
|
|
Hdr RR_Header
|
|
Priority uint16
|
|
Weight uint16
|
|
Port uint16
|
|
Target string `dns:"domain-name"`
|
|
}
|
|
|
|
func (rr *SRV) String() string {
|
|
return rr.Hdr.String() +
|
|
strconv.Itoa(int(rr.Priority)) + " " +
|
|
strconv.Itoa(int(rr.Weight)) + " " +
|
|
strconv.Itoa(int(rr.Port)) + " " + sprintName(rr.Target)
|
|
}
|
|
|
|
// NAPTR RR. See RFC 2915.
|
|
type NAPTR struct {
|
|
Hdr RR_Header
|
|
Order uint16
|
|
Preference uint16
|
|
Flags string
|
|
Service string
|
|
Regexp string
|
|
Replacement string `dns:"domain-name"`
|
|
}
|
|
|
|
func (rr *NAPTR) String() string {
|
|
return rr.Hdr.String() +
|
|
strconv.Itoa(int(rr.Order)) + " " +
|
|
strconv.Itoa(int(rr.Preference)) + " " +
|
|
"\"" + rr.Flags + "\" " +
|
|
"\"" + rr.Service + "\" " +
|
|
"\"" + rr.Regexp + "\" " +
|
|
rr.Replacement
|
|
}
|
|
|
|
// CERT RR. See RFC 4398.
|
|
type CERT struct {
|
|
Hdr RR_Header
|
|
Type uint16
|
|
KeyTag uint16
|
|
Algorithm uint8
|
|
Certificate string `dns:"base64"`
|
|
}
|
|
|
|
func (rr *CERT) String() string {
|
|
var (
|
|
ok bool
|
|
certtype, algorithm string
|
|
)
|
|
if certtype, ok = CertTypeToString[rr.Type]; !ok {
|
|
certtype = strconv.Itoa(int(rr.Type))
|
|
}
|
|
if algorithm, ok = AlgorithmToString[rr.Algorithm]; !ok {
|
|
algorithm = strconv.Itoa(int(rr.Algorithm))
|
|
}
|
|
return rr.Hdr.String() + certtype +
|
|
" " + strconv.Itoa(int(rr.KeyTag)) +
|
|
" " + algorithm +
|
|
" " + rr.Certificate
|
|
}
|
|
|
|
// DNAME RR. See RFC 2672.
|
|
type DNAME struct {
|
|
Hdr RR_Header
|
|
Target string `dns:"domain-name"`
|
|
}
|
|
|
|
func (rr *DNAME) String() string {
|
|
return rr.Hdr.String() + sprintName(rr.Target)
|
|
}
|
|
|
|
// A RR. See RFC 1035.
|
|
type A struct {
|
|
Hdr RR_Header
|
|
A net.IP `dns:"a"`
|
|
}
|
|
|
|
func (rr *A) String() string {
|
|
if rr.A == nil {
|
|
return rr.Hdr.String()
|
|
}
|
|
return rr.Hdr.String() + rr.A.String()
|
|
}
|
|
|
|
// AAAA RR. See RFC 3596.
|
|
type AAAA struct {
|
|
Hdr RR_Header
|
|
AAAA net.IP `dns:"aaaa"`
|
|
}
|
|
|
|
func (rr *AAAA) String() string {
|
|
if rr.AAAA == nil {
|
|
return rr.Hdr.String()
|
|
}
|
|
return rr.Hdr.String() + rr.AAAA.String()
|
|
}
|
|
|
|
// PX RR. See RFC 2163.
|
|
type PX struct {
|
|
Hdr RR_Header
|
|
Preference uint16
|
|
Map822 string `dns:"domain-name"`
|
|
Mapx400 string `dns:"domain-name"`
|
|
}
|
|
|
|
func (rr *PX) String() string {
|
|
return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Map822) + " " + sprintName(rr.Mapx400)
|
|
}
|
|
|
|
// GPOS RR. See RFC 1712.
|
|
type GPOS struct {
|
|
Hdr RR_Header
|
|
Longitude string
|
|
Latitude string
|
|
Altitude string
|
|
}
|
|
|
|
func (rr *GPOS) String() string {
|
|
return rr.Hdr.String() + rr.Longitude + " " + rr.Latitude + " " + rr.Altitude
|
|
}
|
|
|
|
// LOC RR. See RFC RFC 1876.
|
|
type LOC struct {
|
|
Hdr RR_Header
|
|
Version uint8
|
|
Size uint8
|
|
HorizPre uint8
|
|
VertPre uint8
|
|
Latitude uint32
|
|
Longitude uint32
|
|
Altitude uint32
|
|
}
|
|
|
|
// cmToM takes a cm value expressed in RFC 1876 SIZE mantissa/exponent
|
|
// format and returns a string in m (two decimals for the cm).
|
|
func cmToM(m, e uint8) string {
|
|
if e < 2 {
|
|
if e == 1 {
|
|
m *= 10
|
|
}
|
|
|
|
return fmt.Sprintf("0.%02d", m)
|
|
}
|
|
|
|
s := fmt.Sprintf("%d", m)
|
|
for e > 2 {
|
|
s += "0"
|
|
e--
|
|
}
|
|
return s
|
|
}
|
|
|
|
func (rr *LOC) String() string {
|
|
s := rr.Hdr.String()
|
|
|
|
lat := rr.Latitude
|
|
ns := "N"
|
|
if lat > LOC_EQUATOR {
|
|
lat = lat - LOC_EQUATOR
|
|
} else {
|
|
ns = "S"
|
|
lat = LOC_EQUATOR - lat
|
|
}
|
|
h := lat / LOC_DEGREES
|
|
lat = lat % LOC_DEGREES
|
|
m := lat / LOC_HOURS
|
|
lat = lat % LOC_HOURS
|
|
s += fmt.Sprintf("%02d %02d %0.3f %s ", h, m, float64(lat)/1000, ns)
|
|
|
|
lon := rr.Longitude
|
|
ew := "E"
|
|
if lon > LOC_PRIMEMERIDIAN {
|
|
lon = lon - LOC_PRIMEMERIDIAN
|
|
} else {
|
|
ew = "W"
|
|
lon = LOC_PRIMEMERIDIAN - lon
|
|
}
|
|
h = lon / LOC_DEGREES
|
|
lon = lon % LOC_DEGREES
|
|
m = lon / LOC_HOURS
|
|
lon = lon % LOC_HOURS
|
|
s += fmt.Sprintf("%02d %02d %0.3f %s ", h, m, float64(lon)/1000, ew)
|
|
|
|
var alt = float64(rr.Altitude) / 100
|
|
alt -= LOC_ALTITUDEBASE
|
|
if rr.Altitude%100 != 0 {
|
|
s += fmt.Sprintf("%.2fm ", alt)
|
|
} else {
|
|
s += fmt.Sprintf("%.0fm ", alt)
|
|
}
|
|
|
|
s += cmToM(rr.Size&0xf0>>4, rr.Size&0x0f) + "m "
|
|
s += cmToM(rr.HorizPre&0xf0>>4, rr.HorizPre&0x0f) + "m "
|
|
s += cmToM(rr.VertPre&0xf0>>4, rr.VertPre&0x0f) + "m"
|
|
|
|
return s
|
|
}
|
|
|
|
// SIG RR. See RFC 2535. The SIG RR is identical to RRSIG and nowadays only used for SIG(0), See RFC 2931.
|
|
type SIG struct {
|
|
RRSIG
|
|
}
|
|
|
|
// RRSIG RR. See RFC 4034 and RFC 3755.
|
|
type RRSIG struct {
|
|
Hdr RR_Header
|
|
TypeCovered uint16
|
|
Algorithm uint8
|
|
Labels uint8
|
|
OrigTtl uint32
|
|
Expiration uint32
|
|
Inception uint32
|
|
KeyTag uint16
|
|
SignerName string `dns:"domain-name"`
|
|
Signature string `dns:"base64"`
|
|
}
|
|
|
|
func (rr *RRSIG) String() string {
|
|
s := rr.Hdr.String()
|
|
s += Type(rr.TypeCovered).String()
|
|
s += " " + strconv.Itoa(int(rr.Algorithm)) +
|
|
" " + strconv.Itoa(int(rr.Labels)) +
|
|
" " + strconv.FormatInt(int64(rr.OrigTtl), 10) +
|
|
" " + TimeToString(rr.Expiration) +
|
|
" " + TimeToString(rr.Inception) +
|
|
" " + strconv.Itoa(int(rr.KeyTag)) +
|
|
" " + sprintName(rr.SignerName) +
|
|
" " + rr.Signature
|
|
return s
|
|
}
|
|
|
|
// NSEC RR. See RFC 4034 and RFC 3755.
|
|
type NSEC struct {
|
|
Hdr RR_Header
|
|
NextDomain string `dns:"domain-name"`
|
|
TypeBitMap []uint16 `dns:"nsec"`
|
|
}
|
|
|
|
func (rr *NSEC) String() string {
|
|
s := rr.Hdr.String() + sprintName(rr.NextDomain)
|
|
for _, t := range rr.TypeBitMap {
|
|
s += " " + Type(t).String()
|
|
}
|
|
return s
|
|
}
|
|
|
|
func (rr *NSEC) len(off int, compression map[string]struct{}) int {
|
|
l := rr.Hdr.len(off, compression)
|
|
l += domainNameLen(rr.NextDomain, off+l, compression, false)
|
|
l += typeBitMapLen(rr.TypeBitMap)
|
|
return l
|
|
}
|
|
|
|
// DLV RR. See RFC 4431.
|
|
type DLV struct{ DS }
|
|
|
|
// CDS RR. See RFC 7344.
|
|
type CDS struct{ DS }
|
|
|
|
// DS RR. See RFC 4034 and RFC 3658.
|
|
type DS struct {
|
|
Hdr RR_Header
|
|
KeyTag uint16
|
|
Algorithm uint8
|
|
DigestType uint8
|
|
Digest string `dns:"hex"`
|
|
}
|
|
|
|
func (rr *DS) String() string {
|
|
return rr.Hdr.String() + strconv.Itoa(int(rr.KeyTag)) +
|
|
" " + strconv.Itoa(int(rr.Algorithm)) +
|
|
" " + strconv.Itoa(int(rr.DigestType)) +
|
|
" " + strings.ToUpper(rr.Digest)
|
|
}
|
|
|
|
// KX RR. See RFC 2230.
|
|
type KX struct {
|
|
Hdr RR_Header
|
|
Preference uint16
|
|
Exchanger string `dns:"domain-name"`
|
|
}
|
|
|
|
func (rr *KX) String() string {
|
|
return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) +
|
|
" " + sprintName(rr.Exchanger)
|
|
}
|
|
|
|
// TA RR. See http://www.watson.org/~weiler/INI1999-19.pdf.
|
|
type TA struct {
|
|
Hdr RR_Header
|
|
KeyTag uint16
|
|
Algorithm uint8
|
|
DigestType uint8
|
|
Digest string `dns:"hex"`
|
|
}
|
|
|
|
func (rr *TA) String() string {
|
|
return rr.Hdr.String() + strconv.Itoa(int(rr.KeyTag)) +
|
|
" " + strconv.Itoa(int(rr.Algorithm)) +
|
|
" " + strconv.Itoa(int(rr.DigestType)) +
|
|
" " + strings.ToUpper(rr.Digest)
|
|
}
|
|
|
|
// TALINK RR. See https://www.iana.org/assignments/dns-parameters/TALINK/talink-completed-template.
|
|
type TALINK struct {
|
|
Hdr RR_Header
|
|
PreviousName string `dns:"domain-name"`
|
|
NextName string `dns:"domain-name"`
|
|
}
|
|
|
|
func (rr *TALINK) String() string {
|
|
return rr.Hdr.String() +
|
|
sprintName(rr.PreviousName) + " " + sprintName(rr.NextName)
|
|
}
|
|
|
|
// SSHFP RR. See RFC RFC 4255.
|
|
type SSHFP struct {
|
|
Hdr RR_Header
|
|
Algorithm uint8
|
|
Type uint8
|
|
FingerPrint string `dns:"hex"`
|
|
}
|
|
|
|
func (rr *SSHFP) String() string {
|
|
return rr.Hdr.String() + strconv.Itoa(int(rr.Algorithm)) +
|
|
" " + strconv.Itoa(int(rr.Type)) +
|
|
" " + strings.ToUpper(rr.FingerPrint)
|
|
}
|
|
|
|
// KEY RR. See RFC RFC 2535.
|
|
type KEY struct {
|
|
DNSKEY
|
|
}
|
|
|
|
// CDNSKEY RR. See RFC 7344.
|
|
type CDNSKEY struct {
|
|
DNSKEY
|
|
}
|
|
|
|
// DNSKEY RR. See RFC 4034 and RFC 3755.
|
|
type DNSKEY struct {
|
|
Hdr RR_Header
|
|
Flags uint16
|
|
Protocol uint8
|
|
Algorithm uint8
|
|
PublicKey string `dns:"base64"`
|
|
}
|
|
|
|
func (rr *DNSKEY) String() string {
|
|
return rr.Hdr.String() + strconv.Itoa(int(rr.Flags)) +
|
|
" " + strconv.Itoa(int(rr.Protocol)) +
|
|
" " + strconv.Itoa(int(rr.Algorithm)) +
|
|
" " + rr.PublicKey
|
|
}
|
|
|
|
// RKEY RR. See https://www.iana.org/assignments/dns-parameters/RKEY/rkey-completed-template.
|
|
type RKEY struct {
|
|
Hdr RR_Header
|
|
Flags uint16
|
|
Protocol uint8
|
|
Algorithm uint8
|
|
PublicKey string `dns:"base64"`
|
|
}
|
|
|
|
func (rr *RKEY) String() string {
|
|
return rr.Hdr.String() + strconv.Itoa(int(rr.Flags)) +
|
|
" " + strconv.Itoa(int(rr.Protocol)) +
|
|
" " + strconv.Itoa(int(rr.Algorithm)) +
|
|
" " + rr.PublicKey
|
|
}
|
|
|
|
// NSAPPTR RR. See RFC 1348.
|
|
type NSAPPTR struct {
|
|
Hdr RR_Header
|
|
Ptr string `dns:"domain-name"`
|
|
}
|
|
|
|
func (rr *NSAPPTR) String() string { return rr.Hdr.String() + sprintName(rr.Ptr) }
|
|
|
|
// NSEC3 RR. See RFC 5155.
|
|
type NSEC3 struct {
|
|
Hdr RR_Header
|
|
Hash uint8
|
|
Flags uint8
|
|
Iterations uint16
|
|
SaltLength uint8
|
|
Salt string `dns:"size-hex:SaltLength"`
|
|
HashLength uint8
|
|
NextDomain string `dns:"size-base32:HashLength"`
|
|
TypeBitMap []uint16 `dns:"nsec"`
|
|
}
|
|
|
|
func (rr *NSEC3) String() string {
|
|
s := rr.Hdr.String()
|
|
s += strconv.Itoa(int(rr.Hash)) +
|
|
" " + strconv.Itoa(int(rr.Flags)) +
|
|
" " + strconv.Itoa(int(rr.Iterations)) +
|
|
" " + saltToString(rr.Salt) +
|
|
" " + rr.NextDomain
|
|
for _, t := range rr.TypeBitMap {
|
|
s += " " + Type(t).String()
|
|
}
|
|
return s
|
|
}
|
|
|
|
func (rr *NSEC3) len(off int, compression map[string]struct{}) int {
|
|
l := rr.Hdr.len(off, compression)
|
|
l += 6 + len(rr.Salt)/2 + 1 + len(rr.NextDomain) + 1
|
|
l += typeBitMapLen(rr.TypeBitMap)
|
|
return l
|
|
}
|
|
|
|
// NSEC3PARAM RR. See RFC 5155.
|
|
type NSEC3PARAM struct {
|
|
Hdr RR_Header
|
|
Hash uint8
|
|
Flags uint8
|
|
Iterations uint16
|
|
SaltLength uint8
|
|
Salt string `dns:"size-hex:SaltLength"`
|
|
}
|
|
|
|
func (rr *NSEC3PARAM) String() string {
|
|
s := rr.Hdr.String()
|
|
s += strconv.Itoa(int(rr.Hash)) +
|
|
" " + strconv.Itoa(int(rr.Flags)) +
|
|
" " + strconv.Itoa(int(rr.Iterations)) +
|
|
" " + saltToString(rr.Salt)
|
|
return s
|
|
}
|
|
|
|
// TKEY RR. See RFC 2930.
|
|
type TKEY struct {
|
|
Hdr RR_Header
|
|
Algorithm string `dns:"domain-name"`
|
|
Inception uint32
|
|
Expiration uint32
|
|
Mode uint16
|
|
Error uint16
|
|
KeySize uint16
|
|
Key string `dns:"size-hex:KeySize"`
|
|
OtherLen uint16
|
|
OtherData string `dns:"size-hex:OtherLen"`
|
|
}
|
|
|
|
// TKEY has no official presentation format, but this will suffice.
|
|
func (rr *TKEY) String() string {
|
|
s := ";" + rr.Hdr.String() +
|
|
" " + rr.Algorithm +
|
|
" " + TimeToString(rr.Inception) +
|
|
" " + TimeToString(rr.Expiration) +
|
|
" " + strconv.Itoa(int(rr.Mode)) +
|
|
" " + strconv.Itoa(int(rr.Error)) +
|
|
" " + strconv.Itoa(int(rr.KeySize)) +
|
|
" " + rr.Key +
|
|
" " + strconv.Itoa(int(rr.OtherLen)) +
|
|
" " + rr.OtherData
|
|
return s
|
|
}
|
|
|
|
// RFC3597 represents an unknown/generic RR. See RFC 3597.
|
|
type RFC3597 struct {
|
|
Hdr RR_Header
|
|
Rdata string `dns:"hex"`
|
|
}
|
|
|
|
func (rr *RFC3597) String() string {
|
|
// Let's call it a hack
|
|
s := rfc3597Header(rr.Hdr)
|
|
|
|
s += "\\# " + strconv.Itoa(len(rr.Rdata)/2) + " " + rr.Rdata
|
|
return s
|
|
}
|
|
|
|
func rfc3597Header(h RR_Header) string {
|
|
var s string
|
|
|
|
s += sprintName(h.Name) + "\t"
|
|
s += strconv.FormatInt(int64(h.Ttl), 10) + "\t"
|
|
s += "CLASS" + strconv.Itoa(int(h.Class)) + "\t"
|
|
s += "TYPE" + strconv.Itoa(int(h.Rrtype)) + "\t"
|
|
return s
|
|
}
|
|
|
|
// URI RR. See RFC 7553.
|
|
type URI struct {
|
|
Hdr RR_Header
|
|
Priority uint16
|
|
Weight uint16
|
|
Target string `dns:"octet"`
|
|
}
|
|
|
|
// rr.Target to be parsed as a sequence of character encoded octets according to RFC 3986
|
|
func (rr *URI) String() string {
|
|
return rr.Hdr.String() + strconv.Itoa(int(rr.Priority)) +
|
|
" " + strconv.Itoa(int(rr.Weight)) + " " + sprintTxtOctet(rr.Target)
|
|
}
|
|
|
|
// DHCID RR. See RFC 4701.
|
|
type DHCID struct {
|
|
Hdr RR_Header
|
|
Digest string `dns:"base64"`
|
|
}
|
|
|
|
func (rr *DHCID) String() string { return rr.Hdr.String() + rr.Digest }
|
|
|
|
// TLSA RR. See RFC 6698.
|
|
type TLSA struct {
|
|
Hdr RR_Header
|
|
Usage uint8
|
|
Selector uint8
|
|
MatchingType uint8
|
|
Certificate string `dns:"hex"`
|
|
}
|
|
|
|
func (rr *TLSA) String() string {
|
|
return rr.Hdr.String() +
|
|
strconv.Itoa(int(rr.Usage)) +
|
|
" " + strconv.Itoa(int(rr.Selector)) +
|
|
" " + strconv.Itoa(int(rr.MatchingType)) +
|
|
" " + rr.Certificate
|
|
}
|
|
|
|
// SMIMEA RR. See RFC 8162.
|
|
type SMIMEA struct {
|
|
Hdr RR_Header
|
|
Usage uint8
|
|
Selector uint8
|
|
MatchingType uint8
|
|
Certificate string `dns:"hex"`
|
|
}
|
|
|
|
func (rr *SMIMEA) String() string {
|
|
s := rr.Hdr.String() +
|
|
strconv.Itoa(int(rr.Usage)) +
|
|
" " + strconv.Itoa(int(rr.Selector)) +
|
|
" " + strconv.Itoa(int(rr.MatchingType))
|
|
|
|
// Every Nth char needs a space on this output. If we output
|
|
// this as one giant line, we can't read it can in because in some cases
|
|
// the cert length overflows scan.maxTok (2048).
|
|
sx := splitN(rr.Certificate, 1024) // conservative value here
|
|
s += " " + strings.Join(sx, " ")
|
|
return s
|
|
}
|
|
|
|
// HIP RR. See RFC 8005.
|
|
type HIP struct {
|
|
Hdr RR_Header
|
|
HitLength uint8
|
|
PublicKeyAlgorithm uint8
|
|
PublicKeyLength uint16
|
|
Hit string `dns:"size-hex:HitLength"`
|
|
PublicKey string `dns:"size-base64:PublicKeyLength"`
|
|
RendezvousServers []string `dns:"domain-name"`
|
|
}
|
|
|
|
func (rr *HIP) String() string {
|
|
s := rr.Hdr.String() +
|
|
strconv.Itoa(int(rr.PublicKeyAlgorithm)) +
|
|
" " + rr.Hit +
|
|
" " + rr.PublicKey
|
|
for _, d := range rr.RendezvousServers {
|
|
s += " " + sprintName(d)
|
|
}
|
|
return s
|
|
}
|
|
|
|
// NINFO RR. See https://www.iana.org/assignments/dns-parameters/NINFO/ninfo-completed-template.
|
|
type NINFO struct {
|
|
Hdr RR_Header
|
|
ZSData []string `dns:"txt"`
|
|
}
|
|
|
|
func (rr *NINFO) String() string { return rr.Hdr.String() + sprintTxt(rr.ZSData) }
|
|
|
|
// NID RR. See RFC RFC 6742.
|
|
type NID struct {
|
|
Hdr RR_Header
|
|
Preference uint16
|
|
NodeID uint64
|
|
}
|
|
|
|
func (rr *NID) String() string {
|
|
s := rr.Hdr.String() + strconv.Itoa(int(rr.Preference))
|
|
node := fmt.Sprintf("%0.16x", rr.NodeID)
|
|
s += " " + node[0:4] + ":" + node[4:8] + ":" + node[8:12] + ":" + node[12:16]
|
|
return s
|
|
}
|
|
|
|
// L32 RR, See RFC 6742.
|
|
type L32 struct {
|
|
Hdr RR_Header
|
|
Preference uint16
|
|
Locator32 net.IP `dns:"a"`
|
|
}
|
|
|
|
func (rr *L32) String() string {
|
|
if rr.Locator32 == nil {
|
|
return rr.Hdr.String() + strconv.Itoa(int(rr.Preference))
|
|
}
|
|
return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) +
|
|
" " + rr.Locator32.String()
|
|
}
|
|
|
|
// L64 RR, See RFC 6742.
|
|
type L64 struct {
|
|
Hdr RR_Header
|
|
Preference uint16
|
|
Locator64 uint64
|
|
}
|
|
|
|
func (rr *L64) String() string {
|
|
s := rr.Hdr.String() + strconv.Itoa(int(rr.Preference))
|
|
node := fmt.Sprintf("%0.16X", rr.Locator64)
|
|
s += " " + node[0:4] + ":" + node[4:8] + ":" + node[8:12] + ":" + node[12:16]
|
|
return s
|
|
}
|
|
|
|
// LP RR. See RFC 6742.
|
|
type LP struct {
|
|
Hdr RR_Header
|
|
Preference uint16
|
|
Fqdn string `dns:"domain-name"`
|
|
}
|
|
|
|
func (rr *LP) String() string {
|
|
return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Fqdn)
|
|
}
|
|
|
|
// EUI48 RR. See RFC 7043.
|
|
type EUI48 struct {
|
|
Hdr RR_Header
|
|
Address uint64 `dns:"uint48"`
|
|
}
|
|
|
|
func (rr *EUI48) String() string { return rr.Hdr.String() + euiToString(rr.Address, 48) }
|
|
|
|
// EUI64 RR. See RFC 7043.
|
|
type EUI64 struct {
|
|
Hdr RR_Header
|
|
Address uint64
|
|
}
|
|
|
|
func (rr *EUI64) String() string { return rr.Hdr.String() + euiToString(rr.Address, 64) }
|
|
|
|
// CAA RR. See RFC 6844.
|
|
type CAA struct {
|
|
Hdr RR_Header
|
|
Flag uint8
|
|
Tag string
|
|
Value string `dns:"octet"`
|
|
}
|
|
|
|
// rr.Value Is the character-string encoding of the value field as specified in RFC 1035, Section 5.1.
|
|
func (rr *CAA) String() string {
|
|
return rr.Hdr.String() + strconv.Itoa(int(rr.Flag)) + " " + rr.Tag + " " + sprintTxtOctet(rr.Value)
|
|
}
|
|
|
|
// UID RR. Deprecated, IANA-Reserved.
|
|
type UID struct {
|
|
Hdr RR_Header
|
|
Uid uint32
|
|
}
|
|
|
|
func (rr *UID) String() string { return rr.Hdr.String() + strconv.FormatInt(int64(rr.Uid), 10) }
|
|
|
|
// GID RR. Deprecated, IANA-Reserved.
|
|
type GID struct {
|
|
Hdr RR_Header
|
|
Gid uint32
|
|
}
|
|
|
|
func (rr *GID) String() string { return rr.Hdr.String() + strconv.FormatInt(int64(rr.Gid), 10) }
|
|
|
|
// UINFO RR. Deprecated, IANA-Reserved.
|
|
type UINFO struct {
|
|
Hdr RR_Header
|
|
Uinfo string
|
|
}
|
|
|
|
func (rr *UINFO) String() string { return rr.Hdr.String() + sprintTxt([]string{rr.Uinfo}) }
|
|
|
|
// EID RR. See http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt.
|
|
type EID struct {
|
|
Hdr RR_Header
|
|
Endpoint string `dns:"hex"`
|
|
}
|
|
|
|
func (rr *EID) String() string { return rr.Hdr.String() + strings.ToUpper(rr.Endpoint) }
|
|
|
|
// NIMLOC RR. See http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt.
|
|
type NIMLOC struct {
|
|
Hdr RR_Header
|
|
Locator string `dns:"hex"`
|
|
}
|
|
|
|
func (rr *NIMLOC) String() string { return rr.Hdr.String() + strings.ToUpper(rr.Locator) }
|
|
|
|
// OPENPGPKEY RR. See RFC 7929.
|
|
type OPENPGPKEY struct {
|
|
Hdr RR_Header
|
|
PublicKey string `dns:"base64"`
|
|
}
|
|
|
|
func (rr *OPENPGPKEY) String() string { return rr.Hdr.String() + rr.PublicKey }
|
|
|
|
// CSYNC RR. See RFC 7477.
|
|
type CSYNC struct {
|
|
Hdr RR_Header
|
|
Serial uint32
|
|
Flags uint16
|
|
TypeBitMap []uint16 `dns:"nsec"`
|
|
}
|
|
|
|
func (rr *CSYNC) String() string {
|
|
s := rr.Hdr.String() + strconv.FormatInt(int64(rr.Serial), 10) + " " + strconv.Itoa(int(rr.Flags))
|
|
|
|
for _, t := range rr.TypeBitMap {
|
|
s += " " + Type(t).String()
|
|
}
|
|
return s
|
|
}
|
|
|
|
func (rr *CSYNC) len(off int, compression map[string]struct{}) int {
|
|
l := rr.Hdr.len(off, compression)
|
|
l += 4 + 2
|
|
l += typeBitMapLen(rr.TypeBitMap)
|
|
return l
|
|
}
|
|
|
|
// ZONEMD RR, from draft-ietf-dnsop-dns-zone-digest
|
|
type ZONEMD struct {
|
|
Hdr RR_Header
|
|
Serial uint32
|
|
Scheme uint8
|
|
Hash uint8
|
|
Digest string `dns:"hex"`
|
|
}
|
|
|
|
func (rr *ZONEMD) String() string {
|
|
return rr.Hdr.String() +
|
|
strconv.Itoa(int(rr.Serial)) +
|
|
" " + strconv.Itoa(int(rr.Scheme)) +
|
|
" " + strconv.Itoa(int(rr.Hash)) +
|
|
" " + rr.Digest
|
|
}
|
|
|
|
// APL RR. See RFC 3123.
|
|
type APL struct {
|
|
Hdr RR_Header
|
|
Prefixes []APLPrefix `dns:"apl"`
|
|
}
|
|
|
|
// APLPrefix is an address prefix hold by an APL record.
|
|
type APLPrefix struct {
|
|
Negation bool
|
|
Network net.IPNet
|
|
}
|
|
|
|
// String returns presentation form of the APL record.
|
|
func (rr *APL) String() string {
|
|
var sb strings.Builder
|
|
sb.WriteString(rr.Hdr.String())
|
|
for i, p := range rr.Prefixes {
|
|
if i > 0 {
|
|
sb.WriteByte(' ')
|
|
}
|
|
sb.WriteString(p.str())
|
|
}
|
|
return sb.String()
|
|
}
|
|
|
|
// str returns presentation form of the APL prefix.
|
|
func (a *APLPrefix) str() string {
|
|
var sb strings.Builder
|
|
if a.Negation {
|
|
sb.WriteByte('!')
|
|
}
|
|
|
|
switch len(a.Network.IP) {
|
|
case net.IPv4len:
|
|
sb.WriteByte('1')
|
|
case net.IPv6len:
|
|
sb.WriteByte('2')
|
|
}
|
|
|
|
sb.WriteByte(':')
|
|
|
|
switch len(a.Network.IP) {
|
|
case net.IPv4len:
|
|
sb.WriteString(a.Network.IP.String())
|
|
case net.IPv6len:
|
|
// add prefix for IPv4-mapped IPv6
|
|
if v4 := a.Network.IP.To4(); v4 != nil {
|
|
sb.WriteString("::ffff:")
|
|
}
|
|
sb.WriteString(a.Network.IP.String())
|
|
}
|
|
|
|
sb.WriteByte('/')
|
|
|
|
prefix, _ := a.Network.Mask.Size()
|
|
sb.WriteString(strconv.Itoa(prefix))
|
|
|
|
return sb.String()
|
|
}
|
|
|
|
// equals reports whether two APL prefixes are identical.
|
|
func (a *APLPrefix) equals(b *APLPrefix) bool {
|
|
return a.Negation == b.Negation &&
|
|
bytes.Equal(a.Network.IP, b.Network.IP) &&
|
|
bytes.Equal(a.Network.Mask, b.Network.Mask)
|
|
}
|
|
|
|
// copy returns a copy of the APL prefix.
|
|
func (a *APLPrefix) copy() APLPrefix {
|
|
return APLPrefix{
|
|
Negation: a.Negation,
|
|
Network: copyNet(a.Network),
|
|
}
|
|
}
|
|
|
|
// len returns size of the prefix in wire format.
|
|
func (a *APLPrefix) len() int {
|
|
// 4-byte header and the network address prefix (see Section 4 of RFC 3123)
|
|
prefix, _ := a.Network.Mask.Size()
|
|
return 4 + (prefix+7)/8
|
|
}
|
|
|
|
// TimeToString translates the RRSIG's incep. and expir. times to the
|
|
// string representation used when printing the record.
|
|
// It takes serial arithmetic (RFC 1982) into account.
|
|
func TimeToString(t uint32) string {
|
|
mod := (int64(t)-time.Now().Unix())/year68 - 1
|
|
if mod < 0 {
|
|
mod = 0
|
|
}
|
|
ti := time.Unix(int64(t)-mod*year68, 0).UTC()
|
|
return ti.Format("20060102150405")
|
|
}
|
|
|
|
// StringToTime translates the RRSIG's incep. and expir. times from
|
|
// string values like "20110403154150" to an 32 bit integer.
|
|
// It takes serial arithmetic (RFC 1982) into account.
|
|
func StringToTime(s string) (uint32, error) {
|
|
t, err := time.Parse("20060102150405", s)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
mod := t.Unix()/year68 - 1
|
|
if mod < 0 {
|
|
mod = 0
|
|
}
|
|
return uint32(t.Unix() - mod*year68), nil
|
|
}
|
|
|
|
// saltToString converts a NSECX salt to uppercase and returns "-" when it is empty.
|
|
func saltToString(s string) string {
|
|
if s == "" {
|
|
return "-"
|
|
}
|
|
return strings.ToUpper(s)
|
|
}
|
|
|
|
func euiToString(eui uint64, bits int) (hex string) {
|
|
switch bits {
|
|
case 64:
|
|
hex = fmt.Sprintf("%16.16x", eui)
|
|
hex = hex[0:2] + "-" + hex[2:4] + "-" + hex[4:6] + "-" + hex[6:8] +
|
|
"-" + hex[8:10] + "-" + hex[10:12] + "-" + hex[12:14] + "-" + hex[14:16]
|
|
case 48:
|
|
hex = fmt.Sprintf("%12.12x", eui)
|
|
hex = hex[0:2] + "-" + hex[2:4] + "-" + hex[4:6] + "-" + hex[6:8] +
|
|
"-" + hex[8:10] + "-" + hex[10:12]
|
|
}
|
|
return
|
|
}
|
|
|
|
// copyIP returns a copy of ip.
|
|
func copyIP(ip net.IP) net.IP {
|
|
p := make(net.IP, len(ip))
|
|
copy(p, ip)
|
|
return p
|
|
}
|
|
|
|
// copyNet returns a copy of a subnet.
|
|
func copyNet(n net.IPNet) net.IPNet {
|
|
m := make(net.IPMask, len(n.Mask))
|
|
copy(m, n.Mask)
|
|
|
|
return net.IPNet{
|
|
IP: copyIP(n.IP),
|
|
Mask: m,
|
|
}
|
|
}
|
|
|
|
// SplitN splits a string into N sized string chunks.
|
|
// This might become an exported function once.
|
|
func splitN(s string, n int) []string {
|
|
if len(s) < n {
|
|
return []string{s}
|
|
}
|
|
sx := []string{}
|
|
p, i := 0, n
|
|
for {
|
|
if i <= len(s) {
|
|
sx = append(sx, s[p:i])
|
|
} else {
|
|
sx = append(sx, s[p:])
|
|
break
|
|
|
|
}
|
|
p, i = p+n, i+n
|
|
}
|
|
|
|
return sx
|
|
}
|