dns/resolver.go

183 lines
4.3 KiB
Go
Raw Normal View History

2011-02-09 06:33:51 +11:00
// Copyright 2011 Miek Gieben. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// DNS resolver client: see RFC 1035.
2011-02-11 02:23:34 +11:00
2011-02-09 06:33:51 +11:00
package dns
import (
"os"
"net"
2011-03-06 03:27:16 +11:00
"time"
2011-02-09 06:33:51 +11:00
)
type Query struct {
Msg *Msg
Conn *Conn
Err os.Error
}
// A query implementation that is asyn. and concurrent. Is also
// completely mirrors the server side implementation
func QueryTCP(in, out chan Query, f func(*Conn, *Msg, chan Query)) {
query("tcp", in, out, f)
}
func QueryUDP(in, out chan Query, f func(*Conn, *Msg, chan Query)) {
query("udp", in, out, f)
}
func query(n string, in, out chan Query, f func(*Conn, *Msg, chan Query)) {
for {
select {
case q := <-in:
c, err := net.Dial(n, "", q.Conn.RemoteAddr)
if err != nil {
//out <- nil
}
if n == "tcp" {
q.Conn.SetTCPConn(c.(*net.TCPConn), nil)
} else {
q.Conn.SetUDPConn(c.(*net.UDPConn), nil)
}
go f(q.Conn, q.Msg, out)
}
}
panic("not reached")
}
func QueryAndServeTCP(in chan Query, f func(*Conn, *Msg, chan Query)) chan Query {
out := make(chan Query)
go QueryTCP(in, out, f)
return out
}
func QueryAndServeUDP(in chan Query, f func(*Conn, *Msg, chan Query)) chan Query {
out := make(chan Query)
go QueryUDP(in, out, f)
return out
}
2011-02-09 06:33:51 +11:00
type Resolver struct {
Servers []string // servers to use
Search []string // suffixes to append to local name (not implemented)
2011-02-09 06:33:51 +11:00
Port string // what port to use
Ndots int // number of dots in name to trigger absolute lookup (not implemented)
2011-02-09 06:33:51 +11:00
Timeout int // seconds before giving up on packet
Attempts int // lost packets before giving up on server
Tcp bool // use TCP
Mangle func([]byte) []byte // mangle the packet
2011-03-06 03:27:16 +11:00
Rtt map[string]int64 // Store round trip times
2011-03-12 01:02:37 +11:00
Rrb int // Last used server (for round robin)
2011-02-09 06:33:51 +11:00
}
2011-03-24 05:37:07 +11:00
// Send a query to the nameserver using the res.
2011-03-22 08:53:15 +11:00
func (res *Resolver) Query(q *Msg) (d *Msg, err os.Error) {
2011-03-24 19:24:24 +11:00
return res.QueryTsig(q, nil)
2011-03-22 08:53:15 +11:00
}
2011-03-24 05:37:07 +11:00
// Send a query to the nameserver using res, but perform TSIG validation.
2011-03-22 08:53:15 +11:00
func (res *Resolver) QueryTsig(q *Msg, tsig *Tsig) (d *Msg, err os.Error) {
2011-03-21 05:58:55 +11:00
var inb []byte
in := new(Msg)
port, err := check(res, q)
if err != nil {
return nil, err
2011-02-09 06:33:51 +11:00
}
sending, ok := q.Pack()
if !ok {
2011-03-25 21:19:35 +11:00
return nil, ErrPack
}
2011-03-24 19:24:24 +11:00
if res.Mangle != nil {
sending = res.Mangle(sending)
}
2011-02-09 06:33:51 +11:00
for i := 0; i < len(res.Servers); i++ {
var d *Conn
server := res.Servers[i] + ":" + port
2011-03-06 03:27:16 +11:00
t := time.Nanoseconds()
if res.Tcp {
2011-03-27 20:45:01 +11:00
d, err = Dial("tcp", "", server)
2011-03-24 19:24:24 +11:00
if err != nil {
continue
}
} else {
2011-03-27 20:45:01 +11:00
d, err = Dial("udp", "", server)
2011-03-24 19:24:24 +11:00
if err != nil {
continue
}
2011-03-21 05:58:55 +11:00
}
2011-03-24 19:24:24 +11:00
d.Tsig = tsig
2011-03-21 05:58:55 +11:00
inb, err = d.Exchange(sending, false)
if err != nil {
continue
2011-03-21 05:58:55 +11:00
}
2011-03-22 02:28:13 +11:00
in.Unpack(inb) // Discard error.
2011-03-12 01:02:37 +11:00
res.Rtt[server] = time.Nanoseconds() - t
d.Close()
break
}
if err != nil {
return nil, err
}
return in, nil
2011-02-09 06:33:51 +11:00
}
2011-03-25 00:42:35 +11:00
// Perform an incoming Ixfr or Axfr. If the message q's question
// section contains an AXFR type an Axfr is performed. If q's question
// section contains an IXFR type an Ixfr is performed.
2011-03-22 08:53:15 +11:00
func (res *Resolver) Xfr(q *Msg, m chan Xfr) {
2011-03-24 19:24:24 +11:00
res.XfrTsig(q, nil, m)
2011-03-22 08:53:15 +11:00
}
2011-03-25 00:42:35 +11:00
// Perform an incoming Ixfr or Axfr with Tsig validation. If the message
// q's question section contains an AXFR type an Axfr is performed. If q's question
// section contains an IXFR type an Ixfr is performed.
2011-03-22 08:53:15 +11:00
func (res *Resolver) XfrTsig(q *Msg, t *Tsig, m chan Xfr) {
2011-03-21 05:58:55 +11:00
port, err := check(res, q)
if err != nil {
close(m)
2011-03-21 05:58:55 +11:00
return
2011-03-14 04:50:11 +11:00
}
sending, ok := q.Pack()
if !ok {
close(m)
2011-03-14 04:50:11 +11:00
return
}
// No defer close(m) as m is closed in d.XfrRead()
2011-03-14 04:50:11 +11:00
Server:
for i := 0; i < len(res.Servers); i++ {
server := res.Servers[i] + ":" + port
d, err := Dial("tcp", "", server)
2011-03-14 04:50:11 +11:00
if err != nil {
continue Server
}
2011-03-22 02:28:13 +11:00
d.Tsig = t
2011-03-24 19:24:24 +11:00
_, err = d.Write(sending)
if err != nil {
continue Server
}
d.XfrRead(q, m) // check
}
2011-02-09 06:33:51 +11:00
return
}
2011-03-21 05:58:55 +11:00
func check(res *Resolver, q *Msg) (port string, err os.Error) {
if res.Port == "" {
port = "53"
} else {
port = res.Port
}
if res.Rtt == nil {
res.Rtt = make(map[string]int64)
}
if q.Id == 0 {
q.Id = Id()
}
return
}