added quotes as important chars in the parser

This commit is contained in:
Miek Gieben 2012-02-12 19:06:32 +01:00
parent 059c08af9f
commit 5a48b54327
4 changed files with 107 additions and 19 deletions

View File

@ -16,6 +16,7 @@ things that need to be fixed.
* Add tsig check in 'q'? * Add tsig check in 'q'?
* More RRs to add. Parsing of strings within the rdata * More RRs to add. Parsing of strings within the rdata
* Unknown RR parsing * Unknown RR parsing
* \DDD in zonefiles
## BUGS ## BUGS

View File

@ -197,6 +197,43 @@ func TestParseNSEC(t *testing.T) {
} }
} }
func TestQuotes(t *testing.T) {
tests := map[string]string{
`t.example.com. IN TXT "a bc"`: "t.example.com.\t3600\tIN\tTXT\t\"a bc\"",
`t.example.com. IN TXT "a
bc"`: "t.example.com.\t3600\tIN\tTXT\t\"a\n bc\"",
`t.example.com. IN TXT "aaa" ;`: "t.example.com.\t3600\tIN\tTXT\t\"aaa\"",
`t.example.com. IN TXT "abc" "DEF"`: "t.example.com.\t3600\tIN\tTXT\t\"abcDEF\"",
// `t.example.com. IN TXT "abc" ( "DEF" )`: "t.example.com.\t3600\tIN\tTXT\t",
// `t.example.com. IN TXT;`: "t.example.com.\t3600\tIN\tTXT\t",
// `t.example.com. IN TXT ;`: "t.example.com.\t3600\tIN\tTXT\t",
// `t.example.com. IN TXT aaa ;`: "t.example.com.\t3600\tIN\tTXT\t \"aaaa\"",
// `t.example.com. IN TXT aaa`: "t.example.com.\t3600\tIN\tTXT\t\"aaa\"",
// "cid.urn.arpa. NAPTR 100 50 \"s\" \"z3950+I2L+I2C\" \"\" _z3950._tcp.gatech.edu.":
// "cid.urn.arpa.\t3600\tIN\tNAPTR\t100 50 \"s\" \"z3950+I2L+I2C\" \"\" _z3950._tcp.gatech.edu.",
// "cid.urn.arpa. NAPTR 100 50 \"s\" \"rcds+I2C\" \"\" _rcds._udp.gatech.edu.":
// "cid.urn.arpa.\t3600\tIN\tNAPTR\t100 50 \"s\" \"rcds+I2C\" \"\" _rcds._udp.gatech.edu.",
// "cid.urn.arpa. NAPTR 100 50 \"s\" \"http+I2L+I2C+I2R\" \"\" _http._tcp.gatech.edu.":
// "cid.urn.arpa.\t3600\tN\tNAPTR\t100 50 \"s\" \"http+I2L+I2C+I2R\" \"\" _http._tcp.gatech.edu.",
// "cid.urn.arpa. NAPTR 100 10 \"\" \"\" \"/urn:cid:.+@([^\\.]+\\.)(.*)$/\\2/i\" .":
// "cid.urn.arpa.\t3600\tIN\tNAPTR\t100 10 \"\" \"\" \"/urn:cid:.+@([^\\.]+\\.)(.*)$/\\2/i\" .",
}
for i, o := range tests {
rr, e := NewRR(i)
if e != nil {
t.Log("Failed to parse RR: " + e.Error())
t.Fail()
continue
}
if rr.String() != o {
t.Logf("`%s' should be equal to\n`%s', but is `%s'\n", i, o, rr.String())
t.Fail()
} else {
t.Logf("RR is OK: `%s'", rr.String())
}
}
}
func TestParseBrace(t *testing.T) { func TestParseBrace(t *testing.T) {
tests := map[string]string{ tests := map[string]string{
"(miek.nl.) 3600 IN A 127.0.0.1": "miek.nl.\t3600\tIN\tA\t127.0.0.1", "(miek.nl.) 3600 IN A 127.0.0.1": "miek.nl.\t3600\tIN\tA\t127.0.0.1",

View File

@ -10,9 +10,11 @@ import (
) )
// Only used when debugging the parser itself. // Only used when debugging the parser itself.
var _DEBUG = false var _DEBUG = true
const maxTok = 1024 // Complete unsure about the correctness of this value?
// Large blobs of base64 code might get longer than this....
const maxTok = 300
// Tokinize a RFC 1035 zone file. The tokenizer will normalize it: // Tokinize a RFC 1035 zone file. The tokenizer will normalize it:
// * Add ownernames if they are left blank; // * Add ownernames if they are left blank;
@ -25,6 +27,7 @@ const (
_EOF = iota // Don't let it start with zero _EOF = iota // Don't let it start with zero
_STRING _STRING
_BLANK _BLANK
_QUOTE
_NEWLINE _NEWLINE
_RRTYPE _RRTYPE
_OWNER _OWNER
@ -384,6 +387,12 @@ func zlexer(s scanner.Scanner, c chan lex) {
// avoids a len(x) that Go otherwise will perform when comparing strings. // avoids a len(x) that Go otherwise will perform when comparing strings.
switch x := s.TokenText(); x[0] { switch x := s.TokenText(); x[0] {
case ' ', '\t': case ' ', '\t':
if quote {
// Inside quotes this is legal
str[stri] = byte(x[0])
stri++
break
}
escape = false escape = false
if commt { if commt {
break break
@ -429,21 +438,28 @@ func zlexer(s scanner.Scanner, c chan lex) {
owner = false owner = false
space = true space = true
case ';': case ';':
if escape { if quote {
escape = false // Inside quotes this is legal
str[stri] = ';' str[stri] = byte(x[0])
stri++ stri++
break break
} }
if quote { if escape {
// Inside quoted text we allow ; escape = false
str[stri] = ';' str[stri] = byte(x[0])
stri++ stri++
break break
} }
commt = true commt = true
case '\n': case '\n':
// Hmmm, escape newline // Hmmm, escape newline
if quote {
str[stri] = byte(x[0])
stri++
break
}
// inside quotes this is legal
escape = false escape = false
if commt { if commt {
// Reset a comment // Reset a comment
@ -490,16 +506,17 @@ func zlexer(s scanner.Scanner, c chan lex) {
rrtype = false rrtype = false
owner = true owner = true
case '\\': case '\\':
// quote?
if commt { if commt {
break break
} }
if escape { if escape {
str[stri] = '\\' str[stri] = byte(x[0])
stri++ stri++
escape = false escape = false
break break
} }
str[stri] = '\\' str[stri] = byte(x[0])
stri++ stri++
escape = true escape = true
case '"': case '"':
@ -507,30 +524,49 @@ func zlexer(s scanner.Scanner, c chan lex) {
break break
} }
if escape { if escape {
str[stri] = '"' str[stri] = byte(x[0])
stri++ stri++
escape = false escape = false
break break
} }
// str += "\"" don't add quoted quotes // send previous gathered text and the quote
if stri != 0 {
l.value = _STRING
l.token = string(str[:stri])
c <-l
stri = 0
}
l.value = _QUOTE
l.token = "\""
c <- l
quote = !quote quote = !quote
case '(': case '(':
if quote {
str[stri] = byte(x[0])
stri++
break
}
if commt { if commt {
break break
} }
if escape { if escape {
str[stri] = '(' str[stri] = byte(x[0])
stri++ stri++
escape = false escape = false
break break
} }
brace++ brace++
case ')': case ')':
if quote {
str[stri] = byte(x[0])
stri++
break
}
if commt { if commt {
break break
} }
if escape { if escape {
str[stri] = ')' str[stri] = byte(x[0])
stri++ stri++
escape = false escape = false
break break
@ -546,7 +582,7 @@ func zlexer(s scanner.Scanner, c chan lex) {
break break
} }
escape = false escape = false
str[stri] = byte(x[0]) // This should be ok... str[stri] = byte(x[0])
stri++ stri++
space = false space = false
} }

View File

@ -49,6 +49,9 @@ func setRR(h RR_Header, c chan lex, o, f string) (RR, *ParseError) {
case TypeSRV: case TypeSRV:
r, e = setSRV(h, c, o, f) r, e = setSRV(h, c, o, f)
goto Slurp goto Slurp
case TypeNAPTR:
r, e = setNAPTR(h, c, o, f)
goto Slurp
// These types have a variable ending either chunks of txt or chunks/base64 or hex. // These types have a variable ending either chunks of txt or chunks/base64 or hex.
// They need to search for the end of the RR themselves, hence they look for the ending // They need to search for the end of the RR themselves, hence they look for the ending
// newline. Thus there is no need to slurp the remainder, because there is none. // newline. Thus there is no need to slurp the remainder, because there is none.
@ -64,8 +67,6 @@ func setRR(h RR_Header, c chan lex, o, f string) (RR, *ParseError) {
return setNSEC3PARAM(h, c, f) return setNSEC3PARAM(h, c, f)
case TypeDS: case TypeDS:
return setDS(h, c, f) return setDS(h, c, f)
case TypeNAPTR:
return setNAPTR(h, c, o, f)
case TypeTXT: case TypeTXT:
return setTXT(h, c, f) return setTXT(h, c, f)
default: default:
@ -328,19 +329,23 @@ func setNAPTR(h RR_Header, c chan lex, o, f string) (RR, *ParseError) {
} }
<-c // _BLANK <-c // _BLANK
l = <-c // _STRING l = <-c // _STRING
println("Flags", l.token)
rr.Flags = l.token rr.Flags = l.token
<-c // _BLANK <-c // _BLANK
l = <-c // _STRING l = <-c // _STRING
println("Service", l.token)
rr.Service = l.token rr.Service = l.token
<-c // _BLANK <-c // _BLANK
l = <-c // _STRING l = <-c // _STRING
println("Regexp", l.token)
rr.Regexp = l.token rr.Regexp = l.token
<-c // _BLANK <-c // _BLANK
l = <-c // _STRING l = <-c // _STRING
rr.Replacement = l.token rr.Replacement = l.token
println("Replacement", l.token, "A")
_, ld, ok := IsDomainName(l.token) _, ld, ok := IsDomainName(l.token)
if !ok { if !ok {
return nil, &ParseError{f, "bad NAPTR Replacement", l} return nil, &ParseError{f, "bad NAPTR Replacement", l}
@ -703,14 +708,23 @@ func setTXT(h RR_Header, c chan lex, f string) (RR, *ParseError) {
rr.Hdr = h rr.Hdr = h
// Get the remaining data until we see a NEWLINE // Get the remaining data until we see a NEWLINE
quote := false
l := <-c l := <-c
var s string var s string
for l.value != _NEWLINE && l.value != _EOF { for l.value != _NEWLINE && l.value != _EOF {
println("SEEN", l.value, l.token)
switch l.value { switch l.value {
case _STRING: case _STRING:
s += l.token if quote {
s += l.token
}
case _BLANK: case _BLANK:
s += l.token if quote {
// _BLANK can only be seen in between txt parts.
return nil, &ParseError{f, "bad TXT Txt", l}
}
case _QUOTE:
quote = !quote
default: default:
return nil, &ParseError{f, "bad TXT", l} return nil, &ParseError{f, "bad TXT", l}
} }