dns/scan.go

1008 lines
24 KiB
Go
Raw Normal View History

2011-12-14 19:26:31 +11:00
package dns
2011-12-14 19:00:39 +11:00
import (
"fmt"
2011-12-14 19:26:31 +11:00
"io"
"os"
"path/filepath"
2011-12-14 19:00:39 +11:00
"strconv"
"strings"
)
const maxTok = 2048 // Largest token we can return.
const maxUint16 = 1<<16 - 1
2011-12-16 20:30:42 +11:00
2011-12-14 19:00:39 +11:00
// Tokinize a RFC 1035 zone file. The tokenizer will normalize it:
2011-12-15 22:27:05 +11:00
// * Add ownernames if they are left blank;
2011-12-14 19:00:39 +11:00
// * Suppress sequences of spaces;
// * Make each RR fit on one line (_NEWLINE is send as last)
2011-12-14 19:00:39 +11:00
// * Handle comments: ;
// * Handle braces - anywhere.
2011-12-14 19:00:39 +11:00
const (
2011-12-16 08:40:07 +11:00
// Zonefile
2015-02-19 21:45:59 +11:00
zEOF = iota
zString
zBlank
zQuote
zNewline
zRrtpe
zOwner
zClass
zDirOrigin // $ORIGIN
zDirTTL // $TTL
2015-02-19 21:45:59 +11:00
zDirInclude // $INCLUDE
zDirGenerate // $GENERATE
2011-12-14 19:00:39 +11:00
2011-12-16 08:40:07 +11:00
// Privatekey file
2015-02-19 21:45:59 +11:00
zValue
zKey
2015-02-19 21:59:15 +11:00
zExpectOwnerDir // Ownername
zExpectOwnerBl // Whitespace after the ownername
zExpectAny // Expect rrtype, ttl or class
zExpectAnyNoClass // Expect rrtype or ttl
zExpectAnyNoClassBl // The whitespace after _EXPECT_ANY_NOCLASS
zExpectAnyNoTTL // Expect rrtype or class
zExpectAnyNoTTLBl // Whitespace after _EXPECT_ANY_NOTTL
2015-02-19 21:59:15 +11:00
zExpectRrtype // Expect rrtype
zExpectRrtypeBl // Whitespace BEFORE rrtype
zExpectRdata // The first element of the rdata
zExpectDirTTLBl // Space after directive $TTL
zExpectDirTTL // Directive $TTL
2015-02-19 21:59:15 +11:00
zExpectDirOriginBl // Space after directive $ORIGIN
zExpectDirOrigin // Directive $ORIGIN
zExpectDirIncludeBl // Space after directive $INCLUDE
zExpectDirInclude // Directive $INCLUDE
zExpectDirGenerate // Directive $GENERATE
zExpectDirGenerateBl // Space after directive $GENERATE
2011-12-14 19:00:39 +11:00
)
2012-05-08 22:17:17 +10:00
// ParseError is a parsing error. It contains the parse error and the location in the io.Reader
2016-01-04 14:16:50 +11:00
// where the error occurred.
type ParseError struct {
file string
err string
lex lex
}
func (e *ParseError) Error() (s string) {
if e.file != "" {
s = e.file + ": "
}
2012-03-04 03:40:30 +11:00
s += "dns: " + e.err + ": " + strconv.QuoteToASCII(e.lex.token) + " at line: " +
strconv.Itoa(e.lex.line) + ":" + strconv.Itoa(e.lex.column)
return
}
2011-12-17 05:34:30 +11:00
type lex struct {
token string // text of the token
tokenUpper string // uppercase text of the token
2016-01-04 14:16:50 +11:00
length int // length of the token
err bool // when true, token text has lexer error
2015-02-21 08:16:49 +11:00
value uint8 // value: zString, _BLANK, etc.
line int // line in the file
column int // column in the file
torc uint16 // type or class as parsed in the lexer, we only need to look this up in the grammar
comment string // any comment text seen
2011-12-14 19:00:39 +11:00
}
// Token holds the token that are returned when a zone file is parsed.
type Token struct {
// The scanned resource record when error is not nil.
RR
2016-01-04 14:16:50 +11:00
// When an error occurred, this has the error specifics.
Error *ParseError
// A potential comment positioned after the RR and on the same line.
Comment string
}
// ttlState describes the state necessary to fill in an omitted RR TTL
type ttlState struct {
ttl uint32 // ttl is the current default TTL
isByDirective bool // isByDirective indicates whether ttl was set by a $TTL directive
}
// NewRR reads the RR contained in the string s. Only the first RR is
// returned. If s contains no RR, return nil with no error. The class
// defaults to IN and TTL defaults to 3600. The full zone file syntax
// like $TTL, $ORIGIN, etc. is supported. All fields of the returned
// RR are set, except RR.Header().Rdlength which is set to 0.
func NewRR(s string) (RR, error) {
if len(s) > 0 && s[len(s)-1] != '\n' { // We need a closing newline
return ReadRR(strings.NewReader(s+"\n"), "")
}
return ReadRR(strings.NewReader(s), "")
2012-01-23 06:20:30 +11:00
}
2014-04-24 19:55:55 +10:00
// ReadRR reads the RR contained in q.
// See NewRR for more documentation.
2012-01-23 06:20:30 +11:00
func ReadRR(q io.Reader, filename string) (RR, error) {
defttl := &ttlState{defaultTtl, false}
r := <-parseZoneHelper(q, ".", filename, defttl, 1)
if r == nil {
return nil, nil
}
if r.Error != nil {
return nil, r.Error
}
return r.RR, nil
}
2015-08-08 05:58:26 +10:00
// ParseZone reads a RFC 1035 style zonefile from r. It returns *Tokens on the
// returned channel, each consisting of either a parsed RR and optional comment
// or a nil RR and an error. The string file is only used
2012-02-15 22:50:23 +11:00
// in error reporting. The string origin is used as the initial origin, as
// if the file would start with an $ORIGIN directive.
2012-05-21 01:39:06 +10:00
// The directives $INCLUDE, $ORIGIN, $TTL and $GENERATE are supported.
2012-02-14 03:52:53 +11:00
// The channel t is closed by ParseZone when the end of r is reached.
2012-05-03 17:05:30 +10:00
//
2013-05-06 04:30:44 +10:00
// Basic usage pattern when reading from a string (z) containing the
2012-05-03 17:05:30 +10:00
// zone data:
//
2012-05-21 04:46:25 +10:00
// for x := range dns.ParseZone(strings.NewReader(z), "", "") {
2012-05-03 17:05:30 +10:00
// if x.Error != nil {
2015-10-05 21:09:22 +11:00
// // log.Println(x.Error)
// } else {
// // Do something with x.RR
// }
// }
//
2013-03-05 03:21:34 +11:00
// Comments specified after an RR (and on the same line!) are returned too:
2013-05-06 04:30:44 +10:00
//
// foo. IN A 10.0.0.1 ; this is a comment
//
2015-08-08 05:58:26 +10:00
// The text "; this is comment" is returned in Token.Comment. Comments inside the
2013-03-05 03:21:34 +11:00
// RR are discarded. Comments on a line by themselves are discarded too.
func ParseZone(r io.Reader, origin, file string) chan *Token {
return parseZoneHelper(r, origin, file, nil, 10000)
}
func parseZoneHelper(r io.Reader, origin, file string, defttl *ttlState, chansize int) chan *Token {
t := make(chan *Token, chansize)
go parseZone(r, origin, file, defttl, t, 0)
return t
2011-12-20 05:20:55 +11:00
}
func parseZone(r io.Reader, origin, f string, defttl *ttlState, t chan *Token, include int) {
defer func() {
if include == 0 {
close(t)
}
}()
s, cancel := scanInit(r)
c := make(chan lex)
// Start the lexer
go zlexer(s, c)
defer func() {
cancel()
// zlexer can send up to three tokens, the next one and possibly 2 remainders.
// Do a non-blocking read.
_, ok := <-c
_, ok = <-c
_, ok = <-c
if !ok {
// too bad
}
}()
// 6 possible beginnings of a line, _ is a space
2015-02-21 08:16:49 +11:00
// 0. zRRTYPE -> all omitted until the rrtype
// 1. zOwner _ zRrtype -> class/ttl omitted
// 2. zOwner _ zString _ zRrtype -> class omitted
// 3. zOwner _ zString _ zClass _ zRrtype -> ttl/class
// 4. zOwner _ zClass _ zRrtype -> ttl omitted
// 5. zOwner _ zClass _ zString _ zRrtype -> class/ttl (reversed)
// After detecting these, we know the zRrtype so we can jump to functions
2011-12-14 19:00:39 +11:00
// handling the rdata for each of these types.
if origin != "" {
origin = Fqdn(origin)
if _, ok := IsDomainName(origin); !ok {
t <- &Token{Error: &ParseError{f, "bad initial origin name", lex{}}}
return
}
}
2012-07-17 03:46:16 +10:00
2015-02-19 21:59:15 +11:00
st := zExpectOwnerDir // initial state
2011-12-14 19:26:31 +11:00
var h RR_Header
var prevName string
2011-12-14 19:00:39 +11:00
for l := range c {
// Lexer spotted an error already
2012-02-24 06:13:37 +11:00
if l.err == true {
t <- &Token{Error: &ParseError{f, l.token, l}}
return
}
2011-12-14 19:00:39 +11:00
switch st {
2015-02-19 21:59:15 +11:00
case zExpectOwnerDir:
2011-12-19 03:58:06 +11:00
// We can also expect a directive, like $TTL or $ORIGIN
if defttl != nil {
h.Ttl = defttl.ttl
}
h.Class = ClassINET
2011-12-14 19:00:39 +11:00
switch l.value {
2015-02-19 21:45:59 +11:00
case zNewline:
2015-02-19 21:59:15 +11:00
st = zExpectOwnerDir
2015-02-19 21:45:59 +11:00
case zOwner:
2011-12-14 19:00:39 +11:00
h.Name = l.token
name, ok := toAbsoluteName(l.token, origin)
if !ok {
t <- &Token{Error: &ParseError{f, "bad owner name", l}}
2012-01-13 08:49:26 +11:00
return
}
h.Name = name
prevName = h.Name
2015-02-19 21:59:15 +11:00
st = zExpectOwnerBl
case zDirTTL:
st = zExpectDirTTLBl
2015-02-19 21:45:59 +11:00
case zDirOrigin:
2015-02-19 21:59:15 +11:00
st = zExpectDirOriginBl
2015-02-19 21:45:59 +11:00
case zDirInclude:
2015-02-19 21:59:15 +11:00
st = zExpectDirIncludeBl
2015-02-19 21:45:59 +11:00
case zDirGenerate:
2015-02-19 21:59:15 +11:00
st = zExpectDirGenerateBl
2015-02-19 21:45:59 +11:00
case zRrtpe:
h.Name = prevName
2012-02-28 07:12:04 +11:00
h.Rrtype = l.torc
2015-02-19 21:59:15 +11:00
st = zExpectRdata
2015-02-19 21:45:59 +11:00
case zClass:
h.Name = prevName
2012-02-28 07:12:04 +11:00
h.Class = l.torc
2015-02-19 21:59:15 +11:00
st = zExpectAnyNoClassBl
2015-02-19 21:45:59 +11:00
case zBlank:
// Discard, can happen when there is nothing on the
// line except the RR type
2015-02-19 21:45:59 +11:00
case zString:
ttl, ok := stringToTTL(l.token)
if !ok {
t <- &Token{Error: &ParseError{f, "not a TTL", l}}
return
}
h.Ttl = ttl
if defttl == nil || !defttl.isByDirective {
defttl = &ttlState{ttl, false}
}
st = zExpectAnyNoTTLBl
2012-02-28 07:12:04 +11:00
2011-12-14 19:00:39 +11:00
default:
t <- &Token{Error: &ParseError{f, "syntax error at beginning", l}}
return
}
2015-02-19 21:59:15 +11:00
case zExpectDirIncludeBl:
2015-02-19 21:45:59 +11:00
if l.value != zBlank {
t <- &Token{Error: &ParseError{f, "no blank after $INCLUDE-directive", l}}
return
2011-12-14 19:00:39 +11:00
}
2015-02-19 21:59:15 +11:00
st = zExpectDirInclude
case zExpectDirInclude:
2015-02-19 21:45:59 +11:00
if l.value != zString {
t <- &Token{Error: &ParseError{f, "expecting $INCLUDE value, not this...", l}}
return
}
neworigin := origin // There may be optionally a new origin set after the filename, if not use current one
switch l := <-c; l.value {
2015-02-19 21:45:59 +11:00
case zBlank:
l := <-c
2015-02-19 21:45:59 +11:00
if l.value == zString {
name, ok := toAbsoluteName(l.token, origin)
if !ok {
t <- &Token{Error: &ParseError{f, "bad origin name", l}}
return
}
neworigin = name
}
2015-02-19 21:45:59 +11:00
case zNewline, zEOF:
// Ok
default:
t <- &Token{Error: &ParseError{f, "garbage after $INCLUDE", l}}
return
}
// Start with the new file
includePath := l.token
if !filepath.IsAbs(includePath) {
includePath = filepath.Join(filepath.Dir(f), includePath)
}
r1, e1 := os.Open(includePath)
2012-01-22 09:42:33 +11:00
if e1 != nil {
msg := fmt.Sprintf("failed to open `%s'", l.token)
if !filepath.IsAbs(l.token) {
msg += fmt.Sprintf(" as `%s'", includePath)
}
t <- &Token{Error: &ParseError{f, msg, l}}
2012-01-22 09:42:33 +11:00
return
}
if include+1 > 7 {
t <- &Token{Error: &ParseError{f, "too deeply nested $INCLUDE", l}}
return
}
parseZone(r1, neworigin, includePath, defttl, t, include+1)
2015-02-19 21:59:15 +11:00
st = zExpectOwnerDir
case zExpectDirTTLBl:
2015-02-19 21:45:59 +11:00
if l.value != zBlank {
t <- &Token{Error: &ParseError{f, "no blank after $TTL-directive", l}}
2011-12-19 03:58:06 +11:00
return
}
st = zExpectDirTTL
case zExpectDirTTL:
2015-02-19 21:45:59 +11:00
if l.value != zString {
t <- &Token{Error: &ParseError{f, "expecting $TTL value, not this...", l}}
2011-12-19 03:58:06 +11:00
return
}
2013-03-04 21:24:08 +11:00
if e, _ := slurpRemainder(c, f); e != nil {
t <- &Token{Error: e}
return
}
ttl, ok := stringToTTL(l.token)
if !ok {
t <- &Token{Error: &ParseError{f, "expecting $TTL value, not this...", l}}
2011-12-19 03:58:06 +11:00
return
}
defttl = &ttlState{ttl, true}
2015-02-19 21:59:15 +11:00
st = zExpectOwnerDir
case zExpectDirOriginBl:
2015-02-19 21:45:59 +11:00
if l.value != zBlank {
t <- &Token{Error: &ParseError{f, "no blank after $ORIGIN-directive", l}}
2012-01-13 08:49:26 +11:00
return
}
2015-02-19 21:59:15 +11:00
st = zExpectDirOrigin
case zExpectDirOrigin:
2015-02-19 21:45:59 +11:00
if l.value != zString {
t <- &Token{Error: &ParseError{f, "expecting $ORIGIN value, not this...", l}}
2012-01-13 08:49:26 +11:00
return
}
2013-03-04 21:24:08 +11:00
if e, _ := slurpRemainder(c, f); e != nil {
t <- &Token{Error: e}
}
name, ok := toAbsoluteName(l.token, origin)
if !ok {
t <- &Token{Error: &ParseError{f, "bad origin name", l}}
return
}
origin = name
2015-02-19 21:59:15 +11:00
st = zExpectOwnerDir
case zExpectDirGenerateBl:
2015-02-19 21:45:59 +11:00
if l.value != zBlank {
t <- &Token{Error: &ParseError{f, "no blank after $GENERATE-directive", l}}
return
}
2015-02-19 21:59:15 +11:00
st = zExpectDirGenerate
case zExpectDirGenerate:
2015-02-19 21:45:59 +11:00
if l.value != zString {
t <- &Token{Error: &ParseError{f, "expecting $GENERATE value, not this...", l}}
return
}
if errMsg := generate(l, c, t, origin); errMsg != "" {
t <- &Token{Error: &ParseError{f, errMsg, l}}
return
}
2015-02-19 21:59:15 +11:00
st = zExpectOwnerDir
case zExpectOwnerBl:
2015-02-19 21:45:59 +11:00
if l.value != zBlank {
t <- &Token{Error: &ParseError{f, "no blank after owner", l}}
return
2011-12-14 19:00:39 +11:00
}
2015-02-19 21:59:15 +11:00
st = zExpectAny
case zExpectAny:
2011-12-14 19:00:39 +11:00
switch l.value {
2015-02-19 21:45:59 +11:00
case zRrtpe:
if defttl == nil {
t <- &Token{Error: &ParseError{f, "missing TTL with no previous value", l}}
return
}
2012-02-28 07:12:04 +11:00
h.Rrtype = l.torc
2015-02-19 21:59:15 +11:00
st = zExpectRdata
2015-02-19 21:45:59 +11:00
case zClass:
2012-02-28 07:12:04 +11:00
h.Class = l.torc
2015-02-19 21:59:15 +11:00
st = zExpectAnyNoClassBl
2015-02-19 21:45:59 +11:00
case zString:
ttl, ok := stringToTTL(l.token)
if !ok {
t <- &Token{Error: &ParseError{f, "not a TTL", l}}
return
2011-12-14 19:00:39 +11:00
}
h.Ttl = ttl
if defttl == nil || !defttl.isByDirective {
defttl = &ttlState{ttl, false}
}
st = zExpectAnyNoTTLBl
2011-12-14 19:00:39 +11:00
default:
t <- &Token{Error: &ParseError{f, "expecting RR type, TTL or class, not this...", l}}
return
2011-12-14 19:00:39 +11:00
}
2015-02-19 21:59:15 +11:00
case zExpectAnyNoClassBl:
2015-02-19 21:45:59 +11:00
if l.value != zBlank {
t <- &Token{Error: &ParseError{f, "no blank before class", l}}
return
2011-12-14 19:00:39 +11:00
}
2015-02-19 21:59:15 +11:00
st = zExpectAnyNoClass
case zExpectAnyNoTTLBl:
2015-02-19 21:45:59 +11:00
if l.value != zBlank {
t <- &Token{Error: &ParseError{f, "no blank before TTL", l}}
return
2011-12-14 19:00:39 +11:00
}
st = zExpectAnyNoTTL
case zExpectAnyNoTTL:
2011-12-14 19:00:39 +11:00
switch l.value {
2015-02-19 21:45:59 +11:00
case zClass:
2012-02-28 07:12:04 +11:00
h.Class = l.torc
2015-02-19 21:59:15 +11:00
st = zExpectRrtypeBl
2015-02-19 21:45:59 +11:00
case zRrtpe:
2012-02-28 07:12:04 +11:00
h.Rrtype = l.torc
2015-02-19 21:59:15 +11:00
st = zExpectRdata
2012-03-04 20:41:16 +11:00
default:
t <- &Token{Error: &ParseError{f, "expecting RR type or class, not this...", l}}
2012-03-04 03:40:30 +11:00
return
2011-12-14 19:00:39 +11:00
}
2015-02-19 21:59:15 +11:00
case zExpectAnyNoClass:
2011-12-14 19:00:39 +11:00
switch l.value {
2015-02-19 21:45:59 +11:00
case zString:
ttl, ok := stringToTTL(l.token)
if !ok {
t <- &Token{Error: &ParseError{f, "not a TTL", l}}
return
2011-12-14 19:00:39 +11:00
}
h.Ttl = ttl
if defttl == nil || !defttl.isByDirective {
defttl = &ttlState{ttl, false}
}
2015-02-19 21:59:15 +11:00
st = zExpectRrtypeBl
2015-02-19 21:45:59 +11:00
case zRrtpe:
2012-02-28 07:12:04 +11:00
h.Rrtype = l.torc
2015-02-19 21:59:15 +11:00
st = zExpectRdata
2011-12-14 19:00:39 +11:00
default:
t <- &Token{Error: &ParseError{f, "expecting RR type or TTL, not this...", l}}
return
2011-12-14 19:00:39 +11:00
}
2015-02-19 21:59:15 +11:00
case zExpectRrtypeBl:
2015-02-19 21:45:59 +11:00
if l.value != zBlank {
t <- &Token{Error: &ParseError{f, "no blank before RR type", l}}
return
2011-12-14 19:00:39 +11:00
}
2015-02-19 21:59:15 +11:00
st = zExpectRrtype
case zExpectRrtype:
2015-02-19 21:45:59 +11:00
if l.value != zRrtpe {
t <- &Token{Error: &ParseError{f, "unknown RR type", l}}
return
2011-12-14 19:00:39 +11:00
}
2012-02-28 07:12:04 +11:00
h.Rrtype = l.torc
2015-02-19 21:59:15 +11:00
st = zExpectRdata
case zExpectRdata:
r, e, c1 := setRR(h, c, origin, f)
if e != nil {
2011-12-17 05:34:30 +11:00
// If e.lex is nil than we have encounter a unknown RR type
// in that case we substitute our current lex token
2011-12-17 00:48:30 +11:00
if e.lex.token == "" && e.lex.value == 0 {
e.lex = l // Uh, dirty
}
t <- &Token{Error: e}
return
2011-12-14 19:00:39 +11:00
}
t <- &Token{RR: r, Comment: c1}
2015-02-19 21:59:15 +11:00
st = zExpectOwnerDir
2011-12-14 19:00:39 +11:00
}
}
2012-05-20 23:31:35 +10:00
// If we get here, we and the h.Rrtype is still zero, we haven't parsed anything, this
// is not an error, because an empty zone file is still a zone file.
2011-12-14 19:00:39 +11:00
}
// zlexer scans the sourcefile and returns tokens on the channel c.
func zlexer(s *scan, c chan lex) {
2011-12-17 05:34:30 +11:00
var l lex
str := make([]byte, maxTok) // Should be enough for any token
2012-01-28 10:35:37 +11:00
stri := 0 // Offset in str (0 means empty)
2013-03-04 19:40:45 +11:00
com := make([]byte, maxTok) // Hold comment text
comi := 0
2011-12-14 19:00:39 +11:00
quote := false
2011-12-19 05:59:01 +11:00
escape := false
2011-12-14 19:00:39 +11:00
space := false
commt := false
rrtype := false
owner := true
brace := 0
x, err := s.tokenText()
defer close(c)
for err == nil {
l.column = s.position.Column
l.line = s.position.Line
if stri >= maxTok {
2014-08-28 07:09:21 +10:00
l.token = "token length insufficient for parsing"
2012-02-24 06:13:37 +11:00
l.err = true
2012-01-28 10:35:37 +11:00
c <- l
return
}
if comi >= maxTok {
2014-08-28 07:09:21 +10:00
l.token = "comment length insufficient for parsing"
2013-03-04 19:58:09 +11:00
l.err = true
c <- l
return
}
switch x {
case ' ', '\t':
if escape {
escape = false
str[stri] = x
stri++
break
}
if quote {
// Inside quotes this is legal
str[stri] = x
stri++
break
}
2011-12-14 19:00:39 +11:00
if commt {
2013-03-04 19:58:09 +11:00
com[comi] = x
comi++
2011-12-14 19:00:39 +11:00
break
}
if stri == 0 {
// Space directly in the beginning, handled in the grammar
2011-12-14 19:00:39 +11:00
} else if owner {
2011-12-14 19:26:31 +11:00
// If we have a string and its the first, make it an owner
2015-02-19 21:45:59 +11:00
l.value = zOwner
l.token = string(str[:stri])
l.tokenUpper = strings.ToUpper(l.token)
2013-09-10 23:13:10 +10:00
l.length = stri
// escape $... start with a \ not a $, so this will work
switch l.tokenUpper {
2012-01-22 09:42:33 +11:00
case "$TTL":
l.value = zDirTTL
2012-01-22 09:42:33 +11:00
case "$ORIGIN":
2015-02-19 21:45:59 +11:00
l.value = zDirOrigin
2012-01-22 09:42:33 +11:00
case "$INCLUDE":
2015-02-19 21:45:59 +11:00
l.value = zDirInclude
case "$GENERATE":
2015-02-19 21:45:59 +11:00
l.value = zDirGenerate
2012-01-22 09:42:33 +11:00
}
2011-12-14 19:00:39 +11:00
c <- l
} else {
2015-02-19 21:45:59 +11:00
l.value = zString
l.token = string(str[:stri])
l.tokenUpper = strings.ToUpper(l.token)
2013-09-10 23:13:10 +10:00
l.length = stri
2011-12-14 19:26:31 +11:00
if !rrtype {
if t, ok := StringToType[l.tokenUpper]; ok {
2015-02-19 21:45:59 +11:00
l.value = zRrtpe
2012-02-28 07:12:04 +11:00
l.torc = t
rrtype = true
} else {
if strings.HasPrefix(l.tokenUpper, "TYPE") {
t, ok := typeToInt(l.token)
if !ok {
2012-02-28 07:12:04 +11:00
l.token = "unknown RR type"
l.err = true
c <- l
return
}
l.value = zRrtpe
rrtype = true