...

Source file src/github.com/miekg/dns/xfr.go

Documentation: github.com/miekg/dns

     1  package dns
     2  
     3  import (
     4  	"fmt"
     5  	"time"
     6  )
     7  
     8  // Envelope is used when doing a zone transfer with a remote server.
     9  type Envelope struct {
    10  	RR    []RR  // The set of RRs in the answer section of the xfr reply message.
    11  	Error error // If something went wrong, this contains the error.
    12  }
    13  
    14  // A Transfer defines parameters that are used during a zone transfer.
    15  type Transfer struct {
    16  	*Conn
    17  	DialTimeout    time.Duration     // net.DialTimeout, defaults to 2 seconds
    18  	ReadTimeout    time.Duration     // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds
    19  	WriteTimeout   time.Duration     // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds
    20  	TsigProvider   TsigProvider      // An implementation of the TsigProvider interface. If defined it replaces TsigSecret and is used for all TSIG operations.
    21  	TsigSecret     map[string]string // Secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2)
    22  	tsigTimersOnly bool
    23  }
    24  
    25  func (t *Transfer) tsigProvider() TsigProvider {
    26  	if t.TsigProvider != nil {
    27  		return t.TsigProvider
    28  	}
    29  	if t.TsigSecret != nil {
    30  		return tsigSecretProvider(t.TsigSecret)
    31  	}
    32  	return nil
    33  }
    34  
    35  // TODO: Think we need to away to stop the transfer
    36  
    37  // In performs an incoming transfer with the server in a.
    38  // If you would like to set the source IP, or some other attribute
    39  // of a Dialer for a Transfer, you can do so by specifying the attributes
    40  // in the Transfer.Conn:
    41  //
    42  //	d := net.Dialer{LocalAddr: transfer_source}
    43  //	con, err := d.Dial("tcp", master)
    44  //	dnscon := &dns.Conn{Conn:con}
    45  //	transfer = &dns.Transfer{Conn: dnscon}
    46  //	channel, err := transfer.In(message, master)
    47  func (t *Transfer) In(q *Msg, a string) (env chan *Envelope, err error) {
    48  	switch q.Question[0].Qtype {
    49  	case TypeAXFR, TypeIXFR:
    50  	default:
    51  		return nil, &Error{"unsupported question type"}
    52  	}
    53  
    54  	timeout := dnsTimeout
    55  	if t.DialTimeout != 0 {
    56  		timeout = t.DialTimeout
    57  	}
    58  
    59  	if t.Conn == nil {
    60  		t.Conn, err = DialTimeout("tcp", a, timeout)
    61  		if err != nil {
    62  			return nil, err
    63  		}
    64  	}
    65  
    66  	if err := t.WriteMsg(q); err != nil {
    67  		return nil, err
    68  	}
    69  
    70  	env = make(chan *Envelope)
    71  	switch q.Question[0].Qtype {
    72  	case TypeAXFR:
    73  		go t.inAxfr(q, env)
    74  	case TypeIXFR:
    75  		go t.inIxfr(q, env)
    76  	}
    77  
    78  	return env, nil
    79  }
    80  
    81  func (t *Transfer) inAxfr(q *Msg, c chan *Envelope) {
    82  	first := true
    83  	defer func() {
    84  		// First close the connection, then the channel. This allows functions blocked on
    85  		// the channel to assume that the connection is closed and no further operations are
    86  		// pending when they resume.
    87  		t.Close()
    88  		close(c)
    89  	}()
    90  	timeout := dnsTimeout
    91  	if t.ReadTimeout != 0 {
    92  		timeout = t.ReadTimeout
    93  	}
    94  	for {
    95  		t.Conn.SetReadDeadline(time.Now().Add(timeout))
    96  		in, err := t.ReadMsg()
    97  		if err != nil {
    98  			c <- &Envelope{nil, err}
    99  			return
   100  		}
   101  		if q.Id != in.Id {
   102  			c <- &Envelope{in.Answer, ErrId}
   103  			return
   104  		}
   105  		if first {
   106  			if in.Rcode != RcodeSuccess {
   107  				c <- &Envelope{in.Answer, &Error{err: fmt.Sprintf(errXFR, in.Rcode)}}
   108  				return
   109  			}
   110  			if !isSOAFirst(in) {
   111  				c <- &Envelope{in.Answer, ErrSoa}
   112  				return
   113  			}
   114  			first = !first
   115  			// only one answer that is SOA, receive more
   116  			if len(in.Answer) == 1 {
   117  				t.tsigTimersOnly = true
   118  				c <- &Envelope{in.Answer, nil}
   119  				continue
   120  			}
   121  		}
   122  
   123  		if !first {
   124  			t.tsigTimersOnly = true // Subsequent envelopes use this.
   125  			if isSOALast(in) {
   126  				c <- &Envelope{in.Answer, nil}
   127  				return
   128  			}
   129  			c <- &Envelope{in.Answer, nil}
   130  		}
   131  	}
   132  }
   133  
   134  func (t *Transfer) inIxfr(q *Msg, c chan *Envelope) {
   135  	var serial uint32 // The first serial seen is the current server serial
   136  	axfr := true
   137  	n := 0
   138  	qser := q.Ns[0].(*SOA).Serial
   139  	defer func() {
   140  		// First close the connection, then the channel. This allows functions blocked on
   141  		// the channel to assume that the connection is closed and no further operations are
   142  		// pending when they resume.
   143  		t.Close()
   144  		close(c)
   145  	}()
   146  	timeout := dnsTimeout
   147  	if t.ReadTimeout != 0 {
   148  		timeout = t.ReadTimeout
   149  	}
   150  	for {
   151  		t.SetReadDeadline(time.Now().Add(timeout))
   152  		in, err := t.ReadMsg()
   153  		if err != nil {
   154  			c <- &Envelope{nil, err}
   155  			return
   156  		}
   157  		if q.Id != in.Id {
   158  			c <- &Envelope{in.Answer, ErrId}
   159  			return
   160  		}
   161  		if in.Rcode != RcodeSuccess {
   162  			c <- &Envelope{in.Answer, &Error{err: fmt.Sprintf(errXFR, in.Rcode)}}
   163  			return
   164  		}
   165  		if n == 0 {
   166  			// Check if the returned answer is ok
   167  			if !isSOAFirst(in) {
   168  				c <- &Envelope{in.Answer, ErrSoa}
   169  				return
   170  			}
   171  			// This serial is important
   172  			serial = in.Answer[0].(*SOA).Serial
   173  			// Check if there are no changes in zone
   174  			if qser >= serial {
   175  				c <- &Envelope{in.Answer, nil}
   176  				return
   177  			}
   178  		}
   179  		// Now we need to check each message for SOA records, to see what we need to do
   180  		t.tsigTimersOnly = true
   181  		for _, rr := range in.Answer {
   182  			if v, ok := rr.(*SOA); ok {
   183  				if v.Serial == serial {
   184  					n++
   185  					// quit if it's a full axfr or the the servers' SOA is repeated the third time
   186  					if axfr && n == 2 || n == 3 {
   187  						c <- &Envelope{in.Answer, nil}
   188  						return
   189  					}
   190  				} else if axfr {
   191  					// it's an ixfr
   192  					axfr = false
   193  				}
   194  			}
   195  		}
   196  		c <- &Envelope{in.Answer, nil}
   197  	}
   198  }
   199  
   200  // Out performs an outgoing transfer with the client connecting in w.
   201  // Basic use pattern:
   202  //
   203  //	ch := make(chan *dns.Envelope)
   204  //	tr := new(dns.Transfer)
   205  //	var wg sync.WaitGroup
   206  //	go func() {
   207  //		tr.Out(w, r, ch)
   208  //		wg.Done()
   209  //	}()
   210  //	ch <- &dns.Envelope{RR: []dns.RR{soa, rr1, rr2, rr3, soa}}
   211  //	close(ch)
   212  //	wg.Wait() // wait until everything is written out
   213  //	w.Close() // close connection
   214  //
   215  // The server is responsible for sending the correct sequence of RRs through the channel ch.
   216  func (t *Transfer) Out(w ResponseWriter, q *Msg, ch chan *Envelope) error {
   217  	for x := range ch {
   218  		r := new(Msg)
   219  		// Compress?
   220  		r.SetReply(q)
   221  		r.Authoritative = true
   222  		// assume it fits TODO(miek): fix
   223  		r.Answer = append(r.Answer, x.RR...)
   224  		if tsig := q.IsTsig(); tsig != nil && w.TsigStatus() == nil {
   225  			r.SetTsig(tsig.Hdr.Name, tsig.Algorithm, tsig.Fudge, time.Now().Unix())
   226  		}
   227  		if err := w.WriteMsg(r); err != nil {
   228  			return err
   229  		}
   230  		w.TsigTimersOnly(true)
   231  	}
   232  	return nil
   233  }
   234  
   235  // ReadMsg reads a message from the transfer connection t.
   236  func (t *Transfer) ReadMsg() (*Msg, error) {
   237  	m := new(Msg)
   238  	p := make([]byte, MaxMsgSize)
   239  	n, err := t.Read(p)
   240  	if err != nil && n == 0 {
   241  		return nil, err
   242  	}
   243  	p = p[:n]
   244  	if err := m.Unpack(p); err != nil {
   245  		return nil, err
   246  	}
   247  	if ts, tp := m.IsTsig(), t.tsigProvider(); ts != nil && tp != nil {
   248  		// Need to work on the original message p, as that was used to calculate the tsig.
   249  		err = TsigVerifyWithProvider(p, tp, t.tsigRequestMAC, t.tsigTimersOnly)
   250  		t.tsigRequestMAC = ts.MAC
   251  	}
   252  	return m, err
   253  }
   254  
   255  // WriteMsg writes a message through the transfer connection t.
   256  func (t *Transfer) WriteMsg(m *Msg) (err error) {
   257  	var out []byte
   258  	if ts, tp := m.IsTsig(), t.tsigProvider(); ts != nil && tp != nil {
   259  		out, t.tsigRequestMAC, err = TsigGenerateWithProvider(m, tp, t.tsigRequestMAC, t.tsigTimersOnly)
   260  	} else {
   261  		out, err = m.Pack()
   262  	}
   263  	if err != nil {
   264  		return err
   265  	}
   266  	_, err = t.Write(out)
   267  	return err
   268  }
   269  
   270  func isSOAFirst(in *Msg) bool {
   271  	return len(in.Answer) > 0 &&
   272  		in.Answer[0].Header().Rrtype == TypeSOA
   273  }
   274  
   275  func isSOALast(in *Msg) bool {
   276  	return len(in.Answer) > 0 &&
   277  		in.Answer[len(in.Answer)-1].Header().Rrtype == TypeSOA
   278  }
   279  
   280  const errXFR = "bad xfr rcode: %d"
   281  

View as plain text