From e47ebb2e4c75c1d8104da14f9e794e77d7e0a9c3 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Mon, 21 Mar 2011 16:28:13 +0100 Subject: [PATCH] it works very nicely --- Makefile | 2 +- TODO | 2 - resolver.go | 205 +++------------------------------------------------- xfr.go | 125 ++++++++++++++++++++------------ 4 files changed, 89 insertions(+), 245 deletions(-) diff --git a/Makefile b/Makefile index 7f873bc8..c034b904 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,6 @@ include $(GOROOT)/src/Make.inc TARG=dns GOFILES=\ - xfr.go\ config.go\ defaults.go\ dns.go\ @@ -21,6 +20,7 @@ GOFILES=\ string.go\ tsig.go\ types.go\ + xfr.go\ # y.go\ include $(GOROOT)/src/Make.pkg diff --git a/TODO b/TODO index 950f490e..62a4a1fe 100644 --- a/TODO +++ b/TODO @@ -5,8 +5,6 @@ o clean, small API o fast data structures (rb-tree, when they come available) o api-use should lead to self documenting code o compression (only ownernames?) -o Xfr, part of (d *Conn).Xfr(m, channel) - Todo: diff --git a/resolver.go b/resolver.go index 54cb91ef..2afd3605 100644 --- a/resolver.go +++ b/resolver.go @@ -77,7 +77,7 @@ func (res *Resolver) Query(q *Msg, tsig *Tsig) (d *Msg, err os.Error) { if err != nil { continue } - in.Unpack(inb) // Discard error. + in.Unpack(inb) // Discard error. res.Rtt[server] = time.Nanoseconds() - t c.Close() break @@ -97,33 +97,10 @@ type Xfr struct { } func (res *Resolver) Xfr(q *Msg, t *Tsig, m chan Xfr) { - switch q.Question[0].Qtype { - case TypeAXFR: - res.axfr(q, t, m) - case TypeIXFR: - res.ixfr(q, t, m) - default: - // wrong request - return - } -} - -// Start an IXFR, q should contain a *Msg with the question -// for an IXFR: "miek.nl" ANY IXFR. RRs that should be added -// have Xfr.Add set to true otherwise it is false. -// Channel m is closed when the IXFR ends. -func (res *Resolver) ixfr(q *Msg, t *Tsig, m chan Xfr) { - var ( - x Xfr - inb []byte - ) - in := new(Msg) port, err := check(res, q) if err != nil { return } - - defer close(m) sending, ok := q.Pack() if !ok { return @@ -136,182 +113,20 @@ Server: if err != nil { continue Server } - var serial uint32 // The first serial seen is the current server serial - d := new(Conn) - d.TCP = c.(*net.TCPConn) - d.Addr = d.TCP.RemoteAddr() + d := new(Conn) + d.TCP = c.(*net.TCPConn) + d.Addr = d.TCP.RemoteAddr() + d.Tsig = t - first := true - defer c.Close() - for { - if first { - inb, err = d.Exchange(sending, false) - } else { - inb, err = d.Exchange(sending, true) - } - if err != nil { - c.Close() - continue Server - } - - in.Unpack(inb) - if in.Id != q.Id { - return - } - - if first { - // A single SOA RR signals "no changes" - if len(in.Answer) == 1 && checkXfrSOA(in, true) { - return - } - - // But still check if the returned answer is ok - if !checkXfrSOA(in, true) { - c.Close() - continue Server - } - // This serial is important - serial = in.Answer[0].(*RR_SOA).Serial - first = !first - } - - // Now we need to check each message for SOA records, to see what we need to do - x.Add = true - if !first { - for k, r := range in.Answer { - // If the last record in the IXFR contains the servers' SOA, we should quit - if r.Header().Rrtype == TypeSOA { - switch { - case r.(*RR_SOA).Serial == serial: - if k == len(in.Answer)-1 { - // last rr is SOA with correct serial - //m <- r dont' send it - return - } - x.Add = true - if k != 0 { - // Intermediate SOA - continue - } - case r.(*RR_SOA).Serial != serial: - x.Add = false - continue // Don't need to see this SOA - } - } - x.RR = r - m <- x - } - } - return - } - panic("not reached") - return + _, err = d.Write(sending) + if err != nil { + println(err.String()) + } + d.XfrRead(q, m) // check } return } -// Start an AXFR, q should contain a message with the question -// for an AXFR: "miek.nl" ANY AXFR. The closing SOA isn't -// returned over the channel, so the caller will receive -// the zone as-is. Xfr.Add is always true. -// The channel is closed to signal the end of the AXFR. -func (res *Resolver) axfr(q *Msg, t *Tsig, m chan Xfr) { - var inb []byte - in := new(Msg) - port, err := check(res, q) - if err != nil { - return - } - - defer close(m) - sending, ok := q.Pack() - if !ok { - return - } - -Server: - for i := 0; i < len(res.Servers); i++ { - server := res.Servers[i] + ":" + port - c, err := net.Dial("tcp", "", server) - if err != nil { - continue Server - } - d := new(Conn) - d.TCP = c.(*net.TCPConn) - d.Addr = d.TCP.RemoteAddr() - d.Tsig = t - - first := true - defer c.Close() // TODO(mg): if not open? - for { - if first { - inb, err = d.Exchange(sending, false) - } else { - inb, err = d.Exchange(sending, true) - } - if err != nil { - c.Close() - continue Server - } - if !in.Unpack(inb) { - return - } - if in.Id != q.Id { - return - } - - if first { - if !checkXfrSOA(in, true) { - c.Close() - continue Server - } - first = !first - } - - if !first { - d.Tsig.TimersOnly = true - if !checkXfrSOA(in, false) { - // Soa record not the last one - sendMsg(in, m, false) - continue - } else { - sendMsg(in, m, true) - return - } - } - } - panic("not reached") - return - } - return -} - -// Check if he SOA record exists in the Answer section of -// the packet. If first is true the first RR must be a soa -// if false, the last one should be a SOA -func checkXfrSOA(in *Msg, first bool) bool { - if len(in.Answer) > 0 { - if first { - return in.Answer[0].Header().Rrtype == TypeSOA - } else { - return in.Answer[len(in.Answer)-1].Header().Rrtype == TypeSOA - } - } - return false -} - -// Send the answer section to the channel -func sendMsg(in *Msg, c chan Xfr, nosoa bool) { - x := Xfr{Add: true} - for k, r := range in.Answer { - if nosoa && k == len(in.Answer)-1 { - continue - } - x.RR = r - c <- x - } -} - // Some assorted checks on the resolver func check(res *Resolver, q *Msg) (port string, err os.Error) { if res.Port == "" { diff --git a/xfr.go b/xfr.go index 03fd528e..5117dfee 100644 --- a/xfr.go +++ b/xfr.go @@ -5,21 +5,21 @@ package dns // Msg tells use what to do func (d *Conn) XfrRead(q *Msg, m chan Xfr) { - switch q.Question[0].Qtype { - case TypeAXFR: - d.axfrRead(q, m) - case TypeIXFR: - d.ixfrRead(q, m) - } + switch q.Question[0].Qtype { + case TypeAXFR: + d.axfrRead(q, m) + case TypeIXFR: + d.ixfrRead(q, m) + } } func (d *Conn) XfrWrite(q *Msg, m chan Xfr) { - switch q.Question[0].Qtype { - case TypeAXFR: - d.axfrWrite(q, m) - case TypeIXFR: -// d.ixfrWrite(q, m) - } + switch q.Question[0].Qtype { + case TypeAXFR: + d.axfrWrite(q, m) + case TypeIXFR: + // d.ixfrWrite(q, m) + } } func (d *Conn) axfrRead(q *Msg, m chan Xfr) { @@ -49,6 +49,9 @@ func (d *Conn) axfrRead(q *Msg, m chan Xfr) { } if !first { + if d.Tsig != nil { + d.Tsig.TimersOnly = true // Subsequent envelopes use this + } if !checkXfrSOA(in, false) { // Soa record not the last one sendMsg(in, m, false) @@ -57,7 +60,6 @@ func (d *Conn) axfrRead(q *Msg, m chan Xfr) { sendMsg(in, m, true) return } - d.Tsig.TimersOnly = true // Subsequent envelopes use this } } panic("not reached") @@ -67,39 +69,40 @@ func (d *Conn) axfrRead(q *Msg, m chan Xfr) { // Just send the zone func (d *Conn) axfrWrite(q *Msg, m chan Xfr) { out := new(Msg) - out.Id = q.Id - out.Question = q.Question - out.Answer = make([]RR, 1000) - var soa *RR_SOA; - i := 0 - for r := range m { - out.Answer[i] = r.RR - if soa == nil { - if r.RR.Header().Rrtype != TypeSOA { - return - } else { - soa = r.RR.(*RR_SOA) - } - } - i++ - if i > 1000 { - // Send it - send, _ := out.Pack() - _, err := d.Write(send) - if err != nil { - /* ... */ - } - i = 0 - out.Answer = out.Answer[:0] - } - } - // Everything is send, only the closing soa is left. - out.Answer[i] = soa - send, _ := out.Pack() - _, err := d.Write(send) - if err != nil { - /* ... */ - } + out.Id = q.Id + out.Question = q.Question + out.Answer = make([]RR, 1000) + var soa *RR_SOA + i := 0 + for r := range m { + out.Answer[i] = r.RR + if soa == nil { + if r.RR.Header().Rrtype != TypeSOA { + return + } else { + soa = r.RR.(*RR_SOA) + } + } + i++ + if i > 1000 { + // Send it + send, _ := out.Pack() + _, err := d.Write(send) + if err != nil { + /* ... */ + } + i = 0 + out.Answer = out.Answer[:0] + } + // TimersOnly foo + } + // Everything is send, only the closing soa is left. + out.Answer[i] = soa + send, _ := out.Pack() + _, err := d.Write(send) + if err != nil { + /* ... */ + } } func (d *Conn) ixfrRead(q *Msg, m chan Xfr) { @@ -146,7 +149,9 @@ func (d *Conn) ixfrRead(q *Msg, m chan Xfr) { // Now we need to check each message for SOA records, to see what we need to do x.Add = true if !first { - d.Tsig.TimersOnly = true + if d.Tsig != nil { + d.Tsig.TimersOnly = true + } for k, r := range in.Answer { // If the last record in the IXFR contains the servers' SOA, we should quit if r.Header().Rrtype == TypeSOA { @@ -175,3 +180,29 @@ func (d *Conn) ixfrRead(q *Msg, m chan Xfr) { panic("not reached") return } + +// Check if he SOA record exists in the Answer section of +// the packet. If first is true the first RR must be a soa +// if false, the last one should be a SOA +func checkXfrSOA(in *Msg, first bool) bool { + if len(in.Answer) > 0 { + if first { + return in.Answer[0].Header().Rrtype == TypeSOA + } else { + return in.Answer[len(in.Answer)-1].Header().Rrtype == TypeSOA + } + } + return false +} + +// Send the answer section to the channel +func sendMsg(in *Msg, c chan Xfr, nosoa bool) { + x := Xfr{Add: true} + for k, r := range in.Answer { + if nosoa && k == len(in.Answer)-1 { + continue + } + x.RR = r + c <- x + } +}