1package dns
2
3import (
4	"bytes"
5	"fmt"
6	"net"
7	"strconv"
8	"strings"
9	"time"
10)
11
12type (
13	// Type is a DNS type.
14	Type uint16
15	// Class is a DNS class.
16	Class uint16
17	// Name is a DNS domain name.
18	Name string
19)
20
21// Packet formats
22
23// Wire constants and supported types.
24const (
25	// valid RR_Header.Rrtype and Question.qtype
26
27	TypeNone       uint16 = 0
28	TypeA          uint16 = 1
29	TypeNS         uint16 = 2
30	TypeMD         uint16 = 3
31	TypeMF         uint16 = 4
32	TypeCNAME      uint16 = 5
33	TypeSOA        uint16 = 6
34	TypeMB         uint16 = 7
35	TypeMG         uint16 = 8
36	TypeMR         uint16 = 9
37	TypeNULL       uint16 = 10
38	TypePTR        uint16 = 12
39	TypeHINFO      uint16 = 13
40	TypeMINFO      uint16 = 14
41	TypeMX         uint16 = 15
42	TypeTXT        uint16 = 16
43	TypeRP         uint16 = 17
44	TypeAFSDB      uint16 = 18
45	TypeX25        uint16 = 19
46	TypeISDN       uint16 = 20
47	TypeRT         uint16 = 21
48	TypeNSAPPTR    uint16 = 23
49	TypeSIG        uint16 = 24
50	TypeKEY        uint16 = 25
51	TypePX         uint16 = 26
52	TypeGPOS       uint16 = 27
53	TypeAAAA       uint16 = 28
54	TypeLOC        uint16 = 29
55	TypeNXT        uint16 = 30
56	TypeEID        uint16 = 31
57	TypeNIMLOC     uint16 = 32
58	TypeSRV        uint16 = 33
59	TypeATMA       uint16 = 34
60	TypeNAPTR      uint16 = 35
61	TypeKX         uint16 = 36
62	TypeCERT       uint16 = 37
63	TypeDNAME      uint16 = 39
64	TypeOPT        uint16 = 41 // EDNS
65	TypeAPL        uint16 = 42
66	TypeDS         uint16 = 43
67	TypeSSHFP      uint16 = 44
68	TypeRRSIG      uint16 = 46
69	TypeNSEC       uint16 = 47
70	TypeDNSKEY     uint16 = 48
71	TypeDHCID      uint16 = 49
72	TypeNSEC3      uint16 = 50
73	TypeNSEC3PARAM uint16 = 51
74	TypeTLSA       uint16 = 52
75	TypeSMIMEA     uint16 = 53
76	TypeHIP        uint16 = 55
77	TypeNINFO      uint16 = 56
78	TypeRKEY       uint16 = 57
79	TypeTALINK     uint16 = 58
80	TypeCDS        uint16 = 59
81	TypeCDNSKEY    uint16 = 60
82	TypeOPENPGPKEY uint16 = 61
83	TypeCSYNC      uint16 = 62
84	TypeZONEMD     uint16 = 63
85	TypeSVCB       uint16 = 64
86	TypeHTTPS      uint16 = 65
87	TypeSPF        uint16 = 99
88	TypeUINFO      uint16 = 100
89	TypeUID        uint16 = 101
90	TypeGID        uint16 = 102
91	TypeUNSPEC     uint16 = 103
92	TypeNID        uint16 = 104
93	TypeL32        uint16 = 105
94	TypeL64        uint16 = 106
95	TypeLP         uint16 = 107
96	TypeEUI48      uint16 = 108
97	TypeEUI64      uint16 = 109
98	TypeURI        uint16 = 256
99	TypeCAA        uint16 = 257
100	TypeAVC        uint16 = 258
101
102	TypeTKEY uint16 = 249
103	TypeTSIG uint16 = 250
104
105	// valid Question.Qtype only
106	TypeIXFR  uint16 = 251
107	TypeAXFR  uint16 = 252
108	TypeMAILB uint16 = 253
109	TypeMAILA uint16 = 254
110	TypeANY   uint16 = 255
111
112	TypeTA       uint16 = 32768
113	TypeDLV      uint16 = 32769
114	TypeReserved uint16 = 65535
115
116	// valid Question.Qclass
117	ClassINET   = 1
118	ClassCSNET  = 2
119	ClassCHAOS  = 3
120	ClassHESIOD = 4
121	ClassNONE   = 254
122	ClassANY    = 255
123
124	// Message Response Codes, see https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml
125	RcodeSuccess        = 0  // NoError   - No Error                          [DNS]
126	RcodeFormatError    = 1  // FormErr   - Format Error                      [DNS]
127	RcodeServerFailure  = 2  // ServFail  - Server Failure                    [DNS]
128	RcodeNameError      = 3  // NXDomain  - Non-Existent Domain               [DNS]
129	RcodeNotImplemented = 4  // NotImp    - Not Implemented                   [DNS]
130	RcodeRefused        = 5  // Refused   - Query Refused                     [DNS]
131	RcodeYXDomain       = 6  // YXDomain  - Name Exists when it should not    [DNS Update]
132	RcodeYXRrset        = 7  // YXRRSet   - RR Set Exists when it should not  [DNS Update]
133	RcodeNXRrset        = 8  // NXRRSet   - RR Set that should exist does not [DNS Update]
134	RcodeNotAuth        = 9  // NotAuth   - Server Not Authoritative for zone [DNS Update]
135	RcodeNotZone        = 10 // NotZone   - Name not contained in zone        [DNS Update/TSIG]
136	RcodeBadSig         = 16 // BADSIG    - TSIG Signature Failure            [TSIG]
137	RcodeBadVers        = 16 // BADVERS   - Bad OPT Version                   [EDNS0]
138	RcodeBadKey         = 17 // BADKEY    - Key not recognized                [TSIG]
139	RcodeBadTime        = 18 // BADTIME   - Signature out of time window      [TSIG]
140	RcodeBadMode        = 19 // BADMODE   - Bad TKEY Mode                     [TKEY]
141	RcodeBadName        = 20 // BADNAME   - Duplicate key name                [TKEY]
142	RcodeBadAlg         = 21 // BADALG    - Algorithm not supported           [TKEY]
143	RcodeBadTrunc       = 22 // BADTRUNC  - Bad Truncation                    [TSIG]
144	RcodeBadCookie      = 23 // BADCOOKIE - Bad/missing Server Cookie         [DNS Cookies]
145
146	// Message Opcodes. There is no 3.
147	OpcodeQuery  = 0
148	OpcodeIQuery = 1
149	OpcodeStatus = 2
150	OpcodeNotify = 4
151	OpcodeUpdate = 5
152)
153
154// Used in ZONEMD https://tools.ietf.org/html/rfc8976
155
156const (
157	// ZoneMD Accepted Schemes
158	ZoneMDSchemeSimple = 1
159
160	// ZoneMD Hash Algorithms
161	ZoneMDHashAlgSHA384 = 1
162	ZoneMDHashAlgSHA512 = 2
163)
164
165// Header is the wire format for the DNS packet header.
166type Header struct {
167	Id                                 uint16
168	Bits                               uint16
169	Qdcount, Ancount, Nscount, Arcount uint16
170}
171
172const (
173	headerSize = 12
174
175	// Header.Bits
176	_QR = 1 << 15 // query/response (response=1)
177	_AA = 1 << 10 // authoritative
178	_TC = 1 << 9  // truncated
179	_RD = 1 << 8  // recursion desired
180	_RA = 1 << 7  // recursion available
181	_Z  = 1 << 6  // Z
182	_AD = 1 << 5  // authenticated data
183	_CD = 1 << 4  // checking disabled
184)
185
186// Various constants used in the LOC RR. See RFC 1887.
187const (
188	LOC_EQUATOR       = 1 << 31 // RFC 1876, Section 2.
189	LOC_PRIMEMERIDIAN = 1 << 31 // RFC 1876, Section 2.
190	LOC_HOURS         = 60 * 1000
191	LOC_DEGREES       = 60 * LOC_HOURS
192	LOC_ALTITUDEBASE  = 100000
193)
194
195// Different Certificate Types, see RFC 4398, Section 2.1
196const (
197	CertPKIX = 1 + iota
198	CertSPKI
199	CertPGP
200	CertIPIX
201	CertISPKI
202	CertIPGP
203	CertACPKIX
204	CertIACPKIX
205	CertURI = 253
206	CertOID = 254
207)
208
209// CertTypeToString converts the Cert Type to its string representation.
210// See RFC 4398 and RFC 6944.
211var CertTypeToString = map[uint16]string{
212	CertPKIX:    "PKIX",
213	CertSPKI:    "SPKI",
214	CertPGP:     "PGP",
215	CertIPIX:    "IPIX",
216	CertISPKI:   "ISPKI",
217	CertIPGP:    "IPGP",
218	CertACPKIX:  "ACPKIX",
219	CertIACPKIX: "IACPKIX",
220	CertURI:     "URI",
221	CertOID:     "OID",
222}
223
224//go:generate go run types_generate.go
225
226// Question holds a DNS question. Usually there is just one. While the
227// original DNS RFCs allow multiple questions in the question section of a
228// message, in practice it never works. Because most DNS servers see multiple
229// questions as an error, it is recommended to only have one question per
230// message.
231type Question struct {
232	Name   string `dns:"cdomain-name"` // "cdomain-name" specifies encoding (and may be compressed)
233	Qtype  uint16
234	Qclass uint16
235}
236
237func (q *Question) len(off int, compression map[string]struct{}) int {
238	l := domainNameLen(q.Name, off, compression, true)
239	l += 2 + 2
240	return l
241}
242
243func (q *Question) String() (s string) {
244	// prefix with ; (as in dig)
245	s = ";" + sprintName(q.Name) + "\t"
246	s += Class(q.Qclass).String() + "\t"
247	s += " " + Type(q.Qtype).String()
248	return s
249}
250
251// ANY is a wild card record. See RFC 1035, Section 3.2.3. ANY
252// is named "*" there.
253type ANY struct {
254	Hdr RR_Header
255	// Does not have any rdata
256}
257
258func (rr *ANY) String() string { return rr.Hdr.String() }
259
260func (*ANY) parse(c *zlexer, origin string) *ParseError {
261	return &ParseError{err: "ANY records do not have a presentation format"}
262}
263
264// NULL RR. See RFC 1035.
265type NULL struct {
266	Hdr  RR_Header
267	Data string `dns:"any"`
268}
269
270func (rr *NULL) String() string {
271	// There is no presentation format; prefix string with a comment.
272	return ";" + rr.Hdr.String() + rr.Data
273}
274
275func (*NULL) parse(c *zlexer, origin string) *ParseError {
276	return &ParseError{err: "NULL records do not have a presentation format"}
277}
278
279// CNAME RR. See RFC 1034.
280type CNAME struct {
281	Hdr    RR_Header
282	Target string `dns:"cdomain-name"`
283}
284
285func (rr *CNAME) String() string { return rr.Hdr.String() + sprintName(rr.Target) }
286
287// HINFO RR. See RFC 1034.
288type HINFO struct {
289	Hdr RR_Header
290	Cpu string
291	Os  string
292}
293
294func (rr *HINFO) String() string {
295	return rr.Hdr.String() + sprintTxt([]string{rr.Cpu, rr.Os})
296}
297
298// MB RR. See RFC 1035.
299type MB struct {
300	Hdr RR_Header
301	Mb  string `dns:"cdomain-name"`
302}
303
304func (rr *MB) String() string { return rr.Hdr.String() + sprintName(rr.Mb) }
305
306// MG RR. See RFC 1035.
307type MG struct {
308	Hdr RR_Header
309	Mg  string `dns:"cdomain-name"`
310}
311
312func (rr *MG) String() string { return rr.Hdr.String() + sprintName(rr.Mg) }
313
314// MINFO RR. See RFC 1035.
315type MINFO struct {
316	Hdr   RR_Header
317	Rmail string `dns:"cdomain-name"`
318	Email string `dns:"cdomain-name"`
319}
320
321func (rr *MINFO) String() string {
322	return rr.Hdr.String() + sprintName(rr.Rmail) + " " + sprintName(rr.Email)
323}
324
325// MR RR. See RFC 1035.
326type MR struct {
327	Hdr RR_Header
328	Mr  string `dns:"cdomain-name"`
329}
330
331func (rr *MR) String() string {
332	return rr.Hdr.String() + sprintName(rr.Mr)
333}
334
335// MF RR. See RFC 1035.
336type MF struct {
337	Hdr RR_Header
338	Mf  string `dns:"cdomain-name"`
339}
340
341func (rr *MF) String() string {
342	return rr.Hdr.String() + sprintName(rr.Mf)
343}
344
345// MD RR. See RFC 1035.
346type MD struct {
347	Hdr RR_Header
348	Md  string `dns:"cdomain-name"`
349}
350
351func (rr *MD) String() string {
352	return rr.Hdr.String() + sprintName(rr.Md)
353}
354
355// MX RR. See RFC 1035.
356type MX struct {
357	Hdr        RR_Header
358	Preference uint16
359	Mx         string `dns:"cdomain-name"`
360}
361
362func (rr *MX) String() string {
363	return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Mx)
364}
365
366// AFSDB RR. See RFC 1183.
367type AFSDB struct {
368	Hdr      RR_Header
369	Subtype  uint16
370	Hostname string `dns:"domain-name"`
371}
372
373func (rr *AFSDB) String() string {
374	return rr.Hdr.String() + strconv.Itoa(int(rr.Subtype)) + " " + sprintName(rr.Hostname)
375}
376
377// X25 RR. See RFC 1183, Section 3.1.
378type X25 struct {
379	Hdr         RR_Header
380	PSDNAddress string
381}
382
383func (rr *X25) String() string {
384	return rr.Hdr.String() + rr.PSDNAddress
385}
386
387// RT RR. See RFC 1183, Section 3.3.
388type RT struct {
389	Hdr        RR_Header
390	Preference uint16
391	Host       string `dns:"domain-name"` // RFC 3597 prohibits compressing records not defined in RFC 1035.
392}
393
394func (rr *RT) String() string {
395	return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Host)
396}
397
398// NS RR. See RFC 1035.
399type NS struct {
400	Hdr RR_Header
401	Ns  string `dns:"cdomain-name"`
402}
403
404func (rr *NS) String() string {
405	return rr.Hdr.String() + sprintName(rr.Ns)
406}
407
408// PTR RR. See RFC 1035.
409type PTR struct {
410	Hdr RR_Header
411	Ptr string `dns:"cdomain-name"`
412}
413
414func (rr *PTR) String() string {
415	return rr.Hdr.String() + sprintName(rr.Ptr)
416}
417
418// RP RR. See RFC 1138, Section 2.2.
419type RP struct {
420	Hdr  RR_Header
421	Mbox string `dns:"domain-name"`
422	Txt  string `dns:"domain-name"`
423}
424
425func (rr *RP) String() string {
426	return rr.Hdr.String() + sprintName(rr.Mbox) + " " + sprintName(rr.Txt)
427}
428
429// SOA RR. See RFC 1035.
430type SOA struct {
431	Hdr     RR_Header
432	Ns      string `dns:"cdomain-name"`
433	Mbox    string `dns:"cdomain-name"`
434	Serial  uint32
435	Refresh uint32
436	Retry   uint32
437	Expire  uint32
438	Minttl  uint32
439}
440
441func (rr *SOA) String() string {
442	return rr.Hdr.String() + sprintName(rr.Ns) + " " + sprintName(rr.Mbox) +
443		" " + strconv.FormatInt(int64(rr.Serial), 10) +
444		" " + strconv.FormatInt(int64(rr.Refresh), 10) +
445		" " + strconv.FormatInt(int64(rr.Retry), 10) +
446		" " + strconv.FormatInt(int64(rr.Expire), 10) +
447		" " + strconv.FormatInt(int64(rr.Minttl), 10)
448}
449
450// TXT RR. See RFC 1035.
451type TXT struct {
452	Hdr RR_Header
453	Txt []string `dns:"txt"`
454}
455
456func (rr *TXT) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) }
457
458func sprintName(s string) string {
459	var dst strings.Builder
460
461	for i := 0; i < len(s); {
462		if s[i] == '.' {
463			if dst.Len() != 0 {
464				dst.WriteByte('.')
465			}
466			i++
467			continue
468		}
469
470		b, n := nextByte(s, i)
471		if n == 0 {
472			// Drop "dangling" incomplete escapes.
473			if dst.Len() == 0 {
474				return s[:i]
475			}
476			break
477		}
478		if isDomainNameLabelSpecial(b) {
479			if dst.Len() == 0 {
480				dst.Grow(len(s) * 2)
481				dst.WriteString(s[:i])
482			}
483			dst.WriteByte('\\')
484			dst.WriteByte(b)
485		} else if b < ' ' || b > '~' { // unprintable, use \DDD
486			if dst.Len() == 0 {
487				dst.Grow(len(s) * 2)
488				dst.WriteString(s[:i])
489			}
490			dst.WriteString(escapeByte(b))
491		} else {
492			if dst.Len() != 0 {
493				dst.WriteByte(b)
494			}
495		}
496		i += n
497	}
498	if dst.Len() == 0 {
499		return s
500	}
501	return dst.String()
502}
503
504func sprintTxtOctet(s string) string {
505	var dst strings.Builder
506	dst.Grow(2 + len(s))
507	dst.WriteByte('"')
508	for i := 0; i < len(s); {
509		if i+1 < len(s) && s[i] == '\\' && s[i+1] == '.' {
510			dst.WriteString(s[i : i+2])
511			i += 2
512			continue
513		}
514
515		b, n := nextByte(s, i)
516		if n == 0 {
517			i++ // dangling back slash
518		} else {
519			writeTXTStringByte(&dst, b)
520		}
521		i += n
522	}
523	dst.WriteByte('"')
524	return dst.String()
525}
526
527func sprintTxt(txt []string) string {
528	var out strings.Builder
529	for i, s := range txt {
530		out.Grow(3 + len(s))
531		if i > 0 {
532			out.WriteString(` "`)
533		} else {
534			out.WriteByte('"')
535		}
536		for j := 0; j < len(s); {
537			b, n := nextByte(s, j)
538			if n == 0 {
539				break
540			}
541			writeTXTStringByte(&out, b)
542			j += n
543		}
544		out.WriteByte('"')
545	}
546	return out.String()
547}
548
549func writeTXTStringByte(s *strings.Builder, b byte) {
550	switch {
551	case b == '"' || b == '\\':
552		s.WriteByte('\\')
553		s.WriteByte(b)
554	case b < ' ' || b > '~':
555		s.WriteString(escapeByte(b))
556	default:
557		s.WriteByte(b)
558	}
559}
560
561const (
562	escapedByteSmall = "" +
563		`\000\001\002\003\004\005\006\007\008\009` +
564		`\010\011\012\013\014\015\016\017\018\019` +
565		`\020\021\022\023\024\025\026\027\028\029` +
566		`\030\031`
567	escapedByteLarge = `\127\128\129` +
568		`\130\131\132\133\134\135\136\137\138\139` +
569		`\140\141\142\143\144\145\146\147\148\149` +
570		`\150\151\152\153\154\155\156\157\158\159` +
571		`\160\161\162\163\164\165\166\167\168\169` +
572		`\170\171\172\173\174\175\176\177\178\179` +
573		`\180\181\182\183\184\185\186\187\188\189` +
574		`\190\191\192\193\194\195\196\197\198\199` +
575		`\200\201\202\203\204\205\206\207\208\209` +
576		`\210\211\212\213\214\215\216\217\218\219` +
577		`\220\221\222\223\224\225\226\227\228\229` +
578		`\230\231\232\233\234\235\236\237\238\239` +
579		`\240\241\242\243\244\245\246\247\248\249` +
580		`\250\251\252\253\254\255`
581)
582
583// escapeByte returns the \DDD escaping of b which must
584// satisfy b < ' ' || b > '~'.
585func escapeByte(b byte) string {
586	if b < ' ' {
587		return escapedByteSmall[b*4 : b*4+4]
588	}
589
590	b -= '~' + 1
591	// The cast here is needed as b*4 may overflow byte.
592	return escapedByteLarge[int(b)*4 : int(b)*4+4]
593}
594
595// isDomainNameLabelSpecial returns true if
596// a domain name label byte should be prefixed
597// with an escaping backslash.
598func isDomainNameLabelSpecial(b byte) bool {
599	switch b {
600	case '.', ' ', '\'', '@', ';', '(', ')', '"', '\\':
601		return true
602	}
603	return false
604}
605
606func nextByte(s string, offset int) (byte, int) {
607	if offset >= len(s) {
608		return 0, 0
609	}
610	if s[offset] != '\\' {
611		// not an escape sequence
612		return s[offset], 1
613	}
614	switch len(s) - offset {
615	case 1: // dangling escape
616		return 0, 0
617	case 2, 3: // too short to be \ddd
618	default: // maybe \ddd
619		if isDigit(s[offset+1]) && isDigit(s[offset+2]) && isDigit(s[offset+3]) {
620			return dddStringToByte(s[offset+1:]), 4
621		}
622	}
623	// not \ddd, just an RFC 1035 "quoted" character
624	return s[offset+1], 2
625}
626
627// SPF RR. See RFC 4408, Section 3.1.1.
628type SPF struct {
629	Hdr RR_Header
630	Txt []string `dns:"txt"`
631}
632
633func (rr *SPF) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) }
634
635// AVC RR. See https://www.iana.org/assignments/dns-parameters/AVC/avc-completed-template.
636type AVC struct {
637	Hdr RR_Header
638	Txt []string `dns:"txt"`
639}
640
641func (rr *AVC) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) }
642
643// SRV RR. See RFC 2782.
644type SRV struct {
645	Hdr      RR_Header
646	Priority uint16
647	Weight   uint16
648	Port     uint16
649	Target   string `dns:"domain-name"`
650}
651
652func (rr *SRV) String() string {
653	return rr.Hdr.String() +
654		strconv.Itoa(int(rr.Priority)) + " " +
655		strconv.Itoa(int(rr.Weight)) + " " +
656		strconv.Itoa(int(rr.Port)) + " " + sprintName(rr.Target)
657}
658
659// NAPTR RR. See RFC 2915.
660type NAPTR struct {
661	Hdr         RR_Header
662	Order       uint16
663	Preference  uint16
664	Flags       string
665	Service     string
666	Regexp      string
667	Replacement string `dns:"domain-name"`
668}
669
670func (rr *NAPTR) String() string {
671	return rr.Hdr.String() +
672		strconv.Itoa(int(rr.Order)) + " " +
673		strconv.Itoa(int(rr.Preference)) + " " +
674		"\"" + rr.Flags + "\" " +
675		"\"" + rr.Service + "\" " +
676		"\"" + rr.Regexp + "\" " +
677		rr.Replacement
678}
679
680// CERT RR. See RFC 4398.
681type CERT struct {
682	Hdr         RR_Header
683	Type        uint16
684	KeyTag      uint16
685	Algorithm   uint8
686	Certificate string `dns:"base64"`
687}
688
689func (rr *CERT) String() string {
690	var (
691		ok                  bool
692		certtype, algorithm string
693	)
694	if certtype, ok = CertTypeToString[rr.Type]; !ok {
695		certtype = strconv.Itoa(int(rr.Type))
696	}
697	if algorithm, ok = AlgorithmToString[rr.Algorithm]; !ok {
698		algorithm = strconv.Itoa(int(rr.Algorithm))
699	}
700	return rr.Hdr.String() + certtype +
701		" " + strconv.Itoa(int(rr.KeyTag)) +
702		" " + algorithm +
703		" " + rr.Certificate
704}
705
706// DNAME RR. See RFC 2672.
707type DNAME struct {
708	Hdr    RR_Header
709	Target string `dns:"domain-name"`
710}
711
712func (rr *DNAME) String() string {
713	return rr.Hdr.String() + sprintName(rr.Target)
714}
715
716// A RR. See RFC 1035.
717type A struct {
718	Hdr RR_Header
719	A   net.IP `dns:"a"`
720}
721
722func (rr *A) String() string {
723	if rr.A == nil {
724		return rr.Hdr.String()
725	}
726	return rr.Hdr.String() + rr.A.String()
727}
728
729// AAAA RR. See RFC 3596.
730type AAAA struct {
731	Hdr  RR_Header
732	AAAA net.IP `dns:"aaaa"`
733}
734
735func (rr *AAAA) String() string {
736	if rr.AAAA == nil {
737		return rr.Hdr.String()
738	}
739	return rr.Hdr.String() + rr.AAAA.String()
740}
741
742// PX RR. See RFC 2163.
743type PX struct {
744	Hdr        RR_Header
745	Preference uint16
746	Map822     string `dns:"domain-name"`
747	Mapx400    string `dns:"domain-name"`
748}
749
750func (rr *PX) String() string {
751	return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Map822) + " " + sprintName(rr.Mapx400)
752}
753
754// GPOS RR. See RFC 1712.
755type GPOS struct {
756	Hdr       RR_Header
757	Longitude string
758	Latitude  string
759	Altitude  string
760}
761
762func (rr *GPOS) String() string {
763	return rr.Hdr.String() + rr.Longitude + " " + rr.Latitude + " " + rr.Altitude
764}
765
766// LOC RR. See RFC RFC 1876.
767type LOC struct {
768	Hdr       RR_Header
769	Version   uint8
770	Size      uint8
771	HorizPre  uint8
772	VertPre   uint8
773	Latitude  uint32
774	Longitude uint32
775	Altitude  uint32
776}
777
778// cmToM takes a cm value expressed in RFC 1876 SIZE mantissa/exponent
779// format and returns a string in m (two decimals for the cm).
780func cmToM(m, e uint8) string {
781	if e < 2 {
782		if e == 1 {
783			m *= 10
784		}
785
786		return fmt.Sprintf("0.%02d", m)
787	}
788
789	s := fmt.Sprintf("%d", m)
790	for e > 2 {
791		s += "0"
792		e--
793	}
794	return s
795}
796
797func (rr *LOC) String() string {
798	s := rr.Hdr.String()
799
800	lat := rr.Latitude
801	ns := "N"
802	if lat > LOC_EQUATOR {
803		lat = lat - LOC_EQUATOR
804	} else {
805		ns = "S"
806		lat = LOC_EQUATOR - lat
807	}
808	h := lat / LOC_DEGREES
809	lat = lat % LOC_DEGREES
810	m := lat / LOC_HOURS
811	lat = lat % LOC_HOURS
812	s += fmt.Sprintf("%02d %02d %0.3f %s ", h, m, float64(lat)/1000, ns)
813
814	lon := rr.Longitude
815	ew := "E"
816	if lon > LOC_PRIMEMERIDIAN {
817		lon = lon - LOC_PRIMEMERIDIAN
818	} else {
819		ew = "W"
820		lon = LOC_PRIMEMERIDIAN - lon
821	}
822	h = lon / LOC_DEGREES
823	lon = lon % LOC_DEGREES
824	m = lon / LOC_HOURS
825	lon = lon % LOC_HOURS
826	s += fmt.Sprintf("%02d %02d %0.3f %s ", h, m, float64(lon)/1000, ew)
827
828	var alt = float64(rr.Altitude) / 100
829	alt -= LOC_ALTITUDEBASE
830	if rr.Altitude%100 != 0 {
831		s += fmt.Sprintf("%.2fm ", alt)
832	} else {
833		s += fmt.Sprintf("%.0fm ", alt)
834	}
835
836	s += cmToM(rr.Size&0xf0>>4, rr.Size&0x0f) + "m "
837	s += cmToM(rr.HorizPre&0xf0>>4, rr.HorizPre&0x0f) + "m "
838	s += cmToM(rr.VertPre&0xf0>>4, rr.VertPre&0x0f) + "m"
839
840	return s
841}
842
843// SIG RR. See RFC 2535. The SIG RR is identical to RRSIG and nowadays only used for SIG(0), See RFC 2931.
844type SIG struct {
845	RRSIG
846}
847
848// RRSIG RR. See RFC 4034 and RFC 3755.
849type RRSIG struct {
850	Hdr         RR_Header
851	TypeCovered uint16
852	Algorithm   uint8
853	Labels      uint8
854	OrigTtl     uint32
855	Expiration  uint32
856	Inception   uint32
857	KeyTag      uint16
858	SignerName  string `dns:"domain-name"`
859	Signature   string `dns:"base64"`
860}
861
862func (rr *RRSIG) String() string {
863	s := rr.Hdr.String()
864	s += Type(rr.TypeCovered).String()
865	s += " " + strconv.Itoa(int(rr.Algorithm)) +
866		" " + strconv.Itoa(int(rr.Labels)) +
867		" " + strconv.FormatInt(int64(rr.OrigTtl), 10) +
868		" " + TimeToString(rr.Expiration) +
869		" " + TimeToString(rr.Inception) +
870		" " + strconv.Itoa(int(rr.KeyTag)) +
871		" " + sprintName(rr.SignerName) +
872		" " + rr.Signature
873	return s
874}
875
876// NSEC RR. See RFC 4034 and RFC 3755.
877type NSEC struct {
878	Hdr        RR_Header
879	NextDomain string   `dns:"domain-name"`
880	TypeBitMap []uint16 `dns:"nsec"`
881}
882
883func (rr *NSEC) String() string {
884	s := rr.Hdr.String() + sprintName(rr.NextDomain)
885	for _, t := range rr.TypeBitMap {
886		s += " " + Type(t).String()
887	}
888	return s
889}
890
891func (rr *NSEC) len(off int, compression map[string]struct{}) int {
892	l := rr.Hdr.len(off, compression)
893	l += domainNameLen(rr.NextDomain, off+l, compression, false)
894	l += typeBitMapLen(rr.TypeBitMap)
895	return l
896}
897
898// DLV RR. See RFC 4431.
899type DLV struct{ DS }
900
901// CDS RR. See RFC 7344.
902type CDS struct{ DS }
903
904// DS RR. See RFC 4034 and RFC 3658.
905type DS struct {
906	Hdr        RR_Header
907	KeyTag     uint16
908	Algorithm  uint8
909	DigestType uint8
910	Digest     string `dns:"hex"`
911}
912
913func (rr *DS) String() string {
914	return rr.Hdr.String() + strconv.Itoa(int(rr.KeyTag)) +
915		" " + strconv.Itoa(int(rr.Algorithm)) +
916		" " + strconv.Itoa(int(rr.DigestType)) +
917		" " + strings.ToUpper(rr.Digest)
918}
919
920// KX RR. See RFC 2230.
921type KX struct {
922	Hdr        RR_Header
923	Preference uint16
924	Exchanger  string `dns:"domain-name"`
925}
926
927func (rr *KX) String() string {
928	return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) +
929		" " + sprintName(rr.Exchanger)
930}
931
932// TA RR. See http://www.watson.org/~weiler/INI1999-19.pdf.
933type TA struct {
934	Hdr        RR_Header
935	KeyTag     uint16
936	Algorithm  uint8
937	DigestType uint8
938	Digest     string `dns:"hex"`
939}
940
941func (rr *TA) String() string {
942	return rr.Hdr.String() + strconv.Itoa(int(rr.KeyTag)) +
943		" " + strconv.Itoa(int(rr.Algorithm)) +
944		" " + strconv.Itoa(int(rr.DigestType)) +
945		" " + strings.ToUpper(rr.Digest)
946}
947
948// TALINK RR. See https://www.iana.org/assignments/dns-parameters/TALINK/talink-completed-template.
949type TALINK struct {
950	Hdr          RR_Header
951	PreviousName string `dns:"domain-name"`
952	NextName     string `dns:"domain-name"`
953}
954
955func (rr *TALINK) String() string {
956	return rr.Hdr.String() +
957		sprintName(rr.PreviousName) + " " + sprintName(rr.NextName)
958}
959
960// SSHFP RR. See RFC RFC 4255.
961type SSHFP struct {
962	Hdr         RR_Header
963	Algorithm   uint8
964	Type        uint8
965	FingerPrint string `dns:"hex"`
966}
967
968func (rr *SSHFP) String() string {
969	return rr.Hdr.String() + strconv.Itoa(int(rr.Algorithm)) +
970		" " + strconv.Itoa(int(rr.Type)) +
971		" " + strings.ToUpper(rr.FingerPrint)
972}
973
974// KEY RR. See RFC RFC 2535.
975type KEY struct {
976	DNSKEY
977}
978
979// CDNSKEY RR. See RFC 7344.
980type CDNSKEY struct {
981	DNSKEY
982}
983
984// DNSKEY RR. See RFC 4034 and RFC 3755.
985type DNSKEY struct {
986	Hdr       RR_Header
987	Flags     uint16
988	Protocol  uint8
989	Algorithm uint8
990	PublicKey string `dns:"base64"`
991}
992
993func (rr *DNSKEY) String() string {
994	return rr.Hdr.String() + strconv.Itoa(int(rr.Flags)) +
995		" " + strconv.Itoa(int(rr.Protocol)) +
996		" " + strconv.Itoa(int(rr.Algorithm)) +
997		" " + rr.PublicKey
998}
999
1000// RKEY RR. See https://www.iana.org/assignments/dns-parameters/RKEY/rkey-completed-template.
1001type RKEY struct {
1002	Hdr       RR_Header
1003	Flags     uint16
1004	Protocol  uint8
1005	Algorithm uint8
1006	PublicKey string `dns:"base64"`
1007}
1008
1009func (rr *RKEY) String() string {
1010	return rr.Hdr.String() + strconv.Itoa(int(rr.Flags)) +
1011		" " + strconv.Itoa(int(rr.Protocol)) +
1012		" " + strconv.Itoa(int(rr.Algorithm)) +
1013		" " + rr.PublicKey
1014}
1015
1016// NSAPPTR RR. See RFC 1348.
1017type NSAPPTR struct {
1018	Hdr RR_Header
1019	Ptr string `dns:"domain-name"`
1020}
1021
1022func (rr *NSAPPTR) String() string { return rr.Hdr.String() + sprintName(rr.Ptr) }
1023
1024// NSEC3 RR. See RFC 5155.
1025type NSEC3 struct {
1026	Hdr        RR_Header
1027	Hash       uint8
1028	Flags      uint8
1029	Iterations uint16
1030	SaltLength uint8
1031	Salt       string `dns:"size-hex:SaltLength"`
1032	HashLength uint8
1033	NextDomain string   `dns:"size-base32:HashLength"`
1034	TypeBitMap []uint16 `dns:"nsec"`
1035}
1036
1037func (rr *NSEC3) String() string {
1038	s := rr.Hdr.String()
1039	s += strconv.Itoa(int(rr.Hash)) +
1040		" " + strconv.Itoa(int(rr.Flags)) +
1041		" " + strconv.Itoa(int(rr.Iterations)) +
1042		" " + saltToString(rr.Salt) +
1043		" " + rr.NextDomain
1044	for _, t := range rr.TypeBitMap {
1045		s += " " + Type(t).String()
1046	}
1047	return s
1048}
1049
1050func (rr *NSEC3) len(off int, compression map[string]struct{}) int {
1051	l := rr.Hdr.len(off, compression)
1052	l += 6 + len(rr.Salt)/2 + 1 + len(rr.NextDomain) + 1
1053	l += typeBitMapLen(rr.TypeBitMap)
1054	return l
1055}
1056
1057// NSEC3PARAM RR. See RFC 5155.
1058type NSEC3PARAM struct {
1059	Hdr        RR_Header
1060	Hash       uint8
1061	Flags      uint8
1062	Iterations uint16
1063	SaltLength uint8
1064	Salt       string `dns:"size-hex:SaltLength"`
1065}
1066
1067func (rr *NSEC3PARAM) String() string {
1068	s := rr.Hdr.String()
1069	s += strconv.Itoa(int(rr.Hash)) +
1070		" " + strconv.Itoa(int(rr.Flags)) +
1071		" " + strconv.Itoa(int(rr.Iterations)) +
1072		" " + saltToString(rr.Salt)
1073	return s
1074}
1075
1076// TKEY RR. See RFC 2930.
1077type TKEY struct {
1078	Hdr        RR_Header
1079	Algorithm  string `dns:"domain-name"`
1080	Inception  uint32
1081	Expiration uint32
1082	Mode       uint16
1083	Error      uint16
1084	KeySize    uint16
1085	Key        string `dns:"size-hex:KeySize"`
1086	OtherLen   uint16
1087	OtherData  string `dns:"size-hex:OtherLen"`
1088}
1089
1090// TKEY has no official presentation format, but this will suffice.
1091func (rr *TKEY) String() string {
1092	s := ";" + rr.Hdr.String() +
1093		" " + rr.Algorithm +
1094		" " + TimeToString(rr.Inception) +
1095		" " + TimeToString(rr.Expiration) +
1096		" " + strconv.Itoa(int(rr.Mode)) +
1097		" " + strconv.Itoa(int(rr.Error)) +
1098		" " + strconv.Itoa(int(rr.KeySize)) +
1099		" " + rr.Key +
1100		" " + strconv.Itoa(int(rr.OtherLen)) +
1101		" " + rr.OtherData
1102	return s
1103}
1104
1105// RFC3597 represents an unknown/generic RR. See RFC 3597.
1106type RFC3597 struct {
1107	Hdr   RR_Header
1108	Rdata string `dns:"hex"`
1109}
1110
1111func (rr *RFC3597) String() string {
1112	// Let's call it a hack
1113	s := rfc3597Header(rr.Hdr)
1114
1115	s += "\\# " + strconv.Itoa(len(rr.Rdata)/2) + " " + rr.Rdata
1116	return s
1117}
1118
1119func rfc3597Header(h RR_Header) string {
1120	var s string
1121
1122	s += sprintName(h.Name) + "\t"
1123	s += strconv.FormatInt(int64(h.Ttl), 10) + "\t"
1124	s += "CLASS" + strconv.Itoa(int(h.Class)) + "\t"
1125	s += "TYPE" + strconv.Itoa(int(h.Rrtype)) + "\t"
1126	return s
1127}
1128
1129// URI RR. See RFC 7553.
1130type URI struct {
1131	Hdr      RR_Header
1132	Priority uint16
1133	Weight   uint16
1134	Target   string `dns:"octet"`
1135}
1136
1137// rr.Target to be parsed as a sequence of character encoded octets according to RFC 3986
1138func (rr *URI) String() string {
1139	return rr.Hdr.String() + strconv.Itoa(int(rr.Priority)) +
1140		" " + strconv.Itoa(int(rr.Weight)) + " " + sprintTxtOctet(rr.Target)
1141}
1142
1143// DHCID RR. See RFC 4701.
1144type DHCID struct {
1145	Hdr    RR_Header
1146	Digest string `dns:"base64"`
1147}
1148
1149func (rr *DHCID) String() string { return rr.Hdr.String() + rr.Digest }
1150
1151// TLSA RR. See RFC 6698.
1152type TLSA struct {
1153	Hdr          RR_Header
1154	Usage        uint8
1155	Selector     uint8
1156	MatchingType uint8
1157	Certificate  string `dns:"hex"`
1158}
1159
1160func (rr *TLSA) String() string {
1161	return rr.Hdr.String() +
1162		strconv.Itoa(int(rr.Usage)) +
1163		" " + strconv.Itoa(int(rr.Selector)) +
1164		" " + strconv.Itoa(int(rr.MatchingType)) +
1165		" " + rr.Certificate
1166}
1167
1168// SMIMEA RR. See RFC 8162.
1169type SMIMEA struct {
1170	Hdr          RR_Header
1171	Usage        uint8
1172	Selector     uint8
1173	MatchingType uint8
1174	Certificate  string `dns:"hex"`
1175}
1176
1177func (rr *SMIMEA) String() string {
1178	s := rr.Hdr.String() +
1179		strconv.Itoa(int(rr.Usage)) +
1180		" " + strconv.Itoa(int(rr.Selector)) +
1181		" " + strconv.Itoa(int(rr.MatchingType))
1182
1183	// Every Nth char needs a space on this output. If we output
1184	// this as one giant line, we can't read it can in because in some cases
1185	// the cert length overflows scan.maxTok (2048).
1186	sx := splitN(rr.Certificate, 1024) // conservative value here
1187	s += " " + strings.Join(sx, " ")
1188	return s
1189}
1190
1191// HIP RR. See RFC 8005.
1192type HIP struct {
1193	Hdr                RR_Header
1194	HitLength          uint8
1195	PublicKeyAlgorithm uint8
1196	PublicKeyLength    uint16
1197	Hit                string   `dns:"size-hex:HitLength"`
1198	PublicKey          string   `dns:"size-base64:PublicKeyLength"`
1199	RendezvousServers  []string `dns:"domain-name"`
1200}
1201
1202func (rr *HIP) String() string {
1203	s := rr.Hdr.String() +
1204		strconv.Itoa(int(rr.PublicKeyAlgorithm)) +
1205		" " + rr.Hit +
1206		" " + rr.PublicKey
1207	for _, d := range rr.RendezvousServers {
1208		s += " " + sprintName(d)
1209	}
1210	return s
1211}
1212
1213// NINFO RR. See https://www.iana.org/assignments/dns-parameters/NINFO/ninfo-completed-template.
1214type NINFO struct {
1215	Hdr    RR_Header
1216	ZSData []string `dns:"txt"`
1217}
1218
1219func (rr *NINFO) String() string { return rr.Hdr.String() + sprintTxt(rr.ZSData) }
1220
1221// NID RR. See RFC RFC 6742.
1222type NID struct {
1223	Hdr        RR_Header
1224	Preference uint16
1225	NodeID     uint64
1226}
1227
1228func (rr *NID) String() string {
1229	s := rr.Hdr.String() + strconv.Itoa(int(rr.Preference))
1230	node := fmt.Sprintf("%0.16x", rr.NodeID)
1231	s += " " + node[0:4] + ":" + node[4:8] + ":" + node[8:12] + ":" + node[12:16]
1232	return s
1233}
1234
1235// L32 RR, See RFC 6742.
1236type L32 struct {
1237	Hdr        RR_Header
1238	Preference uint16
1239	Locator32  net.IP `dns:"a"`
1240}
1241
1242func (rr *L32) String() string {
1243	if rr.Locator32 == nil {
1244		return rr.Hdr.String() + strconv.Itoa(int(rr.Preference))
1245	}
1246	return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) +
1247		" " + rr.Locator32.String()
1248}
1249
1250// L64 RR, See RFC 6742.
1251type L64 struct {
1252	Hdr        RR_Header
1253	Preference uint16
1254	Locator64  uint64
1255}
1256
1257func (rr *L64) String() string {
1258	s := rr.Hdr.String() + strconv.Itoa(int(rr.Preference))
1259	node := fmt.Sprintf("%0.16X", rr.Locator64)
1260	s += " " + node[0:4] + ":" + node[4:8] + ":" + node[8:12] + ":" + node[12:16]
1261	return s
1262}
1263
1264// LP RR. See RFC 6742.
1265type LP struct {
1266	Hdr        RR_Header
1267	Preference uint16
1268	Fqdn       string `dns:"domain-name"`
1269}
1270
1271func (rr *LP) String() string {
1272	return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Fqdn)
1273}
1274
1275// EUI48 RR. See RFC 7043.
1276type EUI48 struct {
1277	Hdr     RR_Header
1278	Address uint64 `dns:"uint48"`
1279}
1280
1281func (rr *EUI48) String() string { return rr.Hdr.String() + euiToString(rr.Address, 48) }
1282
1283// EUI64 RR. See RFC 7043.
1284type EUI64 struct {
1285	Hdr     RR_Header
1286	Address uint64
1287}
1288
1289func (rr *EUI64) String() string { return rr.Hdr.String() + euiToString(rr.Address, 64) }
1290
1291// CAA RR. See RFC 6844.
1292type CAA struct {
1293	Hdr   RR_Header
1294	Flag  uint8
1295	Tag   string
1296	Value string `dns:"octet"`
1297}
1298
1299// rr.Value Is the character-string encoding of the value field as specified in RFC 1035, Section 5.1.
1300func (rr *CAA) String() string {
1301	return rr.Hdr.String() + strconv.Itoa(int(rr.Flag)) + " " + rr.Tag + " " + sprintTxtOctet(rr.Value)
1302}
1303
1304// UID RR. Deprecated, IANA-Reserved.
1305type UID struct {
1306	Hdr RR_Header
1307	Uid uint32
1308}
1309
1310func (rr *UID) String() string { return rr.Hdr.String() + strconv.FormatInt(int64(rr.Uid), 10) }
1311
1312// GID RR. Deprecated, IANA-Reserved.
1313type GID struct {
1314	Hdr RR_Header
1315	Gid uint32
1316}
1317
1318func (rr *GID) String() string { return rr.Hdr.String() + strconv.FormatInt(int64(rr.Gid), 10) }
1319
1320// UINFO RR. Deprecated, IANA-Reserved.
1321type UINFO struct {
1322	Hdr   RR_Header
1323	Uinfo string
1324}
1325
1326func (rr *UINFO) String() string { return rr.Hdr.String() + sprintTxt([]string{rr.Uinfo}) }
1327
1328// EID RR. See http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt.
1329type EID struct {
1330	Hdr      RR_Header
1331	Endpoint string `dns:"hex"`
1332}
1333
1334func (rr *EID) String() string { return rr.Hdr.String() + strings.ToUpper(rr.Endpoint) }
1335
1336// NIMLOC RR. See http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt.
1337type NIMLOC struct {
1338	Hdr     RR_Header
1339	Locator string `dns:"hex"`
1340}
1341
1342func (rr *NIMLOC) String() string { return rr.Hdr.String() + strings.ToUpper(rr.Locator) }
1343
1344// OPENPGPKEY RR. See RFC 7929.
1345type OPENPGPKEY struct {
1346	Hdr       RR_Header
1347	PublicKey string `dns:"base64"`
1348}
1349
1350func (rr *OPENPGPKEY) String() string { return rr.Hdr.String() + rr.PublicKey }
1351
1352// CSYNC RR. See RFC 7477.
1353type CSYNC struct {
1354	Hdr        RR_Header
1355	Serial     uint32
1356	Flags      uint16
1357	TypeBitMap []uint16 `dns:"nsec"`
1358}
1359
1360func (rr *CSYNC) String() string {
1361	s := rr.Hdr.String() + strconv.FormatInt(int64(rr.Serial), 10) + " " + strconv.Itoa(int(rr.Flags))
1362
1363	for _, t := range rr.TypeBitMap {
1364		s += " " + Type(t).String()
1365	}
1366	return s
1367}
1368
1369func (rr *CSYNC) len(off int, compression map[string]struct{}) int {
1370	l := rr.Hdr.len(off, compression)
1371	l += 4 + 2
1372	l += typeBitMapLen(rr.TypeBitMap)
1373	return l
1374}
1375
1376// ZONEMD RR, from draft-ietf-dnsop-dns-zone-digest
1377type ZONEMD struct {
1378	Hdr    RR_Header
1379	Serial uint32
1380	Scheme uint8
1381	Hash   uint8
1382	Digest string `dns:"hex"`
1383}
1384
1385func (rr *ZONEMD) String() string {
1386	return rr.Hdr.String() +
1387		strconv.Itoa(int(rr.Serial)) +
1388		" " + strconv.Itoa(int(rr.Scheme)) +
1389		" " + strconv.Itoa(int(rr.Hash)) +
1390		" " + rr.Digest
1391}
1392
1393// APL RR. See RFC 3123.
1394type APL struct {
1395	Hdr      RR_Header
1396	Prefixes []APLPrefix `dns:"apl"`
1397}
1398
1399// APLPrefix is an address prefix hold by an APL record.
1400type APLPrefix struct {
1401	Negation bool
1402	Network  net.IPNet
1403}
1404
1405// String returns presentation form of the APL record.
1406func (rr *APL) String() string {
1407	var sb strings.Builder
1408	sb.WriteString(rr.Hdr.String())
1409	for i, p := range rr.Prefixes {
1410		if i > 0 {
1411			sb.WriteByte(' ')
1412		}
1413		sb.WriteString(p.str())
1414	}
1415	return sb.String()
1416}
1417
1418// str returns presentation form of the APL prefix.
1419func (p *APLPrefix) str() string {
1420	var sb strings.Builder
1421	if p.Negation {
1422		sb.WriteByte('!')
1423	}
1424
1425	switch len(p.Network.IP) {
1426	case net.IPv4len:
1427		sb.WriteByte('1')
1428	case net.IPv6len:
1429		sb.WriteByte('2')
1430	}
1431
1432	sb.WriteByte(':')
1433
1434	switch len(p.Network.IP) {
1435	case net.IPv4len:
1436		sb.WriteString(p.Network.IP.String())
1437	case net.IPv6len:
1438		// add prefix for IPv4-mapped IPv6
1439		if v4 := p.Network.IP.To4(); v4 != nil {
1440			sb.WriteString("::ffff:")
1441		}
1442		sb.WriteString(p.Network.IP.String())
1443	}
1444
1445	sb.WriteByte('/')
1446
1447	prefix, _ := p.Network.Mask.Size()
1448	sb.WriteString(strconv.Itoa(prefix))
1449
1450	return sb.String()
1451}
1452
1453// equals reports whether two APL prefixes are identical.
1454func (a *APLPrefix) equals(b *APLPrefix) bool {
1455	return a.Negation == b.Negation &&
1456		bytes.Equal(a.Network.IP, b.Network.IP) &&
1457		bytes.Equal(a.Network.Mask, b.Network.Mask)
1458}
1459
1460// copy returns a copy of the APL prefix.
1461func (p *APLPrefix) copy() APLPrefix {
1462	return APLPrefix{
1463		Negation: p.Negation,
1464		Network:  copyNet(p.Network),
1465	}
1466}
1467
1468// len returns size of the prefix in wire format.
1469func (p *APLPrefix) len() int {
1470	// 4-byte header and the network address prefix (see Section 4 of RFC 3123)
1471	prefix, _ := p.Network.Mask.Size()
1472	return 4 + (prefix+7)/8
1473}
1474
1475// TimeToString translates the RRSIG's incep. and expir. times to the
1476// string representation used when printing the record.
1477// It takes serial arithmetic (RFC 1982) into account.
1478func TimeToString(t uint32) string {
1479	mod := (int64(t)-time.Now().Unix())/year68 - 1
1480	if mod < 0 {
1481		mod = 0
1482	}
1483	ti := time.Unix(int64(t)-mod*year68, 0).UTC()
1484	return ti.Format("20060102150405")
1485}
1486
1487// StringToTime translates the RRSIG's incep. and expir. times from
1488// string values like "20110403154150" to an 32 bit integer.
1489// It takes serial arithmetic (RFC 1982) into account.
1490func StringToTime(s string) (uint32, error) {
1491	t, err := time.Parse("20060102150405", s)
1492	if err != nil {
1493		return 0, err
1494	}
1495	mod := t.Unix()/year68 - 1
1496	if mod < 0 {
1497		mod = 0
1498	}
1499	return uint32(t.Unix() - mod*year68), nil
1500}
1501
1502// saltToString converts a NSECX salt to uppercase and returns "-" when it is empty.
1503func saltToString(s string) string {
1504	if s == "" {
1505		return "-"
1506	}
1507	return strings.ToUpper(s)
1508}
1509
1510func euiToString(eui uint64, bits int) (hex string) {
1511	switch bits {
1512	case 64:
1513		hex = fmt.Sprintf("%16.16x", eui)
1514		hex = hex[0:2] + "-" + hex[2:4] + "-" + hex[4:6] + "-" + hex[6:8] +
1515			"-" + hex[8:10] + "-" + hex[10:12] + "-" + hex[12:14] + "-" + hex[14:16]
1516	case 48:
1517		hex = fmt.Sprintf("%12.12x", eui)
1518		hex = hex[0:2] + "-" + hex[2:4] + "-" + hex[4:6] + "-" + hex[6:8] +
1519			"-" + hex[8:10] + "-" + hex[10:12]
1520	}
1521	return
1522}
1523
1524// copyIP returns a copy of ip.
1525func copyIP(ip net.IP) net.IP {
1526	p := make(net.IP, len(ip))
1527	copy(p, ip)
1528	return p
1529}
1530
1531// copyNet returns a copy of a subnet.
1532func copyNet(n net.IPNet) net.IPNet {
1533	m := make(net.IPMask, len(n.Mask))
1534	copy(m, n.Mask)
1535
1536	return net.IPNet{
1537		IP:   copyIP(n.IP),
1538		Mask: m,
1539	}
1540}
1541
1542// SplitN splits a string into N sized string chunks.
1543// This might become an exported function once.
1544func splitN(s string, n int) []string {
1545	if len(s) < n {
1546		return []string{s}
1547	}
1548	sx := []string{}
1549	p, i := 0, n
1550	for {
1551		if i <= len(s) {
1552			sx = append(sx, s[p:i])
1553		} else {
1554			sx = append(sx, s[p:])
1555			break
1556
1557		}
1558		p, i = p+n, i+n
1559	}
1560
1561	return sx
1562}
1563