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