Error handling

Handle semantic errors in the input stream.
Try to use Ragel's error handling when seeing a non-supported
class. This does not work yet.
This commit is contained in:
Miek Gieben 2011-07-24 17:08:33 +02:00
parent f38060d359
commit 4671072027
10 changed files with 386 additions and 269 deletions

View File

@ -111,3 +111,73 @@ func (dns *Msg) SetTsig(z, algo string, fudge uint16, timesigned uint64) {
t.TimeSigned = timesigned
dns.Extra = append(dns.Extra, t)
}
// IsDomainName checks if s is a valid domainname.
func IsDomainName(s string) bool { // copied from net package.
// See RFC 1035, RFC 3696.
if len(s) == 0 {
return false
}
if len(s) > 255 {
return false
}
if s[len(s)-1] != '.' { // simplify checking loop: make name end in dot
s += "."
}
last := byte('.')
ok := false // ok once we've seen a letter
partlen := 0
for i := 0; i < len(s); i++ {
c := s[i]
switch {
default:
return false
case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_':
ok = true
partlen++
case '0' <= c && c <= '9':
// fine
partlen++
case c == '-':
// byte before dash cannot be dot
if last == '.' {
return false
}
partlen++
case c == '.':
// byte before dot cannot be dot, dash
if last == '.' || last == '-' {
return false
}
if partlen > 63 || partlen == 0 {
return false
}
partlen = 0
}
last = c
}
return ok
}
// Return the number of labels in a domain name.
// Need to add these kind of function in a structured way. TODO(mg)
func Labels(a string) (c uint8) {
// walk the string and count the dots
// except when it is escaped
esc := false
for _, v := range a {
switch v {
case '.':
if esc {
esc = !esc
continue
}
c++
case '\\':
esc = true
}
}
return
}

21
dns.go
View File

@ -145,24 +145,3 @@ func (h *RR_Header) String() string {
}
return s
}
// Return the number of labels in a domain name.
// Need to add these kind of function in a structured way. TODO(mg)
func LabelCount(a string) (c uint8) {
// walk the string and count the dots
// except when it is escaped
esc := false
for _, v := range a {
switch v {
case '.':
if esc {
esc = !esc
continue
}
c++
case '\\':
esc = true
}
}
return
}

View File

@ -183,7 +183,7 @@ func (s *RR_RRSIG) Sign(k PrivateKey, rrset RRset) bool {
s.OrigTtl = rrset[0].Header().Ttl
s.TypeCovered = rrset[0].Header().Rrtype
s.TypeCovered = rrset[0].Header().Rrtype
s.Labels = LabelCount(rrset[0].Header().Name)
s.Labels = Labels(rrset[0].Header().Name)
if strings.HasPrefix(rrset[0].Header().Name, "*") {
s.Labels-- // wildcards, remove from label count
}

View File

@ -100,7 +100,7 @@ func TestSignVerify(t *testing.T) {
sig := new(RR_RRSIG)
sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
sig.TypeCovered = soa.Hdr.Rrtype
sig.Labels = LabelCount(soa.Hdr.Name)
sig.Labels = Labels(soa.Hdr.Name)
sig.OrigTtl = soa.Hdr.Ttl
sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"

View File

@ -114,9 +114,9 @@ func (r *RR_DNSKEY) PrivateKeyString(p PrivateKey) (s string) {
func (k *RR_DNSKEY) Read(q io.Reader) os.Error {
p := NewParser(q)
r := p.RR()
if r == nil {
return nil
r, err := p.RR()
if err != nil {
return err
}
if _, ok := r.(*RR_DNSKEY); !ok {
panic("did not read a DNSKEY")

View File

@ -124,27 +124,21 @@ func TestParse(t *testing.T) {
}
}
func TestSetString(t *testing.T) {
a := NewRR("miek.nl. IN A 127.0.0.1")
if a.String() != "miek.nl.\t0\tIN\tA\t127.0.0.1" {
t.Log(a.String(), "!= miek.nl. IN A 127.0.0.1")
t.Fail()
}
b := NewRR("miek.nl. IN AAAA ::1")
if b.String() != "miek.nl.\t0\tIN\tAAAA\t::1" {
t.Log(b.String(), "!= miek.nl. IN AAAA ::1")
t.Fail()
}
c := NewRR("miek.nl. IN MX 10 miek.nl.")
if c.String() != "miek.nl.\t0\tIN\tMX\t10 miek.nl." {
t.Log(c.String(), "!= miek.nl. IN MX 10 miek.nl.")
t.Fail()
}
d := NewRR("miek.nl. IN NS ns1.miek.nl")
if d.String() != "miek.nl.\t0\tIN\tNS\tns1.miek.nl" {
t.Log(d.String(), "!= miek.nl. IN NS ns1.miek.nl")
t.Fail()
}
func TestParseFailure(t *testing.T) {
tests := []string{"miek.nl. IN A 327.0.0.1",
"miek.nl. IN AAAA ::x",
"miek.nl. IN MX a0 miek.nl.",
"miek.nl. PA MX 10 miek.nl.",
}
for _, t1 := range tests {
_, err := NewRR(t1)
if err == nil {
t.Log("Should have triggered an error")
t.Fail()
}
t.Logf("%s: %s\n", t1, err.String())
}
}
func BenchmarkZoneParsing(b *testing.B) {

View File

@ -6,6 +6,7 @@
package dns
import (
"os"
"net"
"time"
"strconv"
@ -147,13 +148,9 @@ func (q *Question) String() string {
}
// NewRR returns the last RR contained in s.
func NewRR(s string) RR {
func NewRR(s string) (RR, os.Error) {
p := NewParser(strings.NewReader(s))
x := p.RR()
if x == nil {
return nil
}
return x
return p.RR()
}
type RR_CNAME struct {

View File

@ -9,6 +9,9 @@
rr.Hdr.Rrtype = TypeA
rr.A = net.ParseIP(rdf[0])
z.Push(rr)
if rr.A == nil {
return z, &ParseError{Error: "bad A: " + rdf[0], line: l}
}
}
action setAAAA {
@ -18,6 +21,9 @@
rr.Hdr.Rrtype = TypeAAAA
rr.AAAA = net.ParseIP(rdf[0])
z.Push(rr)
if rr.AAAA == nil {
return z, &ParseError{Error: "bad AAAA: " + rdf[0], line: l}
}
}
action setNS {
@ -27,6 +33,9 @@
rr.Hdr.Rrtype = TypeNS
rr.Ns = rdf[0]
z.Push(rr)
if ! IsDomainName(rdf[0]) {
return z, &ParseError{Error: "bad NS: " + rdf[0], line: l}
}
}
action setMX {
@ -34,9 +43,13 @@
rr := new(RR_MX)
rr.Hdr = hdr
rr.Hdr.Rrtype = TypeMX
rr.Pref = uint16(atoi(rdf[0]))
i, err := strconv.Atoui(rdf[0])
rr.Pref = uint16(i)
rr.Mx = rdf[1]
z.Push(rr)
if err != nil {
return z, &ParseError{Error: "bad MX: " + rdf[0], line: l}
}
}
action setCNAME {
@ -46,6 +59,9 @@
rr.Hdr.Rrtype = TypeCNAME
rr.Cname = rdf[0]
z.Push(rr)
if ! IsDomainName(rdf[0]) {
return z, &ParseError{Error: "bad CNAME: " + rdf[0], line: l}
}
}
action setSOA {

448
zparse.go

File diff suppressed because it is too large Load Diff

View File

@ -20,6 +20,16 @@ type Parser struct {
buf []byte
}
type ParseError struct {
Error string
line int
}
func (e *ParseError) String() string {
s := e.Error + " line: " + strconv.Itoa(e.line)
return s
}
// NewParser creates a new DNS file parser from r.
func NewParser(r io.Reader) *Parser {
buf := make([]byte, _IOBUF)
@ -70,12 +80,12 @@ func atoi(s string) uint {
}%%
// RR parses a zone file, but only returns the last RR read.
func (zp *Parser) RR() RR {
func (zp *Parser) RR() (RR, os.Error) {
z, err := zp.Zone()
if err != nil {
return nil
return nil, err
}
return z.Pop().(RR)
return z.Pop().(RR), nil
}
// Zone parses an DNS master zone file.
@ -86,18 +96,29 @@ func (zp *Parser) Zone() (z *Zone, err os.Error) {
eof := len(data)
// brace := false
lines := 0
l := 1 // or... 0?
mark := 0
var hdr RR_Header
%%{
action mark { mark = p }
action setQname { hdr.Name = data[mark:p] }
action lineCount { l++ }
action setQname { if ! IsDomainName(data[mark:p]) {
return z, &ParseError{Error: "bad qname: " + data[mark:p], line: l}
}
hdr.Name = data[mark:p]
}
action errQclass { return z, &ParseError{Error: "bad qclass: " + data[mark:p], line: l} }
action setQclass { hdr.Class = str_class[data[mark:p]] }
action defTtl { /* ... */ }
action setTtl { ttl := atoi(data[mark:p]); hdr.Ttl = uint32(ttl) }
action lineCount { lines++ }
action setTtl { i, err := strconv.Atoui(data[mark:p])
if err != nil {
return z, &ParseError{Error: "bad ttl: " + data[mark:p], line: l}
}
hdr.Ttl = uint32(i)
}
# action openBrace { if brace { println("Brace already open")} ; brace = true }
# action closeBrace { if !brace { println("Brace already closed")}; brace = false }
# action brace { brace }
@ -116,7 +137,7 @@ func (zp *Parser) Zone() (z *Zone, err os.Error) {
rdata = [^\n]+ >mark;
qname = [a-zA-Z0-9.\-_]+ >mark %setQname;
qclass = ('IN'i|'CH'i|'HS'i) >mark %setQclass;
qclass = ('IN'i|'CH'i|'HS'i) >mark %setQclass; # @err(errQclass);
lhs = qname? bl %defTtl (
(ttl %setTtl bl (qclass bl)?)
@ -160,10 +181,10 @@ func (zp *Parser) Zone() (z *Zone, err os.Error) {
if p == pe {
println("p", p, "pe", pe)
println("cs", cs, "z_first_final", z_first_final)
println("unexpected eof at line ", lines)
println("unexpected eof at line ", l)
return z, nil
} else {
println("error at position ", p, "\"",data[mark:p],"\" at line ", lines)
println("error at position ", p, "\"",data[mark:p],"\" at line ", l)
return z, nil
}
}