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