gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

xfr.go (6932B)


      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 t.Close()
     84 	defer close(c)
     85 	timeout := dnsTimeout
     86 	if t.ReadTimeout != 0 {
     87 		timeout = t.ReadTimeout
     88 	}
     89 	for {
     90 		t.Conn.SetReadDeadline(time.Now().Add(timeout))
     91 		in, err := t.ReadMsg()
     92 		if err != nil {
     93 			c <- &Envelope{nil, err}
     94 			return
     95 		}
     96 		if q.Id != in.Id {
     97 			c <- &Envelope{in.Answer, ErrId}
     98 			return
     99 		}
    100 		if first {
    101 			if in.Rcode != RcodeSuccess {
    102 				c <- &Envelope{in.Answer, &Error{err: fmt.Sprintf(errXFR, in.Rcode)}}
    103 				return
    104 			}
    105 			if !isSOAFirst(in) {
    106 				c <- &Envelope{in.Answer, ErrSoa}
    107 				return
    108 			}
    109 			first = !first
    110 			// only one answer that is SOA, receive more
    111 			if len(in.Answer) == 1 {
    112 				t.tsigTimersOnly = true
    113 				c <- &Envelope{in.Answer, nil}
    114 				continue
    115 			}
    116 		}
    117 
    118 		if !first {
    119 			t.tsigTimersOnly = true // Subsequent envelopes use this.
    120 			if isSOALast(in) {
    121 				c <- &Envelope{in.Answer, nil}
    122 				return
    123 			}
    124 			c <- &Envelope{in.Answer, nil}
    125 		}
    126 	}
    127 }
    128 
    129 func (t *Transfer) inIxfr(q *Msg, c chan *Envelope) {
    130 	var serial uint32 // The first serial seen is the current server serial
    131 	axfr := true
    132 	n := 0
    133 	qser := q.Ns[0].(*SOA).Serial
    134 	defer t.Close()
    135 	defer close(c)
    136 	timeout := dnsTimeout
    137 	if t.ReadTimeout != 0 {
    138 		timeout = t.ReadTimeout
    139 	}
    140 	for {
    141 		t.SetReadDeadline(time.Now().Add(timeout))
    142 		in, err := t.ReadMsg()
    143 		if err != nil {
    144 			c <- &Envelope{nil, err}
    145 			return
    146 		}
    147 		if q.Id != in.Id {
    148 			c <- &Envelope{in.Answer, ErrId}
    149 			return
    150 		}
    151 		if in.Rcode != RcodeSuccess {
    152 			c <- &Envelope{in.Answer, &Error{err: fmt.Sprintf(errXFR, in.Rcode)}}
    153 			return
    154 		}
    155 		if n == 0 {
    156 			// Check if the returned answer is ok
    157 			if !isSOAFirst(in) {
    158 				c <- &Envelope{in.Answer, ErrSoa}
    159 				return
    160 			}
    161 			// This serial is important
    162 			serial = in.Answer[0].(*SOA).Serial
    163 			// Check if there are no changes in zone
    164 			if qser >= serial {
    165 				c <- &Envelope{in.Answer, nil}
    166 				return
    167 			}
    168 		}
    169 		// Now we need to check each message for SOA records, to see what we need to do
    170 		t.tsigTimersOnly = true
    171 		for _, rr := range in.Answer {
    172 			if v, ok := rr.(*SOA); ok {
    173 				if v.Serial == serial {
    174 					n++
    175 					// quit if it's a full axfr or the the servers' SOA is repeated the third time
    176 					if axfr && n == 2 || n == 3 {
    177 						c <- &Envelope{in.Answer, nil}
    178 						return
    179 					}
    180 				} else if axfr {
    181 					// it's an ixfr
    182 					axfr = false
    183 				}
    184 			}
    185 		}
    186 		c <- &Envelope{in.Answer, nil}
    187 	}
    188 }
    189 
    190 // Out performs an outgoing transfer with the client connecting in w.
    191 // Basic use pattern:
    192 //
    193 //	ch := make(chan *dns.Envelope)
    194 //	tr := new(dns.Transfer)
    195 //	var wg sync.WaitGroup
    196 //	go func() {
    197 //		tr.Out(w, r, ch)
    198 //		wg.Done()
    199 //	}()
    200 //	ch <- &dns.Envelope{RR: []dns.RR{soa, rr1, rr2, rr3, soa}}
    201 //	close(ch)
    202 //	wg.Wait() // wait until everything is written out
    203 //	w.Close() // close connection
    204 //
    205 // The server is responsible for sending the correct sequence of RRs through the channel ch.
    206 func (t *Transfer) Out(w ResponseWriter, q *Msg, ch chan *Envelope) error {
    207 	for x := range ch {
    208 		r := new(Msg)
    209 		// Compress?
    210 		r.SetReply(q)
    211 		r.Authoritative = true
    212 		// assume it fits TODO(miek): fix
    213 		r.Answer = append(r.Answer, x.RR...)
    214 		if tsig := q.IsTsig(); tsig != nil && w.TsigStatus() == nil {
    215 			r.SetTsig(tsig.Hdr.Name, tsig.Algorithm, tsig.Fudge, time.Now().Unix())
    216 		}
    217 		if err := w.WriteMsg(r); err != nil {
    218 			return err
    219 		}
    220 		w.TsigTimersOnly(true)
    221 	}
    222 	return nil
    223 }
    224 
    225 // ReadMsg reads a message from the transfer connection t.
    226 func (t *Transfer) ReadMsg() (*Msg, error) {
    227 	m := new(Msg)
    228 	p := make([]byte, MaxMsgSize)
    229 	n, err := t.Read(p)
    230 	if err != nil && n == 0 {
    231 		return nil, err
    232 	}
    233 	p = p[:n]
    234 	if err := m.Unpack(p); err != nil {
    235 		return nil, err
    236 	}
    237 	if ts, tp := m.IsTsig(), t.tsigProvider(); ts != nil && tp != nil {
    238 		// Need to work on the original message p, as that was used to calculate the tsig.
    239 		err = TsigVerifyWithProvider(p, tp, t.tsigRequestMAC, t.tsigTimersOnly)
    240 		t.tsigRequestMAC = ts.MAC
    241 	}
    242 	return m, err
    243 }
    244 
    245 // WriteMsg writes a message through the transfer connection t.
    246 func (t *Transfer) WriteMsg(m *Msg) (err error) {
    247 	var out []byte
    248 	if ts, tp := m.IsTsig(), t.tsigProvider(); ts != nil && tp != nil {
    249 		out, t.tsigRequestMAC, err = TsigGenerateWithProvider(m, tp, t.tsigRequestMAC, t.tsigTimersOnly)
    250 	} else {
    251 		out, err = m.Pack()
    252 	}
    253 	if err != nil {
    254 		return err
    255 	}
    256 	_, err = t.Write(out)
    257 	return err
    258 }
    259 
    260 func isSOAFirst(in *Msg) bool {
    261 	return len(in.Answer) > 0 &&
    262 		in.Answer[0].Header().Rrtype == TypeSOA
    263 }
    264 
    265 func isSOALast(in *Msg) bool {
    266 	return len(in.Answer) > 0 &&
    267 		in.Answer[len(in.Answer)-1].Header().Rrtype == TypeSOA
    268 }
    269 
    270 const errXFR = "bad xfr rcode: %d"