1295 lines
27 KiB
Go
1295 lines
27 KiB
Go
|
package dns
|
||
|
|
||
|
import (
|
||
|
"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
|
||
|
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
|
||
|
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
|
||
|
|
||
|
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.
|
||
|
RcodeSuccess = 0
|
||
|
RcodeFormatError = 1
|
||
|
RcodeServerFailure = 2
|
||
|
RcodeNameError = 3
|
||
|
RcodeNotImplemented = 4
|
||
|
RcodeRefused = 5
|
||
|
RcodeYXDomain = 6
|
||
|
RcodeYXRrset = 7
|
||
|
RcodeNXRrset = 8
|
||
|
RcodeNotAuth = 9
|
||
|
RcodeNotZone = 10
|
||
|
RcodeBadSig = 16 // TSIG
|
||
|
RcodeBadVers = 16 // EDNS0
|
||
|
RcodeBadKey = 17
|
||
|
RcodeBadTime = 18
|
||
|
RcodeBadMode = 19 // TKEY
|
||
|
RcodeBadName = 20
|
||
|
RcodeBadAlg = 21
|
||
|
RcodeBadTrunc = 22 // TSIG
|
||
|
RcodeBadCookie = 23 // DNS Cookies
|
||
|
|
||
|
// Message Opcodes. There is no 3.
|
||
|
OpcodeQuery = 0
|
||
|
OpcodeIQuery = 1
|
||
|
OpcodeStatus = 2
|
||
|
OpcodeNotify = 4
|
||
|
OpcodeUpdate = 5
|
||
|
)
|
||
|
|
||
|
// Headers 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 // authticated data
|
||
|
_CD = 1 << 4 // checking disabled
|
||
|
|
||
|
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",
|
||
|
}
|
||
|
|
||
|
// StringToCertType is the reverseof CertTypeToString.
|
||
|
var StringToCertType = reverseInt16(CertTypeToString)
|
||
|
|
||
|
//go:generate go run types_generate.go
|
||
|
|
||
|
// Question holds a DNS question. There can be multiple questions in the
|
||
|
// question section of a message. Usually there is just one.
|
||
|
type Question struct {
|
||
|
Name string `dns:"cdomain-name"` // "cdomain-name" specifies encoding (and may be compressed)
|
||
|
Qtype uint16
|
||
|
Qclass uint16
|
||
|
}
|
||
|
|
||
|
func (q *Question) len() int {
|
||
|
return len(q.Name) + 1 + 2 + 2
|
||
|
}
|
||
|
|
||
|
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 wildcard 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() }
|
||
|
|
||
|
type CNAME struct {
|
||
|
Hdr RR_Header
|
||
|
Target string `dns:"cdomain-name"`
|
||
|
}
|
||
|
|
||
|
func (rr *CNAME) String() string { return rr.Hdr.String() + sprintName(rr.Target) }
|
||
|
|
||
|
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})
|
||
|
}
|
||
|
|
||
|
type MB struct {
|
||
|
Hdr RR_Header
|
||
|
Mb string `dns:"cdomain-name"`
|
||
|
}
|
||
|
|
||
|
func (rr *MB) String() string { return rr.Hdr.String() + sprintName(rr.Mb) }
|
||
|
|
||
|
type MG struct {
|
||
|
Hdr RR_Header
|
||
|
Mg string `dns:"cdomain-name"`
|
||
|
}
|
||
|
|
||
|
func (rr *MG) String() string { return rr.Hdr.String() + sprintName(rr.Mg) }
|
||
|
|
||
|
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)
|
||
|
}
|
||
|
|
||
|
type MR struct {
|
||
|
Hdr RR_Header
|
||
|
Mr string `dns:"cdomain-name"`
|
||
|
}
|
||
|
|
||
|
func (rr *MR) String() string {
|
||
|
return rr.Hdr.String() + sprintName(rr.Mr)
|
||
|
}
|
||
|
|
||
|
type MF struct {
|
||
|
Hdr RR_Header
|
||
|
Mf string `dns:"cdomain-name"`
|
||
|
}
|
||
|
|
||
|
func (rr *MF) String() string {
|
||
|
return rr.Hdr.String() + sprintName(rr.Mf)
|
||
|
}
|
||
|
|
||
|
type MD struct {
|
||
|
Hdr RR_Header
|
||
|
Md string `dns:"cdomain-name"`
|
||
|
}
|
||
|
|
||
|
func (rr *MD) String() string {
|
||
|
return rr.Hdr.String() + sprintName(rr.Md)
|
||
|
}
|
||
|
|
||
|
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)
|
||
|
}
|
||
|
|
||
|
type AFSDB struct {
|
||
|
Hdr RR_Header
|
||
|
Subtype uint16
|
||
|
Hostname string `dns:"cdomain-name"`
|
||
|
}
|
||
|
|
||
|
func (rr *AFSDB) String() string {
|
||
|
return rr.Hdr.String() + strconv.Itoa(int(rr.Subtype)) + " " + sprintName(rr.Hostname)
|
||
|
}
|
||
|
|
||
|
type X25 struct {
|
||
|
Hdr RR_Header
|
||
|
PSDNAddress string
|
||
|
}
|
||
|
|
||
|
func (rr *X25) String() string {
|
||
|
return rr.Hdr.String() + rr.PSDNAddress
|
||
|
}
|
||
|
|
||
|
type RT struct {
|
||
|
Hdr RR_Header
|
||
|
Preference uint16
|
||
|
Host string `dns:"cdomain-name"`
|
||
|
}
|
||
|
|
||
|
func (rr *RT) String() string {
|
||
|
return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Host)
|
||
|
}
|
||
|
|
||
|
type NS struct {
|
||
|
Hdr RR_Header
|
||
|
Ns string `dns:"cdomain-name"`
|
||
|
}
|
||
|
|
||
|
func (rr *NS) String() string {
|
||
|
return rr.Hdr.String() + sprintName(rr.Ns)
|
||
|
}
|
||
|
|
||
|
type PTR struct {
|
||
|
Hdr RR_Header
|
||
|
Ptr string `dns:"cdomain-name"`
|
||
|
}
|
||
|
|
||
|
func (rr *PTR) String() string {
|
||
|
return rr.Hdr.String() + sprintName(rr.Ptr)
|
||
|
}
|
||
|
|
||
|
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() + rr.Mbox + " " + sprintTxt([]string{rr.Txt})
|
||
|
}
|
||
|
|
||
|
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)
|
||
|
}
|
||
|
|
||
|
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 {
|
||
|
src := []byte(s)
|
||
|
dst := make([]byte, 0, len(src))
|
||
|
for i := 0; i < len(src); {
|
||
|
if i+1 < len(src) && src[i] == '\\' && src[i+1] == '.' {
|
||
|
dst = append(dst, src[i:i+2]...)
|
||
|
i += 2
|
||
|
} else {
|
||
|
b, n := nextByte(src, i)
|
||
|
if n == 0 {
|
||
|
i++ // dangling back slash
|
||
|
} else if b == '.' {
|
||
|
dst = append(dst, b)
|
||
|
} else {
|
||
|
dst = appendDomainNameByte(dst, b)
|
||
|
}
|
||
|
i += n
|
||
|
}
|
||
|
}
|
||
|
return string(dst)
|
||
|
}
|
||
|
|
||
|
func sprintTxtOctet(s string) string {
|
||
|
src := []byte(s)
|
||
|
dst := make([]byte, 0, len(src))
|
||
|
dst = append(dst, '"')
|
||
|
for i := 0; i < len(src); {
|
||
|
if i+1 < len(src) && src[i] == '\\' && src[i+1] == '.' {
|
||
|
dst = append(dst, src[i:i+2]...)
|
||
|
i += 2
|
||
|
} else {
|
||
|
b, n := nextByte(src, i)
|
||
|
if n == 0 {
|
||
|
i++ // dangling back slash
|
||
|
} else if b == '.' {
|
||
|
dst = append(dst, b)
|
||
|
} else {
|
||
|
if b < ' ' || b > '~' {
|
||
|
dst = appendByte(dst, b)
|
||
|
} else {
|
||
|
dst = append(dst, b)
|
||
|
}
|
||
|
}
|
||
|
i += n
|
||
|
}
|
||
|
}
|
||
|
dst = append(dst, '"')
|
||
|
return string(dst)
|
||
|
}
|
||
|
|
||
|
func sprintTxt(txt []string) string {
|
||
|
var out []byte
|
||
|
for i, s := range txt {
|
||
|
if i > 0 {
|
||
|
out = append(out, ` "`...)
|
||
|
} else {
|
||
|
out = append(out, '"')
|
||
|
}
|
||
|
bs := []byte(s)
|
||
|
for j := 0; j < len(bs); {
|
||
|
b, n := nextByte(bs, j)
|
||
|
if n == 0 {
|
||
|
break
|
||
|
}
|
||
|
out = appendTXTStringByte(out, b)
|
||
|
j += n
|
||
|
}
|
||
|
out = append(out, '"')
|
||
|
}
|
||
|
return string(out)
|
||
|
}
|
||
|
|
||
|
func appendDomainNameByte(s []byte, b byte) []byte {
|
||
|
switch b {
|
||
|
case '.', ' ', '\'', '@', ';', '(', ')': // additional chars to escape
|
||
|
return append(s, '\\', b)
|
||
|
}
|
||
|
return appendTXTStringByte(s, b)
|
||
|
}
|
||
|
|
||
|
func appendTXTStringByte(s []byte, b byte) []byte {
|
||
|
switch b {
|
||
|
case '\t':
|
||
|
return append(s, '\\', 't')
|
||
|
case '\r':
|
||
|
return append(s, '\\', 'r')
|
||
|
case '\n':
|
||
|
return append(s, '\\', 'n')
|
||
|
case '"', '\\':
|
||
|
return append(s, '\\', b)
|
||
|
}
|
||
|
if b < ' ' || b > '~' {
|
||
|
return appendByte(s, b)
|
||
|
}
|
||
|
return append(s, b)
|
||
|
}
|
||
|
|
||
|
func appendByte(s []byte, b byte) []byte {
|
||
|
var buf [3]byte
|
||
|
bufs := strconv.AppendInt(buf[:0], int64(b), 10)
|
||
|
s = append(s, '\\')
|
||
|
for i := 0; i < 3-len(bufs); i++ {
|
||
|
s = append(s, '0')
|
||
|
}
|
||
|
for _, r := range bufs {
|
||
|
s = append(s, r)
|
||
|
}
|
||
|
return s
|
||
|
}
|
||
|
|
||
|
func nextByte(b []byte, offset int) (byte, int) {
|
||
|
if offset >= len(b) {
|
||
|
return 0, 0
|
||
|
}
|
||
|
if b[offset] != '\\' {
|
||
|
// not an escape sequence
|
||
|
return b[offset], 1
|
||
|
}
|
||
|
switch len(b) - offset {
|
||
|
case 1: // dangling escape
|
||
|
return 0, 0
|
||
|
case 2, 3: // too short to be \ddd
|
||
|
default: // maybe \ddd
|
||
|
if isDigit(b[offset+1]) && isDigit(b[offset+2]) && isDigit(b[offset+3]) {
|
||
|
return dddToByte(b[offset+1:]), 4
|
||
|
}
|
||
|
}
|
||
|
// not \ddd, maybe a control char
|
||
|
switch b[offset+1] {
|
||
|
case 't':
|
||
|
return '\t', 2
|
||
|
case 'r':
|
||
|
return '\r', 2
|
||
|
case 'n':
|
||
|
return '\n', 2
|
||
|
default:
|
||
|
return b[offset+1], 2
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type SPF struct {
|
||
|
Hdr RR_Header
|
||
|
Txt []string `dns:"txt"`
|
||
|
}
|
||
|
|
||
|
func (rr *SPF) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) }
|
||
|
|
||
|
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)
|
||
|
}
|
||
|
|
||
|
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
|
||
|
}
|
||
|
|
||
|
// The CERT resource record, 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
|
||
|
}
|
||
|
|
||
|
// The DNAME resource record, 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)
|
||
|
}
|
||
|
|
||
|
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()
|
||
|
}
|
||
|
|
||
|
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()
|
||
|
}
|
||
|
|
||
|
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)
|
||
|
}
|
||
|
|
||
|
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
|
||
|
}
|
||
|
|
||
|
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 RFC1876 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 is identical to RRSIG and nowadays only used for SIG(0), RFC2931.
|
||
|
type SIG struct {
|
||
|
RRSIG
|
||
|
}
|
||
|
|
||
|
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
|
||
|
}
|
||
|
|
||
|
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 i := 0; i < len(rr.TypeBitMap); i++ {
|
||
|
s += " " + Type(rr.TypeBitMap[i]).String()
|
||
|
}
|
||
|
return s
|
||
|
}
|
||
|
|
||
|
func (rr *NSEC) len() int {
|
||
|
l := rr.Hdr.len() + len(rr.NextDomain) + 1
|
||
|
lastwindow := uint32(2 ^ 32 + 1)
|
||
|
for _, t := range rr.TypeBitMap {
|
||
|
window := t / 256
|
||
|
if uint32(window) != lastwindow {
|
||
|
l += 1 + 32
|
||
|
}
|
||
|
lastwindow = uint32(window)
|
||
|
}
|
||
|
return l
|
||
|
}
|
||
|
|
||
|
type DLV struct {
|
||
|
DS
|
||
|
}
|
||
|
|
||
|
type CDS struct {
|
||
|
DS
|
||
|
}
|
||
|
|
||
|
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)
|
||
|
}
|
||
|
|
||
|
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)
|
||
|
}
|
||
|
|
||
|
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)
|
||
|
}
|
||
|
|
||
|
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)
|
||
|
}
|
||
|
|
||
|
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)
|
||
|
}
|
||
|
|
||
|
type KEY struct {
|
||
|
DNSKEY
|
||
|
}
|
||
|
|
||
|
type CDNSKEY struct {
|
||
|
DNSKEY
|
||
|
}
|
||
|
|
||
|
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
|
||
|
}
|
||
|
|
||
|
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
|
||
|
}
|
||
|
|
||
|
type NSAPPTR struct {
|
||
|
Hdr RR_Header
|
||
|
Ptr string `dns:"domain-name"`
|
||
|
}
|
||
|
|
||
|
func (rr *NSAPPTR) String() string { return rr.Hdr.String() + sprintName(rr.Ptr) }
|
||
|
|
||
|
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 i := 0; i < len(rr.TypeBitMap); i++ {
|
||
|
s += " " + Type(rr.TypeBitMap[i]).String()
|
||
|
}
|
||
|
return s
|
||
|
}
|
||
|
|
||
|
func (rr *NSEC3) len() int {
|
||
|
l := rr.Hdr.len() + 6 + len(rr.Salt)/2 + 1 + len(rr.NextDomain) + 1
|
||
|
lastwindow := uint32(2 ^ 32 + 1)
|
||
|
for _, t := range rr.TypeBitMap {
|
||
|
window := t / 256
|
||
|
if uint32(window) != lastwindow {
|
||
|
l += 1 + 32
|
||
|
}
|
||
|
lastwindow = uint32(window)
|
||
|
}
|
||
|
return l
|
||
|
}
|
||
|
|
||
|
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
|
||
|
}
|
||
|
|
||
|
type TKEY struct {
|
||
|
Hdr RR_Header
|
||
|
Algorithm string `dns:"domain-name"`
|
||
|
Inception uint32
|
||
|
Expiration uint32
|
||
|
Mode uint16
|
||
|
Error uint16
|
||
|
KeySize uint16
|
||
|
Key string
|
||
|
OtherLen uint16
|
||
|
OtherData string
|
||
|
}
|
||
|
|
||
|
func (rr *TKEY) String() string {
|
||
|
// It has no presentation format
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
// RFC3597 represents an unknown/generic RR.
|
||
|
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
|
||
|
}
|
||
|
|
||
|
type URI struct {
|
||
|
Hdr RR_Header
|
||
|
Priority uint16
|
||
|
Weight uint16
|
||
|
Target string `dns:"octet"`
|
||
|
}
|
||
|
|
||
|
func (rr *URI) String() string {
|
||
|
return rr.Hdr.String() + strconv.Itoa(int(rr.Priority)) +
|
||
|
" " + strconv.Itoa(int(rr.Weight)) + " " + sprintTxtOctet(rr.Target)
|
||
|
}
|
||
|
|
||
|
type DHCID struct {
|
||
|
Hdr RR_Header
|
||
|
Digest string `dns:"base64"`
|
||
|
}
|
||
|
|
||
|
func (rr *DHCID) String() string { return rr.Hdr.String() + rr.Digest }
|
||
|
|
||
|
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
|
||
|
}
|
||
|
|
||
|
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
|
||
|
}
|
||
|
|
||
|
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
|
||
|
}
|
||
|
|
||
|
type NINFO struct {
|
||
|
Hdr RR_Header
|
||
|
ZSData []string `dns:"txt"`
|
||
|
}
|
||
|
|
||
|
func (rr *NINFO) String() string { return rr.Hdr.String() + sprintTxt(rr.ZSData) }
|
||
|
|
||
|
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
|
||
|
}
|
||
|
|
||
|
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()
|
||
|
}
|
||
|
|
||
|
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
|
||
|
}
|
||
|
|
||
|
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)
|
||
|
}
|
||
|
|
||
|
type EUI48 struct {
|
||
|
Hdr RR_Header
|
||
|
Address uint64 `dns:"uint48"`
|
||
|
}
|
||
|
|
||
|
func (rr *EUI48) String() string { return rr.Hdr.String() + euiToString(rr.Address, 48) }
|
||
|
|
||
|
type EUI64 struct {
|
||
|
Hdr RR_Header
|
||
|
Address uint64
|
||
|
}
|
||
|
|
||
|
func (rr *EUI64) String() string { return rr.Hdr.String() + euiToString(rr.Address, 64) }
|
||
|
|
||
|
type CAA struct {
|
||
|
Hdr RR_Header
|
||
|
Flag uint8
|
||
|
Tag string
|
||
|
Value string `dns:"octet"`
|
||
|
}
|
||
|
|
||
|
func (rr *CAA) String() string {
|
||
|
return rr.Hdr.String() + strconv.Itoa(int(rr.Flag)) + " " + rr.Tag + " " + sprintTxtOctet(rr.Value)
|
||
|
}
|
||
|
|
||
|
type UID struct {
|
||
|
Hdr RR_Header
|
||
|
Uid uint32
|
||
|
}
|
||
|
|
||
|
func (rr *UID) String() string { return rr.Hdr.String() + strconv.FormatInt(int64(rr.Uid), 10) }
|
||
|
|
||
|
type GID struct {
|
||
|
Hdr RR_Header
|
||
|
Gid uint32
|
||
|
}
|
||
|
|
||
|
func (rr *GID) String() string { return rr.Hdr.String() + strconv.FormatInt(int64(rr.Gid), 10) }
|
||
|
|
||
|
type UINFO struct {
|
||
|
Hdr RR_Header
|
||
|
Uinfo string
|
||
|
}
|
||
|
|
||
|
func (rr *UINFO) String() string { return rr.Hdr.String() + sprintTxt([]string{rr.Uinfo}) }
|
||
|
|
||
|
type EID struct {
|
||
|
Hdr RR_Header
|
||
|
Endpoint string `dns:"hex"`
|
||
|
}
|
||
|
|
||
|
func (rr *EID) String() string { return rr.Hdr.String() + strings.ToUpper(rr.Endpoint) }
|
||
|
|
||
|
type NIMLOC struct {
|
||
|
Hdr RR_Header
|
||
|
Locator string `dns:"hex"`
|
||
|
}
|
||
|
|
||
|
func (rr *NIMLOC) String() string { return rr.Hdr.String() + strings.ToUpper(rr.Locator) }
|
||
|
|
||
|
type OPENPGPKEY struct {
|
||
|
Hdr RR_Header
|
||
|
PublicKey string `dns:"base64"`
|
||
|
}
|
||
|
|
||
|
func (rr *OPENPGPKEY) String() string { return rr.Hdr.String() + rr.PublicKey }
|
||
|
|
||
|
// 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 len(s) == 0 {
|
||
|
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
|
||
|
}
|
||
|
|
||
|
// 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
|
||
|
}
|