From 0adc9a3afeb45196ba05406883b187798c27151c Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Fri, 6 Sep 2013 09:45:30 +0000 Subject: [PATCH 1/5] Update doc and TODO --- TODO.markdown | 4 ---- dns.go | 5 ++++- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/TODO.markdown b/TODO.markdown index b479118a..16582cdd 100644 --- a/TODO.markdown +++ b/TODO.markdown @@ -1,10 +1,6 @@ # TODO * Support for on-the-fly-signing or check how to do it -* Test all rdata packing with zero rdata -- allowed for dynamic updates -* Actually mimic net/ ? Dial. Read/Write ? - - if I want this i need to work on something else than \*Client, because a single client - can have multiple oustanding qeuries * Ratelimiting? server side (rrl) * Ratelimiting? client side diff --git a/dns.go b/dns.go index ed7b87cf..003908c4 100644 --- a/dns.go +++ b/dns.go @@ -61,7 +61,10 @@ // c := new(Client) // in, rtt, err := c.Exchange(m1, "127.0.0.1:53") // -// For asynchronous queries it is easy to wrap Exchange() in a goroutine. +// For asynchronous queries it is easy to wrap Exchange() in a goroutine. Suppressing +// multiple outstanding queries (with the same question, type and class) is as easy as setting: +// +// c.Inflight = true // // A dns message consists out of four sections. // The question section: in.Question, the answer section: in.Answer, From 2b6e9122bd2c44a4b96e12ee4bbd125d569ecd4d Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Fri, 6 Sep 2013 09:49:07 +0000 Subject: [PATCH 2/5] Use the better name: SingleInflight --- client.go | 14 +++++++------- client_test.go | 4 ++-- dns.go | 2 +- server.go | 2 +- xfr.go | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/client.go b/client.go index 847af4f5..02c17396 100644 --- a/client.go +++ b/client.go @@ -31,16 +31,16 @@ type reply struct { // A Client defines parameter for a DNS client. A nil // Client is usable for sending queries. type Client struct { - Net string // if "tcp" a TCP query will be initiated, otherwise an UDP one (default is "" for UDP) - ReadTimeout time.Duration // the net.Conn.SetReadTimeout value for new connections (ns), defaults to 2 * 1e9 - WriteTimeout time.Duration // the net.Conn.SetWriteTimeout value for new connections (ns), defaults to 2 * 1e9 - TsigSecret map[string]string // secret(s) for Tsig map[], zonename must be fully qualified - Inflight bool // if true suppress multiple outstanding queries for the same Qname, Qtype and Qclass - group singleflight + Net string // if "tcp" a TCP query will be initiated, otherwise an UDP one (default is "" for UDP) + ReadTimeout time.Duration // the net.Conn.SetReadTimeout value for new connections (ns), defaults to 2 * 1e9 + WriteTimeout time.Duration // the net.Conn.SetWriteTimeout value for new connections (ns), defaults to 2 * 1e9 + TsigSecret map[string]string // secret(s) for Tsig map[], zonename must be fully qualified + SingleInflight bool // if true suppress multiple outstanding queries for the same Qname, Qtype and Qclass + group singleflight } func (c *Client) exchangeMerge(m *Msg, a string, s net.Conn) (r *Msg, rtt time.Duration, err error) { - if !c.Inflight { + if !c.SingleInflight { if s == nil { return c.exchange(m, a) } diff --git a/client_test.go b/client_test.go index 63b6f326..0d93e961 100644 --- a/client_test.go +++ b/client_test.go @@ -41,12 +41,12 @@ func TestClientEDNS0(t *testing.T) { } } -func TestInflight(t *testing.T) { +func TestSingleSingleInflight(t *testing.T) { m := new(Msg) m.SetQuestion("miek.nl.", TypeDNSKEY) c := new(Client) - c.Inflight = true + c.SingleInflight = true nr := 10 ch := make(chan time.Duration) for i := 0; i < nr; i++ { diff --git a/dns.go b/dns.go index 003908c4..cc442f17 100644 --- a/dns.go +++ b/dns.go @@ -64,7 +64,7 @@ // For asynchronous queries it is easy to wrap Exchange() in a goroutine. Suppressing // multiple outstanding queries (with the same question, type and class) is as easy as setting: // -// c.Inflight = true +// c.SingleInflight = true // // A dns message consists out of four sections. // The question section: in.Question, the answer section: in.Answer, diff --git a/server.go b/server.go index 5314138b..a6eeff43 100644 --- a/server.go +++ b/server.go @@ -174,7 +174,7 @@ func (mux *ServeMux) match(q string, t uint16) Handler { for { l := len(q[off:]) for i := 0; i < l; i++ { - b[i] = q[off+i] | ( 'a' - 'A') + b[i] = q[off+i] | ('a' - 'A') } if h, ok := mux.z[string(b[:l])]; ok { // 'causes garbage, might want to change the map key if t != TypeDS { diff --git a/xfr.go b/xfr.go index 7655c1b1..77ea7e0f 100644 --- a/xfr.go +++ b/xfr.go @@ -70,7 +70,7 @@ func (w *reply) axfrIn(q *Msg, c chan *Envelope) { } first = !first // only one answer that is SOA, receive more - if (len(in.Answer) == 1) { + if len(in.Answer) == 1 { w.tsigTimersOnly = true c <- &Envelope{in.Answer, nil} continue From 47cc5b052df9b4e5d5a9900cfdd622607de10a6d Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Tue, 10 Sep 2013 13:13:10 +0000 Subject: [PATCH 3/5] Work on making IsDomainName better --- defaults.go | 92 +++++++++++++++----------------------------- dnssec.go | 3 +- zscan.go | 33 ++++++++++------ zscan_rr.go | 109 ++++++++++++++++++++++++++-------------------------- 4 files changed, 107 insertions(+), 130 deletions(-) diff --git a/defaults.go b/defaults.go index 0b772505..0736e4b5 100644 --- a/defaults.go +++ b/defaults.go @@ -156,76 +156,44 @@ func (dns *Msg) IsEdns0() *OPT { } // IsDomainName checks if s is a valid domainname, it returns -// the number of labels, total length and true, when a domain name is valid. -// Note that unfully qualified domain name is considered valid, in this case the +// the number of labels and true, when a domain name is valid. +// Note that non fully qualified domain name is considered valid, in this case the // last label is counted in the number of labels. -// When false is returned the labelcount and length are not defined. -func IsDomainName(s string) (uint8, uint8, bool) { // copied from net package. - // See RFC 1035, RFC 3696. - l := len(s) - if l == 0 || l > 255 { - return 0, 0, false +// When false is returned the number of labels is not defined. +func IsDomainName(s string) (int, bool) { + // use PackDomainName + if buf == nil { + buf = make([]byte, 256) } - // Preloop check for root label - if s == "." { - return 0, 1, true + lenmsg, err := PackDomainName(s, buf, 0, nil, false) + if err != nil { + return 0, false } - - last := byte('.') - ok := false // Ok once we've seen a letter or digit. - partlen := 0 - labels := uint8(0) - var c byte - for i := 0; i < l; i++ { - c = s[i] - switch { - default: - // anything escaped is legal - if last != '\\' { - return 0, uint8(l), false - } - partlen++ - case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_' || c == '*' || c == '/': - ok = true - partlen++ - case c == '\\': // OK. - case c == '@': - if last != '\\' { - return 0, uint8(l), false - } - partlen++ - case '0' <= c && c <= '9': - ok = true - partlen++ - case c == '-': - if last == '.' { - return 0, uint8(l), false - } - partlen++ - case c == '.': - // byte before dot cannot be dot - if last == '.' { - return 0, uint8(l), false - } - if last == '\\' { // Ok, escaped dot. - partlen++ - c = 'A' // Make current value not scary. + // There are no compression pointers, because the map was nil, so + // walk the binary name and count the length bits - this is the number + // of labels. + off := 0 + labels := 0 +Loop: + for { + if off >= lenmsg { + return labels, false + } + c := int(buf[off]) + switch c & 0xC0 { + case 0x00: + if c == 0x00 { + // end of name break } - if partlen > 63 || partlen == 0 { - return 0, uint8(l), false + if off+c > lenmsg { + return labels, false } - partlen = 0 labels++ + off += c } - last = c } - // If last isn't a dot, the name was unqualified, but we still want to count - // the last label. - if last == '.' { - return labels, uint8(l), ok - } - return labels + 1, uint8(l), ok + return labels, true } // IsSubDomain checks if child is indeed a child of the parent. Both child and @@ -253,7 +221,7 @@ func Fqdn(s string) string { return s + "." } -// Copied from the official Go code +// Copied from the official Go code. // ReverseAddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP // address addr suitable for rDNS (PTR) record lookup or an error if it fails diff --git a/dnssec.go b/dnssec.go index cde778a7..af28ea17 100644 --- a/dnssec.go +++ b/dnssec.go @@ -222,7 +222,8 @@ func (rr *RRSIG) Sign(k PrivateKey, rrset []RR) error { rr.OrigTtl = rrset[0].Header().Ttl rr.TypeCovered = rrset[0].Header().Rrtype rr.TypeCovered = rrset[0].Header().Rrtype - rr.Labels, _, _ = IsDomainName(rrset[0].Header().Name) + x := CountLabel(rrset[0].Header().Name) + rr.Labels = uint8(x) if strings.HasPrefix(rrset[0].Header().Name, "*") { rr.Labels-- // wildcard, remove from label count diff --git a/zscan.go b/zscan.go index 6a6bdbc4..102bcefc 100644 --- a/zscan.go +++ b/zscan.go @@ -88,6 +88,7 @@ func (e *ParseError) Error() (s string) { type lex struct { token string // text of the token + length int // lenght of the token err bool // when true, token text has lexer error value uint8 // value: _STRING, _BLANK, etc. line int // line in the file @@ -179,11 +180,11 @@ func parseZone(r io.Reader, origin, f string, t chan Token, include int) { if origin == "" { origin = "." } - if _, _, ok := IsDomainName(origin); !ok { + origin = Fqdn(origin) + if _, ok := IsDomainName(origin); !ok { t <- Token{Error: &ParseError{f, "bad initial origin name", lex{}}} return } - origin = Fqdn(origin) st := _EXPECT_OWNER_DIR // initial state var h RR_Header @@ -212,14 +213,14 @@ func parseZone(r io.Reader, origin, f string, t chan Token, include int) { st = _EXPECT_OWNER_BL break } - _, ld, ok := IsDomainName(l.token) + if h.Name[l.length-1] != '.' { + h.Name = appendOrigin(h.Name, origin) + } + _, ok := IsDomainName(l.token) if !ok { t <- Token{Error: &ParseError{f, "bad owner name", l}} return } - if h.Name[ld-1] != '.' { - h.Name = appendOrigin(h.Name, origin) - } prevName = h.Name st = _EXPECT_OWNER_BL case _DIRTTL: @@ -273,12 +274,12 @@ func parseZone(r io.Reader, origin, f string, t chan Token, include int) { case _BLANK: l := <-c if l.value == _STRING { - if _, _, ok := IsDomainName(l.token); !ok { + if _, ok := IsDomainName(l.token); !ok { t <- Token{Error: &ParseError{f, "bad origin name", l}} return } // a new origin is specified. - if !IsFqdn(l.token) { + if l.token[l.length-1] != '.' { if origin != "." { // Prevent .. endings neworigin = l.token + "." + origin } else { @@ -342,11 +343,11 @@ func parseZone(r io.Reader, origin, f string, t chan Token, include int) { if e, _ := slurpRemainder(c, f); e != nil { t <- Token{Error: e} } - if _, _, ok := IsDomainName(l.token); !ok { + if _, ok := IsDomainName(l.token); !ok { t <- Token{Error: &ParseError{f, "bad origin name", l}} return } - if !IsFqdn(l.token) { + if l.token[l.length-1] != '.' { if origin != "." { // Prevent .. endings origin = l.token + "." + origin } else { @@ -527,6 +528,7 @@ func zlexer(s *scan, c chan lex) { // If we have a string and its the first, make it an owner l.value = _OWNER l.token = string(str[:stri]) + l.length = stri // escape $... start with a \ not a $, so this will work switch l.token { case "$TTL": @@ -543,7 +545,7 @@ func zlexer(s *scan, c chan lex) { } else { l.value = _STRING l.token = string(str[:stri]) - + l.length = stri if !rrtype { if t, ok := StringToType[l.token]; ok { l.value = _RRTYPE @@ -589,6 +591,7 @@ func zlexer(s *scan, c chan lex) { if !space && !commt { l.value = _BLANK l.token = " " + l.length = 1 debug.Printf("[5 %+v]", l.token) c <- l } @@ -610,6 +613,7 @@ func zlexer(s *scan, c chan lex) { if stri > 0 { l.value = _STRING l.token = string(str[:stri]) + l.length = stri debug.Printf("[4 %+v]", l.token) c <- l stri = 0 @@ -640,6 +644,7 @@ func zlexer(s *scan, c chan lex) { owner = true l.value = _NEWLINE l.token = "\n" + l.length = 1 l.comment = string(com[:comi]) debug.Printf("[3 %+v %+v]", l.token, l.comment) c <- l @@ -657,6 +662,7 @@ func zlexer(s *scan, c chan lex) { if stri != 0 { l.value = _STRING l.token = string(str[:stri]) + l.length = stri if !rrtype { if t, ok := StringToType[strings.ToUpper(l.token)]; ok { l.value = _RRTYPE @@ -669,6 +675,7 @@ func zlexer(s *scan, c chan lex) { } l.value = _NEWLINE l.token = "\n" + l.length = 1 debug.Printf("[1 %+v]", l.token) c <- l stri = 0 @@ -710,12 +717,14 @@ func zlexer(s *scan, c chan lex) { if stri != 0 { l.value = _STRING l.token = string(str[:stri]) + l.length = stri debug.Printf("[%+v]", l.token) c <- l stri = 0 } l.value = _QUOTE l.token = "\"" + l.length = 1 c <- l quote = !quote case '(', ')': @@ -761,10 +770,10 @@ func zlexer(s *scan, c chan lex) { } x, err = s.tokenText() } - // Hmm. if stri > 0 { // Send remainder l.token = string(str[:stri]) + l.length = stri l.value = _STRING debug.Printf("[%+v]", l.token) c <- l diff --git a/zscan_rr.go b/zscan_rr.go index db4068b5..4fd9b3ef 100644 --- a/zscan_rr.go +++ b/zscan_rr.go @@ -271,11 +271,11 @@ func setNS(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr.Ns = o return rr, nil } - _, ld, ok := IsDomainName(l.token) + _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad NS Ns", l} } - if rr.Ns[ld-1] != '.' { + if rr.Ns[l.length-1] != '.' { rr.Ns = appendOrigin(rr.Ns, o) } return rr, nil @@ -291,11 +291,11 @@ func setPTR(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr.Ptr = o return rr, nil } - _, ld, ok := IsDomainName(l.token) + _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad PTR Ptr", l} } - if rr.Ptr[ld-1] != '.' { + if rr.Ptr[l.length-1] != '.' { rr.Ptr = appendOrigin(rr.Ptr, o) } return rr, nil @@ -310,11 +310,11 @@ func setRP(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { if l.token == "@" { rr.Mbox = o } else { - _, ld, ok := IsDomainName(l.token) + _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad RP Mbox", l} } - if rr.Mbox[ld-1] != '.' { + if rr.Mbox[l.length-1] != '.' { rr.Mbox = appendOrigin(rr.Mbox, o) } } @@ -325,14 +325,13 @@ func setRP(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr.Txt = o return rr, nil } - _, ld, ok := IsDomainName(l.token) + _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad RP Txt", l} } - if rr.Txt[ld-1] != '.' { + if rr.Txt[l.length-1] != '.' { rr.Txt = appendOrigin(rr.Txt, o) } - return rr, nil } @@ -346,11 +345,11 @@ func setMR(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr.Mr = o return rr, nil } - _, ld, ok := IsDomainName(l.token) + _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad MR Mr", l} } - if rr.Mr[ld-1] != '.' { + if rr.Mr[l.length-1] != '.' { rr.Mr = appendOrigin(rr.Mr, o) } return rr, nil @@ -366,11 +365,11 @@ func setMB(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr.Mb = o return rr, nil } - _, ld, ok := IsDomainName(l.token) + _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad MB Mb", l} } - if rr.Mb[ld-1] != '.' { + if rr.Mb[l.length-1] != '.' { rr.Mb = appendOrigin(rr.Mb, o) } return rr, nil @@ -386,11 +385,11 @@ func setMG(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr.Mg = o return rr, nil } - _, ld, ok := IsDomainName(l.token) + _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad MG Mg", l} } - if rr.Mg[ld-1] != '.' { + if rr.Mg[l.length-1] != '.' { rr.Mg = appendOrigin(rr.Mg, o) } return rr, nil @@ -418,11 +417,11 @@ func setMINFO(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { if l.token == "@" { rr.Rmail = o } else { - _, ld, ok := IsDomainName(l.token) + _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad MINFO Rmail", l} } - if rr.Rmail[ld-1] != '.' { + if rr.Rmail[l.length-1] != '.' { rr.Rmail = appendOrigin(rr.Rmail, o) } } @@ -433,11 +432,11 @@ func setMINFO(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr.Email = o return rr, nil } - _, ld, ok := IsDomainName(l.token) + _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad MINFO Email", l} } - if rr.Email[ld-1] != '.' { + if rr.Email[l.length-1] != '.' { rr.Email = appendOrigin(rr.Email, o) } return rr, nil @@ -453,11 +452,11 @@ func setMF(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr.Mf = o return rr, nil } - _, ld, ok := IsDomainName(l.token) + _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad MF Mf", l} } - if rr.Mf[ld-1] != '.' { + if rr.Mf[l.length-1] != '.' { rr.Mf = appendOrigin(rr.Mf, o) } return rr, nil @@ -473,11 +472,11 @@ func setMD(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr.Md = o return rr, nil } - _, ld, ok := IsDomainName(l.token) + _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad MD Md", l} } - if rr.Md[ld-1] != '.' { + if rr.Md[l.length-1] != '.' { rr.Md = appendOrigin(rr.Md, o) } return rr, nil @@ -500,11 +499,11 @@ func setMX(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr.Mx = o return rr, nil } - _, ld, ok := IsDomainName(l.token) + _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad MX Mx", l} } - if rr.Mx[ld-1] != '.' { + if rr.Mx[l.length-1] != '.' { rr.Mx = appendOrigin(rr.Mx, o) } return rr, nil @@ -527,11 +526,11 @@ func setRT(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr.Host = o return rr, nil } - _, ld, ok := IsDomainName(l.token) + _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad RT Host", l} } - if rr.Host[ld-1] != '.' { + if rr.Host[l.length-1] != '.' { rr.Host = appendOrigin(rr.Host, o) } return rr, nil @@ -554,11 +553,11 @@ func setAFSDB(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr.Hostname = o return rr, nil } - _, ld, ok := IsDomainName(l.token) + _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad AFSDB Hostname", l} } - if rr.Hostname[ld-1] != '.' { + if rr.Hostname[l.length-1] != '.' { rr.Hostname = appendOrigin(rr.Hostname, o) } return rr, nil @@ -590,11 +589,11 @@ func setKX(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr.Exchanger = o return rr, nil } - _, ld, ok := IsDomainName(l.token) + _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad KX Exchanger", l} } - if rr.Exchanger[ld-1] != '.' { + if rr.Exchanger[l.length-1] != '.' { rr.Exchanger = appendOrigin(rr.Exchanger, o) } return rr, nil @@ -610,11 +609,11 @@ func setCNAME(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr.Target = o return rr, nil } - _, ld, ok := IsDomainName(l.token) + _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad CNAME Target", l} } - if rr.Target[ld-1] != '.' { + if rr.Target[l.length-1] != '.' { rr.Target = appendOrigin(rr.Target, o) } return rr, nil @@ -630,11 +629,11 @@ func setDNAME(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr.Target = o return rr, nil } - _, ld, ok := IsDomainName(l.token) + _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad CNAME Target", l} } - if rr.Target[ld-1] != '.' { + if rr.Target[l.length-1] != '.' { rr.Target = appendOrigin(rr.Target, o) } return rr, nil @@ -650,11 +649,11 @@ func setSOA(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { if l.token == "@" { rr.Ns = o } else { - _, ld, ok := IsDomainName(l.token) + _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad SOA Ns", l} } - if rr.Ns[ld-1] != '.' { + if rr.Ns[l.length-1] != '.' { rr.Ns = appendOrigin(rr.Ns, o) } } @@ -664,11 +663,11 @@ func setSOA(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { if l.token == "@" { rr.Mbox = o } else { - _, ld, ok := IsDomainName(l.token) + _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad SOA Mbox", l} } - if rr.Mbox[ld-1] != '.' { + if rr.Mbox[l.length-1] != '.' { rr.Mbox = appendOrigin(rr.Mbox, o) } } @@ -743,11 +742,11 @@ func setSRV(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr.Target = o return rr, nil } - _, ld, ok := IsDomainName(l.token) + _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad SRV Target", l} } - if rr.Target[ld-1] != '.' { + if rr.Target[l.length-1] != '.' { rr.Target = appendOrigin(rr.Target, o) } return rr, nil @@ -834,11 +833,11 @@ func setNAPTR(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr.Replacement = o return rr, nil } - _, ld, ok := IsDomainName(l.token) + _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad NAPTR Replacement", l} } - if rr.Replacement[ld-1] != '.' { + if rr.Replacement[l.length-1] != '.' { rr.Replacement = appendOrigin(rr.Replacement, o) } return rr, nil @@ -853,11 +852,11 @@ func setTALINK(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { if l.token == "@" { rr.PreviousName = o } else { - _, ld, ok := IsDomainName(l.token) + _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad TALINK PreviousName", l} } - if rr.PreviousName[ld-1] != '.' { + if rr.PreviousName[l.length-1] != '.' { rr.PreviousName = appendOrigin(rr.PreviousName, o) } } @@ -868,11 +867,11 @@ func setTALINK(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr.NextName = o return rr, nil } - _, ld, ok := IsDomainName(l.token) + _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad TALINK NextName", l} } - if rr.NextName[ld-1] != '.' { + if rr.NextName[l.length-1] != '.' { rr.NextName = appendOrigin(rr.NextName, o) } return rr, nil @@ -1036,11 +1035,11 @@ func setHIP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { xs = append(xs, o) continue } - _, ld, ok := IsDomainName(l.token) + _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad HIP RendezvousServers", l}, "" } - if l.token[ld-1] != '.' { + if l.token[l.length-1] != '.' { l.token = appendOrigin(l.token, o) } xs = append(xs, l.token) @@ -1144,11 +1143,11 @@ func setRRSIG(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { if l.token == "@" { rr.SignerName = o } else { - _, ld, ok := IsDomainName(l.token) + _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad RRSIG SignerName", l}, "" } - if rr.SignerName[ld-1] != '.' { + if rr.SignerName[l.length-1] != '.' { rr.SignerName = appendOrigin(rr.SignerName, o) } } @@ -1169,11 +1168,11 @@ func setNSEC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { if l.token == "@" { rr.NextDomain = o } else { - _, ld, ok := IsDomainName(l.token) + _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad NSEC NextDomain", l}, "" } - if rr.NextDomain[ld-1] != '.' { + if rr.NextDomain[l.length-1] != '.' { rr.NextDomain = appendOrigin(rr.NextDomain, o) } } @@ -1861,11 +1860,11 @@ func setLP(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr.Fqdn = o return rr, nil } - _, ld, ok := IsDomainName(l.token) + _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad LP Fqdn", l} } - if rr.Fqdn[ld-1] != '.' { + if rr.Fqdn[l.length-1] != '.' { rr.Fqdn = appendOrigin(rr.Fqdn, o) } return rr, nil From 9c72d3aa52d649dc0064ed9a600b23d9b4c6f065 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Tue, 10 Sep 2013 13:21:55 +0000 Subject: [PATCH 4/5] Fix docs --- defaults.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/defaults.go b/defaults.go index 0736e4b5..e97ef309 100644 --- a/defaults.go +++ b/defaults.go @@ -156,7 +156,7 @@ func (dns *Msg) IsEdns0() *OPT { } // IsDomainName checks if s is a valid domainname, it returns -// the number of labels and true, when a domain name is valid. +// the number of labels and true, when a domain name is valid. // Note that non fully qualified domain name is considered valid, in this case the // last label is counted in the number of labels. // When false is returned the number of labels is not defined. From 9c1ee5d5ca5abaaecdc2920269cbd2a71aa09ad2 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Tue, 10 Sep 2013 18:09:22 +0000 Subject: [PATCH 5/5] Update IsDomainName This new functions just compiles the domain to wire format, if that works, the name is deemed OK. It is also much less strict than the older code. Almost everything is allowed in the name, except two dots back to back (there is an explicit test for that). --- defaults.go | 37 +++----------------------------- dnssec.go | 3 +-- dnssec_test.go | 2 +- labels_test.go | 21 ++++++++++--------- msg.go | 57 +++++++++++++++++++++++++++++++++++++------------- parse_test.go | 5 +++-- 6 files changed, 62 insertions(+), 63 deletions(-) diff --git a/defaults.go b/defaults.go index e97ef309..f4b80341 100644 --- a/defaults.go +++ b/defaults.go @@ -160,40 +160,9 @@ func (dns *Msg) IsEdns0() *OPT { // Note that non fully qualified domain name is considered valid, in this case the // last label is counted in the number of labels. // When false is returned the number of labels is not defined. -func IsDomainName(s string) (int, bool) { - // use PackDomainName - if buf == nil { - buf = make([]byte, 256) - } - lenmsg, err := PackDomainName(s, buf, 0, nil, false) - if err != nil { - return 0, false - } - // There are no compression pointers, because the map was nil, so - // walk the binary name and count the length bits - this is the number - // of labels. - off := 0 - labels := 0 -Loop: - for { - if off >= lenmsg { - return labels, false - } - c := int(buf[off]) - switch c & 0xC0 { - case 0x00: - if c == 0x00 { - // end of name - break - } - if off+c > lenmsg { - return labels, false - } - labels++ - off += c - } - } - return labels, true +func IsDomainName(s string) (labels int, ok bool) { + _, labels, err := packDomainName(s, nil, 0, nil, false) + return labels, err == nil } // IsSubDomain checks if child is indeed a child of the parent. Both child and diff --git a/dnssec.go b/dnssec.go index af28ea17..fdb103bf 100644 --- a/dnssec.go +++ b/dnssec.go @@ -222,8 +222,7 @@ func (rr *RRSIG) Sign(k PrivateKey, rrset []RR) error { rr.OrigTtl = rrset[0].Header().Ttl rr.TypeCovered = rrset[0].Header().Rrtype rr.TypeCovered = rrset[0].Header().Rrtype - x := CountLabel(rrset[0].Header().Name) - rr.Labels = uint8(x) + rr.Labels = uint8(CountLabel(rrset[0].Header().Name)) if strings.HasPrefix(rrset[0].Header().Name, "*") { rr.Labels-- // wildcard, remove from label count diff --git a/dnssec_test.go b/dnssec_test.go index 9456744f..ac6ab765 100644 --- a/dnssec_test.go +++ b/dnssec_test.go @@ -173,7 +173,7 @@ func TestSignVerify(t *testing.T) { sig := new(RRSIG) sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0} sig.TypeCovered = soa.Hdr.Rrtype - sig.Labels, _, _ = IsDomainName(soa.Hdr.Name) + sig.Labels = uint8(CountLabel(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" diff --git a/labels_test.go b/labels_test.go index 2e335a78..0493d830 100644 --- a/labels_test.go +++ b/labels_test.go @@ -95,20 +95,21 @@ domainLoop: func TestIsDomainName(t *testing.T) { type ret struct { ok bool - lab uint8 - l uint8 + lab int } names := map[string]*ret{ - "www.example.com": &ret{true, 3, 15}, - "www.example.com.": &ret{true, 3, 16}, - "mi\\k.nl.": &ret{true, 2, 8}, - "mi\\k.nl": &ret{true, 2, 7}, + "..": &ret{false, 1}, + "@.": &ret{true, 1}, + "www.example.com": &ret{true, 3}, + "www.example.com.": &ret{true, 3}, + "mi\\k.nl.": &ret{true, 2}, + "mi\\k.nl": &ret{true, 2}, } for d, ok := range names { - l1, l2, k := IsDomainName(d) - if ok.ok != k || ok.lab != l1 || ok.l != l2 { - t.Logf("Got %v %d %d for %s ", k, l1, l2, d) - t.Logf(" %v %d %d for %s ", ok.ok, ok.lab, ok.l, d) + l, k := IsDomainName(d) + if ok.ok != k || ok.lab != l { + t.Logf(" got %v %d for %s ", k, l, d) + t.Logf("have %v %d for %s ", ok.ok, ok.lab, d) t.Fail() } } diff --git a/msg.go b/msg.go index d5ccd821..e647d6fa 100644 --- a/msg.go +++ b/msg.go @@ -212,14 +212,31 @@ var RcodeToString = map[int]string{ // map needs to hold a mapping between domain names and offsets // pointing into msg[]. func PackDomainName(s string, msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) { - lenmsg := len(msg) + off1, _, err = packDomainName(s, msg, off, compression, compress) + return +} + +func packDomainName(s string, msg []byte, off int, compression map[string]int, compress bool) (off1 int, labels int, err error) { + // special case if msg == nil + lenmsg := 256 + if msg != nil { + lenmsg = len(msg) + } ls := len(s) if ls == 0 { // Ok, for instance when dealing with update RR without any rdata. - return off, nil + return off, 0, nil } - // If not fully qualified, error out - if s[ls-1] != '.' { - return lenmsg, ErrFqdn + // If not fully qualified, error out, but only if msg == nil #ugly + switch { + case msg == nil: + if s[ls-1] != '.' { + s += "." + ls++ + } + case msg != nil: + if s[ls-1] != '.' { + return lenmsg, 0, ErrFqdn + } } // Each dot ends a segment of the name. // We trade each dot byte for a length byte. @@ -239,7 +256,7 @@ func PackDomainName(s string, msg []byte, off int, compression map[string]int, c } ls-- if off+1 > lenmsg { - return lenmsg, ErrBuf + return lenmsg, labels, ErrBuf } // check for \DDD if i+2 < ls && bs[i] >= '0' && bs[i] <= '9' && @@ -255,22 +272,30 @@ func PackDomainName(s string, msg []byte, off int, compression map[string]int, c } if bs[i] == '.' { + if i > 0 && bs[i-1] == '.' { + // two dots back to back is not legal + return lenmsg, labels, ErrRdata + } if i-begin >= 1<<6 { // top two bits of length must be clear - return lenmsg, ErrRdata + return lenmsg, labels, ErrRdata } // off can already (we're in a loop) be bigger than len(msg) // this happens when a name isn't fully qualified if off+1 > lenmsg { - return lenmsg, ErrBuf + return lenmsg, labels, ErrBuf + } + if msg != nil { + msg[off] = byte(i - begin) } - msg[off] = byte(i - begin) offset := off off++ for j := begin; j < i; j++ { if off+1 > lenmsg { - return lenmsg, ErrBuf + return lenmsg, labels, ErrBuf + } + if msg != nil { + msg[off] = bs[j] } - msg[off] = bs[j] off++ } // Dont try to compress '.' @@ -294,24 +319,28 @@ func PackDomainName(s string, msg []byte, off int, compression map[string]int, c } } } + labels++ begin = i + 1 } } // Root label is special if len(bs) == 1 && bs[0] == '.' { - return off, nil + return off, labels, nil } // If we did compression and we find something add the pointer here if pointer != -1 { // We have two bytes (14 bits) to put the pointer in + // if msg == nil, we will never do compression msg[nameoffset], msg[nameoffset+1] = packUint16(uint16(pointer ^ 0xC000)) off = nameoffset + 1 goto End } - msg[off] = 0 + if msg != nil { + msg[off] = 0 + } End: off++ - return off, nil + return off, labels, nil } // Unpack a domain name. diff --git a/parse_test.go b/parse_test.go index 2f23d4fa..99edc590 100644 --- a/parse_test.go +++ b/parse_test.go @@ -357,7 +357,8 @@ func TestParseFailure(t *testing.T) { "miek.nl. IN AAAA ::x", "miek.nl. IN MX a0 miek.nl.", "miek.nl aap IN MX mx.miek.nl.", - "miek.nl. IN CNAME ", + // "miek.nl. IN CNAME ", // actually valid nowadays, zero size rdata + "miek.nl. IN CNAME ..", "miek.nl. PA MX 10 miek.nl.", "miek.nl. ) IN MX 10 miek.nl.", } @@ -365,7 +366,7 @@ func TestParseFailure(t *testing.T) { for _, s := range tests { _, err := NewRR(s) if err == nil { - t.Log("Should have triggered an error") + t.Logf("Should have triggered an error: \"%s\"", s) t.Fail() } }