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 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 ) // 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 // 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 '"', '\\': 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, just an RFC 1035 "quoted" character 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 AVC struct { Hdr RR_Header Txt []string `dns:"txt"` } func (rr *AVC) 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 }