1package dns
2
3import (
4	"bufio"
5	"fmt"
6	"io"
7	"os"
8	"path/filepath"
9	"strconv"
10	"strings"
11)
12
13const maxTok = 2048 // Largest token we can return.
14
15// The maximum depth of $INCLUDE directives supported by the
16// ZoneParser API.
17const maxIncludeDepth = 7
18
19// Tokinize a RFC 1035 zone file. The tokenizer will normalize it:
20// * Add ownernames if they are left blank;
21// * Suppress sequences of spaces;
22// * Make each RR fit on one line (_NEWLINE is send as last)
23// * Handle comments: ;
24// * Handle braces - anywhere.
25const (
26	// Zonefile
27	zEOF = iota
28	zString
29	zBlank
30	zQuote
31	zNewline
32	zRrtpe
33	zOwner
34	zClass
35	zDirOrigin   // $ORIGIN
36	zDirTTL      // $TTL
37	zDirInclude  // $INCLUDE
38	zDirGenerate // $GENERATE
39
40	// Privatekey file
41	zValue
42	zKey
43
44	zExpectOwnerDir      // Ownername
45	zExpectOwnerBl       // Whitespace after the ownername
46	zExpectAny           // Expect rrtype, ttl or class
47	zExpectAnyNoClass    // Expect rrtype or ttl
48	zExpectAnyNoClassBl  // The whitespace after _EXPECT_ANY_NOCLASS
49	zExpectAnyNoTTL      // Expect rrtype or class
50	zExpectAnyNoTTLBl    // Whitespace after _EXPECT_ANY_NOTTL
51	zExpectRrtype        // Expect rrtype
52	zExpectRrtypeBl      // Whitespace BEFORE rrtype
53	zExpectRdata         // The first element of the rdata
54	zExpectDirTTLBl      // Space after directive $TTL
55	zExpectDirTTL        // Directive $TTL
56	zExpectDirOriginBl   // Space after directive $ORIGIN
57	zExpectDirOrigin     // Directive $ORIGIN
58	zExpectDirIncludeBl  // Space after directive $INCLUDE
59	zExpectDirInclude    // Directive $INCLUDE
60	zExpectDirGenerate   // Directive $GENERATE
61	zExpectDirGenerateBl // Space after directive $GENERATE
62)
63
64// ParseError is a parsing error. It contains the parse error and the location in the io.Reader
65// where the error occurred.
66type ParseError struct {
67	file string
68	err  string
69	lex  lex
70}
71
72func (e *ParseError) Error() (s string) {
73	if e.file != "" {
74		s = e.file + ": "
75	}
76	s += "dns: " + e.err + ": " + strconv.QuoteToASCII(e.lex.token) + " at line: " +
77		strconv.Itoa(e.lex.line) + ":" + strconv.Itoa(e.lex.column)
78	return
79}
80
81type lex struct {
82	token  string // text of the token
83	err    bool   // when true, token text has lexer error
84	value  uint8  // value: zString, _BLANK, etc.
85	torc   uint16 // type or class as parsed in the lexer, we only need to look this up in the grammar
86	line   int    // line in the file
87	column int    // column in the file
88}
89
90// ttlState describes the state necessary to fill in an omitted RR TTL
91type ttlState struct {
92	ttl           uint32 // ttl is the current default TTL
93	isByDirective bool   // isByDirective indicates whether ttl was set by a $TTL directive
94}
95
96// NewRR reads the RR contained in the string s. Only the first RR is returned.
97// If s contains no records, NewRR will return nil with no error.
98//
99// The class defaults to IN and TTL defaults to 3600. The full zone file syntax
100// like $TTL, $ORIGIN, etc. is supported. All fields of the returned RR are
101// set, except RR.Header().Rdlength which is set to 0.
102func NewRR(s string) (RR, error) {
103	if len(s) > 0 && s[len(s)-1] != '\n' { // We need a closing newline
104		return ReadRR(strings.NewReader(s+"\n"), "")
105	}
106	return ReadRR(strings.NewReader(s), "")
107}
108
109// ReadRR reads the RR contained in r.
110//
111// The string file is used in error reporting and to resolve relative
112// $INCLUDE directives.
113//
114// See NewRR for more documentation.
115func ReadRR(r io.Reader, file string) (RR, error) {
116	zp := NewZoneParser(r, ".", file)
117	zp.SetDefaultTTL(defaultTtl)
118	zp.SetIncludeAllowed(true)
119	rr, _ := zp.Next()
120	return rr, zp.Err()
121}
122
123// ZoneParser is a parser for an RFC 1035 style zonefile.
124//
125// Each parsed RR in the zone is returned sequentially from Next. An
126// optional comment can be retrieved with Comment.
127//
128// The directives $INCLUDE, $ORIGIN, $TTL and $GENERATE are all
129// supported. Although $INCLUDE is disabled by default.
130// Note that $GENERATE's range support up to a maximum of 65535 steps.
131//
132// Basic usage pattern when reading from a string (z) containing the
133// zone data:
134//
135//	zp := NewZoneParser(strings.NewReader(z), "", "")
136//
137//	for rr, ok := zp.Next(); ok; rr, ok = zp.Next() {
138//		// Do something with rr
139//	}
140//
141//	if err := zp.Err(); err != nil {
142//		// log.Println(err)
143//	}
144//
145// Comments specified after an RR (and on the same line!) are
146// returned too:
147//
148//	foo. IN A 10.0.0.1 ; this is a comment
149//
150// The text "; this is comment" is returned from Comment. Comments inside
151// the RR are returned concatenated along with the RR. Comments on a line
152// by themselves are discarded.
153type ZoneParser struct {
154	c *zlexer
155
156	parseErr *ParseError
157
158	origin string
159	file   string
160
161	defttl *ttlState
162
163	h RR_Header
164
165	// sub is used to parse $INCLUDE files and $GENERATE directives.
166	// Next, by calling subNext, forwards the resulting RRs from this
167	// sub parser to the calling code.
168	sub    *ZoneParser
169	osFile *os.File
170
171	includeDepth uint8
172
173	includeAllowed     bool
174	generateDisallowed bool
175}
176
177// NewZoneParser returns an RFC 1035 style zonefile parser that reads
178// from r.
179//
180// The string file is used in error reporting and to resolve relative
181// $INCLUDE directives. The string origin is used as the initial
182// origin, as if the file would start with an $ORIGIN directive.
183func NewZoneParser(r io.Reader, origin, file string) *ZoneParser {
184	var pe *ParseError
185	if origin != "" {
186		origin = Fqdn(origin)
187		if _, ok := IsDomainName(origin); !ok {
188			pe = &ParseError{file, "bad initial origin name", lex{}}
189		}
190	}
191
192	return &ZoneParser{
193		c: newZLexer(r),
194
195		parseErr: pe,
196
197		origin: origin,
198		file:   file,
199	}
200}
201
202// SetDefaultTTL sets the parsers default TTL to ttl.
203func (zp *ZoneParser) SetDefaultTTL(ttl uint32) {
204	zp.defttl = &ttlState{ttl, false}
205}
206
207// SetIncludeAllowed controls whether $INCLUDE directives are
208// allowed. $INCLUDE directives are not supported by default.
209//
210// The $INCLUDE directive will open and read from a user controlled
211// file on the system. Even if the file is not a valid zonefile, the
212// contents of the file may be revealed in error messages, such as:
213//
214//	/etc/passwd: dns: not a TTL: "root:x:0:0:root:/root:/bin/bash" at line: 1:31
215//	/etc/shadow: dns: not a TTL: "root:$6$<redacted>::0:99999:7:::" at line: 1:125
216func (zp *ZoneParser) SetIncludeAllowed(v bool) {
217	zp.includeAllowed = v
218}
219
220// Err returns the first non-EOF error that was encountered by the
221// ZoneParser.
222func (zp *ZoneParser) Err() error {
223	if zp.parseErr != nil {
224		return zp.parseErr
225	}
226
227	if zp.sub != nil {
228		if err := zp.sub.Err(); err != nil {
229			return err
230		}
231	}
232
233	return zp.c.Err()
234}
235
236func (zp *ZoneParser) setParseError(err string, l lex) (RR, bool) {
237	zp.parseErr = &ParseError{zp.file, err, l}
238	return nil, false
239}
240
241// Comment returns an optional text comment that occurred alongside
242// the RR.
243func (zp *ZoneParser) Comment() string {
244	if zp.parseErr != nil {
245		return ""
246	}
247
248	if zp.sub != nil {
249		return zp.sub.Comment()
250	}
251
252	return zp.c.Comment()
253}
254
255func (zp *ZoneParser) subNext() (RR, bool) {
256	if rr, ok := zp.sub.Next(); ok {
257		return rr, true
258	}
259
260	if zp.sub.osFile != nil {
261		zp.sub.osFile.Close()
262		zp.sub.osFile = nil
263	}
264
265	if zp.sub.Err() != nil {
266		// We have errors to surface.
267		return nil, false
268	}
269
270	zp.sub = nil
271	return zp.Next()
272}
273
274// Next advances the parser to the next RR in the zonefile and
275// returns the (RR, true). It will return (nil, false) when the
276// parsing stops, either by reaching the end of the input or an
277// error. After Next returns (nil, false), the Err method will return
278// any error that occurred during parsing.
279func (zp *ZoneParser) Next() (RR, bool) {
280	if zp.parseErr != nil {
281		return nil, false
282	}
283	if zp.sub != nil {
284		return zp.subNext()
285	}
286
287	// 6 possible beginnings of a line (_ is a space):
288	//
289	//   0. zRRTYPE                              -> all omitted until the rrtype
290	//   1. zOwner _ zRrtype                     -> class/ttl omitted
291	//   2. zOwner _ zString _ zRrtype           -> class omitted
292	//   3. zOwner _ zString _ zClass  _ zRrtype -> ttl/class
293	//   4. zOwner _ zClass  _ zRrtype           -> ttl omitted
294	//   5. zOwner _ zClass  _ zString _ zRrtype -> class/ttl (reversed)
295	//
296	// After detecting these, we know the zRrtype so we can jump to functions
297	// handling the rdata for each of these types.
298
299	st := zExpectOwnerDir // initial state
300	h := &zp.h
301
302	for l, ok := zp.c.Next(); ok; l, ok = zp.c.Next() {
303		// zlexer spotted an error already
304		if l.err {
305			return zp.setParseError(l.token, l)
306		}
307
308		switch st {
309		case zExpectOwnerDir:
310			// We can also expect a directive, like $TTL or $ORIGIN
311			if zp.defttl != nil {
312				h.Ttl = zp.defttl.ttl
313			}
314
315			h.Class = ClassINET
316
317			switch l.value {
318			case zNewline:
319				st = zExpectOwnerDir
320			case zOwner:
321				name, ok := toAbsoluteName(l.token, zp.origin)
322				if !ok {
323					return zp.setParseError("bad owner name", l)
324				}
325
326				h.Name = name
327
328				st = zExpectOwnerBl
329			case zDirTTL:
330				st = zExpectDirTTLBl
331			case zDirOrigin:
332				st = zExpectDirOriginBl
333			case zDirInclude:
334				st = zExpectDirIncludeBl
335			case zDirGenerate:
336				st = zExpectDirGenerateBl
337			case zRrtpe:
338				h.Rrtype = l.torc
339
340				st = zExpectRdata
341			case zClass:
342				h.Class = l.torc
343
344				st = zExpectAnyNoClassBl
345			case zBlank:
346				// Discard, can happen when there is nothing on the
347				// line except the RR type
348			case zString:
349				ttl, ok := stringToTTL(l.token)
350				if !ok {
351					return zp.setParseError("not a TTL", l)
352				}
353
354				h.Ttl = ttl
355
356				if zp.defttl == nil || !zp.defttl.isByDirective {
357					zp.defttl = &ttlState{ttl, false}
358				}
359
360				st = zExpectAnyNoTTLBl
361			default:
362				return zp.setParseError("syntax error at beginning", l)
363			}
364		case zExpectDirIncludeBl:
365			if l.value != zBlank {
366				return zp.setParseError("no blank after $INCLUDE-directive", l)
367			}
368
369			st = zExpectDirInclude
370		case zExpectDirInclude:
371			if l.value != zString {
372				return zp.setParseError("expecting $INCLUDE value, not this...", l)
373			}
374
375			neworigin := zp.origin // There may be optionally a new origin set after the filename, if not use current one
376			switch l, _ := zp.c.Next(); l.value {
377			case zBlank:
378				l, _ := zp.c.Next()
379				if l.value == zString {
380					name, ok := toAbsoluteName(l.token, zp.origin)
381					if !ok {
382						return zp.setParseError("bad origin name", l)
383					}
384
385					neworigin = name
386				}
387			case zNewline, zEOF:
388				// Ok
389			default:
390				return zp.setParseError("garbage after $INCLUDE", l)
391			}
392
393			if !zp.includeAllowed {
394				return zp.setParseError("$INCLUDE directive not allowed", l)
395			}
396			if zp.includeDepth >= maxIncludeDepth {
397				return zp.setParseError("too deeply nested $INCLUDE", l)
398			}
399
400			// Start with the new file
401			includePath := l.token
402			if !filepath.IsAbs(includePath) {
403				includePath = filepath.Join(filepath.Dir(zp.file), includePath)
404			}
405
406			r1, e1 := os.Open(includePath)
407			if e1 != nil {
408				var as string
409				if !filepath.IsAbs(l.token) {
410					as = fmt.Sprintf(" as `%s'", includePath)
411				}
412
413				msg := fmt.Sprintf("failed to open `%s'%s: %v", l.token, as, e1)
414				return zp.setParseError(msg, l)
415			}
416
417			zp.sub = NewZoneParser(r1, neworigin, includePath)
418			zp.sub.defttl, zp.sub.includeDepth, zp.sub.osFile = zp.defttl, zp.includeDepth+1, r1
419			zp.sub.SetIncludeAllowed(true)
420			return zp.subNext()
421		case zExpectDirTTLBl:
422			if l.value != zBlank {
423				return zp.setParseError("no blank after $TTL-directive", l)
424			}
425
426			st = zExpectDirTTL
427		case zExpectDirTTL:
428			if l.value != zString {
429				return zp.setParseError("expecting $TTL value, not this...", l)
430			}
431
432			if err := slurpRemainder(zp.c); err != nil {
433				return zp.setParseError(err.err, err.lex)
434			}
435
436			ttl, ok := stringToTTL(l.token)
437			if !ok {
438				return zp.setParseError("expecting $TTL value, not this...", l)
439			}
440
441			zp.defttl = &ttlState{ttl, true}
442
443			st = zExpectOwnerDir
444		case zExpectDirOriginBl:
445			if l.value != zBlank {
446				return zp.setParseError("no blank after $ORIGIN-directive", l)
447			}
448
449			st = zExpectDirOrigin
450		case zExpectDirOrigin:
451			if l.value != zString {
452				return zp.setParseError("expecting $ORIGIN value, not this...", l)
453			}
454
455			if err := slurpRemainder(zp.c); err != nil {
456				return zp.setParseError(err.err, err.lex)
457			}
458
459			name, ok := toAbsoluteName(l.token, zp.origin)
460			if !ok {
461				return zp.setParseError("bad origin name", l)
462			}
463
464			zp.origin = name
465
466			st = zExpectOwnerDir
467		case zExpectDirGenerateBl:
468			if l.value != zBlank {
469				return zp.setParseError("no blank after $GENERATE-directive", l)
470			}
471
472			st = zExpectDirGenerate
473		case zExpectDirGenerate:
474			if zp.generateDisallowed {
475				return zp.setParseError("nested $GENERATE directive not allowed", l)
476			}
477			if l.value != zString {
478				return zp.setParseError("expecting $GENERATE value, not this...", l)
479			}
480
481			return zp.generate(l)
482		case zExpectOwnerBl:
483			if l.value != zBlank {
484				return zp.setParseError("no blank after owner", l)
485			}
486
487			st = zExpectAny
488		case zExpectAny:
489			switch l.value {
490			case zRrtpe:
491				if zp.defttl == nil {
492					return zp.setParseError("missing TTL with no previous value", l)
493				}
494
495				h.Rrtype = l.torc
496
497				st = zExpectRdata
498			case zClass:
499				h.Class = l.torc
500
501				st = zExpectAnyNoClassBl
502			case zString:
503				ttl, ok := stringToTTL(l.token)
504				if !ok {
505					return zp.setParseError("not a TTL", l)
506				}
507
508				h.Ttl = ttl
509
510				if zp.defttl == nil || !zp.defttl.isByDirective {
511					zp.defttl = &ttlState{ttl, false}
512				}
513
514				st = zExpectAnyNoTTLBl
515			default:
516				return zp.setParseError("expecting RR type, TTL or class, not this...", l)
517			}
518		case zExpectAnyNoClassBl:
519			if l.value != zBlank {
520				return zp.setParseError("no blank before class", l)
521			}
522
523			st = zExpectAnyNoClass
524		case zExpectAnyNoTTLBl:
525			if l.value != zBlank {
526				return zp.setParseError("no blank before TTL", l)
527			}
528
529			st = zExpectAnyNoTTL
530		case zExpectAnyNoTTL:
531			switch l.value {
532			case zClass:
533				h.Class = l.torc
534
535				st = zExpectRrtypeBl
536			case zRrtpe:
537				h.Rrtype = l.torc
538
539				st = zExpectRdata
540			default:
541				return zp.setParseError("expecting RR type or class, not this...", l)
542			}
543		case zExpectAnyNoClass:
544			switch l.value {
545			case zString:
546				ttl, ok := stringToTTL(l.token)
547				if !ok {
548					return zp.setParseError("not a TTL", l)
549				}
550
551				h.Ttl = ttl
552
553				if zp.defttl == nil || !zp.defttl.isByDirective {
554					zp.defttl = &ttlState{ttl, false}
555				}
556
557				st = zExpectRrtypeBl
558			case zRrtpe:
559				h.Rrtype = l.torc
560
561				st = zExpectRdata
562			default:
563				return zp.setParseError("expecting RR type or TTL, not this...", l)
564			}
565		case zExpectRrtypeBl:
566			if l.value != zBlank {
567				return zp.setParseError("no blank before RR type", l)
568			}
569
570			st = zExpectRrtype
571		case zExpectRrtype:
572			if l.value != zRrtpe {
573				return zp.setParseError("unknown RR type", l)
574			}
575
576			h.Rrtype = l.torc
577
578			st = zExpectRdata
579		case zExpectRdata:
580			var (
581				rr             RR
582				parseAsRFC3597 bool
583			)
584			if newFn, ok := TypeToRR[h.Rrtype]; ok {
585				rr = newFn()
586				*rr.Header() = *h
587
588				// We may be parsing a known RR type using the RFC3597 format.
589				// If so, we handle that here in a generic way.
590				//
591				// This is also true for PrivateRR types which will have the
592				// RFC3597 parsing done for them and the Unpack method called
593				// to populate the RR instead of simply deferring to Parse.
594				if zp.c.Peek().token == "\\#" {
595					parseAsRFC3597 = true
596				}
597			} else {
598				rr = &RFC3597{Hdr: *h}
599			}
600
601			_, isPrivate := rr.(*PrivateRR)
602			if !isPrivate && zp.c.Peek().token == "" {
603				// This is a dynamic update rr.
604
605				// TODO(tmthrgd): Previously slurpRemainder was only called
606				// for certain RR types, which may have been important.
607				if err := slurpRemainder(zp.c); err != nil {
608					return zp.setParseError(err.err, err.lex)
609				}
610
611				return rr, true
612			} else if l.value == zNewline {
613				return zp.setParseError("unexpected newline", l)
614			}
615
616			parseAsRR := rr
617			if parseAsRFC3597 {
618				parseAsRR = &RFC3597{Hdr: *h}
619			}
620
621			if err := parseAsRR.parse(zp.c, zp.origin); err != nil {
622				// err is a concrete *ParseError without the file field set.
623				// The setParseError call below will construct a new
624				// *ParseError with file set to zp.file.
625
626				// err.lex may be nil in which case we substitute our current
627				// lex token.
628				if err.lex == (lex{}) {
629					return zp.setParseError(err.err, l)
630				}
631
632				return zp.setParseError(err.err, err.lex)
633			}
634
635			if parseAsRFC3597 {
636				err := parseAsRR.(*RFC3597).fromRFC3597(rr)
637				if err != nil {
638					return zp.setParseError(err.Error(), l)
639				}
640			}
641
642			return rr, true
643		}
644	}
645
646	// If we get here, we and the h.Rrtype is still zero, we haven't parsed anything, this
647	// is not an error, because an empty zone file is still a zone file.
648	return nil, false
649}
650
651type zlexer struct {
652	br io.ByteReader
653
654	readErr error
655
656	line   int
657	column int
658
659	comBuf  string
660	comment string
661
662	l       lex
663	cachedL *lex
664
665	brace  int
666	quote  bool
667	space  bool
668	commt  bool
669	rrtype bool
670	owner  bool
671
672	nextL bool
673
674	eol bool // end-of-line
675}
676
677func newZLexer(r io.Reader) *zlexer {
678	br, ok := r.(io.ByteReader)
679	if !ok {
680		br = bufio.NewReaderSize(r, 1024)
681	}
682
683	return &zlexer{
684		br: br,
685
686		line: 1,
687
688		owner: true,
689	}
690}
691
692func (zl *zlexer) Err() error {
693	if zl.readErr == io.EOF {
694		return nil
695	}
696
697	return zl.readErr
698}
699
700// readByte returns the next byte from the input
701func (zl *zlexer) readByte() (byte, bool) {
702	if zl.readErr != nil {
703		return 0, false
704	}
705
706	c, err := zl.br.ReadByte()
707	if err != nil {
708		zl.readErr = err
709		return 0, false
710	}
711
712	// delay the newline handling until the next token is delivered,
713	// fixes off-by-one errors when reporting a parse error.
714	if zl.eol {
715		zl.line++
716		zl.column = 0
717		zl.eol = false
718	}
719
720	if c == '\n' {
721		zl.eol = true
722	} else {
723		zl.column++
724	}
725
726	return c, true
727}
728
729func (zl *zlexer) Peek() lex {
730	if zl.nextL {
731		return zl.l
732	}
733
734	l, ok := zl.Next()
735	if !ok {
736		return l
737	}
738
739	if zl.nextL {
740		// Cache l. Next returns zl.cachedL then zl.l.
741		zl.cachedL = &l
742	} else {
743		// In this case l == zl.l, so we just tell Next to return zl.l.
744		zl.nextL = true
745	}
746
747	return l
748}
749
750func (zl *zlexer) Next() (lex, bool) {
751	l := &zl.l
752	switch {
753	case zl.cachedL != nil:
754		l, zl.cachedL = zl.cachedL, nil
755		return *l, true
756	case zl.nextL:
757		zl.nextL = false
758		return *l, true
759	case l.err:
760		// Parsing errors should be sticky.
761		return lex{value: zEOF}, false
762	}
763
764	var (
765		str [maxTok]byte // Hold string text
766		com [maxTok]byte // Hold comment text
767
768		stri int // Offset in str (0 means empty)
769		comi int // Offset in com (0 means empty)
770
771		escape bool
772	)
773
774	if zl.comBuf != "" {
775		comi = copy(com[:], zl.comBuf)
776		zl.comBuf = ""
777	}
778
779	zl.comment = ""
780
781	for x, ok := zl.readByte(); ok; x, ok = zl.readByte() {
782		l.line, l.column = zl.line, zl.column
783
784		if stri >= len(str) {
785			l.token = "token length insufficient for parsing"
786			l.err = true
787			return *l, true
788		}
789		if comi >= len(com) {
790			l.token = "comment length insufficient for parsing"
791			l.err = true
792			return *l, true
793		}
794
795		switch x {
796		case ' ', '\t':
797			if escape || zl.quote {
798				// Inside quotes or escaped this is legal.
799				str[stri] = x
800				stri++
801
802				escape = false
803				break
804			}
805
806			if zl.commt {
807				com[comi] = x
808				comi++
809				break
810			}
811
812			var retL lex
813			if stri == 0 {
814				// Space directly in the beginning, handled in the grammar
815			} else if zl.owner {
816				// If we have a string and its the first, make it an owner
817				l.value = zOwner
818				l.token = string(str[:stri])
819
820				// escape $... start with a \ not a $, so this will work
821				switch strings.ToUpper(l.token) {
822				case "$TTL":
823					l.value = zDirTTL
824				case "$ORIGIN":
825					l.value = zDirOrigin
826				case "$INCLUDE":
827					l.value = zDirInclude
828				case "$GENERATE":
829					l.value = zDirGenerate
830				}
831
832				retL = *l
833			} else {
834				l.value = zString
835				l.token = string(str[:stri])
836
837				if !zl.rrtype {
838					tokenUpper := strings.ToUpper(l.token)
839					if t, ok := StringToType[tokenUpper]; ok {
840						l.value = zRrtpe
841						l.torc = t
842
843						zl.rrtype = true
844					} else if strings.HasPrefix(tokenUpper, "TYPE") {
845						t, ok := typeToInt(l.token)
846						if !ok {
847							l.token = "unknown RR type"
848							l.err = true
849							return *l, true
850						}
851
852						l.value = zRrtpe
853						l.torc = t
854
855						zl.rrtype = true
856					}
857
858					if t, ok := StringToClass[tokenUpper]; ok {
859						l.value = zClass
860						l.torc = t
861					} else if strings.HasPrefix(tokenUpper, "CLASS") {
862						t, ok := classToInt(l.token)
863						if !ok {
864							l.token = "unknown class"
865							l.err = true
866							return *l, true
867						}
868
869						l.value = zClass
870						l.torc = t
871					}
872				}
873
874				retL = *l
875			}
876
877			zl.owner = false
878
879			if !zl.space {
880				zl.space = true
881
882				l.value = zBlank
883				l.token = " "
884
885				if retL == (lex{}) {
886					return *l, true
887				}
888
889				zl.nextL = true
890			}
891
892			if retL != (lex{}) {
893				return retL, true
894			}
895		case ';':
896			if escape || zl.quote {
897				// Inside quotes or escaped this is legal.
898				str[stri] = x
899				stri++
900
901				escape = false
902				break
903			}
904
905			zl.commt = true
906			zl.comBuf = ""
907
908			if comi > 1 {
909				// A newline was previously seen inside a comment that
910				// was inside braces and we delayed adding it until now.
911				com[comi] = ' ' // convert newline to space
912				comi++
913				if comi >= len(com) {
914					l.token = "comment length insufficient for parsing"
915					l.err = true
916					return *l, true
917				}
918			}
919
920			com[comi] = ';'
921			comi++
922
923			if stri > 0 {
924				zl.comBuf = string(com[:comi])
925
926				l.value = zString
927				l.token = string(str[:stri])
928				return *l, true
929			}
930		case '\r':
931			escape = false
932
933			if zl.quote {
934				str[stri] = x
935				stri++
936			}
937
938			// discard if outside of quotes
939		case '\n':
940			escape = false
941
942			// Escaped newline
943			if zl.quote {
944				str[stri] = x
945				stri++
946				break
947			}
948
949			if zl.commt {
950				// Reset a comment
951				zl.commt = false
952				zl.rrtype = false
953
954				// If not in a brace this ends the comment AND the RR
955				if zl.brace == 0 {
956					zl.owner = true
957
958					l.value = zNewline
959					l.token = "\n"
960					zl.comment = string(com[:comi])
961					return *l, true
962				}
963
964				zl.comBuf = string(com[:comi])
965				break
966			}
967
968			if zl.brace == 0 {
969				// If there is previous text, we should output it here
970				var retL lex
971				if stri != 0 {
972					l.value = zString
973					l.token = string(str[:stri])
974
975					if !zl.rrtype {
976						tokenUpper := strings.ToUpper(l.token)
977						if t, ok := StringToType[tokenUpper]; ok {
978							zl.rrtype = true
979
980							l.value = zRrtpe
981							l.torc = t
982						}
983					}
984
985					retL = *l
986				}
987
988				l.value = zNewline
989				l.token = "\n"
990
991				zl.comment = zl.comBuf
992				zl.comBuf = ""
993				zl.rrtype = false
994				zl.owner = true
995
996				if retL != (lex{}) {
997					zl.nextL = true
998					return retL, true
999				}
1000
1001				return *l, true
1002			}
1003		case '\\':
1004			// comments do not get escaped chars, everything is copied
1005			if zl.commt {
1006				com[comi] = x
1007				comi++
1008				break
1009			}
1010
1011			// something already escaped must be in string
1012			if escape {
1013				str[stri] = x
1014				stri++
1015
1016				escape = false
1017				break
1018			}
1019
1020			// something escaped outside of string gets added to string
1021			str[stri] = x
1022			stri++
1023
1024			escape = true
1025		case '"':
1026			if zl.commt {
1027				com[comi] = x
1028				comi++
1029				break
1030			}
1031
1032			if escape {
1033				str[stri] = x
1034				stri++
1035
1036				escape = false
1037				break
1038			}
1039
1040			zl.space = false
1041
1042			// send previous gathered text and the quote
1043			var retL lex
1044			if stri != 0 {
1045				l.value = zString
1046				l.token = string(str[:stri])
1047
1048				retL = *l
1049			}
1050
1051			// send quote itself as separate token
1052			l.value = zQuote
1053			l.token = "\""
1054
1055			zl.quote = !zl.quote
1056
1057			if retL != (lex{}) {
1058				zl.nextL = true
1059				return retL, true
1060			}
1061
1062			return *l, true
1063		case '(', ')':
1064			if zl.commt {
1065				com[comi] = x
1066				comi++
1067				break
1068			}
1069
1070			if escape || zl.quote {
1071				// Inside quotes or escaped this is legal.
1072				str[stri] = x
1073				stri++
1074
1075				escape = false
1076				break
1077			}
1078
1079			switch x {
1080			case ')':
1081				zl.brace--
1082
1083				if zl.brace < 0 {
1084					l.token = "extra closing brace"
1085					l.err = true
1086					return *l, true
1087				}
1088			case '(':
1089				zl.brace++
1090			}
1091		default:
1092			escape = false
1093
1094			if zl.commt {
1095				com[comi] = x
1096				comi++
1097				break
1098			}
1099
1100			str[stri] = x
1101			stri++
1102
1103			zl.space = false
1104		}
1105	}
1106
1107	if zl.readErr != nil && zl.readErr != io.EOF {
1108		// Don't return any tokens after a read error occurs.
1109		return lex{value: zEOF}, false
1110	}
1111
1112	var retL lex
1113	if stri > 0 {
1114		// Send remainder of str
1115		l.value = zString
1116		l.token = string(str[:stri])
1117		retL = *l
1118
1119		if comi <= 0 {
1120			return retL, true
1121		}
1122	}
1123
1124	if comi > 0 {
1125		// Send remainder of com
1126		l.value = zNewline
1127		l.token = "\n"
1128		zl.comment = string(com[:comi])
1129
1130		if retL != (lex{}) {
1131			zl.nextL = true
1132			return retL, true
1133		}
1134
1135		return *l, true
1136	}
1137
1138	if zl.brace != 0 {
1139		l.token = "unbalanced brace"
1140		l.err = true
1141		return *l, true
1142	}
1143
1144	return lex{value: zEOF}, false
1145}
1146
1147func (zl *zlexer) Comment() string {
1148	if zl.l.err {
1149		return ""
1150	}
1151
1152	return zl.comment
1153}
1154
1155// Extract the class number from CLASSxx
1156func classToInt(token string) (uint16, bool) {
1157	offset := 5
1158	if len(token) < offset+1 {
1159		return 0, false
1160	}
1161	class, err := strconv.ParseUint(token[offset:], 10, 16)
1162	if err != nil {
1163		return 0, false
1164	}
1165	return uint16(class), true
1166}
1167
1168// Extract the rr number from TYPExxx
1169func typeToInt(token string) (uint16, bool) {
1170	offset := 4
1171	if len(token) < offset+1 {
1172		return 0, false
1173	}
1174	typ, err := strconv.ParseUint(token[offset:], 10, 16)
1175	if err != nil {
1176		return 0, false
1177	}
1178	return uint16(typ), true
1179}
1180
1181// stringToTTL parses things like 2w, 2m, etc, and returns the time in seconds.
1182func stringToTTL(token string) (uint32, bool) {
1183	var s, i uint32
1184	for _, c := range token {
1185		switch c {
1186		case 's', 'S':
1187			s += i
1188			i = 0
1189		case 'm', 'M':
1190			s += i * 60
1191			i = 0
1192		case 'h', 'H':
1193			s += i * 60 * 60
1194			i = 0
1195		case 'd', 'D':
1196			s += i * 60 * 60 * 24
1197			i = 0
1198		case 'w', 'W':
1199			s += i * 60 * 60 * 24 * 7
1200			i = 0
1201		case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
1202			i *= 10
1203			i += uint32(c) - '0'
1204		default:
1205			return 0, false
1206		}
1207	}
1208	return s + i, true
1209}
1210
1211// Parse LOC records' <digits>[.<digits>][mM] into a
1212// mantissa exponent format. Token should contain the entire
1213// string (i.e. no spaces allowed)
1214func stringToCm(token string) (e, m uint8, ok bool) {
1215	if token[len(token)-1] == 'M' || token[len(token)-1] == 'm' {
1216		token = token[0 : len(token)-1]
1217	}
1218	s := strings.SplitN(token, ".", 2)
1219	var meters, cmeters, val int
1220	var err error
1221	switch len(s) {
1222	case 2:
1223		if cmeters, err = strconv.Atoi(s[1]); err != nil {
1224			return
1225		}
1226		// There's no point in having more than 2 digits in this part, and would rather make the implementation complicated ('123' should be treated as '12').
1227		// So we simply reject it.
1228		// We also make sure the first character is a digit to reject '+-' signs.
1229		if len(s[1]) > 2 || s[1][0] < '0' || s[1][0] > '9' {
1230			return
1231		}
1232		if len(s[1]) == 1 {
1233			// 'nn.1' must be treated as 'nn-meters and 10cm, not 1cm.
1234			cmeters *= 10
1235		}
1236		if len(s[0]) == 0 {
1237			// This will allow omitting the 'meter' part, like .01 (meaning 0.01m = 1cm).
1238			break
1239		}
1240		fallthrough
1241	case 1:
1242		if meters, err = strconv.Atoi(s[0]); err != nil {
1243			return
1244		}
1245		// RFC1876 states the max value is 90000000.00.  The latter two conditions enforce it.
1246		if s[0][0] < '0' || s[0][0] > '9' || meters > 90000000 || (meters == 90000000 && cmeters != 0) {
1247			return
1248		}
1249	case 0:
1250		// huh?
1251		return 0, 0, false
1252	}
1253	ok = true
1254	if meters > 0 {
1255		e = 2
1256		val = meters
1257	} else {
1258		e = 0
1259		val = cmeters
1260	}
1261	for val >= 10 {
1262		e++
1263		val /= 10
1264	}
1265	m = uint8(val)
1266	return
1267}
1268
1269func toAbsoluteName(name, origin string) (absolute string, ok bool) {
1270	// check for an explicit origin reference
1271	if name == "@" {
1272		// require a nonempty origin
1273		if origin == "" {
1274			return "", false
1275		}
1276		return origin, true
1277	}
1278
1279	// require a valid domain name
1280	_, ok = IsDomainName(name)
1281	if !ok || name == "" {
1282		return "", false
1283	}
1284
1285	// check if name is already absolute
1286	if IsFqdn(name) {
1287		return name, true
1288	}
1289
1290	// require a nonempty origin
1291	if origin == "" {
1292		return "", false
1293	}
1294	return appendOrigin(name, origin), true
1295}
1296
1297func appendOrigin(name, origin string) string {
1298	if origin == "." {
1299		return name + origin
1300	}
1301	return name + "." + origin
1302}
1303
1304// LOC record helper function
1305func locCheckNorth(token string, latitude uint32) (uint32, bool) {
1306	if latitude > 90*1000*60*60 {
1307		return latitude, false
1308	}
1309	switch token {
1310	case "n", "N":
1311		return LOC_EQUATOR + latitude, true
1312	case "s", "S":
1313		return LOC_EQUATOR - latitude, true
1314	}
1315	return latitude, false
1316}
1317
1318// LOC record helper function
1319func locCheckEast(token string, longitude uint32) (uint32, bool) {
1320	if longitude > 180*1000*60*60 {
1321		return longitude, false
1322	}
1323	switch token {
1324	case "e", "E":
1325		return LOC_EQUATOR + longitude, true
1326	case "w", "W":
1327		return LOC_EQUATOR - longitude, true
1328	}
1329	return longitude, false
1330}
1331
1332// "Eat" the rest of the "line"
1333func slurpRemainder(c *zlexer) *ParseError {
1334	l, _ := c.Next()
1335	switch l.value {
1336	case zBlank:
1337		l, _ = c.Next()
1338		if l.value != zNewline && l.value != zEOF {
1339			return &ParseError{"", "garbage after rdata", l}
1340		}
1341	case zNewline:
1342	case zEOF:
1343	default:
1344		return &ParseError{"", "garbage after rdata", l}
1345	}
1346	return nil
1347}
1348
1349// Parse a 64 bit-like ipv6 address: "0014:4fff:ff20:ee64"
1350// Used for NID and L64 record.
1351func stringToNodeID(l lex) (uint64, *ParseError) {
1352	if len(l.token) < 19 {
1353		return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l}
1354	}
1355	// There must be three colons at fixes postitions, if not its a parse error
1356	if l.token[4] != ':' && l.token[9] != ':' && l.token[14] != ':' {
1357		return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l}
1358	}
1359	s := l.token[0:4] + l.token[5:9] + l.token[10:14] + l.token[15:19]
1360	u, err := strconv.ParseUint(s, 16, 64)
1361	if err != nil {
1362		return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l}
1363	}
1364	return u, nil
1365}
1366