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