From 32a879d9709b26c8874c18738ca3bd41bfa6f34d Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Wed, 14 Dec 2011 11:30:29 +0100 Subject: [PATCH] New parsing works, but a lot of stuff needs to be fixed: * Parsing the different records * Parsing private key files (trivial, but needs to be done) --- Makefile | 1 + keygen.go | 6 +- types.go | 23 ++- zparse.go | 47 ----- zparse.rl | 13 -- zscan.go | 44 +++-- zscan_rr.go | 500 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 555 insertions(+), 79 deletions(-) create mode 100644 zscan_rr.go diff --git a/Makefile b/Makefile index 43e03c04..009b4fbb 100644 --- a/Makefile +++ b/Makefile @@ -26,6 +26,7 @@ GOFILES=\ xfr.go\ zparse.go\ zscan.go\ + zscan_rr.go\ include $(GOROOT)/src/Make.pkg diff --git a/keygen.go b/keygen.go index e6b69c69..58c6447f 100644 --- a/keygen.go +++ b/keygen.go @@ -113,8 +113,7 @@ func (r *RR_DNSKEY) PrivateKeyString(p PrivateKey) (s string) { // Read reads a DNSKEY from the io.Reader q. func (k *RR_DNSKEY) Read(q io.Reader) error { - p := NewParser(q) - r, err := p.First() + r, err := newRRReader(q) if err != nil { return err } @@ -131,6 +130,7 @@ func (k *RR_DNSKEY) Read(q io.Reader) error { // ReadPrivateKey reads a private key from the io.Reader q. func (k *RR_DNSKEY) ReadPrivateKey(q io.Reader) (PrivateKey, error) { +/* p := NewParser(q) kv, _ := p.PrivateKey() if kv == nil { @@ -149,6 +149,8 @@ func (k *RR_DNSKEY) ReadPrivateKey(q io.Reader) (PrivateKey, error) { return k.readPrivateKeyECDSA(kv) } return nil, ErrPrivKey +*/ + return nil, nil } // Read a private key (file) string and create a public key. Return the private key. diff --git a/types.go b/types.go index 891fbb4f..dcf66bb0 100644 --- a/types.go +++ b/types.go @@ -9,6 +9,7 @@ import ( "net" "strconv" "strings" + "time" ) // Packet formats @@ -145,12 +146,6 @@ func (q *Question) String() string { return s } -// NewRRString returns the last RR contained in s. -func NewRRString(s string) (RR, error) { - p := NewParser(strings.NewReader(s)) - return p.First() -} - // NewRR returns a new RR with the hdr.Rrtype also set. // If the type i is not known, nil is returned. func NewRR(i uint16) RR { @@ -829,6 +824,22 @@ func tsigTimeToDate(t uint64) string { */ } +// 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, error) { + _, e := time.Parse("20060102150405", s) + if e != nil { + return 0, e + } + return 0, nil + /* + mod := t.Seconds() / Year68 + ti := uint32(t.Seconds() - (mod * Year68)) + return ti, nil + */ +} + // Map of constructors for each RR wire type. var rr_mk = map[uint16]func() RR{ TypeCNAME: func() RR { return new(RR_CNAME) }, diff --git a/zparse.go b/zparse.go index adb14376..5b313a87 100644 --- a/zparse.go +++ b/zparse.go @@ -5,11 +5,7 @@ package dns // With the thankful help of gdnsd and the Go examples for Ragel. import ( - "io" - // "net" - "strconv" "strings" - "time" ) const _IOBUF = MaxMsgSize @@ -21,55 +17,12 @@ type Parser struct { buf []byte } -type ParseError struct { - Err string - name string - line int -} - -func (e *ParseError) Error() string { - s := e.Err + ": \"" + e.name + "\" at line: " + strconv.Itoa(e.line) - return s -} - // First will return the first RR found when parsing. func (zp *Parser) First() (RR, error) { // defer close something return nil, nil } -// NewParser creates a new DNS file parser from r. -func NewParser(r io.Reader) *Parser { - buf := make([]byte, _IOBUF) - 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 - return p -} - -// 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, error) { - _, e := time.Parse("20060102150405", s) - if e != nil { - return 0, e - } - return 0, nil - /* - mod := t.Seconds() / Year68 - ti := uint32(t.Seconds() - (mod * Year68)) - return ti, nil - */ -} // Return the rdata fields as a string slice. // All starting whitespace is deleted. diff --git a/zparse.rl b/zparse.rl index 11bb6ffc..2b685a80 100644 --- a/zparse.rl +++ b/zparse.rl @@ -55,19 +55,6 @@ func NewParser(r io.Reader) *Parser { return p } -// 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 -} - // Return the rdata fields as a string slice. // All starting whitespace is deleted. // If i is 0 no spaces are deleted from the final rdfs. diff --git a/zscan.go b/zscan.go index 7891449f..0939a4ee 100644 --- a/zscan.go +++ b/zscan.go @@ -36,6 +36,17 @@ const ( _EXPECT_RDATA_BL // Whitespace BEFORE rdata starts ) +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) + + "and column: " + strconv.Itoa(e.lex.column) + return s +} + type Lex struct { token string value int @@ -43,17 +54,33 @@ type Lex struct { column int } +// ParseString parses a string and returns the RR contained in there. If they string +// contains more than one RR, only the first is returned. +func NewRRString(s string) (RR, error) { + 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) { + cr := make(chan RR) + go ParseZone(q, cr) + r := <-cr + return r, nil +} + // ParseZone reads a RFC 1035 zone from r. It returns each parsed RR on the // channel cr. The channel cr is closed by ParseZone when the end of r is // reached. func ParseZone(r io.Reader, cr chan RR) { - defer close(cr) + defer close(cr) var s scanner.Scanner c := make(chan Lex) s.Init(r) s.Mode = 0 s.Whitespace = 0 - // Start the lexer + // Start the lexer go lexer(s, c) // 5 possible beginnings of a line, _ is a space // 1. _OWNER _ _RRTYPE -> class/ttl omitted @@ -174,16 +201,11 @@ func ParseZone(r io.Reader, cr chan RR) { } 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 - } + r, e := setRR(h, c) + if e != nil { + fmt.Printf("%v\n", e) } + cr <- r st = _EXPECT_OWNER } } diff --git a/zscan_rr.go b/zscan_rr.go new file mode 100644 index 00000000..f812809a --- /dev/null +++ b/zscan_rr.go @@ -0,0 +1,500 @@ +package dns + +import ( + "net" +) + +// All data from c is either _STRING or _BLANK +// After the rdata there may come 1 _BLANK and then a _NEWLINE +// or immediately a _NEWLINE. If this is not the case we flag +// an error: garbage after rdata. + +func slurpRemainder(c chan Lex) error { + l := <-c + switch l.value { + case _BLANK: + l = <-c + if l.value != _NEWLINE { + return &ParseError{err: "garbage after rdata", lex: l} + } + // Ok + case _NEWLINE: + // Ok + default: + return &ParseError{err: "garbage after rdata", lex: l} + } + return nil +} + +func setRR(h RR_Header, c chan Lex) (RR, error) { + var ( + r RR + e error + ) + switch h.Rrtype { + case TypeA: + r, e = setA(h, c) + default: + println("RR not supported") + } + if se := slurpRemainder(c); se != nil { + return nil, se + } + return r, e +} + +func setA(h RR_Header, c chan Lex) (RR, error) { + rr := new(RR_A) + rr.Hdr = h + + l := <-c + rr.A = net.ParseIP(l.token) + if rr.A == nil { + return nil, &ParseError{err: "bad a", lex: l} + } + return rr, nil +} + +func setAAAA(h RR_Header, c chan Lex) (RR, error) { + rr := new(RR_AAAA) + rr.Hdr = h + + l := <-c + rr.AAAA = net.ParseIP(l.token) + if rr.AAAA == nil { + return nil, &ParseError{err: "bad AAAA", lex: l} + } + return rr, nil +} + +func setNS(h RR_Header, c chan Lex) (RR, error) { + rr := new(RR_NS) + rr.Hdr = h + + l := <-c + rr.Ns = l.token + if ! IsDomainName(l.token) { + return nil, &ParseError{err: "bad NS", lex: l} + } + return rr, nil +} + +func setMX(h RR_Header, c chan Lex) (RR, error) { + rr := new(RR_MX) + rr.Hdr = h + + l := <-c + i, e := strconv.Atoui(l.token) + rr.Pref = uint16(i) + l = <-c // _BLANK + l = <-c // _STRING + rr.Mx = l.token + if e != nil { + return nil, &ParseError{err: "bad MX", lex: l} + } + return rr, nil +} + +func setCNAME(h RR_Header, c chan Lex) (RR, error) { + rr := new(RR_CNAME) + rr.Hdr = h + + action setCNAME { + rdf := fields(data[mark:p], 1) + rr := new(RR_CNAME) + rr.Hdr = hdr + rr.Hdr.Rrtype = TypeCNAME + rr.Cname = rdf[0] + if ! IsDomainName(rdf[0]) { + zp.Err <- &ParseError{Error: "bad CNAME", name: rdf[0], line: l} + return + } + zp.RR <- rr + } + +func setCNAME(h RR_Header, c chan Lex) (RR, error) { + rr := new(RR_CNAME) + rr.Hdr = h + action setSOA { + var ( + i uint + err os.Error + ) + rdf := fields(data[mark:p], 7) + rr := new(RR_SOA) + rr.Hdr = hdr + rr.Hdr.Rrtype = TypeSOA + rr.Ns = rdf[0] + rr.Mbox = rdf[1] + if ! IsDomainName(rdf[0]) { + zp.Err <- &ParseError{Error: "bad SOA", name: rdf[0], line: l} + return + } + if ! IsDomainName(rdf[1]) { + zp.Err <- &ParseError{Error: "bad SOA", name: rdf[1], line: l} + return + } + for j, s := range rdf[2:7] { + if i, err = strconv.Atoui(s); err != nil { + zp.Err <- &ParseError{Error: "bad SOA", name: s, line: l} + return + } + switch j { + case 0: rr.Serial = uint32(i) + case 1: rr.Refresh = uint32(i) + case 2: rr.Retry = uint32(i) + case 3: rr.Expire = uint32(i) + case 4: rr.Minttl = uint32(i) + } + } + z.PushRR(rr) + zp.RR <- rr + } + +func setCNAME(h RR_Header, c chan Lex) (RR, error) { + rr := new(RR_CNAME) + rr.Hdr = h + action setDS { + var ( + i uint + e os.Error + ) + rdf := fields(data[mark:p], 4) + rr := new(RR_DS) + rr.Hdr = hdr + rr.Hdr.Rrtype = TypeDS + if i, e = strconv.Atoui(rdf[0]); e != nil { + zp.Err <- &ParseError{Error: "bad DS", name: rdf[0], line: l} + return + } + rr.KeyTag = uint16(i) + if i, e = strconv.Atoui(rdf[1]); e != nil { + zp.Err <- &ParseError{Error: "bad DS", name: rdf[1], line: l} + return + } + rr.Algorithm = uint8(i) + if i, e = strconv.Atoui(rdf[2]); e != nil { + zp.Err <- &ParseError{Error: "bad DS", name: rdf[2], line: l} + return + } + rr.DigestType = uint8(i) + rr.Digest = rdf[3] + zp.RR <- rr + } + +func setCNAME(h RR_Header, c chan Lex) (RR, error) { + rr := new(RR_CNAME) + rr.Hdr = h + action setDLV { + var ( + i uint + e os.Error + ) + rdf := fields(data[mark:p], 4) + rr := new(RR_DLV) + rr.Hdr = hdr + rr.Hdr.Rrtype = TypeDLV + if i, e = strconv.Atoui(rdf[0]); e != nil { + zp.Err <- &ParseError{Error: "bad DS", name: rdf[0], line: l} + return + } + rr.KeyTag = uint16(i) + if i, e = strconv.Atoui(rdf[1]); e != nil { + zp.Err <- &ParseError{Error: "bad DS", name: rdf[1], line: l} + return + } + rr.Algorithm = uint8(i) + if i, e = strconv.Atoui(rdf[2]); e != nil { + zp.Err <- &ParseError{Error: "bad DS", name: rdf[2], line: l} + return + } + rr.DigestType = uint8(i) + rr.Digest = rdf[3] + zp.RR <- rr + } + +func setCNAME(h RR_Header, c chan Lex) (RR, error) { + rr := new(RR_CNAME) + rr.Hdr = h + action setTA { + var ( + i uint + e os.Error + ) + rdf := fields(data[mark:p], 4) + rr := new(RR_TA) + rr.Hdr = hdr + rr.Hdr.Rrtype = TypeTA + if i, e = strconv.Atoui(rdf[0]); e != nil { + zp.Err <- &ParseError{Error: "bad DS", name: rdf[0], line: l} + return + } + rr.KeyTag = uint16(i) + if i, e = strconv.Atoui(rdf[1]); e != nil { + zp.Err <- &ParseError{Error: "bad DS", name: rdf[1], line: l} + return + } + rr.Algorithm = uint8(i) + if i, e = strconv.Atoui(rdf[2]); e != nil { + zp.Err <- &ParseError{Error: "bad DS", name: rdf[2], line: l} + return + } + rr.DigestType = uint8(i) + rr.Digest = rdf[3] + zp.RR <- rr + } + +func setCNAME(h RR_Header, c chan Lex) (RR, error) { + rr := new(RR_CNAME) + rr.Hdr = h + action setDNSKEY { + var ( + i uint + e os.Error + ) + rdf := fields(data[mark:p], 4) + rr := new(RR_DNSKEY) + rr.Hdr = hdr + rr.Hdr.Rrtype = TypeDNSKEY + + if i, e = strconv.Atoui(rdf[0]); e != nil { + zp.Err <- &ParseError{Error: "bad DNSKEY", name: rdf[0], line: l} + return + } + rr.Flags = uint16(i) + if i, e = strconv.Atoui(rdf[1]); e != nil { + zp.Err <- &ParseError{Error: "bad DNSKEY", name: rdf[1], line: l} + return + } + rr.Protocol = uint8(i) + if i, e = strconv.Atoui(rdf[2]); e != nil { + zp.Err <- &ParseError{Error: "bad DNSKEY", name: rdf[2], line: l} + return + } + rr.Algorithm = uint8(i) + rr.PublicKey = rdf[3] + zp.RR <- rr + } + +func setCNAME(h RR_Header, c chan Lex) (RR, error) { + rr := new(RR_CNAME) + rr.Hdr = h + action setRRSIG { + var ( + i uint + j uint32 + err os.Error + ) + rdf := fields(data[mark:p], 9) + rr := new(RR_RRSIG) + rr.Hdr = hdr + rr.Hdr.Rrtype = TypeRRSIG + + if _, ok := str_rr[strings.ToUpper(rdf[0])]; !ok { + zp.Err <- &ParseError{Error: "bad RRSIG", name: rdf[0], line: l} + return + } + rr.TypeCovered = str_rr[strings.ToUpper(rdf[0])] + + if i, err = strconv.Atoui(rdf[1]); err != nil { + zp.Err <- &ParseError{Error: "bad RRSIG", name: rdf[1], line: l} + return + } + rr.Algorithm = uint8(i) + if i, err = strconv.Atoui(rdf[2]); err != nil { + zp.Err <- &ParseError{Error: "bad RRSIG", name: rdf[2], line: l} + return + } + rr.Labels = uint8(i) + if i, err = strconv.Atoui(rdf[3]); err != nil { + zp.Err <- &ParseError{Error: "bad RRSIG", name: rdf[3], line: l} + return + } + rr.OrigTtl = uint32(i) + + if j, err = dateToTime(rdf[4]); err != nil { + zp.Err <- &ParseError{Error: "bad RRSIG", name: rdf[4], line: l} + return + } + rr.Expiration = j + if j, err = dateToTime(rdf[5]); err != nil { + zp.Err <- &ParseError{Error: "bad RRSIG", name: rdf[5], line: l} + return + } + rr.Inception = j + + if i, err = strconv.Atoui(rdf[6]); err != nil { + zp.Err <- &ParseError{Error: "bad RRSIG", name: rdf[3], line: l} + return + } + rr.KeyTag = uint16(i) + + rr.SignerName = rdf[7] + if ! IsDomainName(rdf[7]) { + zp.Err <- &ParseError{Error: "bad RRSIG", name: rdf[7], line: l} + return + } + // Check base64 TODO + rr.Signature = rdf[8] + zp.RR <- rr + } + +func setCNAME(h RR_Header, c chan Lex) (RR, error) { + rr := new(RR_CNAME) + rr.Hdr = h + action setNSEC { + rdf := fields(data[mark:p], 0) + rr := new(RR_NSEC) + rr.Hdr = hdr + rr.Hdr.Rrtype = TypeNSEC + rr.NextDomain = rdf[0] + rr.TypeBitMap = make([]uint16, len(rdf)-1) + // Fill the Type Bit Map + for i := 1; i < len(rdf); i++ { + // Check if its there in the map TODO + rr.TypeBitMap[i-1] = str_rr[strings.ToUpper(rdf[i])] + } + zp.RR <- rr + } + +func setCNAME(h RR_Header, c chan Lex) (RR, error) { + rr := new(RR_CNAME) + rr.Hdr = h + action setNSEC3 { + var ( + i uint + e os.Error + ) + rdf := fields(data[mark:p], 0) + rr := new(RR_NSEC3) + rr.Hdr = hdr + rr.Hdr.Rrtype = TypeNSEC3 + + if i, e = strconv.Atoui(rdf[0]); e != nil { + zp.Err <- &ParseError{Error: "bad NSEC3", name: rdf[0], line: l} + return + } + rr.Hash = uint8(i) + if i, e = strconv.Atoui(rdf[1]); e != nil { + zp.Err <- &ParseError{Error: "bad NSEC3", name: rdf[1], line: l} + return + } + rr.Flags = uint8(i) + if i, e = strconv.Atoui(rdf[2]); e != nil { + zp.Err <- &ParseError{Error: "bad NSEC3", name: rdf[2], line: l} + return + } + rr.Iterations = uint16(i) + rr.SaltLength = uint8(len(rdf[3])) + rr.Salt = rdf[3] + + rr.HashLength = uint8(len(rdf[4])) + rr.NextDomain = rdf[4] + rr.TypeBitMap = make([]uint16, len(rdf)-5) + // Fill the Type Bit Map + for i := 5; i < len(rdf); i++ { + // Check if its there in the map TODO + rr.TypeBitMap[i-5] = str_rr[strings.ToUpper(rdf[i])] + } + zp.RR <- rr + } + +func setCNAME(h RR_Header, c chan Lex) (RR, error) { + rr := new(RR_CNAME) + rr.Hdr = h + action setNSEC3PARAM { + var ( + i int + e os.Error + ) + rdf := fields(data[mark:p], 4) + rr := new(RR_NSEC3PARAM) + rr.Hdr = hdr + rr.Hdr.Rrtype = TypeNSEC3PARAM + if i, e = strconv.Atoi(rdf[0]); e != nil { + zp.Err <- &ParseError{Error: "bad NSEC3PARAM", name: rdf[0], line: l} + return + } + rr.Hash = uint8(i) + if i, e = strconv.Atoi(rdf[1]); e != nil { + zp.Err <- &ParseError{Error: "bad NSEC3PARAM", name: rdf[1], line: l} + return + } + rr.Flags = uint8(i) + if i, e = strconv.Atoi(rdf[2]); e != nil { + zp.Err <- &ParseError{Error: "bad NSEC3PARAM", name: rdf[2], line: l} + return + } + rr.Iterations = uint16(i) + rr.Salt = rdf[3] + rr.SaltLength = uint8(len(rr.Salt)) + zp.RR <- rr + } + +func setCNAME(h RR_Header, c chan Lex) (RR, error) { + rr := new(RR_CNAME) + rr.Hdr = h + action setTXT { + rdf := fields(data[mark:p], 1) + rr := new(RR_TXT) + rr.Hdr = hdr + rr.Hdr.Rrtype = TypeTXT + rr.Txt = rdf[0] + zp.RR <- rr + } + +func setCNAME(h RR_Header, c chan Lex) (RR, error) { + rr := new(RR_CNAME) + rr.Hdr = h + action setSRV { + } +func setCNAME(h RR_Header, c chan Lex) (RR, error) { + rr := new(RR_CNAME) + rr.Hdr = h + + action setCERT { + } + +func setCNAME(h RR_Header, c chan Lex) (RR, error) { + rr := new(RR_CNAME) + rr.Hdr = h + action setPTR { + } + +func setCNAME(h RR_Header, c chan Lex) (RR, error) { + rr := new(RR_CNAME) + rr.Hdr = h + action setDNAME { + } + +func setCNAME(h RR_Header, c chan Lex) (RR, error) { + rr := new(RR_CNAME) + rr.Hdr = h + action setNAPTR { + } + +func setCNAME(h RR_Header, c chan Lex) (RR, error) { + rr := new(RR_CNAME) + rr.Hdr = h + action setSSHFP { + var ( + i int + e os.Error + ) + rdf := fields(data[mark:p], 3) + rr := new(RR_SSHFP) + rr.Hdr = hdr + rr.Hdr.Rrtype = TypeSSHFP + if i, e = strconv.Atoi(rdf[0]); e != nil { + zp.Err <- &ParseError{Error: "bad SSHFP", name: rdf[0], line: l} + return + } + rr.Algorithm = uint8(i) + if i, e = strconv.Atoi(rdf[1]); e != nil { + zp.Err <- &ParseError{Error: "bad SSHFP", name: rdf[1], line: l} + return + } + rr.Type = uint8(i) + rr.FingerPrint = rdf[2] + zp.RR <- rr + }