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