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