Create my own parser
This commit is contained in:
parent
f1ff334ab1
commit
eddd1d33d1
|
@ -0,0 +1,356 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"dns"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/scanner"
|
||||
)
|
||||
|
||||
// Tokinize a RFC 1035 zone file. The tokenizer will normalize it:
|
||||
// * Add ownernames;
|
||||
// * Suppress sequences of spaces;
|
||||
// * Make each RR fit on one line (NEWLINE is send as last)
|
||||
// * Handle comments: ;
|
||||
const (
|
||||
_STRING = iota
|
||||
_BLANK
|
||||
_NEWLINE
|
||||
_RRTYPE
|
||||
_OWNER
|
||||
_CLASS
|
||||
)
|
||||
|
||||
const (
|
||||
_EXPECT_OWNER = iota // Ownername
|
||||
_EXPECT_OWNER_BL // Whitespace after the ownername
|
||||
_EXPECT_ANY // Expect rrtype, ttl or class
|
||||
_EXPECT_ANY_NO_CLASS // Expect rrtype or ttl
|
||||
_EXPECT_ANY_NO_CLASS_BL // The Whitespace after _EXPECT_ANY_NO_CLASS
|
||||
_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 rrype
|
||||
_EXPECT_RDATA // The first element of the rdata
|
||||
_EXPECT_RDATA_BL // Whitespace BEFORE rdata starts
|
||||
)
|
||||
|
||||
type Lex struct {
|
||||
token string
|
||||
value int
|
||||
line int
|
||||
column int
|
||||
}
|
||||
|
||||
func main() {
|
||||
//f, e := os.Open("test")
|
||||
f, e := os.Open("dnssex.nl.signed")
|
||||
if e != nil {
|
||||
fmt.Printf("Err: " + e.Error())
|
||||
return
|
||||
}
|
||||
var s scanner.Scanner
|
||||
c := make(chan Lex)
|
||||
s.Init(f)
|
||||
s.Mode = 0
|
||||
s.Whitespace = 0
|
||||
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
|
||||
var h dns.RR_Header
|
||||
var ok bool
|
||||
for l := range c {
|
||||
switch st {
|
||||
case _EXPECT_OWNER: // Owername
|
||||
switch l.value {
|
||||
case _NEWLINE:
|
||||
// empty line
|
||||
st = _EXPECT_OWNER
|
||||
case _OWNER:
|
||||
h.Name = l.token
|
||||
st = _EXPECT_OWNER_BL
|
||||
default:
|
||||
println("Error at the start")
|
||||
// Reset
|
||||
st = _EXPECT_OWNER
|
||||
}
|
||||
case _EXPECT_OWNER_BL: // Expect a blank
|
||||
if l.value != _BLANK {
|
||||
println("No blank after owner error")
|
||||
}
|
||||
st = _EXPECT_ANY
|
||||
case _EXPECT_ANY: // Expect _CLASS _STRING (TTL here) or _RRTYPE
|
||||
switch l.value {
|
||||
case _RRTYPE:
|
||||
h.Rrtype, ok = dns.Str_rr[strings.ToUpper(l.token)]
|
||||
if !ok {
|
||||
println("Unknown RR type")
|
||||
}
|
||||
h.Ttl = dns.DefaultTTL
|
||||
st = _EXPECT_RDATA_BL
|
||||
case _CLASS:
|
||||
h.Class, ok = dns.Str_class[strings.ToUpper(l.token)]
|
||||
if !ok {
|
||||
println("Unknown Class")
|
||||
}
|
||||
st = _EXPECT_ANY_NO_CLASS_BL
|
||||
case _STRING: // TTL
|
||||
ttl, ok := strconv.Atoi(l.token)
|
||||
if ok != nil {
|
||||
println("Not a TTL")
|
||||
} else {
|
||||
h.Ttl = uint32(ttl)
|
||||
}
|
||||
st = _EXPECT_ANY_NOTTL_BL
|
||||
default:
|
||||
println("Error not expected")
|
||||
}
|
||||
case _EXPECT_ANY_NO_CLASS_BL:
|
||||
if l.value != _BLANK {
|
||||
println("No blank before NO_CLASS error")
|
||||
}
|
||||
st = _EXPECT_ANY_NO_CLASS
|
||||
case _EXPECT_ANY_NOTTL_BL:
|
||||
if l.value != _BLANK {
|
||||
println("No blank before NOTTL error")
|
||||
}
|
||||
st = _EXPECT_ANY_NOTTL
|
||||
case _EXPECT_ANY_NOTTL:
|
||||
switch l.value {
|
||||
case _CLASS:
|
||||
h.Class, ok = dns.Str_class[strings.ToUpper(l.token)]
|
||||
if !ok {
|
||||
println("Unknown Class")
|
||||
}
|
||||
st = _EXPECT_RRTYPE_BL
|
||||
case _RRTYPE:
|
||||
h.Rrtype, ok = dns.Str_rr[strings.ToUpper(l.token)]
|
||||
if !ok {
|
||||
println("Unknown RR type")
|
||||
}
|
||||
st = _EXPECT_RDATA_BL
|
||||
}
|
||||
case _EXPECT_ANY_NO_CLASS:
|
||||
switch l.value {
|
||||
case _STRING: // TTL
|
||||
ttl, ok := strconv.Atoi(l.token)
|
||||
if ok != nil {
|
||||
println("Not a TTL")
|
||||
} else {
|
||||
h.Ttl = uint32(ttl)
|
||||
}
|
||||
st = _EXPECT_RDATA_BL
|
||||
case _RRTYPE:
|
||||
h.Rrtype, ok = dns.Str_rr[strings.ToUpper(l.token)]
|
||||
if !ok {
|
||||
println("Unknown RR type")
|
||||
}
|
||||
st = _EXPECT_RDATA_BL
|
||||
default:
|
||||
println("Error not TTL nor _RRTYPE seen")
|
||||
}
|
||||
case _EXPECT_RRTYPE_BL:
|
||||
if l.value != _BLANK {
|
||||
println("No blank after error")
|
||||
}
|
||||
st = _EXPECT_RRTYPE
|
||||
case _EXPECT_RRTYPE:
|
||||
if l.value != _RRTYPE {
|
||||
println("Error, not an rrtype")
|
||||
}
|
||||
h.Rrtype, ok = dns.Str_rr[strings.ToUpper(l.token)]
|
||||
if !ok {
|
||||
println("Unknown RR type")
|
||||
}
|
||||
st = _EXPECT_RDATA_BL
|
||||
case _EXPECT_RDATA_BL:
|
||||
if l.value != _BLANK {
|
||||
println("No blank after error")
|
||||
}
|
||||
st = _EXPECT_RDATA
|
||||
case _EXPECT_RDATA:
|
||||
fmt.Printf("%v\n", h)
|
||||
// Remaining items until newline are rdata
|
||||
// reset
|
||||
fmt.Printf("%v", l)
|
||||
for rdata := range c {
|
||||
fmt.Printf("%v", rdata)
|
||||
if rdata.value == _NEWLINE {
|
||||
break
|
||||
}
|
||||
}
|
||||
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 ""
|
||||
}
|
||||
|
||||
func isrrtype(s string) bool {
|
||||
switch strings.ToUpper(s) {
|
||||
case "SOA":
|
||||
fallthrough
|
||||
case "TXT":
|
||||
fallthrough
|
||||
case "CNAME":
|
||||
fallthrough
|
||||
case "NSEC":
|
||||
fallthrough
|
||||
case "RRSIG":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isclass(s string) bool {
|
||||
switch strings.ToUpper(s) {
|
||||
case "IN":
|
||||
fallthrough
|
||||
case "CH":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// lexer scans the sourcefile and returns tokens on the channel c.
|
||||
func lexer(s scanner.Scanner, c chan Lex) {
|
||||
var l Lex
|
||||
str := "" // Hold the current read text
|
||||
quote := false
|
||||
space := false
|
||||
commt := false
|
||||
rrtype := false
|
||||
owner := true
|
||||
brace := 0
|
||||
tok := s.Scan()
|
||||
defer close(c)
|
||||
for tok != scanner.EOF {
|
||||
switch x := s.TokenText(); x {
|
||||
case " ", "\t":
|
||||
if commt {
|
||||
break
|
||||
}
|
||||
if str == "" {
|
||||
//l.value = _BLANK
|
||||
//l.token = " "
|
||||
} else if owner {
|
||||
// If we have a string, its the first make it an owner
|
||||
l.value = _OWNER
|
||||
l.token = str
|
||||
c <- l
|
||||
} else {
|
||||
l.value = _STRING
|
||||
l.token = str
|
||||
|
||||
if !rrtype && isrrtype(str) {
|
||||
l.value = _RRTYPE
|
||||
rrtype = true // We've seen one
|
||||
}
|
||||
if !rrtype && isclass(str) {
|
||||
l.value = _CLASS
|
||||
}
|
||||
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 {
|
||||
// Reset
|
||||
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 += "\""
|
||||
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)
|
||||
}
|
Loading…
Reference in New Issue