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