...

Source file src/github.com/miekg/dns/scan.go

Documentation: github.com/miekg/dns

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

View as plain text