fix merge
This commit is contained in:
commit
4a4292c1b9
|
@ -1,7 +1,6 @@
|
|||
# TODO
|
||||
|
||||
* outgoing [AI]xfr
|
||||
* fix 'q' standardize ipv6 input with [::1]#53 ?
|
||||
* 'q' standardize ipv6 input with [::1]#53 ?
|
||||
|
||||
## Nice to have
|
||||
|
||||
|
|
|
@ -155,7 +155,7 @@ func (w *reply) receive() (*Msg, error) {
|
|||
p = make([]byte, DefaultMsgSize)
|
||||
}
|
||||
n, err := w.readClient(p)
|
||||
if err != nil || n == 0 {
|
||||
if err != nil && n == 0 {
|
||||
return nil, err
|
||||
}
|
||||
p = p[:n]
|
||||
|
@ -180,7 +180,7 @@ func (w *reply) readClient(p []byte) (n int, err error) {
|
|||
if w.conn == nil {
|
||||
return 0, ErrConnEmpty
|
||||
}
|
||||
if len(p) < 1 {
|
||||
if len(p) < 2 {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
attempts := w.client.Attempts
|
||||
|
@ -279,9 +279,6 @@ func (w *reply) writeClient(p []byte) (n int, err error) {
|
|||
if attempts == 0 {
|
||||
attempts = 1
|
||||
}
|
||||
if err = w.dial(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
switch w.client.Net {
|
||||
case "tcp", "tcp4", "tcp6":
|
||||
if len(p) < 2 {
|
||||
|
|
|
@ -66,16 +66,14 @@ func TestClientTsigAXFR(t *testing.T) {
|
|||
return
|
||||
} else {
|
||||
for ex := range a {
|
||||
t.Log(ex.Reply.String())
|
||||
if ex.Error != nil {
|
||||
t.Logf("Error %s\n", ex.Error.Error())
|
||||
t.Fail()
|
||||
break
|
||||
}
|
||||
if ex.Reply.Rcode != RcodeSuccess {
|
||||
break
|
||||
for _, rr := range ex.RR {
|
||||
t.Logf("%s\n", rr.String())
|
||||
}
|
||||
t.Logf("%s\n", ex.Reply.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -86,7 +84,6 @@ func TestClientAXFRMultipleMessages(t *testing.T) {
|
|||
|
||||
c := new(Client)
|
||||
c.Net = "tcp"
|
||||
// timeout?
|
||||
|
||||
if a, err := c.XfrReceive(m, "85.223.71.124:53"); err != nil {
|
||||
t.Log("Failed to setup axfr" + err.Error())
|
||||
|
@ -94,15 +91,11 @@ func TestClientAXFRMultipleMessages(t *testing.T) {
|
|||
return
|
||||
} else {
|
||||
for ex := range a {
|
||||
t.Log(ex.Reply.String())
|
||||
if ex.Error != nil {
|
||||
t.Logf("Error %s\n", ex.Error.Error())
|
||||
t.Fail()
|
||||
break
|
||||
}
|
||||
if ex.Reply.Rcode != RcodeSuccess {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/miekg/dns"
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/miekg/dns"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
@ -33,7 +33,11 @@ func main() {
|
|||
if t, e := client.XfrReceive(m, *nameserver); e == nil {
|
||||
for r := range t {
|
||||
if r.Error == nil {
|
||||
fmt.Printf("%v\n", r.Reply)
|
||||
for _, rr := range r.RR {
|
||||
fmt.Printf("%v\n", rr)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("error: %s\n", r.Error.Error())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -28,6 +28,7 @@ import (
|
|||
"runtime/pprof"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -76,6 +77,24 @@ func handleReflect(w dns.ResponseWriter, r *dns.Msg) {
|
|||
t.Txt = []string{str}
|
||||
|
||||
switch r.Question[0].Qtype {
|
||||
case dns.TypeAXFR:
|
||||
c := make(chan *dns.XfrToken)
|
||||
var e *error
|
||||
if err := dns.XfrSend(w, r, c, e); err != nil {
|
||||
close(c)
|
||||
return
|
||||
}
|
||||
soa, _ := dns.NewRR(`whoami.miek.nl. IN SOA elektron.atoom.net. miekg.atoom.net. (
|
||||
2009032802
|
||||
21600
|
||||
7200
|
||||
604800
|
||||
3600)`)
|
||||
c <- &dns.XfrToken{RR: []dns.RR{soa, t, rr, soa}}
|
||||
close(c)
|
||||
w.Hijack()
|
||||
// w.Close() // Client closes
|
||||
return
|
||||
case dns.TypeTXT:
|
||||
m.Answer = append(m.Answer, t)
|
||||
m.Extra = append(m.Extra, rr)
|
||||
|
@ -86,7 +105,7 @@ func handleReflect(w dns.ResponseWriter, r *dns.Msg) {
|
|||
m.Extra = append(m.Extra, t)
|
||||
}
|
||||
|
||||
if r.IsTsig() {
|
||||
if r.IsTsig() != nil {
|
||||
if w.TsigStatus() == nil {
|
||||
m.SetTsig(r.Extra[len(r.Extra)-1].(*dns.RR_TSIG).Hdr.Name, dns.HmacMD5, 300, time.Now().Unix())
|
||||
} else {
|
||||
|
@ -142,12 +161,12 @@ func main() {
|
|||
go serve("tcp", name, secret)
|
||||
go serve("udp", name, secret)
|
||||
sig := make(chan os.Signal)
|
||||
signal.Notify(sig)
|
||||
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
|
||||
forever:
|
||||
for {
|
||||
select {
|
||||
case <-sig:
|
||||
fmt.Printf("Signal received, stopping\n")
|
||||
case s:=<-sig:
|
||||
fmt.Printf("Signal (%d) received, stopping\n", s)
|
||||
break forever
|
||||
}
|
||||
}
|
||||
|
|
64
server.go
64
server.go
|
@ -22,12 +22,19 @@ type Handler interface {
|
|||
type ResponseWriter interface {
|
||||
// RemoteAddr returns the net.Addr of the client that sent the current request.
|
||||
RemoteAddr() net.Addr
|
||||
// TsigStatus returns the status of the Tsig.
|
||||
TsigStatus() error
|
||||
// Write writes a reply back to the client.
|
||||
Write(*Msg) error
|
||||
// WriteBuf writes a raw buffer back to the client.
|
||||
WriteBuf([]byte) error
|
||||
// Close closes the connection.
|
||||
Close() error
|
||||
// TsigStatus returns the status of the Tsig.
|
||||
TsigStatus() error
|
||||
// TsigTimersOnly sets the tsig timers only boolean.
|
||||
TsigTimersOnly(bool)
|
||||
// Hijack lets the caller take over the connection.
|
||||
// After a call to Hijack(), the DNS package will not do anything with the connection
|
||||
Hijack()
|
||||
}
|
||||
|
||||
type conn struct {
|
||||
|
@ -36,12 +43,16 @@ type conn struct {
|
|||
request []byte // bytes read
|
||||
_UDP *net.UDPConn // i/o connection if UDP was used
|
||||
_TCP *net.TCPConn // i/o connection if TCP was used
|
||||
hijacked bool // connection has been hijacked by hander TODO(mg)
|
||||
tsigSecret map[string]string // the tsig secrets
|
||||
}
|
||||
|
||||
type response struct {
|
||||
conn *conn
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
hijacked bool // connection has been hijacked by handler
|
||||
req *Msg
|
||||
>>>>>>> axfr
|
||||
tsigStatus error
|
||||
tsigTimersOnly bool
|
||||
tsigRequestMAC string
|
||||
|
@ -303,18 +314,6 @@ func newConn(t *net.TCPConn, u *net.UDPConn, a net.Addr, buf []byte, handler Han
|
|||
return c, nil
|
||||
}
|
||||
|
||||
// Close the connection.
|
||||
func (c *conn) close() {
|
||||
switch {
|
||||
case c._UDP != nil:
|
||||
c._UDP.Close()
|
||||
c._UDP = nil
|
||||
case c._TCP != nil:
|
||||
c._TCP.Close()
|
||||
c._TCP = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Serve a new connection.
|
||||
func (c *conn) serve() {
|
||||
// for block to make it easy to break out to close the tcp connection
|
||||
|
@ -338,17 +337,26 @@ func (c *conn) serve() {
|
|||
w.tsigStatus = ErrKeyAlg
|
||||
}
|
||||
w.tsigStatus = TsigVerify(c.request, w.conn.tsigSecret[secret], "", false)
|
||||
w.tsigTimersOnly = false // Will this ever be true?
|
||||
w.tsigTimersOnly = false
|
||||
w.tsigRequestMAC = req.Extra[len(req.Extra)-1].(*RR_TSIG).MAC
|
||||
}
|
||||
c.handler.ServeDNS(w, req) // this does the writing back to the client
|
||||
if c.hijacked {
|
||||
// client takes care of the connection, i.e. calls Close()
|
||||
return
|
||||
}
|
||||
break
|
||||
}
|
||||
// quite elaborate, but this was the original c.close() function
|
||||
if c._TCP != nil {
|
||||
c.close() // Listen and Serve is closed then
|
||||
switch {
|
||||
case c._UDP != nil:
|
||||
c._UDP.Close()
|
||||
c._UDP = nil
|
||||
case c._TCP != nil:
|
||||
c._TCP.Close()
|
||||
c._TCP = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -421,3 +429,25 @@ func (w *response) RemoteAddr() net.Addr { return w.conn.remoteAddr }
|
|||
|
||||
// TsigStatus implements the ResponseWriter.TsigStatus method.
|
||||
func (w *response) TsigStatus() error { return w.tsigStatus }
|
||||
|
||||
// TsigTimersOnly implements the ResponseWriter.TsigTimersOnly method.
|
||||
func (w *response) TsigTimersOnly(b bool) { w.tsigTimersOnly = b }
|
||||
|
||||
// Hijack implements the ResponseWriter.Hijack method.
|
||||
func (w *response) Hijack() { w.hijacked = true }
|
||||
|
||||
// Close implements the ResponseWriter.Close method
|
||||
func (w *response) Close() error {
|
||||
if w.conn._UDP != nil {
|
||||
e := w.conn._UDP.Close()
|
||||
w.conn._UDP = nil
|
||||
return e
|
||||
}
|
||||
if w.conn._TCP != nil {
|
||||
e := w.conn._TCP.Close()
|
||||
w.conn._TCP = nil
|
||||
return e
|
||||
}
|
||||
// no-op
|
||||
return nil
|
||||
}
|
||||
|
|
178
xfr.go
178
xfr.go
|
@ -1,21 +1,13 @@
|
|||
package dns
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
// XfrMsg is used when doing [IA]xfr with a remote server.
|
||||
type XfrMsg struct {
|
||||
Request *Msg // the question sent
|
||||
Reply *Msg // the answer to the question that was sent
|
||||
Rtt time.Duration // round trip time
|
||||
RemoteAddr net.Addr // address of the server
|
||||
Error error // if something went wrong, this contains the error
|
||||
// XfrToken is used when doing [IA]xfr with a remote server.
|
||||
type XfrToken struct {
|
||||
RR []RR // the set of RRs in the answer section of the AXFR reply message
|
||||
Error error // if something went wrong, this contains the error
|
||||
}
|
||||
|
||||
// XfrReceive performs a [AI]xfr request (depends on the message's Qtype). It returns
|
||||
// a channel of XfrMsg on which the replies from the server are sent. At the end of
|
||||
// a channel of XfrToken on which the replies from the server are sent. At the end of
|
||||
// the transfer the channel is closed.
|
||||
// It panics if the Qtype does not equal TypeAXFR or TypeIXFR. The messages are TSIG checked if
|
||||
// needed, no other post-processing is performed. The caller must dissect the returned
|
||||
|
@ -23,12 +15,12 @@ type XfrMsg struct {
|
|||
//
|
||||
// Basic use pattern for receiving an AXFR:
|
||||
//
|
||||
// // m contains the [AI]xfr request
|
||||
// // m contains the AXFR request
|
||||
// t, e := client.XfrReceive(m, "127.0.0.1:53")
|
||||
// for r := range t {
|
||||
// // ... deal with r.Reply or r.Error
|
||||
// // ... deal with r.RR or r.Error
|
||||
// }
|
||||
func (c *Client) XfrReceive(q *Msg, a string) (chan *XfrMsg, error) {
|
||||
func (c *Client) XfrReceive(q *Msg, a string) (chan *XfrToken, error) {
|
||||
w := new(reply)
|
||||
w.client = c
|
||||
w.addr = a
|
||||
|
@ -39,13 +31,13 @@ func (c *Client) XfrReceive(q *Msg, a string) (chan *XfrMsg, error) {
|
|||
if err := w.send(q); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
e := make(chan *XfrMsg)
|
||||
e := make(chan *XfrToken)
|
||||
switch q.Question[0].Qtype {
|
||||
case TypeAXFR:
|
||||
go w.axfrReceive(e)
|
||||
go w.axfrReceive(q, e)
|
||||
return e, nil
|
||||
case TypeIXFR:
|
||||
go w.ixfrReceive(e)
|
||||
go w.ixfrReceive(q, e)
|
||||
return e, nil
|
||||
default:
|
||||
return nil, ErrXfrType
|
||||
|
@ -53,23 +45,23 @@ func (c *Client) XfrReceive(q *Msg, a string) (chan *XfrMsg, error) {
|
|||
panic("dns: not reached")
|
||||
}
|
||||
|
||||
func (w *reply) axfrReceive(c chan *XfrMsg) {
|
||||
func (w *reply) axfrReceive(q *Msg, c chan *XfrToken) {
|
||||
first := true
|
||||
defer w.conn.Close()
|
||||
defer close(c)
|
||||
for {
|
||||
in, err := w.receive()
|
||||
if err != nil {
|
||||
c <- &XfrMsg{Request: w.req, Reply: in, Rtt: w.rtt, RemoteAddr: w.conn.RemoteAddr(), Error: err}
|
||||
c <- &XfrToken{in.Answer, err}
|
||||
return
|
||||
}
|
||||
if w.req.Id != in.Id {
|
||||
c <- &XfrMsg{Request: w.req, Reply: in, Rtt: w.rtt, RemoteAddr: w.conn.RemoteAddr(), Error: ErrId}
|
||||
if in.Id != q.Id {
|
||||
c <- &XfrToken{in.Answer, ErrId}
|
||||
return
|
||||
}
|
||||
if first {
|
||||
if !checkXfrSOA(in, true) {
|
||||
c <- &XfrMsg{Request: w.req, Reply: in, Rtt: w.rtt, RemoteAddr: w.conn.RemoteAddr(), Error: ErrXfrSoa}
|
||||
c <- &XfrToken{in.Answer, ErrXfrSoa}
|
||||
return
|
||||
}
|
||||
first = !first
|
||||
|
@ -78,16 +70,16 @@ func (w *reply) axfrReceive(c chan *XfrMsg) {
|
|||
if !first {
|
||||
w.tsigTimersOnly = true // Subsequent envelopes use this.
|
||||
if checkXfrSOA(in, false) {
|
||||
c <- &XfrMsg{Request: w.req, Reply: in, Rtt: w.rtt, RemoteAddr: w.conn.RemoteAddr(), Error: nil}
|
||||
c <- &XfrToken{in.Answer, nil}
|
||||
return
|
||||
}
|
||||
c <- &XfrMsg{Request: w.req, Reply: in, Rtt: w.rtt, RemoteAddr: w.conn.RemoteAddr(), Error: nil}
|
||||
c <- &XfrToken{in.Answer, nil}
|
||||
}
|
||||
}
|
||||
panic("dns: not reached")
|
||||
}
|
||||
|
||||
func (w *reply) ixfrReceive(c chan *XfrMsg) {
|
||||
func (w *reply) ixfrReceive(q *Msg, c chan *XfrToken) {
|
||||
var serial uint32 // The first serial seen is the current server serial
|
||||
first := true
|
||||
defer w.conn.Close()
|
||||
|
@ -95,23 +87,23 @@ func (w *reply) ixfrReceive(c chan *XfrMsg) {
|
|||
for {
|
||||
in, err := w.receive()
|
||||
if err != nil {
|
||||
c <- &XfrMsg{Request: w.req, Reply: in, Rtt: w.rtt, RemoteAddr: w.conn.RemoteAddr(), Error: err}
|
||||
c <- &XfrToken{in.Answer, err}
|
||||
return
|
||||
}
|
||||
if w.req.Id != in.Id {
|
||||
c <- &XfrMsg{Request: w.req, Reply: in, Rtt: w.rtt, RemoteAddr: w.conn.RemoteAddr(), Error: ErrId}
|
||||
if q.Id != in.Id {
|
||||
c <- &XfrToken{in.Answer, ErrId}
|
||||
return
|
||||
}
|
||||
if first {
|
||||
// A single SOA RR signals "no changes"
|
||||
if len(in.Answer) == 1 && checkXfrSOA(in, true) {
|
||||
c <- &XfrMsg{Request: w.req, Reply: in, Rtt: w.rtt, RemoteAddr: w.conn.RemoteAddr(), Error: nil}
|
||||
c <- &XfrToken{in.Answer, nil}
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the returned answer is ok
|
||||
if !checkXfrSOA(in, true) {
|
||||
c <- &XfrMsg{Request: w.req, Reply: in, Rtt: w.rtt, RemoteAddr: w.conn.RemoteAddr(), Error: ErrXfrSoa}
|
||||
c <- &XfrToken{in.Answer, ErrXfrSoa}
|
||||
return
|
||||
}
|
||||
// This serial is important
|
||||
|
@ -125,82 +117,19 @@ func (w *reply) ixfrReceive(c chan *XfrMsg) {
|
|||
// If the last record in the IXFR contains the servers' SOA, we should quit
|
||||
if v, ok := in.Answer[len(in.Answer)-1].(*RR_SOA); ok {
|
||||
if v.Serial == serial {
|
||||
c <- &XfrMsg{Request: w.req, Reply: in, Rtt: w.rtt, RemoteAddr: w.conn.RemoteAddr(), Error: nil}
|
||||
c <- &XfrToken{in.Answer, nil}
|
||||
return
|
||||
}
|
||||
}
|
||||
c <- &XfrMsg{Request: w.req, Reply: in, Rtt: w.rtt, RemoteAddr: w.conn.RemoteAddr()}
|
||||
c <- &XfrToken{in.Answer, nil}
|
||||
}
|
||||
}
|
||||
panic("dns: not reached")
|
||||
}
|
||||
|
||||
// XfrSend performs an outgoing Ixfr or Axfr. The function is [AI]xfr agnostic, it is
|
||||
// up to the caller to correctly send the sequence of messages.
|
||||
func XfrSend(w ResponseWriter, q *Msg, a string) error {
|
||||
switch q.Question[0].Qtype {
|
||||
case TypeAXFR, TypeIXFR:
|
||||
// go d.xfrWrite(q, m, e)
|
||||
default:
|
||||
return ErrXfrType
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
// Just send the zone
|
||||
func (d *Conn) axfrSend(q *Msg, m chan *Xfr, e chan os.Error) {
|
||||
out := new(Msg)
|
||||
out.Id = q.Id
|
||||
out.Question = q.Question
|
||||
out.Answer = make([]RR, 1001) // TODO(mg) look at this number
|
||||
out.MsgHdr.Response = true
|
||||
out.MsgHdr.Authoritative = true
|
||||
first := true
|
||||
var soa *RR_SOA
|
||||
i := 0
|
||||
for r := range m {
|
||||
out.Answer[i] = r.RR
|
||||
if soa == nil {
|
||||
if r.RR.Header().Rrtype != TypeSOA {
|
||||
e <- ErrXfrSoa
|
||||
return
|
||||
} else {
|
||||
soa = r.RR.(*RR_SOA)
|
||||
}
|
||||
}
|
||||
i++
|
||||
if i > 1000 {
|
||||
// Send it
|
||||
err := d.WriteMsg(out)
|
||||
if err != nil {
|
||||
e <- err
|
||||
return
|
||||
}
|
||||
i = 0
|
||||
// Gaat dit goed?
|
||||
out.Answer = out.Answer[:0]
|
||||
if first {
|
||||
if d.Tsig != nil {
|
||||
d.Tsig.TimersOnly = true
|
||||
}
|
||||
first = !first
|
||||
}
|
||||
}
|
||||
}
|
||||
// Everything is sent, only the closing soa is left.
|
||||
out.Answer[i] = soa
|
||||
out.Answer = out.Answer[:i+1]
|
||||
err := d.WriteMsg(out)
|
||||
if err != nil {
|
||||
e <- err
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// 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
|
||||
// if false, the last one should be a SOA.
|
||||
func checkXfrSOA(in *Msg, first bool) bool {
|
||||
if len(in.Answer) > 0 {
|
||||
if first {
|
||||
|
@ -211,3 +140,56 @@ func checkXfrSOA(in *Msg, first bool) bool {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
|
||||
// XfrSend performs an outgoing [AI]xfr depending on the request message. The
|
||||
// caller is responsible for sending the correct sequence of RR sets through
|
||||
// the channel c. For reasons of symmetry XfrToken is re-used.
|
||||
// Errors are signaled via the error pointer, when an error occurs the function
|
||||
// sets the error and returns (it does not close the channel).
|
||||
// TSIG and enveloping is handled by XfrSend.
|
||||
//
|
||||
// Basic use pattern for sending an AXFR:
|
||||
//
|
||||
// // q contains the AXFR request
|
||||
// c := make(chan *XfrToken)
|
||||
// var e *error
|
||||
// err := XfrSend(w, q, c, e)
|
||||
// w.Hijack() // hijack the connection so that the library doesn't close it
|
||||
// for _, rrset := range rrsets { // rrset is a []RR
|
||||
// c <- &{XfrToken{RR: rrset}
|
||||
// if e != nil {
|
||||
// close(c)
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// // w.Close() // Don't! Let the client close the connection
|
||||
func XfrSend(w ResponseWriter, q *Msg, c chan *XfrToken, e *error) error {
|
||||
switch q.Question[0].Qtype {
|
||||
case TypeAXFR, TypeIXFR:
|
||||
go axfrSend(w, q, c, e)
|
||||
return nil
|
||||
default:
|
||||
return ErrXfrType
|
||||
}
|
||||
panic("not reached")
|
||||
}
|
||||
|
||||
// TODO(mg): count the RRs and the resulting size.
|
||||
func axfrSend(w ResponseWriter, req *Msg, c chan *XfrToken, e *error) {
|
||||
rep := new(Msg)
|
||||
rep.SetReply(req)
|
||||
rep.MsgHdr.Authoritative = true
|
||||
|
||||
for x := range c {
|
||||
// assume it fits
|
||||
rep.Answer = append(rep.Answer, x.RR...)
|
||||
if err := w.Write(rep); e != nil {
|
||||
*e = err
|
||||
return
|
||||
}
|
||||
w.TsigTimersOnly(true)
|
||||
rep.Answer = nil
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue