Fix up API and documentation

This commit is contained in:
Miek Gieben 2011-03-30 14:54:43 +02:00
parent 0449ff62a2
commit e5a769baf0
5 changed files with 108 additions and 100 deletions

View File

@ -12,7 +12,6 @@ import (
func main() {
var dnssec *bool = flag.Bool("dnssec", false, "Request DNSSEC records")
var short *bool = flag.Bool("short", false, "Abbriate long DNSKEY and RRSIG RRs")
//var port *string = flag.String("port", "53", "Set the query port")
var aa *bool = flag.Bool("aa", false, "Set AA flag in query")
var ad *bool = flag.Bool("ad", false, "Set AD flag in query")
var cd *bool = flag.Bool("cd", false, "Set CD flag in query")
@ -20,7 +19,7 @@ func main() {
var tcp *bool = flag.Bool("tcp", false, "TCP mode")
var nsid *bool = flag.Bool("nsid", false, "Ask for the NSID")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage: %s [@server] [qtype] [qclass] [name ...]\n", os.Args[0])
fmt.Fprintf(os.Stderr, "Usage: %s [@server(:port)] [qtype] [qclass] [name ...]\n", os.Args[0])
flag.PrintDefaults()
}
@ -69,72 +68,54 @@ Flags:
}
nameserver = string([]byte(nameserver)[1:]) // chop off @
// Port stuff does not work
d := new(dns.Conn)
// d.RemoteAddr = nameserver + ":" + *port
d.RemoteAddr = nameserver // works with /etc/resolv.conf
d.Attempts = 1
m := new(dns.Msg)
m.MsgHdr.Authoritative = *aa
m.MsgHdr.AuthenticatedData = *ad
m.MsgHdr.CheckingDisabled = *cd
m.MsgHdr.RecursionDesired = *rd
m.Question = make([]dns.Question, 1)
if *dnssec || *nsid {
opt := new(dns.RR_OPT)
opt.SetDo()
opt.SetVersion(0)
opt.SetUDPSize(dns.DefaultMsgSize)
if *nsid {
opt.SetNsid("")
}
m.Extra = make([]dns.RR, 1)
m.Extra[0] = opt
if !strings.HasSuffix(nameserver, ":53") {
nameserver += ":53"
}
err := make(chan os.Error)
if *tcp {
go query(err, "tcp")
go query("tcp", err)
} else {
go query(err, "udp")
go query("udp", err)
}
dns.QueryReply = make(chan dns.Query)
dns.QueryRequest = make(chan dns.Query)
// Start the quering in a closure
dns.QueryInitChannels()
// Start the querier in a closure
go func() {
for _, v := range qname {
m.Question[0] = dns.Question{v, qtype, qclass}
m.Id = dns.Id()
dns.QueryRequest <- dns.Query{Msg: m, Conn: d}
println("querying")
d, m := newConnMsg(v, nameserver, c.Attempts, qtype, qclass, *aa, *ad, *cd, *rd, *dnssec, *nsid)
dns.QueryRequest <- dns.Query{Query: m, Conn: d}
}
}()
i :=0
i := 0
forever:
for {
select {
case r := <-dns.QueryReply:
if r.Msg != nil {
if r.Msg.Id != m.Id {
if r.Reply != nil {
if r.Query.Id != r.Reply.Id {
fmt.Printf("Id mismatch\n")
}
if *short {
r.Msg = shortMsg(r.Msg)
r.Reply = shortMsg(r.Reply)
}
fmt.Printf("%v", r.Msg)
fmt.Printf("%v", r.Reply)
} else {
fmt.Printf("%v\n", r.Err.String())
}
default:
i++
if i == len(qname) {
break forever
}
case e := <-err:
fmt.Printf("%v", e.String())
break forever
}
}
}
func query(e chan os.Error, tcp string) {
func query(tcp string, e chan os.Error) {
switch tcp {
case "tcp":
err := dns.QueryAndServeTCP(qhandle)
@ -149,10 +130,38 @@ func query(e chan os.Error, tcp string) {
// reply checking 'n stuff
func qhandle(d *dns.Conn, i *dns.Msg) {
o, err := d.ExchangeMsg(i, false)
dns.QueryReply <- dns.Query{Msg: o, Conn: d, Err: err}
dns.QueryReply <- dns.Query{Query: i, Reply: o, Conn: d, Err: err}
d.Close()
return
}
func newConnMsg(qname, nameserver string, attempts int, qtype, qclass uint16, aa, ad, cd, rd, dnssec, nsid bool) (*dns.Conn, *dns.Msg) {
d := new(dns.Conn)
d.RemoteAddr = nameserver
d.Attempts = attempts
m := new(dns.Msg)
m.MsgHdr.Authoritative = aa
m.MsgHdr.AuthenticatedData = ad
m.MsgHdr.CheckingDisabled = cd
m.MsgHdr.RecursionDesired = rd
m.Question = make([]dns.Question, 1)
if dnssec || nsid {
opt := new(dns.RR_OPT)
opt.SetDo()
opt.SetVersion(0)
opt.SetUDPSize(dns.DefaultMsgSize)
if nsid {
opt.SetNsid("")
}
m.Extra = make([]dns.RR, 1)
m.Extra[0] = opt
}
m.Question[0] = dns.Question{qname, qtype, qclass}
m.Id = dns.Id()
return d, m
}
// Walk trough message and short Key data and Sig data
func shortMsg(in *dns.Msg) *dns.Msg {
for i := 0; i < len(in.Answer); i++ {

View File

@ -74,6 +74,7 @@ func main() {
err := make(chan os.Error)
// Outgoing queries
dns.QueryInitChannels()
go query(err, "tcp")
go query(err, "udp")

33
dns.go
View File

@ -16,12 +16,13 @@
// The package dns supports (async) querying/replying, incoming/outgoing Axfr/Ixfr,
// TSIG, EDNS0, dynamic updates, notifies and DNSSEC validation/signing.
//
// The patterns described here are cumulative: earlier declared variables
// are reused.
// In the DNS messages are exchanged. Use pattern for creating one:
//
// message := new(Msg)
// // Create message with the desired options.
// // Set the desired options.
// message.MsgHdr.Recursion_desired = true
// // Create room in for the question and set it.
// message.Question = make([]Question, 1)
// message.Question[0] = Question{"miek.nl", TypeSOA, ClassINET}
//
@ -29,32 +30,36 @@
//
// dnsconn := new(Conn)
// dnsconn.RemoteAddr = "127.0.0.1:53"
// dnsconn.Dial("udp") // "tcp" for tcp connection.
// inmessage, err := SimpleQuery(dnsconn, message)
// dnsconn.Close()
// inmessage, err := SimpleQuery("udp", dnsconn, message) // or "tcp".
//
// (Asynchronize) querying the DNS is also supported. The Query structure
// (Asynchronized) querying the DNS is supported. The Query structure
// is used for communicating with the Query* functions.
// Basic use pattern for creating such a resolver:
//
// in := make(chan Query)
// out := QueryAndServeUDP(in, nil) // nil means use QueryDefault()
// dnsconn := new(Conn)
// dnsconn.RemoteAddr = "8.8.8.8:53"
// func qhandle(*Conn, *Msg) { /* handle query */ }
//
// func query(e chan os.Error) {
// err := QueryAndServeUDP(qhandle)
// e <- err
// }
// QueryInitChannels()
// err := make(chan os.Error)
// go query(err)
//
// in <- Query{Msg: message, Conn: dnscon}
// reply := <-out
// QueryRequest <- Query{Query: message, Conn: dnsconn}
// /* ... later ... */
// reply := <-QueryReply
//
// Server side programming is also supported also by using a Conn structure.
// Basic use pattern for creating an UDP DNS server:
//
// func handle(*Conn, *Msg, ...interface{}) { /* handle request */ }
// func handle(*Conn, *Msg) { /* handle request */ }
//
// func listen(addr string, e chan os.Error) {
// err := ListenAndServeUDP(addr, handle)
// e <- err
// }
// err := make(chan os.Error)
//
// go listen("127.0.0.1:8053", err)
//
package dns

3
msg.go
View File

@ -45,7 +45,8 @@ var (
ErrSigGen os.Error = &Error{Error: "bad signature generation"}
ErrAuth os.Error = &Error{Error: "bad authentication"}
ErrXfrSoa os.Error = &Error{Error: "no SOA seen"}
ErrHandle os.Error = &Error{Error: "handle is nill"}
ErrHandle os.Error = &Error{Error: "handle is nil"}
ErrChan os.Error = &Error{Error: "channel is nil"}
)
// A manually-unpacked version of (id, bits).

View File

@ -18,29 +18,36 @@ var QueryReply chan Query
// Query is used to communicate with the Query* functions.
type Query struct {
// The query message.
Msg *Msg
Query *Msg
// The reply message.
Reply *Msg
// A Conn. Its only required to fill out Conn.RemoteAddr.
// Optionally you may set Conn.Tsig if TSIG is required.
// The rest of the structure is filled by the Query functions.
Conn *Conn
//
Err os.Error
//
Err os.Error
}
// Initialize the QueryRequest and QueryReply
// channels.
func QueryInitChannels() {
QueryRequest = make(chan Query)
QueryReply = make(chan Query)
}
// QueryAndServeTCP listens for incoming requests on channel in and
// then calls f.
// The function f is executed in a seperate goroutine and performs the actual
// TCP query.
func QueryAndServeTCP(f func(*Conn, *Msg)) os.Error {
if f == nil {
return ErrHandle
}
if QueryReply == nil {
QueryReply = make(chan Query)
}
if QueryRequest == nil {
QueryRequest = make(chan Query)
}
if f == nil {
return ErrHandle
}
if QueryReply == nil || QueryRequest == nil {
return ErrChan
}
query("tcp", f)
return nil
}
@ -50,52 +57,37 @@ func QueryAndServeTCP(f func(*Conn, *Msg)) os.Error {
// The function f is executed in a seperate goroutine and performs the actual
// UDP query.
func QueryAndServeUDP(f func(*Conn, *Msg)) os.Error {
if f == nil {
return ErrHandle
}
if QueryReply == nil {
QueryReply = make(chan Query)
println("Creating channel reply")
}
if QueryRequest == nil {
QueryRequest = make(chan Query)
println("Creating channel request")
}
if f == nil {
return ErrHandle
}
if QueryReply == nil || QueryRequest == nil {
return ErrChan
}
query("udp", f)
return nil
}
func query(n string, f func(*Conn, *Msg)) {
println("in query")
for {
select {
case q := <-QueryRequest:
println("recveived request")
err := q.Conn.Dial(n)
if err != nil {
QueryReply <- Query{Err: err}
}
go f(q.Conn, q.Msg)
go f(q.Conn, q.Query)
}
}
panic("not reached")
}
// Simple query function that waits for and returns the reply.
func QuerySimple(d *Conn, m *Msg) (*Msg, os.Error) {
buf, ok := m.Pack()
if !ok {
return nil, ErrPack
}
// Dialing should happen in the client
ret, err := d.Exchange(buf, false)
if err != nil {
return nil, err
}
o := new(Msg)
if ok := o.Unpack(ret); !ok {
return nil, ErrUnpack
}
return o, nil
func QuerySimple(n string, d *Conn, m *Msg) (*Msg, os.Error) {
err := d.Dial(n)
if err != nil {
return nil, err
}
o, err := d.ExchangeMsg(m, false)
d.Close()
return o, err
}