dns/zscan.go

340 lines
7.6 KiB
Go
Raw Normal View History

2011-12-14 08:26:31 +00:00
package dns
2011-12-14 08:00:39 +00:00
import (
"fmt"
2011-12-14 08:26:31 +00:00
"io"
2011-12-14 08:00:39 +00:00
"strconv"
"strings"
"text/scanner"
)
// Tokinize a RFC 1035 zone file. The tokenizer will normalize it:
2011-12-15 11:27:05 +00:00
// * Add ownernames if they are left blank;
2011-12-14 08:00:39 +00:00
// * Suppress sequences of spaces;
// * Make each RR fit on one line (NEWLINE is send as last)
// * Handle comments: ;
2011-12-15 11:27:05 +00:00
// * Handle braces.
2011-12-14 08:00:39 +00:00
const (
2011-12-14 14:37:36 +00:00
_EOF = iota // Don't let it start with zero
_STRING
2011-12-14 08:00:39 +00:00
_BLANK
_NEWLINE
_RRTYPE
_OWNER
_CLASS
2011-12-14 14:37:36 +00:00
_EXPECT_OWNER // Ownername
_EXPECT_OWNER_BL // Whitespace after the ownername
_EXPECT_ANY // Expect rrtype, ttl or class
_EXPECT_ANY_NOCLASS // Expect rrtype or ttl
_EXPECT_ANY_NOCLASS_BL // The Whitespace after _EXPECT_ANY_NOCLASS
_EXPECT_ANY_NOTTL // Expect rrtype or class
_EXPECT_ANY_NOTTL_BL // Whitespace after _EXPECT_ANY_NOTTL
_EXPECT_RRTYPE // Expect rrtype
_EXPECT_RRTYPE_BL // Whitespace BEFORE rrtype
_EXPECT_RDATA // The first element of the rdata
2011-12-14 08:00:39 +00:00
)
2011-12-15 11:27:05 +00:00
// Only used when debugging the parser itself.
2011-12-15 10:33:28 +00:00
var DEBUG = false
2011-12-14 14:37:36 +00:00
type ParseError struct {
err string
lex Lex
}
func (e *ParseError) Error() string {
s := e.err + ": `" + e.lex.token + "' at line: " + strconv.Itoa(e.lex.line) +
2011-12-14 14:37:36 +00:00
" and column: " + strconv.Itoa(e.lex.column)
return s
}
2011-12-14 08:00:39 +00:00
type Lex struct {
2011-12-15 11:27:05 +00:00
token string // text of the token
value int // value: _STRING, _BLANK, etc.
line int // line in the file
column int // column in the fil
2011-12-14 08:00:39 +00:00
}
2011-12-15 11:27:05 +00:00
// ParseString parses a string and returns the RR contained in there. If the string
// contains more than one RR, only the first is returned.
func NewRRString(s string) (RR, error) {
2011-12-14 14:37:36 +00:00
cr := make(chan RR)
go ParseZone(strings.NewReader(s), cr)
r := <-cr // There are no error send as of yet
return r, nil // Todo: errors
}
func newRRReader(q io.Reader) (RR, error) {
2011-12-14 14:37:36 +00:00
cr := make(chan RR)
go ParseZone(q, cr)
r := <-cr
return r, nil
}
2011-12-14 08:26:31 +00:00
// ParseZone reads a RFC 1035 zone from r. It returns each parsed RR on the
2011-12-15 11:27:05 +00:00
// channel cr. The channel cr is closed by ParseZone when the end of r is reached.
2011-12-14 08:26:31 +00:00
func ParseZone(r io.Reader, cr chan RR) {
defer close(cr)
2011-12-14 08:00:39 +00:00
var s scanner.Scanner
c := make(chan Lex)
2011-12-14 08:26:31 +00:00
s.Init(r)
2011-12-14 08:00:39 +00:00
s.Mode = 0
s.Whitespace = 0
// Start the lexer
2011-12-14 08:00:39 +00:00
go lexer(s, c)
// 5 possible beginnings of a line, _ is a space
// 1. _OWNER _ _RRTYPE -> class/ttl omitted
// 2. _OWNER _ _STRING _ _RRTYPE -> class omitted
// 3. _OWNER _ _STRING _ _CLASS _ _RRTYPE -> ttl/class
// 4. _OWNER _ _CLASS _ _RRTYPE -> ttl omitted
// 5. _OWNER _ _CLASS _ _STRING _ _RRTYPE -> class/ttl (reversed)
// After detecting these, we know the _RRTYPE so we can jump to functions
// handling the rdata for each of these types.
st := _EXPECT_OWNER
2011-12-14 08:26:31 +00:00
var h RR_Header
2011-12-14 08:00:39 +00:00
var ok bool
for l := range c {
2011-12-14 14:37:36 +00:00
if DEBUG {
fmt.Printf("[%v]\n", l)
}
2011-12-14 08:00:39 +00:00
switch st {
2011-12-14 08:26:31 +00:00
case _EXPECT_OWNER:
2011-12-14 08:00:39 +00:00
switch l.value {
2011-12-14 08:26:31 +00:00
case _NEWLINE: // Empty line
2011-12-14 08:00:39 +00:00
st = _EXPECT_OWNER
case _OWNER:
h.Name = l.token
st = _EXPECT_OWNER_BL
default:
2011-12-14 14:37:36 +00:00
fmt.Printf("%s\n", &ParseError{"Error at the start", l})
2011-12-14 08:00:39 +00:00
st = _EXPECT_OWNER
}
2011-12-14 08:26:31 +00:00
case _EXPECT_OWNER_BL:
2011-12-14 08:00:39 +00:00
if l.value != _BLANK {
2011-12-14 14:37:36 +00:00
fmt.Printf("%s\n", &ParseError{"No blank after owner", l})
2011-12-14 08:00:39 +00:00
}
st = _EXPECT_ANY
2011-12-14 08:26:31 +00:00
case _EXPECT_ANY:
2011-12-14 08:00:39 +00:00
switch l.value {
case _RRTYPE:
2011-12-14 13:35:45 +00:00
h.Rrtype, _ = Str_rr[strings.ToUpper(l.token)]
2011-12-14 08:26:31 +00:00
h.Ttl = DefaultTtl
2011-12-14 14:37:36 +00:00
st = _EXPECT_RDATA
2011-12-14 08:00:39 +00:00
case _CLASS:
2011-12-14 08:26:31 +00:00
h.Class, ok = Str_class[strings.ToUpper(l.token)]
2011-12-14 08:00:39 +00:00
if !ok {
2011-12-14 14:37:36 +00:00
fmt.Printf("%s\n", &ParseError{"Unknown class", l})
2011-12-14 08:00:39 +00:00
}
2011-12-14 13:35:45 +00:00
st = _EXPECT_ANY_NOCLASS_BL
2011-12-14 08:26:31 +00:00
case _STRING: // TTL is this case
2011-12-14 08:00:39 +00:00
ttl, ok := strconv.Atoi(l.token)
if ok != nil {
2011-12-14 14:37:36 +00:00
fmt.Printf("%s\n", &ParseError{"Not a TTL", l})
2011-12-14 08:00:39 +00:00
} else {
h.Ttl = uint32(ttl)
}
st = _EXPECT_ANY_NOTTL_BL
default:
2011-12-14 14:37:36 +00:00
fmt.Printf("%s\n", &ParseError{"Expecting RR type, TTL or class, not this...", l})
2011-12-14 08:00:39 +00:00
}
2011-12-14 13:35:45 +00:00
case _EXPECT_ANY_NOCLASS_BL:
2011-12-14 08:00:39 +00:00
if l.value != _BLANK {
2011-12-14 14:37:36 +00:00
fmt.Printf("%s\n", &ParseError{"No blank before NOCLASS", l})
2011-12-14 08:00:39 +00:00
}
2011-12-14 13:35:45 +00:00
st = _EXPECT_ANY_NOCLASS
2011-12-14 08:00:39 +00:00
case _EXPECT_ANY_NOTTL_BL:
if l.value != _BLANK {
2011-12-14 14:37:36 +00:00
fmt.Printf("%s\n", &ParseError{"No blank before NOTTL", l})
2011-12-14 08:00:39 +00:00
}
st = _EXPECT_ANY_NOTTL
case _EXPECT_ANY_NOTTL:
switch l.value {
case _CLASS:
2011-12-14 08:26:31 +00:00
h.Class, ok = Str_class[strings.ToUpper(l.token)]
2011-12-14 08:00:39 +00:00
if !ok {
2011-12-14 14:37:36 +00:00
fmt.Printf("%s\n", &ParseError{"Unknown class", l})
2011-12-14 08:00:39 +00:00
}
st = _EXPECT_RRTYPE_BL
case _RRTYPE:
2011-12-14 13:35:45 +00:00
h.Rrtype, _ = Str_rr[strings.ToUpper(l.token)]
2011-12-14 14:37:36 +00:00
st = _EXPECT_RDATA
2011-12-14 08:00:39 +00:00
}
2011-12-14 13:35:45 +00:00
case _EXPECT_ANY_NOCLASS:
2011-12-14 08:00:39 +00:00
switch l.value {
case _STRING: // TTL
ttl, ok := strconv.Atoi(l.token)
if ok != nil {
2011-12-14 14:37:36 +00:00
fmt.Printf("%s\n", &ParseError{"Not a TTL", l})
2011-12-14 08:00:39 +00:00
} else {
h.Ttl = uint32(ttl)
}
2011-12-15 10:22:54 +00:00
st = _EXPECT_RRTYPE_BL
2011-12-14 08:00:39 +00:00
case _RRTYPE:
2011-12-14 13:35:45 +00:00
h.Rrtype, _ = Str_rr[strings.ToUpper(l.token)]
2011-12-14 14:37:36 +00:00
st = _EXPECT_RDATA
2011-12-14 08:00:39 +00:00
default:
2011-12-14 14:37:36 +00:00
fmt.Printf("%s\n", &ParseError{"Expecting RR type or TTL, not this...", l})
2011-12-14 08:00:39 +00:00
}
case _EXPECT_RRTYPE_BL:
if l.value != _BLANK {
2011-12-14 14:37:36 +00:00
fmt.Printf("%s\n", &ParseError{"No blank after", l})
2011-12-14 08:00:39 +00:00
}
st = _EXPECT_RRTYPE
case _EXPECT_RRTYPE:
if l.value != _RRTYPE {
2011-12-14 14:37:36 +00:00
fmt.Printf("%s\n", &ParseError{"Unknown RR type", l})
2011-12-14 08:00:39 +00:00
}
2011-12-14 13:35:45 +00:00
h.Rrtype, _ = Str_rr[strings.ToUpper(l.token)]
2011-12-14 08:00:39 +00:00
st = _EXPECT_RDATA
case _EXPECT_RDATA:
r, e := setRR(h, c)
if e != nil {
fmt.Printf("%v\n", e)
2011-12-14 08:00:39 +00:00
}
cr <- r
2011-12-14 08:00:39 +00:00
st = _EXPECT_OWNER
}
}
}
func (l Lex) String() string {
switch l.value {
case _STRING:
return l.token
case _BLANK:
return " " //"_" // seems to work, make then invisible for now
case _NEWLINE:
return "|\n"
case _RRTYPE:
return "R:" + l.token
case _OWNER:
return "O:" + l.token
case _CLASS:
return "C:" + l.token
}
return ""
}
// lexer scans the sourcefile and returns tokens on the channel c.
func lexer(s scanner.Scanner, c chan Lex) {
var l Lex
2011-12-14 08:26:31 +00:00
str := "" // Hold the current read text
2011-12-14 08:00:39 +00:00
quote := false
space := false
commt := false
rrtype := false
owner := true
brace := 0
tok := s.Scan()
defer close(c)
for tok != scanner.EOF {
2011-12-14 08:26:31 +00:00
l.column = s.Position.Column
l.line = s.Position.Line
2011-12-14 08:00:39 +00:00
switch x := s.TokenText(); x {
case " ", "\t":
if commt {
break
}
if str == "" {
//l.value = _BLANK
//l.token = " "
} else if owner {
2011-12-14 08:26:31 +00:00
// If we have a string and its the first, make it an owner
2011-12-14 08:00:39 +00:00
l.value = _OWNER
l.token = str
c <- l
} else {
l.value = _STRING
l.token = str
2011-12-14 08:26:31 +00:00
if !rrtype {
if _, ok := Str_rr[strings.ToUpper(l.token)]; ok {
l.value = _RRTYPE
rrtype = true // We've seen one
}
if _, ok := Str_class[strings.ToUpper(l.token)]; ok {
l.value = _CLASS
}
2011-12-14 08:00:39 +00:00
}
c <- l
}
str = ""
if !space && !commt {
l.value = _BLANK
l.token = " "
c <- l
}
space = true
owner = false
case ";":
if quote {
// Inside quoted text we allow ;
str += ";"
break
}
commt = true
case "\n":
if commt {
2011-12-14 08:26:31 +00:00
// Reset a comment
2011-12-14 08:00:39 +00:00
commt = false
rrtype = false
str = ""
break
}
if str != "" {
l.value = _STRING
l.token = str
c <- l
}
if brace > 0 {
l.value = _BLANK
l.token = " "
if !space {
c <- l
}
} else {
l.value = _NEWLINE
l.token = "\n"
c <- l
}
if l.value == _BLANK {
space = true
}
str = ""
commt = false
rrtype = false
owner = true
case "\"":
if commt {
break
}
// str += "\"" don't add quoted quotes
2011-12-14 08:00:39 +00:00
quote = !quote
case "(":
if commt {
break
}
brace++
case ")":
if commt {
break
}
brace--
if brace < 0 {
fmt.Printf("Error\n")
}
default:
if commt {
break
}
str += x
space = false
}
tok = s.Scan()
}
// Hmm
// fmt.Printf("XX %s XXX", str)
}