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 rr RR
581			if newFn, ok := TypeToRR[h.Rrtype]; ok && canParseAsRR(h.Rrtype) {
582				rr = newFn()
583				*rr.Header() = *h
584			} else {
585				rr = &RFC3597{Hdr: *h}
586			}
587
588			_, isPrivate := rr.(*PrivateRR)
589			if !isPrivate && zp.c.Peek().token == "" {
590				// This is a dynamic update rr.
591
592				// TODO(tmthrgd): Previously slurpRemainder was only called
593				// for certain RR types, which may have been important.
594				if err := slurpRemainder(zp.c); err != nil {
595					return zp.setParseError(err.err, err.lex)
596				}
597
598				return rr, true
599			} else if l.value == zNewline {
600				return zp.setParseError("unexpected newline", l)
601			}
602
603			if err := rr.parse(zp.c, zp.origin); err != nil {
604				// err is a concrete *ParseError without the file field set.
605				// The setParseError call below will construct a new
606				// *ParseError with file set to zp.file.
607
608				// If err.lex is nil than we have encounter an unknown RR type
609				// in that case we substitute our current lex token.
610				if err.lex == (lex{}) {
611					return zp.setParseError(err.err, l)
612				}
613
614				return zp.setParseError(err.err, err.lex)
615			}
616
617			return rr, true
618		}
619	}
620
621	// If we get here, we and the h.Rrtype is still zero, we haven't parsed anything, this
622	// is not an error, because an empty zone file is still a zone file.
623	return nil, false
624}
625
626// canParseAsRR returns true if the record type can be parsed as a
627// concrete RR. It blacklists certain record types that must be parsed
628// according to RFC 3597 because they lack a presentation format.
629func canParseAsRR(rrtype uint16) bool {
630	switch rrtype {
631	case TypeANY, TypeNULL, TypeOPT, TypeTSIG:
632		return false
633	default:
634		return true
635	}
636}
637
638type zlexer struct {
639	br io.ByteReader
640
641	readErr error
642
643	line   int
644	column int
645
646	comBuf  string
647	comment string
648
649	l       lex
650	cachedL *lex
651
652	brace  int
653	quote  bool
654	space  bool
655	commt  bool
656	rrtype bool
657	owner  bool
658
659	nextL bool
660
661	eol bool // end-of-line
662}
663
664func newZLexer(r io.Reader) *zlexer {
665	br, ok := r.(io.ByteReader)
666	if !ok {
667		br = bufio.NewReaderSize(r, 1024)
668	}
669
670	return &zlexer{
671		br: br,
672
673		line: 1,
674
675		owner: true,
676	}
677}
678
679func (zl *zlexer) Err() error {
680	if zl.readErr == io.EOF {
681		return nil
682	}
683
684	return zl.readErr
685}
686
687// readByte returns the next byte from the input
688func (zl *zlexer) readByte() (byte, bool) {
689	if zl.readErr != nil {
690		return 0, false
691	}
692
693	c, err := zl.br.ReadByte()
694	if err != nil {
695		zl.readErr = err
696		return 0, false
697	}
698
699	// delay the newline handling until the next token is delivered,
700	// fixes off-by-one errors when reporting a parse error.
701	if zl.eol {
702		zl.line++
703		zl.column = 0
704		zl.eol = false
705	}
706
707	if c == '\n' {
708		zl.eol = true
709	} else {
710		zl.column++
711	}
712
713	return c, true
714}
715
716func (zl *zlexer) Peek() lex {
717	if zl.nextL {
718		return zl.l
719	}
720
721	l, ok := zl.Next()
722	if !ok {
723		return l
724	}
725
726	if zl.nextL {
727		// Cache l. Next returns zl.cachedL then zl.l.
728		zl.cachedL = &l
729	} else {
730		// In this case l == zl.l, so we just tell Next to return zl.l.
731		zl.nextL = true
732	}
733
734	return l
735}
736
737func (zl *zlexer) Next() (lex, bool) {
738	l := &zl.l
739	switch {
740	case zl.cachedL != nil:
741		l, zl.cachedL = zl.cachedL, nil
742		return *l, true
743	case zl.nextL:
744		zl.nextL = false
745		return *l, true
746	case l.err:
747		// Parsing errors should be sticky.
748		return lex{value: zEOF}, false
749	}
750
751	var (
752		str [maxTok]byte // Hold string text
753		com [maxTok]byte // Hold comment text
754
755		stri int // Offset in str (0 means empty)
756		comi int // Offset in com (0 means empty)
757
758		escape bool
759	)
760
761	if zl.comBuf != "" {
762		comi = copy(com[:], zl.comBuf)
763		zl.comBuf = ""
764	}
765
766	zl.comment = ""
767
768	for x, ok := zl.readByte(); ok; x, ok = zl.readByte() {
769		l.line, l.column = zl.line, zl.column
770
771		if stri >= len(str) {
772			l.token = "token length insufficient for parsing"
773			l.err = true
774			return *l, true
775		}
776		if comi >= len(com) {
777			l.token = "comment length insufficient for parsing"
778			l.err = true
779			return *l, true
780		}
781
782		switch x {
783		case ' ', '\t':
784			if escape || zl.quote {
785				// Inside quotes or escaped this is legal.
786				str[stri] = x
787				stri++
788
789				escape = false
790				break
791			}
792
793			if zl.commt {
794				com[comi] = x
795				comi++
796				break
797			}
798
799			var retL lex
800			if stri == 0 {
801				// Space directly in the beginning, handled in the grammar
802			} else if zl.owner {
803				// If we have a string and its the first, make it an owner
804				l.value = zOwner
805				l.token = string(str[:stri])
806
807				// escape $... start with a \ not a $, so this will work
808				switch strings.ToUpper(l.token) {
809				case "$TTL":
810					l.value = zDirTTL
811				case "$ORIGIN":
812					l.value = zDirOrigin
813				case "$INCLUDE":
814					l.value = zDirInclude
815				case "$GENERATE":
816					l.value = zDirGenerate
817				}
818
819				retL = *l
820			} else {
821				l.value = zString
822				l.token = string(str[:stri])
823
824				if !zl.rrtype {
825					tokenUpper := strings.ToUpper(l.token)
826					if t, ok := StringToType[tokenUpper]; ok {
827						l.value = zRrtpe
828						l.torc = t
829
830						zl.rrtype = true
831					} else if strings.HasPrefix(tokenUpper, "TYPE") {
832						t, ok := typeToInt(l.token)
833						if !ok {
834							l.token = "unknown RR type"
835							l.err = true
836							return *l, true
837						}
838
839						l.value = zRrtpe
840						l.torc = t
841
842						zl.rrtype = true
843					}
844
845					if t, ok := StringToClass[tokenUpper]; ok {
846						l.value = zClass
847						l.torc = t
848					} else if strings.HasPrefix(tokenUpper, "CLASS") {
849						t, ok := classToInt(l.token)
850						if !ok {
851							l.token = "unknown class"
852							l.err = true
853							return *l, true
854						}
855
856						l.value = zClass
857						l.torc = t
858					}
859				}
860
861				retL = *l
862			}
863
864			zl.owner = false
865
866			if !zl.space {
867				zl.space = true
868
869				l.value = zBlank
870				l.token = " "
871
872				if retL == (lex{}) {
873					return *l, true
874				}
875
876				zl.nextL = true
877			}
878
879			if retL != (lex{}) {
880				return retL, true
881			}
882		case ';':
883			if escape || zl.quote {
884				// Inside quotes or escaped this is legal.
885				str[stri] = x
886				stri++
887
888				escape = false
889				break
890			}
891
892			zl.commt = true
893			zl.comBuf = ""
894
895			if comi > 1 {
896				// A newline was previously seen inside a comment that
897				// was inside braces and we delayed adding it until now.
898				com[comi] = ' ' // convert newline to space
899				comi++
900				if comi >= len(com) {
901					l.token = "comment length insufficient for parsing"
902					l.err = true
903					return *l, true
904				}
905			}
906
907			com[comi] = ';'
908			comi++
909
910			if stri > 0 {
911				zl.comBuf = string(com[:comi])
912
913				l.value = zString
914				l.token = string(str[:stri])
915				return *l, true
916			}
917		case '\r':
918			escape = false
919
920			if zl.quote {
921				str[stri] = x
922				stri++
923			}
924
925			// discard if outside of quotes
926		case '\n':
927			escape = false
928
929			// Escaped newline
930			if zl.quote {
931				str[stri] = x
932				stri++
933				break
934			}
935
936			if zl.commt {
937				// Reset a comment
938				zl.commt = false
939				zl.rrtype = false
940
941				// If not in a brace this ends the comment AND the RR
942				if zl.brace == 0 {
943					zl.owner = true
944
945					l.value = zNewline
946					l.token = "\n"
947					zl.comment = string(com[:comi])
948					return *l, true
949				}
950
951				zl.comBuf = string(com[:comi])
952				break
953			}
954
955			if zl.brace == 0 {
956				// If there is previous text, we should output it here
957				var retL lex
958				if stri != 0 {
959					l.value = zString
960					l.token = string(str[:stri])
961
962					if !zl.rrtype {
963						tokenUpper := strings.ToUpper(l.token)
964						if t, ok := StringToType[tokenUpper]; ok {
965							zl.rrtype = true
966
967							l.value = zRrtpe
968							l.torc = t
969						}
970					}
971
972					retL = *l
973				}
974
975				l.value = zNewline
976				l.token = "\n"
977
978				zl.comment = zl.comBuf
979				zl.comBuf = ""
980				zl.rrtype = false
981				zl.owner = true
982
983				if retL != (lex{}) {
984					zl.nextL = true
985					return retL, true
986				}
987
988				return *l, true
989			}
990		case '\\':
991			// comments do not get escaped chars, everything is copied
992			if zl.commt {
993				com[comi] = x
994				comi++
995				break
996			}
997
998			// something already escaped must be in string
999			if escape {
1000				str[stri] = x
1001				stri++
1002
1003				escape = false
1004				break
1005			}
1006
1007			// something escaped outside of string gets added to string
1008			str[stri] = x
1009			stri++
1010
1011			escape = true
1012		case '"':
1013			if zl.commt {
1014				com[comi] = x
1015				comi++
1016				break
1017			}
1018
1019			if escape {
1020				str[stri] = x
1021				stri++
1022
1023				escape = false
1024				break
1025			}
1026
1027			zl.space = false
1028
1029			// send previous gathered text and the quote
1030			var retL lex
1031			if stri != 0 {
1032				l.value = zString
1033				l.token = string(str[:stri])
1034
1035				retL = *l
1036			}
1037
1038			// send quote itself as separate token
1039			l.value = zQuote
1040			l.token = "\""
1041
1042			zl.quote = !zl.quote
1043
1044			if retL != (lex{}) {
1045				zl.nextL = true
1046				return retL, true
1047			}
1048
1049			return *l, true
1050		case '(', ')':
1051			if zl.commt {
1052				com[comi] = x
1053				comi++
1054				break
1055			}
1056
1057			if escape || zl.quote {
1058				// Inside quotes or escaped this is legal.
1059				str[stri] = x
1060				stri++
1061
1062				escape = false
1063				break
1064			}
1065
1066			switch x {
1067			case ')':
1068				zl.brace--
1069
1070				if zl.brace < 0 {
1071					l.token = "extra closing brace"
1072					l.err = true
1073					return *l, true
1074				}
1075			case '(':
1076				zl.brace++
1077			}
1078		default:
1079			escape = false
1080
1081			if zl.commt {
1082				com[comi] = x
1083				comi++
1084				break
1085			}
1086
1087			str[stri] = x
1088			stri++
1089
1090			zl.space = false
1091		}
1092	}
1093
1094	if zl.readErr != nil && zl.readErr != io.EOF {
1095		// Don't return any tokens after a read error occurs.
1096		return lex{value: zEOF}, false
1097	}
1098
1099	var retL lex
1100	if stri > 0 {
1101		// Send remainder of str
1102		l.value = zString
1103		l.token = string(str[:stri])
1104		retL = *l
1105
1106		if comi <= 0 {
1107			return retL, true
1108		}
1109	}
1110
1111	if comi > 0 {
1112		// Send remainder of com
1113		l.value = zNewline
1114		l.token = "\n"
1115		zl.comment = string(com[:comi])
1116
1117		if retL != (lex{}) {
1118			zl.nextL = true
1119			return retL, true
1120		}
1121
1122		return *l, true
1123	}
1124
1125	if zl.brace != 0 {
1126		l.token = "unbalanced brace"
1127		l.err = true
1128		return *l, true
1129	}
1130
1131	return lex{value: zEOF}, false
1132}
1133
1134func (zl *zlexer) Comment() string {
1135	if zl.l.err {
1136		return ""
1137	}
1138
1139	return zl.comment
1140}
1141
1142// Extract the class number from CLASSxx
1143func classToInt(token string) (uint16, bool) {
1144	offset := 5
1145	if len(token) < offset+1 {
1146		return 0, false
1147	}
1148	class, err := strconv.ParseUint(token[offset:], 10, 16)
1149	if err != nil {
1150		return 0, false
1151	}
1152	return uint16(class), true
1153}
1154
1155// Extract the rr number from TYPExxx
1156func typeToInt(token string) (uint16, bool) {
1157	offset := 4
1158	if len(token) < offset+1 {
1159		return 0, false
1160	}
1161	typ, err := strconv.ParseUint(token[offset:], 10, 16)
1162	if err != nil {
1163		return 0, false
1164	}
1165	return uint16(typ), true
1166}
1167
1168// stringToTTL parses things like 2w, 2m, etc, and returns the time in seconds.
1169func stringToTTL(token string) (uint32, bool) {
1170	var s, i uint32
1171	for _, c := range token {
1172		switch c {
1173		case 's', 'S':
1174			s += i
1175			i = 0
1176		case 'm', 'M':
1177			s += i * 60
1178			i = 0
1179		case 'h', 'H':
1180			s += i * 60 * 60
1181			i = 0
1182		case 'd', 'D':
1183			s += i * 60 * 60 * 24
1184			i = 0
1185		case 'w', 'W':
1186			s += i * 60 * 60 * 24 * 7
1187			i = 0
1188		case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
1189			i *= 10
1190			i += uint32(c) - '0'
1191		default:
1192			return 0, false
1193		}
1194	}
1195	return s + i, true
1196}
1197
1198// Parse LOC records' <digits>[.<digits>][mM] into a
1199// mantissa exponent format. Token should contain the entire
1200// string (i.e. no spaces allowed)
1201func stringToCm(token string) (e, m uint8, ok bool) {
1202	if token[len(token)-1] == 'M' || token[len(token)-1] == 'm' {
1203		token = token[0 : len(token)-1]
1204	}
1205	s := strings.SplitN(token, ".", 2)
1206	var meters, cmeters, val int
1207	var err error
1208	switch len(s) {
1209	case 2:
1210		if cmeters, err = strconv.Atoi(s[1]); err != nil {
1211			return
1212		}
1213		// 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').
1214		// So we simply reject it.
1215		// We also make sure the first character is a digit to reject '+-' signs.
1216		if len(s[1]) > 2 || s[1][0] < '0' || s[1][0] > '9' {
1217			return
1218		}
1219		if len(s[1]) == 1 {
1220			// 'nn.1' must be treated as 'nn-meters and 10cm, not 1cm.
1221			cmeters *= 10
1222		}
1223		if len(s[0]) == 0 {
1224			// This will allow omitting the 'meter' part, like .01 (meaning 0.01m = 1cm).
1225			break
1226		}
1227		fallthrough
1228	case 1:
1229		if meters, err = strconv.Atoi(s[0]); err != nil {
1230			return
1231		}
1232		// RFC1876 states the max value is 90000000.00.  The latter two conditions enforce it.
1233		if s[0][0] < '0' || s[0][0] > '9' || meters > 90000000 || (meters == 90000000 && cmeters != 0) {
1234			return
1235		}
1236	case 0:
1237		// huh?
1238		return 0, 0, false
1239	}
1240	ok = true
1241	if meters > 0 {
1242		e = 2
1243		val = meters
1244	} else {
1245		e = 0
1246		val = cmeters
1247	}
1248	for val >= 10 {
1249		e++
1250		val /= 10
1251	}
1252	m = uint8(val)
1253	return
1254}
1255
1256func toAbsoluteName(name, origin string) (absolute string, ok bool) {
1257	// check for an explicit origin reference
1258	if name == "@" {
1259		// require a nonempty origin
1260		if origin == "" {
1261			return "", false
1262		}
1263		return origin, true
1264	}
1265
1266	// require a valid domain name
1267	_, ok = IsDomainName(name)
1268	if !ok || name == "" {
1269		return "", false
1270	}
1271
1272	// check if name is already absolute
1273	if IsFqdn(name) {
1274		return name, true
1275	}
1276
1277	// require a nonempty origin
1278	if origin == "" {
1279		return "", false
1280	}
1281	return appendOrigin(name, origin), true
1282}
1283
1284func appendOrigin(name, origin string) string {
1285	if origin == "." {
1286		return name + origin
1287	}
1288	return name + "." + origin
1289}
1290
1291// LOC record helper function
1292func locCheckNorth(token string, latitude uint32) (uint32, bool) {
1293	if latitude > 90 * 1000 * 60 * 60 {
1294		return latitude, false
1295	}
1296	switch token {
1297	case "n", "N":
1298		return LOC_EQUATOR + latitude, true
1299	case "s", "S":
1300		return LOC_EQUATOR - latitude, true
1301	}
1302	return latitude, false
1303}
1304
1305// LOC record helper function
1306func locCheckEast(token string, longitude uint32) (uint32, bool) {
1307	if longitude > 180 * 1000 * 60 * 60 {
1308		return longitude, false
1309	}
1310	switch token {
1311	case "e", "E":
1312		return LOC_EQUATOR + longitude, true
1313	case "w", "W":
1314		return LOC_EQUATOR - longitude, true
1315	}
1316	return longitude, false
1317}
1318
1319// "Eat" the rest of the "line"
1320func slurpRemainder(c *zlexer) *ParseError {
1321	l, _ := c.Next()
1322	switch l.value {
1323	case zBlank:
1324		l, _ = c.Next()
1325		if l.value != zNewline && l.value != zEOF {
1326			return &ParseError{"", "garbage after rdata", l}
1327		}
1328	case zNewline:
1329	case zEOF:
1330	default:
1331		return &ParseError{"", "garbage after rdata", l}
1332	}
1333	return nil
1334}
1335
1336// Parse a 64 bit-like ipv6 address: "0014:4fff:ff20:ee64"
1337// Used for NID and L64 record.
1338func stringToNodeID(l lex) (uint64, *ParseError) {
1339	if len(l.token) < 19 {
1340		return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l}
1341	}
1342	// There must be three colons at fixes postitions, if not its a parse error
1343	if l.token[4] != ':' && l.token[9] != ':' && l.token[14] != ':' {
1344		return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l}
1345	}
1346	s := l.token[0:4] + l.token[5:9] + l.token[10:14] + l.token[15:19]
1347	u, err := strconv.ParseUint(s, 16, 64)
1348	if err != nil {
1349		return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l}
1350	}
1351	return u, nil
1352}
1353