1package dns
2
3import (
4	"fmt"
5	"net"
6	"strconv"
7	"strings"
8	"time"
9)
10
11type (
12	// Type is a DNS type.
13	Type uint16
14	// Class is a DNS class.
15	Class uint16
16	// Name is a DNS domain name.
17	Name string
18)
19
20// Packet formats
21
22// Wire constants and supported types.
23const (
24	// valid RR_Header.Rrtype and Question.qtype
25
26	TypeNone       uint16 = 0
27	TypeA          uint16 = 1
28	TypeNS         uint16 = 2
29	TypeMD         uint16 = 3
30	TypeMF         uint16 = 4
31	TypeCNAME      uint16 = 5
32	TypeSOA        uint16 = 6
33	TypeMB         uint16 = 7
34	TypeMG         uint16 = 8
35	TypeMR         uint16 = 9
36	TypeNULL       uint16 = 10
37	TypePTR        uint16 = 12
38	TypeHINFO      uint16 = 13
39	TypeMINFO      uint16 = 14
40	TypeMX         uint16 = 15
41	TypeTXT        uint16 = 16
42	TypeRP         uint16 = 17
43	TypeAFSDB      uint16 = 18
44	TypeX25        uint16 = 19
45	TypeISDN       uint16 = 20
46	TypeRT         uint16 = 21
47	TypeNSAPPTR    uint16 = 23
48	TypeSIG        uint16 = 24
49	TypeKEY        uint16 = 25
50	TypePX         uint16 = 26
51	TypeGPOS       uint16 = 27
52	TypeAAAA       uint16 = 28
53	TypeLOC        uint16 = 29
54	TypeNXT        uint16 = 30
55	TypeEID        uint16 = 31
56	TypeNIMLOC     uint16 = 32
57	TypeSRV        uint16 = 33
58	TypeATMA       uint16 = 34
59	TypeNAPTR      uint16 = 35
60	TypeKX         uint16 = 36
61	TypeCERT       uint16 = 37
62	TypeDNAME      uint16 = 39
63	TypeOPT        uint16 = 41 // EDNS
64	TypeDS         uint16 = 43
65	TypeSSHFP      uint16 = 44
66	TypeRRSIG      uint16 = 46
67	TypeNSEC       uint16 = 47
68	TypeDNSKEY     uint16 = 48
69	TypeDHCID      uint16 = 49
70	TypeNSEC3      uint16 = 50
71	TypeNSEC3PARAM uint16 = 51
72	TypeTLSA       uint16 = 52
73	TypeSMIMEA     uint16 = 53
74	TypeHIP        uint16 = 55
75	TypeNINFO      uint16 = 56
76	TypeRKEY       uint16 = 57
77	TypeTALINK     uint16 = 58
78	TypeCDS        uint16 = 59
79	TypeCDNSKEY    uint16 = 60
80	TypeOPENPGPKEY uint16 = 61
81	TypeSPF        uint16 = 99
82	TypeUINFO      uint16 = 100
83	TypeUID        uint16 = 101
84	TypeGID        uint16 = 102
85	TypeUNSPEC     uint16 = 103
86	TypeNID        uint16 = 104
87	TypeL32        uint16 = 105
88	TypeL64        uint16 = 106
89	TypeLP         uint16 = 107
90	TypeEUI48      uint16 = 108
91	TypeEUI64      uint16 = 109
92	TypeURI        uint16 = 256
93	TypeCAA        uint16 = 257
94
95	TypeTKEY uint16 = 249
96	TypeTSIG uint16 = 250
97
98	// valid Question.Qtype only
99	TypeIXFR  uint16 = 251
100	TypeAXFR  uint16 = 252
101	TypeMAILB uint16 = 253
102	TypeMAILA uint16 = 254
103	TypeANY   uint16 = 255
104
105	TypeTA       uint16 = 32768
106	TypeDLV      uint16 = 32769
107	TypeReserved uint16 = 65535
108
109	// valid Question.Qclass
110	ClassINET   = 1
111	ClassCSNET  = 2
112	ClassCHAOS  = 3
113	ClassHESIOD = 4
114	ClassNONE   = 254
115	ClassANY    = 255
116
117	// Message Response Codes.
118	RcodeSuccess        = 0
119	RcodeFormatError    = 1
120	RcodeServerFailure  = 2
121	RcodeNameError      = 3
122	RcodeNotImplemented = 4
123	RcodeRefused        = 5
124	RcodeYXDomain       = 6
125	RcodeYXRrset        = 7
126	RcodeNXRrset        = 8
127	RcodeNotAuth        = 9
128	RcodeNotZone        = 10
129	RcodeBadSig         = 16 // TSIG
130	RcodeBadVers        = 16 // EDNS0
131	RcodeBadKey         = 17
132	RcodeBadTime        = 18
133	RcodeBadMode        = 19 // TKEY
134	RcodeBadName        = 20
135	RcodeBadAlg         = 21
136	RcodeBadTrunc       = 22 // TSIG
137	RcodeBadCookie      = 23 // DNS Cookies
138
139	// Message Opcodes. There is no 3.
140	OpcodeQuery  = 0
141	OpcodeIQuery = 1
142	OpcodeStatus = 2
143	OpcodeNotify = 4
144	OpcodeUpdate = 5
145)
146
147// Headers is the wire format for the DNS packet header.
148type Header struct {
149	Id                                 uint16
150	Bits                               uint16
151	Qdcount, Ancount, Nscount, Arcount uint16
152}
153
154const (
155	headerSize = 12
156
157	// Header.Bits
158	_QR = 1 << 15 // query/response (response=1)
159	_AA = 1 << 10 // authoritative
160	_TC = 1 << 9  // truncated
161	_RD = 1 << 8  // recursion desired
162	_RA = 1 << 7  // recursion available
163	_Z  = 1 << 6  // Z
164	_AD = 1 << 5  // authticated data
165	_CD = 1 << 4  // checking disabled
166
167	LOC_EQUATOR       = 1 << 31 // RFC 1876, Section 2.
168	LOC_PRIMEMERIDIAN = 1 << 31 // RFC 1876, Section 2.
169
170	LOC_HOURS   = 60 * 1000
171	LOC_DEGREES = 60 * LOC_HOURS
172
173	LOC_ALTITUDEBASE = 100000
174)
175
176// Different Certificate Types, see RFC 4398, Section 2.1
177const (
178	CertPKIX = 1 + iota
179	CertSPKI
180	CertPGP
181	CertIPIX
182	CertISPKI
183	CertIPGP
184	CertACPKIX
185	CertIACPKIX
186	CertURI = 253
187	CertOID = 254
188)
189
190// CertTypeToString converts the Cert Type to its string representation.
191// See RFC 4398 and RFC 6944.
192var CertTypeToString = map[uint16]string{
193	CertPKIX:    "PKIX",
194	CertSPKI:    "SPKI",
195	CertPGP:     "PGP",
196	CertIPIX:    "IPIX",
197	CertISPKI:   "ISPKI",
198	CertIPGP:    "IPGP",
199	CertACPKIX:  "ACPKIX",
200	CertIACPKIX: "IACPKIX",
201	CertURI:     "URI",
202	CertOID:     "OID",
203}
204
205// StringToCertType is the reverseof CertTypeToString.
206var StringToCertType = reverseInt16(CertTypeToString)
207
208//go:generate go run types_generate.go
209
210// Question holds a DNS question. There can be multiple questions in the
211// question section of a message. Usually there is just one.
212type Question struct {
213	Name   string `dns:"cdomain-name"` // "cdomain-name" specifies encoding (and may be compressed)
214	Qtype  uint16
215	Qclass uint16
216}
217
218func (q *Question) len() int {
219	return len(q.Name) + 1 + 2 + 2
220}
221
222func (q *Question) String() (s string) {
223	// prefix with ; (as in dig)
224	s = ";" + sprintName(q.Name) + "\t"
225	s += Class(q.Qclass).String() + "\t"
226	s += " " + Type(q.Qtype).String()
227	return s
228}
229
230// ANY is a wildcard record. See RFC 1035, Section 3.2.3. ANY
231// is named "*" there.
232type ANY struct {
233	Hdr RR_Header
234	// Does not have any rdata
235}
236
237func (rr *ANY) String() string { return rr.Hdr.String() }
238
239type CNAME struct {
240	Hdr    RR_Header
241	Target string `dns:"cdomain-name"`
242}
243
244func (rr *CNAME) String() string { return rr.Hdr.String() + sprintName(rr.Target) }
245
246type HINFO struct {
247	Hdr RR_Header
248	Cpu string
249	Os  string
250}
251
252func (rr *HINFO) String() string {
253	return rr.Hdr.String() + sprintTxt([]string{rr.Cpu, rr.Os})
254}
255
256type MB struct {
257	Hdr RR_Header
258	Mb  string `dns:"cdomain-name"`
259}
260
261func (rr *MB) String() string { return rr.Hdr.String() + sprintName(rr.Mb) }
262
263type MG struct {
264	Hdr RR_Header
265	Mg  string `dns:"cdomain-name"`
266}
267
268func (rr *MG) String() string { return rr.Hdr.String() + sprintName(rr.Mg) }
269
270type MINFO struct {
271	Hdr   RR_Header
272	Rmail string `dns:"cdomain-name"`
273	Email string `dns:"cdomain-name"`
274}
275
276func (rr *MINFO) String() string {
277	return rr.Hdr.String() + sprintName(rr.Rmail) + " " + sprintName(rr.Email)
278}
279
280type MR struct {
281	Hdr RR_Header
282	Mr  string `dns:"cdomain-name"`
283}
284
285func (rr *MR) String() string {
286	return rr.Hdr.String() + sprintName(rr.Mr)
287}
288
289type MF struct {
290	Hdr RR_Header
291	Mf  string `dns:"cdomain-name"`
292}
293
294func (rr *MF) String() string {
295	return rr.Hdr.String() + sprintName(rr.Mf)
296}
297
298type MD struct {
299	Hdr RR_Header
300	Md  string `dns:"cdomain-name"`
301}
302
303func (rr *MD) String() string {
304	return rr.Hdr.String() + sprintName(rr.Md)
305}
306
307type MX struct {
308	Hdr        RR_Header
309	Preference uint16
310	Mx         string `dns:"cdomain-name"`
311}
312
313func (rr *MX) String() string {
314	return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Mx)
315}
316
317type AFSDB struct {
318	Hdr      RR_Header
319	Subtype  uint16
320	Hostname string `dns:"cdomain-name"`
321}
322
323func (rr *AFSDB) String() string {
324	return rr.Hdr.String() + strconv.Itoa(int(rr.Subtype)) + " " + sprintName(rr.Hostname)
325}
326
327type X25 struct {
328	Hdr         RR_Header
329	PSDNAddress string
330}
331
332func (rr *X25) String() string {
333	return rr.Hdr.String() + rr.PSDNAddress
334}
335
336type RT struct {
337	Hdr        RR_Header
338	Preference uint16
339	Host       string `dns:"cdomain-name"`
340}
341
342func (rr *RT) String() string {
343	return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Host)
344}
345
346type NS struct {
347	Hdr RR_Header
348	Ns  string `dns:"cdomain-name"`
349}
350
351func (rr *NS) String() string {
352	return rr.Hdr.String() + sprintName(rr.Ns)
353}
354
355type PTR struct {
356	Hdr RR_Header
357	Ptr string `dns:"cdomain-name"`
358}
359
360func (rr *PTR) String() string {
361	return rr.Hdr.String() + sprintName(rr.Ptr)
362}
363
364type RP struct {
365	Hdr  RR_Header
366	Mbox string `dns:"domain-name"`
367	Txt  string `dns:"domain-name"`
368}
369
370func (rr *RP) String() string {
371	return rr.Hdr.String() + rr.Mbox + " " + sprintTxt([]string{rr.Txt})
372}
373
374type SOA struct {
375	Hdr     RR_Header
376	Ns      string `dns:"cdomain-name"`
377	Mbox    string `dns:"cdomain-name"`
378	Serial  uint32
379	Refresh uint32
380	Retry   uint32
381	Expire  uint32
382	Minttl  uint32
383}
384
385func (rr *SOA) String() string {
386	return rr.Hdr.String() + sprintName(rr.Ns) + " " + sprintName(rr.Mbox) +
387		" " + strconv.FormatInt(int64(rr.Serial), 10) +
388		" " + strconv.FormatInt(int64(rr.Refresh), 10) +
389		" " + strconv.FormatInt(int64(rr.Retry), 10) +
390		" " + strconv.FormatInt(int64(rr.Expire), 10) +
391		" " + strconv.FormatInt(int64(rr.Minttl), 10)
392}
393
394type TXT struct {
395	Hdr RR_Header
396	Txt []string `dns:"txt"`
397}
398
399func (rr *TXT) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) }
400
401func sprintName(s string) string {
402	src := []byte(s)
403	dst := make([]byte, 0, len(src))
404	for i := 0; i < len(src); {
405		if i+1 < len(src) && src[i] == '\\' && src[i+1] == '.' {
406			dst = append(dst, src[i:i+2]...)
407			i += 2
408		} else {
409			b, n := nextByte(src, i)
410			if n == 0 {
411				i++ // dangling back slash
412			} else if b == '.' {
413				dst = append(dst, b)
414			} else {
415				dst = appendDomainNameByte(dst, b)
416			}
417			i += n
418		}
419	}
420	return string(dst)
421}
422
423func sprintTxtOctet(s string) string {
424	src := []byte(s)
425	dst := make([]byte, 0, len(src))
426	dst = append(dst, '"')
427	for i := 0; i < len(src); {
428		if i+1 < len(src) && src[i] == '\\' && src[i+1] == '.' {
429			dst = append(dst, src[i:i+2]...)
430			i += 2
431		} else {
432			b, n := nextByte(src, i)
433			if n == 0 {
434				i++ // dangling back slash
435			} else if b == '.' {
436				dst = append(dst, b)
437			} else {
438				if b < ' ' || b > '~' {
439					dst = appendByte(dst, b)
440				} else {
441					dst = append(dst, b)
442				}
443			}
444			i += n
445		}
446	}
447	dst = append(dst, '"')
448	return string(dst)
449}
450
451func sprintTxt(txt []string) string {
452	var out []byte
453	for i, s := range txt {
454		if i > 0 {
455			out = append(out, ` "`...)
456		} else {
457			out = append(out, '"')
458		}
459		bs := []byte(s)
460		for j := 0; j < len(bs); {
461			b, n := nextByte(bs, j)
462			if n == 0 {
463				break
464			}
465			out = appendTXTStringByte(out, b)
466			j += n
467		}
468		out = append(out, '"')
469	}
470	return string(out)
471}
472
473func appendDomainNameByte(s []byte, b byte) []byte {
474	switch b {
475	case '.', ' ', '\'', '@', ';', '(', ')': // additional chars to escape
476		return append(s, '\\', b)
477	}
478	return appendTXTStringByte(s, b)
479}
480
481func appendTXTStringByte(s []byte, b byte) []byte {
482	switch b {
483	case '\t':
484		return append(s, '\\', 't')
485	case '\r':
486		return append(s, '\\', 'r')
487	case '\n':
488		return append(s, '\\', 'n')
489	case '"', '\\':
490		return append(s, '\\', b)
491	}
492	if b < ' ' || b > '~' {
493		return appendByte(s, b)
494	}
495	return append(s, b)
496}
497
498func appendByte(s []byte, b byte) []byte {
499	var buf [3]byte
500	bufs := strconv.AppendInt(buf[:0], int64(b), 10)
501	s = append(s, '\\')
502	for i := 0; i < 3-len(bufs); i++ {
503		s = append(s, '0')
504	}
505	for _, r := range bufs {
506		s = append(s, r)
507	}
508	return s
509}
510
511func nextByte(b []byte, offset int) (byte, int) {
512	if offset >= len(b) {
513		return 0, 0
514	}
515	if b[offset] != '\\' {
516		// not an escape sequence
517		return b[offset], 1
518	}
519	switch len(b) - offset {
520	case 1: // dangling escape
521		return 0, 0
522	case 2, 3: // too short to be \ddd
523	default: // maybe \ddd
524		if isDigit(b[offset+1]) && isDigit(b[offset+2]) && isDigit(b[offset+3]) {
525			return dddToByte(b[offset+1:]), 4
526		}
527	}
528	// not \ddd, maybe a control char
529	switch b[offset+1] {
530	case 't':
531		return '\t', 2
532	case 'r':
533		return '\r', 2
534	case 'n':
535		return '\n', 2
536	default:
537		return b[offset+1], 2
538	}
539}
540
541type SPF struct {
542	Hdr RR_Header
543	Txt []string `dns:"txt"`
544}
545
546func (rr *SPF) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) }
547
548type SRV struct {
549	Hdr      RR_Header
550	Priority uint16
551	Weight   uint16
552	Port     uint16
553	Target   string `dns:"domain-name"`
554}
555
556func (rr *SRV) String() string {
557	return rr.Hdr.String() +
558		strconv.Itoa(int(rr.Priority)) + " " +
559		strconv.Itoa(int(rr.Weight)) + " " +
560		strconv.Itoa(int(rr.Port)) + " " + sprintName(rr.Target)
561}
562
563type NAPTR struct {
564	Hdr         RR_Header
565	Order       uint16
566	Preference  uint16
567	Flags       string
568	Service     string
569	Regexp      string
570	Replacement string `dns:"domain-name"`
571}
572
573func (rr *NAPTR) String() string {
574	return rr.Hdr.String() +
575		strconv.Itoa(int(rr.Order)) + " " +
576		strconv.Itoa(int(rr.Preference)) + " " +
577		"\"" + rr.Flags + "\" " +
578		"\"" + rr.Service + "\" " +
579		"\"" + rr.Regexp + "\" " +
580		rr.Replacement
581}
582
583// The CERT resource record, see RFC 4398.
584type CERT struct {
585	Hdr         RR_Header
586	Type        uint16
587	KeyTag      uint16
588	Algorithm   uint8
589	Certificate string `dns:"base64"`
590}
591
592func (rr *CERT) String() string {
593	var (
594		ok                  bool
595		certtype, algorithm string
596	)
597	if certtype, ok = CertTypeToString[rr.Type]; !ok {
598		certtype = strconv.Itoa(int(rr.Type))
599	}
600	if algorithm, ok = AlgorithmToString[rr.Algorithm]; !ok {
601		algorithm = strconv.Itoa(int(rr.Algorithm))
602	}
603	return rr.Hdr.String() + certtype +
604		" " + strconv.Itoa(int(rr.KeyTag)) +
605		" " + algorithm +
606		" " + rr.Certificate
607}
608
609// The DNAME resource record, see RFC 2672.
610type DNAME struct {
611	Hdr    RR_Header
612	Target string `dns:"domain-name"`
613}
614
615func (rr *DNAME) String() string {
616	return rr.Hdr.String() + sprintName(rr.Target)
617}
618
619type A struct {
620	Hdr RR_Header
621	A   net.IP `dns:"a"`
622}
623
624func (rr *A) String() string {
625	if rr.A == nil {
626		return rr.Hdr.String()
627	}
628	return rr.Hdr.String() + rr.A.String()
629}
630
631type AAAA struct {
632	Hdr  RR_Header
633	AAAA net.IP `dns:"aaaa"`
634}
635
636func (rr *AAAA) String() string {
637	if rr.AAAA == nil {
638		return rr.Hdr.String()
639	}
640	return rr.Hdr.String() + rr.AAAA.String()
641}
642
643type PX struct {
644	Hdr        RR_Header
645	Preference uint16
646	Map822     string `dns:"domain-name"`
647	Mapx400    string `dns:"domain-name"`
648}
649
650func (rr *PX) String() string {
651	return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Map822) + " " + sprintName(rr.Mapx400)
652}
653
654type GPOS struct {
655	Hdr       RR_Header
656	Longitude string
657	Latitude  string
658	Altitude  string
659}
660
661func (rr *GPOS) String() string {
662	return rr.Hdr.String() + rr.Longitude + " " + rr.Latitude + " " + rr.Altitude
663}
664
665type LOC struct {
666	Hdr       RR_Header
667	Version   uint8
668	Size      uint8
669	HorizPre  uint8
670	VertPre   uint8
671	Latitude  uint32
672	Longitude uint32
673	Altitude  uint32
674}
675
676// cmToM takes a cm value expressed in RFC1876 SIZE mantissa/exponent
677// format and returns a string in m (two decimals for the cm)
678func cmToM(m, e uint8) string {
679	if e < 2 {
680		if e == 1 {
681			m *= 10
682		}
683
684		return fmt.Sprintf("0.%02d", m)
685	}
686
687	s := fmt.Sprintf("%d", m)
688	for e > 2 {
689		s += "0"
690		e--
691	}
692	return s
693}
694
695func (rr *LOC) String() string {
696	s := rr.Hdr.String()
697
698	lat := rr.Latitude
699	ns := "N"
700	if lat > LOC_EQUATOR {
701		lat = lat - LOC_EQUATOR
702	} else {
703		ns = "S"
704		lat = LOC_EQUATOR - lat
705	}
706	h := lat / LOC_DEGREES
707	lat = lat % LOC_DEGREES
708	m := lat / LOC_HOURS
709	lat = lat % LOC_HOURS
710	s += fmt.Sprintf("%02d %02d %0.3f %s ", h, m, (float64(lat) / 1000), ns)
711
712	lon := rr.Longitude
713	ew := "E"
714	if lon > LOC_PRIMEMERIDIAN {
715		lon = lon - LOC_PRIMEMERIDIAN
716	} else {
717		ew = "W"
718		lon = LOC_PRIMEMERIDIAN - lon
719	}
720	h = lon / LOC_DEGREES
721	lon = lon % LOC_DEGREES
722	m = lon / LOC_HOURS
723	lon = lon % LOC_HOURS
724	s += fmt.Sprintf("%02d %02d %0.3f %s ", h, m, (float64(lon) / 1000), ew)
725
726	var alt = float64(rr.Altitude) / 100
727	alt -= LOC_ALTITUDEBASE
728	if rr.Altitude%100 != 0 {
729		s += fmt.Sprintf("%.2fm ", alt)
730	} else {
731		s += fmt.Sprintf("%.0fm ", alt)
732	}
733
734	s += cmToM((rr.Size&0xf0)>>4, rr.Size&0x0f) + "m "
735	s += cmToM((rr.HorizPre&0xf0)>>4, rr.HorizPre&0x0f) + "m "
736	s += cmToM((rr.VertPre&0xf0)>>4, rr.VertPre&0x0f) + "m"
737
738	return s
739}
740
741// SIG is identical to RRSIG and nowadays only used for SIG(0), RFC2931.
742type SIG struct {
743	RRSIG
744}
745
746type RRSIG struct {
747	Hdr         RR_Header
748	TypeCovered uint16
749	Algorithm   uint8
750	Labels      uint8
751	OrigTtl     uint32
752	Expiration  uint32
753	Inception   uint32
754	KeyTag      uint16
755	SignerName  string `dns:"domain-name"`
756	Signature   string `dns:"base64"`
757}
758
759func (rr *RRSIG) String() string {
760	s := rr.Hdr.String()
761	s += Type(rr.TypeCovered).String()
762	s += " " + strconv.Itoa(int(rr.Algorithm)) +
763		" " + strconv.Itoa(int(rr.Labels)) +
764		" " + strconv.FormatInt(int64(rr.OrigTtl), 10) +
765		" " + TimeToString(rr.Expiration) +
766		" " + TimeToString(rr.Inception) +
767		" " + strconv.Itoa(int(rr.KeyTag)) +
768		" " + sprintName(rr.SignerName) +
769		" " + rr.Signature
770	return s
771}
772
773type NSEC struct {
774	Hdr        RR_Header
775	NextDomain string   `dns:"domain-name"`
776	TypeBitMap []uint16 `dns:"nsec"`
777}
778
779func (rr *NSEC) String() string {
780	s := rr.Hdr.String() + sprintName(rr.NextDomain)
781	for i := 0; i < len(rr.TypeBitMap); i++ {
782		s += " " + Type(rr.TypeBitMap[i]).String()
783	}
784	return s
785}
786
787func (rr *NSEC) len() int {
788	l := rr.Hdr.len() + len(rr.NextDomain) + 1
789	lastwindow := uint32(2 ^ 32 + 1)
790	for _, t := range rr.TypeBitMap {
791		window := t / 256
792		if uint32(window) != lastwindow {
793			l += 1 + 32
794		}
795		lastwindow = uint32(window)
796	}
797	return l
798}
799
800type DLV struct {
801	DS
802}
803
804type CDS struct {
805	DS
806}
807
808type DS struct {
809	Hdr        RR_Header
810	KeyTag     uint16
811	Algorithm  uint8
812	DigestType uint8
813	Digest     string `dns:"hex"`
814}
815
816func (rr *DS) String() string {
817	return rr.Hdr.String() + strconv.Itoa(int(rr.KeyTag)) +
818		" " + strconv.Itoa(int(rr.Algorithm)) +
819		" " + strconv.Itoa(int(rr.DigestType)) +
820		" " + strings.ToUpper(rr.Digest)
821}
822
823type KX struct {
824	Hdr        RR_Header
825	Preference uint16
826	Exchanger  string `dns:"domain-name"`
827}
828
829func (rr *KX) String() string {
830	return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) +
831		" " + sprintName(rr.Exchanger)
832}
833
834type TA struct {
835	Hdr        RR_Header
836	KeyTag     uint16
837	Algorithm  uint8
838	DigestType uint8
839	Digest     string `dns:"hex"`
840}
841
842func (rr *TA) String() string {
843	return rr.Hdr.String() + strconv.Itoa(int(rr.KeyTag)) +
844		" " + strconv.Itoa(int(rr.Algorithm)) +
845		" " + strconv.Itoa(int(rr.DigestType)) +
846		" " + strings.ToUpper(rr.Digest)
847}
848
849type TALINK struct {
850	Hdr          RR_Header
851	PreviousName string `dns:"domain-name"`
852	NextName     string `dns:"domain-name"`
853}
854
855func (rr *TALINK) String() string {
856	return rr.Hdr.String() +
857		sprintName(rr.PreviousName) + " " + sprintName(rr.NextName)
858}
859
860type SSHFP struct {
861	Hdr         RR_Header
862	Algorithm   uint8
863	Type        uint8
864	FingerPrint string `dns:"hex"`
865}
866
867func (rr *SSHFP) String() string {
868	return rr.Hdr.String() + strconv.Itoa(int(rr.Algorithm)) +
869		" " + strconv.Itoa(int(rr.Type)) +
870		" " + strings.ToUpper(rr.FingerPrint)
871}
872
873type KEY struct {
874	DNSKEY
875}
876
877type CDNSKEY struct {
878	DNSKEY
879}
880
881type DNSKEY struct {
882	Hdr       RR_Header
883	Flags     uint16
884	Protocol  uint8
885	Algorithm uint8
886	PublicKey string `dns:"base64"`
887}
888
889func (rr *DNSKEY) String() string {
890	return rr.Hdr.String() + strconv.Itoa(int(rr.Flags)) +
891		" " + strconv.Itoa(int(rr.Protocol)) +
892		" " + strconv.Itoa(int(rr.Algorithm)) +
893		" " + rr.PublicKey
894}
895
896type RKEY struct {
897	Hdr       RR_Header
898	Flags     uint16
899	Protocol  uint8
900	Algorithm uint8
901	PublicKey string `dns:"base64"`
902}
903
904func (rr *RKEY) String() string {
905	return rr.Hdr.String() + strconv.Itoa(int(rr.Flags)) +
906		" " + strconv.Itoa(int(rr.Protocol)) +
907		" " + strconv.Itoa(int(rr.Algorithm)) +
908		" " + rr.PublicKey
909}
910
911type NSAPPTR struct {
912	Hdr RR_Header
913	Ptr string `dns:"domain-name"`
914}
915
916func (rr *NSAPPTR) String() string { return rr.Hdr.String() + sprintName(rr.Ptr) }
917
918type NSEC3 struct {
919	Hdr        RR_Header
920	Hash       uint8
921	Flags      uint8
922	Iterations uint16
923	SaltLength uint8
924	Salt       string `dns:"size-hex:SaltLength"`
925	HashLength uint8
926	NextDomain string   `dns:"size-base32:HashLength"`
927	TypeBitMap []uint16 `dns:"nsec"`
928}
929
930func (rr *NSEC3) String() string {
931	s := rr.Hdr.String()
932	s += strconv.Itoa(int(rr.Hash)) +
933		" " + strconv.Itoa(int(rr.Flags)) +
934		" " + strconv.Itoa(int(rr.Iterations)) +
935		" " + saltToString(rr.Salt) +
936		" " + rr.NextDomain
937	for i := 0; i < len(rr.TypeBitMap); i++ {
938		s += " " + Type(rr.TypeBitMap[i]).String()
939	}
940	return s
941}
942
943func (rr *NSEC3) len() int {
944	l := rr.Hdr.len() + 6 + len(rr.Salt)/2 + 1 + len(rr.NextDomain) + 1
945	lastwindow := uint32(2 ^ 32 + 1)
946	for _, t := range rr.TypeBitMap {
947		window := t / 256
948		if uint32(window) != lastwindow {
949			l += 1 + 32
950		}
951		lastwindow = uint32(window)
952	}
953	return l
954}
955
956type NSEC3PARAM struct {
957	Hdr        RR_Header
958	Hash       uint8
959	Flags      uint8
960	Iterations uint16
961	SaltLength uint8
962	Salt       string `dns:"size-hex:SaltLength"`
963}
964
965func (rr *NSEC3PARAM) String() string {
966	s := rr.Hdr.String()
967	s += strconv.Itoa(int(rr.Hash)) +
968		" " + strconv.Itoa(int(rr.Flags)) +
969		" " + strconv.Itoa(int(rr.Iterations)) +
970		" " + saltToString(rr.Salt)
971	return s
972}
973
974type TKEY struct {
975	Hdr        RR_Header
976	Algorithm  string `dns:"domain-name"`
977	Inception  uint32
978	Expiration uint32
979	Mode       uint16
980	Error      uint16
981	KeySize    uint16
982	Key        string
983	OtherLen   uint16
984	OtherData  string
985}
986
987func (rr *TKEY) String() string {
988	// It has no presentation format
989	return ""
990}
991
992// RFC3597 represents an unknown/generic RR.
993type RFC3597 struct {
994	Hdr   RR_Header
995	Rdata string `dns:"hex"`
996}
997
998func (rr *RFC3597) String() string {
999	// Let's call it a hack
1000	s := rfc3597Header(rr.Hdr)
1001
1002	s += "\\# " + strconv.Itoa(len(rr.Rdata)/2) + " " + rr.Rdata
1003	return s
1004}
1005
1006func rfc3597Header(h RR_Header) string {
1007	var s string
1008
1009	s += sprintName(h.Name) + "\t"
1010	s += strconv.FormatInt(int64(h.Ttl), 10) + "\t"
1011	s += "CLASS" + strconv.Itoa(int(h.Class)) + "\t"
1012	s += "TYPE" + strconv.Itoa(int(h.Rrtype)) + "\t"
1013	return s
1014}
1015
1016type URI struct {
1017	Hdr      RR_Header
1018	Priority uint16
1019	Weight   uint16
1020	Target   string `dns:"octet"`
1021}
1022
1023func (rr *URI) String() string {
1024	return rr.Hdr.String() + strconv.Itoa(int(rr.Priority)) +
1025		" " + strconv.Itoa(int(rr.Weight)) + " " + sprintTxtOctet(rr.Target)
1026}
1027
1028type DHCID struct {
1029	Hdr    RR_Header
1030	Digest string `dns:"base64"`
1031}
1032
1033func (rr *DHCID) String() string { return rr.Hdr.String() + rr.Digest }
1034
1035type TLSA struct {
1036	Hdr          RR_Header
1037	Usage        uint8
1038	Selector     uint8
1039	MatchingType uint8
1040	Certificate  string `dns:"hex"`
1041}
1042
1043func (rr *TLSA) String() string {
1044	return rr.Hdr.String() +
1045		strconv.Itoa(int(rr.Usage)) +
1046		" " + strconv.Itoa(int(rr.Selector)) +
1047		" " + strconv.Itoa(int(rr.MatchingType)) +
1048		" " + rr.Certificate
1049}
1050
1051type SMIMEA struct {
1052	Hdr          RR_Header
1053	Usage        uint8
1054	Selector     uint8
1055	MatchingType uint8
1056	Certificate  string `dns:"hex"`
1057}
1058
1059func (rr *SMIMEA) String() string {
1060	s := rr.Hdr.String() +
1061		strconv.Itoa(int(rr.Usage)) +
1062		" " + strconv.Itoa(int(rr.Selector)) +
1063		" " + strconv.Itoa(int(rr.MatchingType))
1064
1065	// Every Nth char needs a space on this output. If we output
1066	// this as one giant line, we can't read it can in because in some cases
1067	// the cert length overflows scan.maxTok (2048).
1068	sx := splitN(rr.Certificate, 1024) // conservative value here
1069	s += " " + strings.Join(sx, " ")
1070	return s
1071}
1072
1073type HIP struct {
1074	Hdr                RR_Header
1075	HitLength          uint8
1076	PublicKeyAlgorithm uint8
1077	PublicKeyLength    uint16
1078	Hit                string   `dns:"size-hex:HitLength"`
1079	PublicKey          string   `dns:"size-base64:PublicKeyLength"`
1080	RendezvousServers  []string `dns:"domain-name"`
1081}
1082
1083func (rr *HIP) String() string {
1084	s := rr.Hdr.String() +
1085		strconv.Itoa(int(rr.PublicKeyAlgorithm)) +
1086		" " + rr.Hit +
1087		" " + rr.PublicKey
1088	for _, d := range rr.RendezvousServers {
1089		s += " " + sprintName(d)
1090	}
1091	return s
1092}
1093
1094type NINFO struct {
1095	Hdr    RR_Header
1096	ZSData []string `dns:"txt"`
1097}
1098
1099func (rr *NINFO) String() string { return rr.Hdr.String() + sprintTxt(rr.ZSData) }
1100
1101type NID struct {
1102	Hdr        RR_Header
1103	Preference uint16
1104	NodeID     uint64
1105}
1106
1107func (rr *NID) String() string {
1108	s := rr.Hdr.String() + strconv.Itoa(int(rr.Preference))
1109	node := fmt.Sprintf("%0.16x", rr.NodeID)
1110	s += " " + node[0:4] + ":" + node[4:8] + ":" + node[8:12] + ":" + node[12:16]
1111	return s
1112}
1113
1114type L32 struct {
1115	Hdr        RR_Header
1116	Preference uint16
1117	Locator32  net.IP `dns:"a"`
1118}
1119
1120func (rr *L32) String() string {
1121	if rr.Locator32 == nil {
1122		return rr.Hdr.String() + strconv.Itoa(int(rr.Preference))
1123	}
1124	return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) +
1125		" " + rr.Locator32.String()
1126}
1127
1128type L64 struct {
1129	Hdr        RR_Header
1130	Preference uint16
1131	Locator64  uint64
1132}
1133
1134func (rr *L64) String() string {
1135	s := rr.Hdr.String() + strconv.Itoa(int(rr.Preference))
1136	node := fmt.Sprintf("%0.16X", rr.Locator64)
1137	s += " " + node[0:4] + ":" + node[4:8] + ":" + node[8:12] + ":" + node[12:16]
1138	return s
1139}
1140
1141type LP struct {
1142	Hdr        RR_Header
1143	Preference uint16
1144	Fqdn       string `dns:"domain-name"`
1145}
1146
1147func (rr *LP) String() string {
1148	return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Fqdn)
1149}
1150
1151type EUI48 struct {
1152	Hdr     RR_Header
1153	Address uint64 `dns:"uint48"`
1154}
1155
1156func (rr *EUI48) String() string { return rr.Hdr.String() + euiToString(rr.Address, 48) }
1157
1158type EUI64 struct {
1159	Hdr     RR_Header
1160	Address uint64
1161}
1162
1163func (rr *EUI64) String() string { return rr.Hdr.String() + euiToString(rr.Address, 64) }
1164
1165type CAA struct {
1166	Hdr   RR_Header
1167	Flag  uint8
1168	Tag   string
1169	Value string `dns:"octet"`
1170}
1171
1172func (rr *CAA) String() string {
1173	return rr.Hdr.String() + strconv.Itoa(int(rr.Flag)) + " " + rr.Tag + " " + sprintTxtOctet(rr.Value)
1174}
1175
1176type UID struct {
1177	Hdr RR_Header
1178	Uid uint32
1179}
1180
1181func (rr *UID) String() string { return rr.Hdr.String() + strconv.FormatInt(int64(rr.Uid), 10) }
1182
1183type GID struct {
1184	Hdr RR_Header
1185	Gid uint32
1186}
1187
1188func (rr *GID) String() string { return rr.Hdr.String() + strconv.FormatInt(int64(rr.Gid), 10) }
1189
1190type UINFO struct {
1191	Hdr   RR_Header
1192	Uinfo string
1193}
1194
1195func (rr *UINFO) String() string { return rr.Hdr.String() + sprintTxt([]string{rr.Uinfo}) }
1196
1197type EID struct {
1198	Hdr      RR_Header
1199	Endpoint string `dns:"hex"`
1200}
1201
1202func (rr *EID) String() string { return rr.Hdr.String() + strings.ToUpper(rr.Endpoint) }
1203
1204type NIMLOC struct {
1205	Hdr     RR_Header
1206	Locator string `dns:"hex"`
1207}
1208
1209func (rr *NIMLOC) String() string { return rr.Hdr.String() + strings.ToUpper(rr.Locator) }
1210
1211type OPENPGPKEY struct {
1212	Hdr       RR_Header
1213	PublicKey string `dns:"base64"`
1214}
1215
1216func (rr *OPENPGPKEY) String() string { return rr.Hdr.String() + rr.PublicKey }
1217
1218// TimeToString translates the RRSIG's incep. and expir. times to the
1219// string representation used when printing the record.
1220// It takes serial arithmetic (RFC 1982) into account.
1221func TimeToString(t uint32) string {
1222	mod := ((int64(t) - time.Now().Unix()) / year68) - 1
1223	if mod < 0 {
1224		mod = 0
1225	}
1226	ti := time.Unix(int64(t)-(mod*year68), 0).UTC()
1227	return ti.Format("20060102150405")
1228}
1229
1230// StringToTime translates the RRSIG's incep. and expir. times from
1231// string values like "20110403154150" to an 32 bit integer.
1232// It takes serial arithmetic (RFC 1982) into account.
1233func StringToTime(s string) (uint32, error) {
1234	t, err := time.Parse("20060102150405", s)
1235	if err != nil {
1236		return 0, err
1237	}
1238	mod := (t.Unix() / year68) - 1
1239	if mod < 0 {
1240		mod = 0
1241	}
1242	return uint32(t.Unix() - (mod * year68)), nil
1243}
1244
1245// saltToString converts a NSECX salt to uppercase and returns "-" when it is empty.
1246func saltToString(s string) string {
1247	if len(s) == 0 {
1248		return "-"
1249	}
1250	return strings.ToUpper(s)
1251}
1252
1253func euiToString(eui uint64, bits int) (hex string) {
1254	switch bits {
1255	case 64:
1256		hex = fmt.Sprintf("%16.16x", eui)
1257		hex = hex[0:2] + "-" + hex[2:4] + "-" + hex[4:6] + "-" + hex[6:8] +
1258			"-" + hex[8:10] + "-" + hex[10:12] + "-" + hex[12:14] + "-" + hex[14:16]
1259	case 48:
1260		hex = fmt.Sprintf("%12.12x", eui)
1261		hex = hex[0:2] + "-" + hex[2:4] + "-" + hex[4:6] + "-" + hex[6:8] +
1262			"-" + hex[8:10] + "-" + hex[10:12]
1263	}
1264	return
1265}
1266
1267// copyIP returns a copy of ip.
1268func copyIP(ip net.IP) net.IP {
1269	p := make(net.IP, len(ip))
1270	copy(p, ip)
1271	return p
1272}
1273
1274// SplitN splits a string into N sized string chunks.
1275// This might become an exported function once.
1276func splitN(s string, n int) []string {
1277	if len(s) < n {
1278		return []string{s}
1279	}
1280	sx := []string{}
1281	p, i := 0, n
1282	for {
1283		if i <= len(s) {
1284			sx = append(sx, s[p:i])
1285		} else {
1286			sx = append(sx, s[p:])
1287			break
1288
1289		}
1290		p, i = p+n, i+n
1291	}
1292
1293	return sx
1294}
1295