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
* HIP RR (needs list of domain names, need slice stuff for that)
* Is subdomain, is glue helper functions for this kind of stuff
* Make QuerySimple even simpler??
Issues:
* FunkenSturm is kaput.
* Check the network order, it works now, but this is on Intel??
* Make the testsuite work with public DNS servers
* pack/Unpack smaller. EDNS 'n stuff can be folded in

View File

@ -12,7 +12,7 @@ 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 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")
@ -24,8 +24,8 @@ func main() {
flag.PrintDefaults()
}
// Need to think about it... Config
c, _ := dns.ClientConfigFromFile("/etc/resolv.conf")
// Need to think about it... Config
c, _ := dns.ClientConfigFromFile("/etc/resolv.conf")
nameserver := "@" + c.Servers[0]
qtype := uint16(dns.TypeA) // Default qtype
qclass := uint16(dns.ClassINET) // Default qclass
@ -70,9 +70,11 @@ Flags:
nameserver = string([]byte(nameserver)[1:]) // chop off @
d := new(dns.Conn)
d.RemoteAddr = nameserver + ":" + *port
d.Attempts = 1
// 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
@ -92,35 +94,65 @@ Flags:
m.Extra[0] = opt
}
in := make(chan dns.Query)
var out chan dns.Query
if *tcp {
out = dns.QueryAndServeTCP(in, nil)
} else {
out = dns.QueryAndServeUDP(in, nil)
}
err := make(chan os.Error)
if *tcp {
go query(err, "tcp")
} else {
go query(err, "udp")
}
for _, v := range qname {
m.Question[0] = dns.Question{v, qtype, qclass}
m.Id = dns.Id()
in <- dns.Query{Msg: m, Conn: d}
dns.QueryReply = make(chan dns.Query)
dns.QueryRequest = make(chan dns.Query)
// Start the quering 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")
}
}()
r := <-out
if r.Msg != nil {
if r.Msg.Id != m.Id {
fmt.Printf("Id mismatch\n")
i :=0
for {
select {
case r := <-dns.QueryReply:
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 {
r.Msg = shortMsg(r.Msg)
}
fmt.Printf("%v", r.Msg)
} else {
fmt.Printf("%v\n", r.Err.String())
default:
}
}
}
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
func shortMsg(in *dns.Msg) *dns.Msg {
for i := 0; i < len(in.Answer); i++ {

View File

@ -5,41 +5,66 @@ package main
// zone contents.
// This zone is then checked cryptographically is
// 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
// it validates it writes this record to disk and
// this new key will be used in future validations.
import (
"os"
"os/signal"
"os"
"os/signal"
"fmt"
"dns"
)
// Static amount of RRs...
type zone struct {
name string
rrs [10000]dns.RR
size int
name string
rrs [10000]dns.RR
size int
}
var Zone zone
func handle(d *dns.Conn, i *dns.Msg) {
if i.MsgHdr.Response == true {
return
}
handleNotify(d, i)
handleXfr(d, i)
/* send response here, how ??? */
if i.MsgHdr.Response == true {
return
}
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) {
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 {
case "tcp":
err := dns.ListenAndServeTCP(addr, handle)
err := dns.QueryAndServeTCP(qhandle)
e <- err
case "udp":
err := dns.ListenAndServeUDP(addr, handle)
err := dns.QueryAndServeUDP(qhandle)
e <- err
}
return
@ -47,6 +72,12 @@ func listen(addr string, e chan os.Error, tcp string) {
func main() {
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("[::1]:8053", err, "tcp")
go listen("127.0.0.1:8053", err, "udp")
@ -61,6 +92,9 @@ forever:
case <-signal.Incoming:
fmt.Printf("Signal received, stopping")
break forever
case q := <-dns.QueryReply:
var _ = q
/* ... */
}
}
close(err)

View File

@ -1,6 +1,7 @@
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.
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.
// 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) {
// err := ListenAndServeUDP(addr, handle)
@ -433,6 +433,25 @@ func (d *Conn) Exchange(request []byte, nosend bool) (reply []byte, err os.Error
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 {
Header() *RR_Header
String() string

View File

@ -10,132 +10,92 @@ import (
"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.
type Query struct {
// The query message.
Msg *Msg
// 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.
Conn *Conn
// Any error when querying is returned in Err. The caller
// should just set this to nil.
Err os.Error
// Query time in here?
//
Err os.Error
}
// QueryUDP handles one query. It reads an incoming request from
// the in channel. The function f is executed in a seperate
// goroutine and performs the actual UDP query.
func QueryUDP(in, out chan Query, f func(*Conn, *Msg, chan Query)) {
query("udp", in, out, f)
// 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)
}
query("tcp", f)
return nil
}
// QueryTCP handles one query. It reads an incoming request from
// the in channel. The function f is executed in a seperate
// goroutine and performas the actual TCP query.
func QueryTCP(in, out chan Query, f func(*Conn, *Msg, chan Query)) {
query("tcp", in, out, f)
// QueryAndServeUDP listens for incoming requests on channel in and
// then calls f.
// 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")
}
query("udp", f)
return nil
}
// helper function.
func query(n string, in, out chan Query, f func(*Conn, *Msg, chan Query)) {
func query(n string, f func(*Conn, *Msg)) {
println("in query")
for {
select {
case q := <-in:
case q := <-QueryRequest:
println("recveived request")
err := q.Conn.Dial(n)
if err != nil {
out <- Query{Err: err}
}
if f == nil {
go QueryDefault(q.Conn, q.Msg, out)
} else {
go f(q.Conn, q.Msg, out)
QueryReply <- Query{Err: err}
}
go f(q.Conn, q.Msg)
}
}
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.
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
}
// 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
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
}