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:
parent
f38060d359
commit
4671072027
70
defaults.go
70
defaults.go
|
@ -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
21
dns.go
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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) {
|
||||
|
|
9
types.go
9
types.go
|
@ -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 {
|
||||
|
|
18
types.rl
18
types.rl
|
@ -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 {
|
||||
|
|
43
zparse.rl
43
zparse.rl
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue