2011-07-21 12:32:02 +00:00
|
|
|
package dns
|
|
|
|
|
|
|
|
// Parse RRs
|
|
|
|
// With the thankful help of gdnsd and the Go examples for Ragel.
|
|
|
|
|
|
|
|
import (
|
|
|
|
"os"
|
|
|
|
"io"
|
|
|
|
"net"
|
2011-07-24 19:29:16 +00:00
|
|
|
"time"
|
2011-07-22 10:26:31 +00:00
|
|
|
"strings"
|
2011-07-21 12:32:02 +00:00
|
|
|
"strconv"
|
|
|
|
)
|
|
|
|
|
2011-07-24 11:54:34 +00:00
|
|
|
const _IOBUF = MaxMsgSize
|
2011-07-22 21:57:56 +00:00
|
|
|
|
2011-07-23 06:54:44 +00:00
|
|
|
// A Parser represents a DNS parser for a
|
2011-09-02 11:02:29 +00:00
|
|
|
// particular input stream. Each parsed RR will be returned
|
|
|
|
// on the channel RR.
|
2011-07-22 21:28:04 +00:00
|
|
|
type Parser struct {
|
2011-07-24 19:29:16 +00:00
|
|
|
// nothing here yet
|
|
|
|
buf []byte
|
2011-09-02 11:02:29 +00:00
|
|
|
RR chan RR
|
2011-09-02 11:44:35 +00:00
|
|
|
Error chan *ParseError
|
2011-07-22 21:28:04 +00:00
|
|
|
}
|
|
|
|
|
2011-07-24 15:08:33 +00:00
|
|
|
type ParseError struct {
|
2011-07-24 19:29:16 +00:00
|
|
|
Error string
|
|
|
|
name string
|
|
|
|
line int
|
2011-07-24 15:08:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (e *ParseError) String() string {
|
2011-07-24 19:29:16 +00:00
|
|
|
s := e.Error + ": \"" + e.name + "\" at line: " + strconv.Itoa(e.line)
|
|
|
|
return s
|
2011-07-24 15:08:33 +00:00
|
|
|
}
|
|
|
|
|
2011-07-23 06:54:44 +00:00
|
|
|
// NewParser creates a new DNS file parser from r.
|
2011-09-02 11:44:35 +00:00
|
|
|
// Need sliding window stuff TODO.
|
2011-07-22 21:28:04 +00:00
|
|
|
func NewParser(r io.Reader) *Parser {
|
2011-09-01 19:35:43 +00:00
|
|
|
buf := make([]byte, _IOBUF)
|
2011-07-22 21:28:04 +00:00
|
|
|
n, err := r.Read(buf)
|
|
|
|
if err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if buf[n-1] != '\n' {
|
|
|
|
buf[n] = '\n'
|
|
|
|
n++
|
|
|
|
}
|
|
|
|
buf = buf[:n]
|
|
|
|
p := new(Parser)
|
|
|
|
p.buf = buf
|
2011-09-02 11:44:35 +00:00
|
|
|
p.RR = make(chan RR)
|
|
|
|
p.Error = make(chan ParseError)
|
2011-07-22 21:28:04 +00:00
|
|
|
return p
|
|
|
|
}
|
|
|
|
|
2011-07-24 19:29:16 +00:00
|
|
|
// Translate the RRSIG's incep. and expir. times from
|
|
|
|
// string values ("20110403154150") to an integer.
|
|
|
|
// Taking into account serial arithmetic (RFC 1982)
|
|
|
|
func dateToTime(s string) (uint32, os.Error) {
|
|
|
|
t, e := time.Parse("20060102150405", s)
|
|
|
|
if e != nil {
|
|
|
|
return 0, e
|
|
|
|
}
|
|
|
|
mod := t.Seconds() / Year68
|
|
|
|
ti := uint32(t.Seconds() - (mod * Year68))
|
|
|
|
return ti, nil
|
|
|
|
}
|
2011-07-21 12:32:02 +00:00
|
|
|
|
2011-07-22 15:29:56 +00:00
|
|
|
// Return the rdata fields as a string slice.
|
|
|
|
// All starting whitespace is deleted.
|
2011-07-22 20:06:07 +00:00
|
|
|
// If i is 0 no spaces are deleted from the final rdfs.
|
2011-07-22 10:26:31 +00:00
|
|
|
func fields(s string, i int) (rdf []string) {
|
2011-07-22 20:06:07 +00:00
|
|
|
rdf = strings.Fields(s)
|
2011-07-22 10:26:31 +00:00
|
|
|
for i, _ := range rdf {
|
|
|
|
rdf[i] = strings.TrimSpace(rdf[i])
|
|
|
|
}
|
2011-07-22 18:23:36 +00:00
|
|
|
if i > 0 && len(rdf) > i {
|
2011-07-22 15:29:56 +00:00
|
|
|
// The last rdf contained embedded spaces, glue it back together.
|
|
|
|
for j := i; j < len(rdf); j++ {
|
|
|
|
rdf[i-1] += rdf[j]
|
|
|
|
}
|
|
|
|
}
|
2011-07-22 10:26:31 +00:00
|
|
|
return
|
2011-07-21 12:32:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
%%{
|
|
|
|
machine z;
|
|
|
|
write data;
|
|
|
|
}%%
|
|
|
|
|
2011-09-02 11:44:35 +00:00
|
|
|
// First will return the first RR found when parsing.
|
|
|
|
func (zp *Parser) First() (RR, os.Error) {
|
|
|
|
// defer close something
|
|
|
|
go run(zp, quit)
|
|
|
|
select {
|
|
|
|
case r := <-zp.RR:
|
|
|
|
return r, nil
|
|
|
|
case e := <-zp.Error:
|
|
|
|
return nil, e
|
2011-07-22 21:28:04 +00:00
|
|
|
}
|
2011-09-02 11:44:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Run starts the parsers and returns the parsed Rr on the RR channel.
|
|
|
|
// Errors are return on the Error channel. After an error the parsing stops.
|
|
|
|
func (zp *Parser) Run(quit chan bool) {
|
|
|
|
go run(zp, quit)
|
2011-07-22 21:28:04 +00:00
|
|
|
}
|
|
|
|
|
2011-09-01 19:35:43 +00:00
|
|
|
// Run parses an DNS master zone file. It returns each parsed RR
|
|
|
|
// on the channel as soon as it has been parsed.
|
2011-09-02 11:44:35 +00:00
|
|
|
func run(zp *Parser, quit chan bool) (err os.Error) {
|
2011-07-22 21:28:04 +00:00
|
|
|
data := string(zp.buf)
|
2011-07-21 12:32:02 +00:00
|
|
|
cs, p, pe := 0, 0, len(data)
|
|
|
|
eof := len(data)
|
|
|
|
|
2011-09-02 11:44:35 +00:00
|
|
|
defer close(zp.Error)
|
|
|
|
defer close(zp.RR)
|
|
|
|
|
2011-07-22 10:26:31 +00:00
|
|
|
// brace := false
|
2011-07-24 15:08:33 +00:00
|
|
|
l := 1 // or... 0?
|
2011-07-21 12:32:02 +00:00
|
|
|
mark := 0
|
2011-07-23 20:26:50 +00:00
|
|
|
var hdr RR_Header
|
2011-09-02 12:01:55 +00:00
|
|
|
// Need to listen to the quit channel, do this in the mark action, or something?
|
2011-07-21 12:32:02 +00:00
|
|
|
|
|
|
|
%%{
|
2011-07-24 15:08:33 +00:00
|
|
|
|
2011-07-21 12:32:02 +00:00
|
|
|
action mark { mark = p }
|
2011-07-24 15:08:33 +00:00
|
|
|
action lineCount { l++ }
|
|
|
|
action setQname { if ! IsDomainName(data[mark:p]) {
|
2011-09-02 11:44:35 +00:00
|
|
|
zp.Error <- &ParseError{Error: "bad qname: " + data[mark:p], line: l}
|
|
|
|
return
|
2011-07-24 15:08:33 +00:00
|
|
|
}
|
|
|
|
hdr.Name = data[mark:p]
|
|
|
|
}
|
2011-09-02 11:44:35 +00:00
|
|
|
action errQclass { zp.Error <- &ParseError{Error: "bad qclass: " + data[mark:p], line: l}; return }
|
2011-07-23 21:15:40 +00:00
|
|
|
action setQclass { hdr.Class = str_class[data[mark:p]] }
|
2011-07-21 12:32:02 +00:00
|
|
|
action defTtl { /* ... */ }
|
2011-07-24 15:32:41 +00:00
|
|
|
action errTtl { /* ... */ }
|
|
|
|
action setTtl { i, _ := strconv.Atoui(data[mark:p]); hdr.Ttl = uint32(i) }
|
2011-07-22 10:26:31 +00:00
|
|
|
# action openBrace { if brace { println("Brace already open")} ; brace = true }
|
|
|
|
# action closeBrace { if !brace { println("Brace already closed")}; brace = false }
|
|
|
|
# action brace { brace }
|
2011-07-21 12:32:02 +00:00
|
|
|
|
2011-07-22 10:26:31 +00:00
|
|
|
include "types.rl";
|
2011-07-21 12:32:02 +00:00
|
|
|
|
2011-07-22 10:26:31 +00:00
|
|
|
nl = [\n]+ $lineCount;
|
|
|
|
comment = ';' [^\n]*;
|
2011-07-24 15:32:41 +00:00
|
|
|
ttl = digit+ >mark; #@err(errTtl)
|
2011-07-22 10:26:31 +00:00
|
|
|
# bl = ( [ \t]+
|
|
|
|
# | '(' $openBrace
|
|
|
|
# | ')' $closeBrace
|
|
|
|
# | (comment? nl)+ when brace
|
2011-07-22 15:29:56 +00:00
|
|
|
# )+;
|
2011-07-22 10:26:31 +00:00
|
|
|
bl = [ \t]+;
|
|
|
|
|
|
|
|
rdata = [^\n]+ >mark;
|
2011-07-24 20:21:48 +00:00
|
|
|
qname = [a-zA-Z0-9.\-_*]+ >mark %setQname;
|
2011-07-24 15:08:33 +00:00
|
|
|
qclass = ('IN'i|'CH'i|'HS'i) >mark %setQclass; # @err(errQclass);
|
2011-07-21 12:32:02 +00:00
|
|
|
|
|
|
|
lhs = qname? bl %defTtl (
|
|
|
|
(ttl %setTtl bl (qclass bl)?)
|
|
|
|
| (qclass bl (ttl %setTtl bl)?)
|
|
|
|
)?;
|
|
|
|
|
2011-07-22 10:26:31 +00:00
|
|
|
rhs = (
|
2011-07-22 20:06:07 +00:00
|
|
|
( 'A'i bl rdata ) %setA
|
|
|
|
| ( 'PTR'i bl rdata ) %setPTR
|
|
|
|
| ( 'TXT'i bl rdata ) %setTXT
|
|
|
|
| ( 'SRV'i bl rdata ) %setSRV
|
|
|
|
| ( 'CERT'i bl rdata ) %setCERT
|
|
|
|
| ( 'NAPTR'i bl rdata ) %setNAPTR
|
|
|
|
| ( 'AAAA'i bl rdata ) %setAAAA
|
|
|
|
| ( 'SOA'i bl rdata ) %setSOA
|
|
|
|
| ( 'CNAME'i bl rdata ) %setCNAME
|
|
|
|
| ( 'DNAME'i bl rdata ) %setDNAME
|
|
|
|
| ( 'NS'i bl rdata ) %setNS
|
|
|
|
| ( 'MX'i bl rdata ) %setMX
|
|
|
|
| ( 'DS'i bl rdata ) %setDS
|
|
|
|
| ( 'DLV'i bl rdata ) %setDLV
|
|
|
|
| ( 'TA'i bl rdata ) %setTA
|
|
|
|
| ( 'DNSKEY'i bl rdata ) %setDNSKEY
|
|
|
|
| ( 'RRSIG'i bl rdata ) %setRRSIG
|
|
|
|
| ( 'NSEC'i bl rdata ) %setNSEC
|
|
|
|
| ( 'NSEC3'i bl rdata ) %setNSEC3
|
2011-07-24 20:21:48 +00:00
|
|
|
| ( 'SSHFP'i bl rdata ) %setSSHFP
|
2011-07-22 20:06:07 +00:00
|
|
|
| ( 'NSEC3PARAM'i bl rdata ) %setNSEC3PARAM
|
2011-07-22 10:26:31 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
rr = lhs rhs;
|
|
|
|
# main := (rr? bl? ((comment? nl) when !brace))*;
|
2011-07-22 15:29:56 +00:00
|
|
|
main := ((rr?|comment?) nl)*;
|
2011-07-21 12:32:02 +00:00
|
|
|
|
|
|
|
write init;
|
|
|
|
write exec;
|
|
|
|
}%%
|
2011-09-02 11:02:29 +00:00
|
|
|
|
2011-07-21 12:32:02 +00:00
|
|
|
if eof > -1 {
|
|
|
|
if cs < z_first_final {
|
|
|
|
// No clue what I'm doing what so ever
|
|
|
|
if p == pe {
|
2011-07-23 06:54:44 +00:00
|
|
|
println("p", p, "pe", pe)
|
|
|
|
println("cs", cs, "z_first_final", z_first_final)
|
2011-07-24 15:08:33 +00:00
|
|
|
println("unexpected eof at line ", l)
|
2011-09-02 11:44:35 +00:00
|
|
|
return
|
2011-07-21 12:32:02 +00:00
|
|
|
} else {
|
2011-07-24 15:08:33 +00:00
|
|
|
println("error at position ", p, "\"",data[mark:p],"\" at line ", l)
|
2011-09-02 11:44:35 +00:00
|
|
|
return
|
2011-07-21 12:32:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-09-02 11:44:35 +00:00
|
|
|
return
|
2011-07-21 12:32:02 +00:00
|
|
|
}
|