package dns
import (
"crypto/tls"
"fmt"
"time"
)
type Envelope struct {
RR []RR
Error error
}
type Transfer struct {
*Conn
DialTimeout time .Duration
ReadTimeout time .Duration
WriteTimeout time .Duration
TsigProvider TsigProvider
TsigSecret map [string ]string
tsigTimersOnly bool
TLS *tls .Config
}
func (t *Transfer ) tsigProvider () TsigProvider {
if t .TsigProvider != nil {
return t .TsigProvider
}
if t .TsigSecret != nil {
return tsigSecretProvider (t .TsigSecret )
}
return nil
}
func (t *Transfer ) In (q *Msg , a string ) (env chan *Envelope , err error ) {
switch q .Question [0 ].Qtype {
case TypeAXFR , TypeIXFR :
default :
return nil , &Error {"unsupported question type" }
}
timeout := dnsTimeout
if t .DialTimeout != 0 {
timeout = t .DialTimeout
}
if t .Conn == nil {
if t .TLS != nil {
t .Conn , err = DialTimeoutWithTLS ("tcp-tls" , a , t .TLS , timeout )
} else {
t .Conn , err = DialTimeout ("tcp" , a , timeout )
}
if err != nil {
return nil , err
}
}
if err := t .WriteMsg (q ); err != nil {
return nil , err
}
env = make (chan *Envelope )
switch q .Question [0 ].Qtype {
case TypeAXFR :
go t .inAxfr (q , env )
case TypeIXFR :
go t .inIxfr (q , env )
}
return env , nil
}
func (t *Transfer ) inAxfr (q *Msg , c chan *Envelope ) {
first := true
defer func () {
t .Close ()
close (c )
}()
timeout := dnsTimeout
if t .ReadTimeout != 0 {
timeout = t .ReadTimeout
}
for {
t .Conn .SetReadDeadline (time .Now ().Add (timeout ))
in , err := t .ReadMsg ()
if err != nil {
c <- &Envelope {nil , err }
return
}
if q .Id != in .Id {
c <- &Envelope {in .Answer , ErrId }
return
}
if first {
if in .Rcode != RcodeSuccess {
c <- &Envelope {in .Answer , &Error {err : fmt .Sprintf (errXFR , in .Rcode )}}
return
}
if !isSOAFirst (in ) {
c <- &Envelope {in .Answer , ErrSoa }
return
}
first = !first
if len (in .Answer ) == 1 {
t .tsigTimersOnly = true
c <- &Envelope {in .Answer , nil }
continue
}
}
if !first {
t .tsigTimersOnly = true
if isSOALast (in ) {
c <- &Envelope {in .Answer , nil }
return
}
c <- &Envelope {in .Answer , nil }
}
}
}
func (t *Transfer ) inIxfr (q *Msg , c chan *Envelope ) {
var serial uint32
axfr := true
n := 0
qser := q .Ns [0 ].(*SOA ).Serial
defer func () {
t .Close ()
close (c )
}()
timeout := dnsTimeout
if t .ReadTimeout != 0 {
timeout = t .ReadTimeout
}
for {
t .SetReadDeadline (time .Now ().Add (timeout ))
in , err := t .ReadMsg ()
if err != nil {
c <- &Envelope {nil , err }
return
}
if q .Id != in .Id {
c <- &Envelope {in .Answer , ErrId }
return
}
if in .Rcode != RcodeSuccess {
c <- &Envelope {in .Answer , &Error {err : fmt .Sprintf (errXFR , in .Rcode )}}
return
}
if n == 0 {
if !isSOAFirst (in ) {
c <- &Envelope {in .Answer , ErrSoa }
return
}
serial = in .Answer [0 ].(*SOA ).Serial
if qser >= serial {
c <- &Envelope {in .Answer , nil }
return
}
}
t .tsigTimersOnly = true
for _ , rr := range in .Answer {
if v , ok := rr .(*SOA ); ok {
if v .Serial == serial {
n ++
if axfr && n == 2 || n == 3 {
c <- &Envelope {in .Answer , nil }
return
}
} else if axfr {
axfr = false
}
}
}
c <- &Envelope {in .Answer , nil }
}
}
func (t *Transfer ) Out (w ResponseWriter , q *Msg , ch chan *Envelope ) error {
for x := range ch {
r := new (Msg )
r .SetReply (q )
r .Authoritative = true
r .Answer = append (r .Answer , x .RR ...)
if tsig := q .IsTsig (); tsig != nil && w .TsigStatus () == nil {
r .SetTsig (tsig .Hdr .Name , tsig .Algorithm , tsig .Fudge , time .Now ().Unix ())
}
if err := w .WriteMsg (r ); err != nil {
return err
}
w .TsigTimersOnly (true )
}
return nil
}
func (t *Transfer ) ReadMsg () (*Msg , error ) {
m := new (Msg )
p := make ([]byte , MaxMsgSize )
n , err := t .Read (p )
if err != nil && n == 0 {
return nil , err
}
p = p [:n ]
if err := m .Unpack (p ); err != nil {
return nil , err
}
if tp := t .tsigProvider (); tp != nil {
err = TsigVerifyWithProvider (p , tp , t .tsigRequestMAC , t .tsigTimersOnly )
if ts := m .IsTsig (); ts != nil {
t .tsigRequestMAC = ts .MAC
}
}
return m , err
}
func (t *Transfer ) WriteMsg (m *Msg ) (err error ) {
var out []byte
if ts , tp := m .IsTsig (), t .tsigProvider (); ts != nil && tp != nil {
out , t .tsigRequestMAC , err = TsigGenerateWithProvider (m , tp , t .tsigRequestMAC , t .tsigTimersOnly )
} else {
out , err = m .Pack ()
}
if err != nil {
return err
}
_, err = t .Write (out )
return err
}
func isSOAFirst(in *Msg ) bool {
return len (in .Answer ) > 0 &&
in .Answer [0 ].Header ().Rrtype == TypeSOA
}
func isSOALast(in *Msg ) bool {
return len (in .Answer ) > 0 &&
in .Answer [len (in .Answer )-1 ].Header ().Rrtype == TypeSOA
}
const errXFR = "bad xfr rcode: %d"
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .