seeing some light in the query API

global channels look to be helpfull
This commit is contained in:
Miek Gieben 2011-03-30 11:14:36 +02:00
parent 518ba6f506
commit 0449ff62a2
6 changed files with 193 additions and 145 deletions

2
TODO
View File

@ -16,8 +16,10 @@ Todo:
* encoding NSEC3/NSEC bitmaps, DEcoding works * encoding NSEC3/NSEC bitmaps, DEcoding works
* HIP RR (needs list of domain names, need slice stuff for that) * HIP RR (needs list of domain names, need slice stuff for that)
* Is subdomain, is glue helper functions for this kind of stuff * Is subdomain, is glue helper functions for this kind of stuff
* Make QuerySimple even simpler??
Issues: Issues:
* FunkenSturm is kaput.
* Check the network order, it works now, but this is on Intel?? * Check the network order, it works now, but this is on Intel??
* Make the testsuite work with public DNS servers * Make the testsuite work with public DNS servers
* pack/Unpack smaller. EDNS 'n stuff can be folded in * pack/Unpack smaller. EDNS 'n stuff can be folded in

View File

@ -12,7 +12,7 @@ import (
func main() { func main() {
var dnssec *bool = flag.Bool("dnssec", false, "Request DNSSEC records") var dnssec *bool = flag.Bool("dnssec", false, "Request DNSSEC records")
var short *bool = flag.Bool("short", false, "Abbriate long DNSKEY and RRSIG RRs") 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 port *string = flag.String("port", "53", "Set the query port")
var aa *bool = flag.Bool("aa", false, "Set AA flag in query") 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 ad *bool = flag.Bool("ad", false, "Set AD flag in query")
var cd *bool = flag.Bool("cd", false, "Set CD flag in query") var cd *bool = flag.Bool("cd", false, "Set CD flag in query")
@ -24,8 +24,8 @@ func main() {
flag.PrintDefaults() flag.PrintDefaults()
} }
// Need to think about it... Config // Need to think about it... Config
c, _ := dns.ClientConfigFromFile("/etc/resolv.conf") c, _ := dns.ClientConfigFromFile("/etc/resolv.conf")
nameserver := "@" + c.Servers[0] nameserver := "@" + c.Servers[0]
qtype := uint16(dns.TypeA) // Default qtype qtype := uint16(dns.TypeA) // Default qtype
qclass := uint16(dns.ClassINET) // Default qclass qclass := uint16(dns.ClassINET) // Default qclass
@ -70,9 +70,11 @@ Flags:
nameserver = string([]byte(nameserver)[1:]) // chop off @ nameserver = string([]byte(nameserver)[1:]) // chop off @
d := new(dns.Conn) // Port stuff does not work
d.RemoteAddr = nameserver + ":" + *port d := new(dns.Conn)
d.Attempts = 1 // d.RemoteAddr = nameserver + ":" + *port
d.RemoteAddr = nameserver // works with /etc/resolv.conf
d.Attempts = 1
m := new(dns.Msg) m := new(dns.Msg)
m.MsgHdr.Authoritative = *aa m.MsgHdr.Authoritative = *aa
@ -92,35 +94,65 @@ Flags:
m.Extra[0] = opt m.Extra[0] = opt
} }
in := make(chan dns.Query) err := make(chan os.Error)
var out chan dns.Query if *tcp {
if *tcp { go query(err, "tcp")
out = dns.QueryAndServeTCP(in, nil) } else {
} else { go query(err, "udp")
out = dns.QueryAndServeUDP(in, nil) }
}
for _, v := range qname { dns.QueryReply = make(chan dns.Query)
m.Question[0] = dns.Question{v, qtype, qclass} dns.QueryRequest = make(chan dns.Query)
m.Id = dns.Id() // Start the quering in a closure
in <- dns.Query{Msg: m, Conn: d} 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")
}
}()
r := <-out i :=0
for {
if r.Msg != nil { select {
if r.Msg.Id != m.Id { case r := <-dns.QueryReply:
fmt.Printf("Id mismatch\n") if r.Msg != nil {
if r.Msg.Id != m.Id {
fmt.Printf("Id mismatch\n")
}
if *short {
r.Msg = shortMsg(r.Msg)
}
fmt.Printf("%v", r.Msg)
} else {
fmt.Printf("%v\n", r.Err.String())
} }
if *short { default:
r.Msg = shortMsg(r.Msg)
}
fmt.Printf("%v", r.Msg)
} else {
fmt.Printf("%v\n", r.Err.String())
} }
} }
} }
func query(e chan os.Error, tcp string) {
switch tcp {
case "tcp":
err := dns.QueryAndServeTCP(qhandle)
e <- err
case "udp":
err := dns.QueryAndServeUDP(qhandle)
e <- err
}
return
}
// 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}
return
}
// Walk trough message and short Key data and Sig data // Walk trough message and short Key data and Sig data
func shortMsg(in *dns.Msg) *dns.Msg { func shortMsg(in *dns.Msg) *dns.Msg {
for i := 0; i < len(in.Answer); i++ { for i := 0; i < len(in.Answer); i++ {

View File

@ -5,41 +5,66 @@ package main
// zone contents. // zone contents.
// This zone is then checked cryptographically is // This zone is then checked cryptographically is
// everything is correct. // everything is correct.
// When the message is deemed correct a remote
// server is sent a notify to retrieve the ixfr/axfr.
// If a new DNSKEY record is seen for the apex and // If a new DNSKEY record is seen for the apex and
// it validates it writes this record to disk and // it validates it writes this record to disk and
// this new key will be used in future validations. // this new key will be used in future validations.
import ( import (
"os" "os"
"os/signal" "os/signal"
"fmt" "fmt"
"dns" "dns"
) )
// Static amount of RRs... // Static amount of RRs...
type zone struct { type zone struct {
name string name string
rrs [10000]dns.RR rrs [10000]dns.RR
size int size int
} }
var Zone zone var Zone zone
func handle(d *dns.Conn, i *dns.Msg) { func handle(d *dns.Conn, i *dns.Msg) {
if i.MsgHdr.Response == true { /* send response here, how ??? */
return if i.MsgHdr.Response == true {
} return
handleNotify(d, i) }
handleXfr(d, i) handleNotify(d, i)
handleXfr(d, i)
}
func qhandle(d *dns.Conn, i *dns.Msg) {
// We should send i to d.RemoteAddr
// simpleQuery here
// what do we do with the reply
///handle HERE!!?? Need globals or stuff in d...
// in/out channel must be accessible
} }
func listen(addr string, e chan os.Error, tcp string) { func listen(addr string, e chan os.Error, tcp string) {
switch tcp {
case "tcp":
err := dns.ListenAndServeTCP(addr, handle)
e <- err
case "udp":
err := dns.ListenAndServeUDP(addr, handle)
e <- err
}
return
}
func query(e chan os.Error, tcp string) {
switch tcp { switch tcp {
case "tcp": case "tcp":
err := dns.ListenAndServeTCP(addr, handle) err := dns.QueryAndServeTCP(qhandle)
e <- err e <- err
case "udp": case "udp":
err := dns.ListenAndServeUDP(addr, handle) err := dns.QueryAndServeUDP(qhandle)
e <- err e <- err
} }
return return
@ -47,6 +72,12 @@ func listen(addr string, e chan os.Error, tcp string) {
func main() { func main() {
err := make(chan os.Error) err := make(chan os.Error)
// Outgoing queries
go query(err, "tcp")
go query(err, "udp")
// Incoming queries
go listen("127.0.0.1:8053", err, "tcp") go listen("127.0.0.1:8053", err, "tcp")
go listen("[::1]:8053", err, "tcp") go listen("[::1]:8053", err, "tcp")
go listen("127.0.0.1:8053", err, "udp") go listen("127.0.0.1:8053", err, "udp")
@ -61,6 +92,9 @@ forever:
case <-signal.Incoming: case <-signal.Incoming:
fmt.Printf("Signal received, stopping") fmt.Printf("Signal received, stopping")
break forever break forever
case q := <-dns.QueryReply:
var _ = q
/* ... */
} }
} }
close(err) close(err)

View File

@ -1,6 +1,7 @@
package dns package dns
// Everything is assumed in the ClassINET class // Everything is assumed in the ClassINET class. If
// you need other classes you are on your own.
// Create a reply packet from a request message. // Create a reply packet from a request message.
func (dns *Msg) SetReply(request *Msg) { func (dns *Msg) SetReply(request *Msg) {

21
dns.go
View File

@ -48,7 +48,7 @@
// Server side programming is also supported also by using a Conn structure. // Server side programming is also supported also by using a Conn structure.
// Basic use pattern for creating an UDP DNS server: // Basic use pattern for creating an UDP DNS server:
// //
// func handle(d *Conn, m *Msg) { /* handle request */ } // func handle(*Conn, *Msg, ...interface{}) { /* handle request */ }
// //
// func listen(addr string, e chan os.Error) { // func listen(addr string, e chan os.Error) {
// err := ListenAndServeUDP(addr, handle) // err := ListenAndServeUDP(addr, handle)
@ -433,6 +433,25 @@ func (d *Conn) Exchange(request []byte, nosend bool) (reply []byte, err os.Error
return return
} }
// ExchangeMsg combines a WriteMSg and a ReadMsg.
// First the request is written to d and then it waits
// for a reply with ReadMsg.
// If nosend is true, the write is skipped.
func (d *Conn) ExchangeMsg(request *Msg, nosend bool) (reply *Msg, err os.Error) {
if !nosend {
err = d.WriteMsg(request)
if err != nil {
return nil, err
}
}
reply = new(Msg)
err = d.ReadMsg(reply)
if err != nil {
return nil, err
}
return reply, nil
}
type RR interface { type RR interface {
Header() *RR_Header Header() *RR_Header
String() string String() string

View File

@ -10,132 +10,92 @@ import (
"os" "os"
) )
// Request a query by sending to this channel.
var QueryRequest chan Query // *Query
// Listen for replies on this channel.
var QueryReply chan Query
// Query is used to communicate with the Query* functions. // Query is used to communicate with the Query* functions.
type Query struct { type Query struct {
// The query message. // The query message.
Msg *Msg Msg *Msg
// A Conn. Its only required to fill out Conn.RemoteAddr. // A Conn. Its only required to fill out Conn.RemoteAddr.
// Optionally you may set Conn.Tsig if TSIG is required. // Optionally you may set Conn.Tsig if TSIG is required.
// The rest of the structure is filled by the Query functions. // The rest of the structure is filled by the Query functions.
Conn *Conn Conn *Conn
// Any error when querying is returned in Err. The caller //
// should just set this to nil. Err os.Error
Err os.Error
// Query time in here?
} }
// QueryUDP handles one query. It reads an incoming request from // QueryAndServeTCP listens for incoming requests on channel in and
// the in channel. The function f is executed in a seperate // then calls f.
// goroutine and performs the actual UDP query. // The function f is executed in a seperate goroutine and performs the actual
func QueryUDP(in, out chan Query, f func(*Conn, *Msg, chan Query)) { // TCP query.
query("udp", in, out, f) 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)
}
query("tcp", f)
return nil
} }
// QueryTCP handles one query. It reads an incoming request from // QueryAndServeUDP listens for incoming requests on channel in and
// the in channel. The function f is executed in a seperate // then calls f.
// goroutine and performas the actual TCP query. // The function f is executed in a seperate goroutine and performs the actual
func QueryTCP(in, out chan Query, f func(*Conn, *Msg, chan Query)) { // UDP query.
query("tcp", in, out, f) 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")
}
query("udp", f)
return nil
} }
// helper function. func query(n string, f func(*Conn, *Msg)) {
func query(n string, in, out chan Query, f func(*Conn, *Msg, chan Query)) { println("in query")
for { for {
select { select {
case q := <-in: case q := <-QueryRequest:
println("recveived request")
err := q.Conn.Dial(n) err := q.Conn.Dial(n)
if err != nil { if err != nil {
out <- Query{Err: err} QueryReply <- Query{Err: err}
}
if f == nil {
go QueryDefault(q.Conn, q.Msg, out)
} else {
go f(q.Conn, q.Msg, out)
} }
go f(q.Conn, q.Msg)
} }
} }
panic("not reached") panic("not reached")
} }
// Default Handler when none is given.
func QueryDefault(d *Conn, m *Msg, q chan Query) {
defer d.Close()
buf, ok := m.Pack()
if !ok {
q <- Query{nil, d, ErrPack}
}
ret, err := d.Exchange(buf, false)
if err != nil {
q <- Query{nil, d, err}
}
out := new(Msg)
if ok1 := out.Unpack(ret); !ok1 {
q <- Query{nil, d, ErrUnpack}
}
q <- Query{out, d, nil}
return
}
// Simple query function that waits for and returns the reply. // Simple query function that waits for and returns the reply.
func QuerySimple(d *Conn, m *Msg) (*Msg, os.Error) { func QuerySimple(d *Conn, m *Msg) (*Msg, os.Error) {
buf, ok := m.Pack() buf, ok := m.Pack()
if !ok { if !ok {
return nil, ErrPack 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
}
// QueryAndServeTCP listens for incoming requests on channel in and
// then calls QueryTCP with f to the handle the request.
// It returns a channel on which the response is returned.
func QueryAndServeTCP(in chan Query, f func(*Conn, *Msg, chan Query)) chan Query {
out := make(chan Query)
go QueryTCP(in, out, f)
return out
}
// QueryAndServeUDP listens for incoming requests on channel in and
// then calls QueryUDP with f to the handle the request.
// It returns a channel on which the response is returned.
func QueryAndServeUDP(in chan Query, f func(*Conn, *Msg, chan Query)) chan Query {
out := make(chan Query)
go QueryUDP(in, out, f)
return out
}
/* // alg for querying a list of servers, not sure if we going to keep it
for i := 0; i < len(res.Servers); i++ {
var d *Conn
server := res.Servers[i] + ":" + port
t := time.Nanoseconds()
if res.Tcp {
d, err = Dial("tcp", "", server)
if err != nil {
continue
}
} else {
d, err = Dial("udp", "", server)
if err != nil {
continue
}
}
inb, err = d.Exchange(sending, false)
if err != nil {
continue
}
in.Unpack(inb) // Discard error.
res.Rtt[server] = time.Nanoseconds() - t
d.Close()
break
} }
*/ // 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
}