Merge branch 'reader'

This commit is contained in:
Miek Gieben 2012-11-21 22:32:23 +01:00
commit 9e02a40cc8
19 changed files with 397 additions and 288 deletions

View File

@ -27,8 +27,7 @@ If you like this, you may also be interested in:
* Reply speed around ~ 50K qps (faster hardware results in more qps);
* Parsing RRs with ~ 100K RR/s, that's 5M records in about 50 seconds;
* Server side programming (mimicking the net/http package);
* Client side programming with asynchronous calls;
* Asynchronous queries/replies for client and server;
* Client side programming;
* DNSSEC: signing, validating and key generation for DSA, RSA and ECDSA;
* EDNS0, NSID;
* AXFR/IXFR;

View File

@ -9,8 +9,8 @@ import (
)
// Order of events:
// *client -> *reply -> Exchange*() -> dial()/send()->write()/receive()->read()
// *client -> *reply -> Exchange() -> dial()/send()->write()/receive()->read()
// Do I want make this an interface thingy?
type reply struct {
client *Client
addr string
@ -29,50 +29,18 @@ type Client struct {
Net string // if "tcp" a TCP query will be initiated, otherwise an UDP one (default is "" for UDP)
Attempts int // number of attempts, if not set defaults to 1
Retry bool // retry with TCP
ReadTimeout time.Duration // the net.Conn.SetReadTimeout value for new connections (ns), defauls to 2 * 1e9
WriteTimeout time.Duration // the net.Conn.SetWriteTimeout value for new connections (ns), defauls to 2 * 1e9
ReadTimeout time.Duration // the net.Conn.SetReadTimeout value for new connections (ns), defaults to 2 * 1e9
WriteTimeout time.Duration // the net.Conn.SetWriteTimeout value for new connections (ns), defaults to 2 * 1e9
TsigSecret map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be fully qualified
}
// Do performs an asynchronous query. The msg *Msg is the question to ask, the
// string addr is the address of the nameserver, the parameter data is used
// in the callback function. The call backback function is called with the
// original query, the answer returned from the nameserver an optional error and
// data.
func (c *Client) Do(msg *Msg, addr string, data interface{}, callback func(*Msg, *Msg, error, interface{})) {
go func() {
r, err := c.Exchange(msg, addr)
callback(msg, r, err, data)
}()
}
// DoRtt is equivalent to Do, except that is calls ExchangeRtt.
func (c *Client) DoRtt(msg *Msg, addr string, data interface{}, callback func(*Msg, *Msg, time.Duration, error, interface{})) {
go func() {
r, rtt, err := c.ExchangeRtt(msg, addr)
callback(msg, r, rtt, err, data)
}()
}
// Exchange performs an synchronous query. It sends the message m to the address
// contained in a and waits for an reply. Basic use pattern with a *Client:
//
// c := new(dns.Client)
// in, err := c.Exchange(message, "127.0.0.1:53")
//
// See Client.ExchangeRtt(...) to get the round trip time.
func (c *Client) Exchange(m *Msg, a string) (r *Msg, err error) {
r, _, err = c.ExchangeRtt(m, a)
return
}
// ExchangeRtt performs an synchronous query. It sends the message m to the address
// contained in a and waits for an reply. Basic use pattern with a *Client:
//
// c := new(dns.Client)
// in, rtt, err := c.ExchangeRtt(message, "127.0.0.1:53")
// in, rtt, err := c.Exchange(message, "127.0.0.1:53")
//
func (c *Client) ExchangeRtt(m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
func (c *Client) Exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
w := new(reply)
w.client = c
w.addr = a

View File

@ -10,7 +10,7 @@ func TestClientSync(t *testing.T) {
m.SetQuestion("miek.nl.", TypeSOA)
c := new(Client)
r, _ := c.Exchange(m, "85.223.71.124:53")
r, _, _ := c.Exchange(m, "37.251.95.53:53")
if r != nil && r.Rcode != RcodeSuccess {
t.Log("Failed to get an valid answer")
@ -19,20 +19,6 @@ func TestClientSync(t *testing.T) {
}
}
func TestClientASync(t *testing.T) {
m := new(Msg)
m.SetQuestion("miek.nl.", TypeSOA)
c := new(Client)
c.Do(m, "85.223.71.124:53", nil, func(m, r *Msg, e error, d interface{}) {
if r != nil && r.Rcode != RcodeSuccess {
t.Log("Failed to get an valid answer")
t.Fail()
t.Logf("%v\n", r)
}
})
}
func TestClientEDNS0(t *testing.T) {
m := new(Msg)
m.SetQuestion("miek.nl.", TypeDNSKEY)
@ -42,7 +28,7 @@ func TestClientEDNS0(t *testing.T) {
//edns.SetNsid("") // Empty to request it
c := new(Client)
r, _ := c.Exchange(m, "85.223.71.124:53")
r, _, _ := c.Exchange(m, "37.251.95.53:53")
if r != nil && r.Rcode != RcodeSuccess {
t.Log("Failed to get an valid answer")
@ -60,7 +46,7 @@ func TestClientTsigAXFR(t *testing.T) {
c.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}
c.Net = "tcp"
if a, err := c.XfrReceive(m, "85.223.71.124:53"); err != nil {
if a, err := c.XfrReceive(m, "37.251.95.53:53"); err != nil {
t.Log("Failed to setup axfr: " + err.Error())
t.Fail()
return
@ -85,7 +71,7 @@ func TestClientAXFRMultipleMessages(t *testing.T) {
c := new(Client)
c.Net = "tcp"
if a, err := c.XfrReceive(m, "85.223.71.124:53"); err != nil {
if a, err := c.XfrReceive(m, "37.251.95.53:53"); err != nil {
t.Log("Failed to setup axfr" + err.Error())
t.Fail()
return

View File

@ -155,6 +155,7 @@ func (dns *Msg) IsEdns0() *RR_OPT {
// IsDomainName checks if s is a valid domainname, it returns
// the number of labels, total length and true, when a domain name is valid.
// When false is returned the labelcount and length are not defined.
// TODO(mg): checks for \DDD
func IsDomainName(s string) (uint8, uint8, bool) { // copied from net package.
// See RFC 1035, RFC 3696.
l := len(s)
@ -189,6 +190,11 @@ func IsDomainName(s string) (uint8, uint8, bool) { // copied from net package.
partlen++
case c == '\\':
// Ok
// case c == '@':
// if last != '\\' {
// return 0, uint8(l - longer), false
// }
// partlen++
case '0' <= c && c <= '9':
ok = true
partlen++

5
dns.go
View File

@ -59,10 +59,9 @@
// server configured on 127.0.0.1 and port 53:
//
// c := new(Client)
// in, err := c.Exchange(m1, "127.0.0.1:53")
// in, rtt, err := c.Exchange(m1, "127.0.0.1:53")
//
// An asynchronous query is also possible, see client.Do or client.DoRtt, when
// you are interested in the round trip time of the exchange.
// For asynchronous queries it is easy to wrap Exchange().
//
// From a birds eye view a dns message consists out of four sections.
// The question section: in.Question, the answer section: in.Answer,

View File

@ -13,6 +13,8 @@
// m.SetEdns0(4096, true)
//
// Signature generation, signature verification and key generation are all supported.
// Writing a DNSSEC validating resolver is hard, if you need something like that you
// might want to use the unbound wrapper found at github.com/miekg/unbound .
package dns
import (

View File

@ -1,2 +1,3 @@
package dns
// Find better solution

View File

@ -26,57 +26,59 @@ func main() {
}
for _, a := range addr {
m.Question[0] = dns.Question{"version.bind.", dns.TypeTXT, dns.ClassCHAOS}
in, rtt, _ := c.ExchangeRtt(m, a)
in, rtt, _ := c.Exchange(m, a)
if in != nil && len(in.Answer) > 0 {
fmt.Printf("(time %.3d µs) %v\n", rtt/1e3, in.Answer[0])
}
m.Question[0] = dns.Question{"hostname.bind.", dns.TypeTXT, dns.ClassCHAOS}
in, rtt, _ = c.ExchangeRtt(m, a)
in, rtt, _ = c.Exchange(m, a)
if in != nil && len(in.Answer) > 0 {
fmt.Printf("(time %.3d µs) %v\n", rtt/1e3, in.Answer[0])
}
}
}
func qhandler(m, r *dns.Msg, e error, data interface{}) {
ips := make([]string, 0)
if r != nil && r.Rcode == dns.RcodeSuccess {
for _, aa := range r.Answer {
switch aa.(type) {
case *dns.RR_A:
ips = append(ips, aa.(*dns.RR_A).A.String()+":53")
case *dns.RR_AAAA:
ips = append(ips, "["+aa.(*dns.RR_AAAA).AAAA.String()+"]:53")
}
}
data.(chan []string) <- ips
return
}
data.(chan []string) <- nil
}
func addresses(conf *dns.ClientConfig, c *dns.Client, name string) []string {
func addresses(conf *dns.ClientConfig, c *dns.Client, name string) (ips []string) {
m4 := new(dns.Msg)
m4.SetQuestion(dns.Fqdn(os.Args[1]), dns.TypeA)
m6 := new(dns.Msg)
m6.SetQuestion(dns.Fqdn(os.Args[1]), dns.TypeAAAA)
c4 := c.Do(m4, conf.Servers[0]+":"+conf.Port)
c6 := c.Do(m6, conf.Servers[0]+":"+conf.Port)
addr := make(chan []string)
defer close(addr)
c.Do(m4, conf.Servers[0]+":"+conf.Port, addr, qhandler)
c.Do(m6, conf.Servers[0]+":"+conf.Port, addr, qhandler)
var ips []string
i := 2 // two outstanding queries
forever:
for {
select {
case ip := <-addr:
ips = append(ips, ip...)
case ip4 := <-c4:
if ip4.Reply != nil && ip4.Reply.Rcode == dns.RcodeSuccess {
for _, a := range ip4.Reply.Answer {
switch a.(type) {
case *dns.RR_A:
ips = append(ips, a.(*dns.RR_A).A.String()+":53")
}
}
}
i--
if i == 0 {
break forever
}
case ip6 := <-c6:
if ip6.Reply != nil && ip6.Reply.Rcode == dns.RcodeSuccess {
for _, a := range ip6.Reply.Answer {
switch a.(type) {
case *dns.RR_AAAA:
ips = append(ips, a.(*dns.RR_AAAA).AAAA.String()+":53")
}
}
}
i--
if i == 0 {
break forever
}
}
}
return ips

View File

@ -24,7 +24,7 @@ func localQuery(qname string, qtype uint16) (*dns.Msg, error) {
localm.SetQuestion(qname, qtype)
for i := range conf.Servers {
server := conf.Servers[i]
r, err := localc.Exchange(localm, server+":"+conf.Port)
r, _, err := localc.Exchange(localm, server+":"+conf.Port)
if r == nil || r.Rcode == dns.RcodeNameError || r.Rcode == dns.RcodeSuccess {
return r, err
}
@ -114,7 +114,7 @@ func main() {
} else {
nsAddressPort = ip + ":53"
}
soa, err := c.Exchange(m, nsAddressPort)
soa, _, err := c.Exchange(m, nsAddressPort)
// TODO: retry if timeout? Otherwise, one lost UDP packet and it is the end
if soa == nil {
success = false

View File

@ -190,7 +190,8 @@ Flags:
m.Extra = append(m.Extra, o)
}
for i, v := range qname {
ch := make(chan *dns.Exchange)
for _, v := range qname {
m.Question[0] = dns.Question{dns.Fqdn(v), qtype, qclass}
m.Id = dns.Id()
if *query {
@ -217,12 +218,26 @@ Flags:
continue
}
c.DoRtt(m, nameserver, nil, func(m, r *dns.Msg, rtt time.Duration, e error, data interface{}) {
defer func() {
if i == len(qname)-1 {
os.Exit(0)
}
}()
c.Do(m, nameserver, ch)
}
// HUH?
if qtype != dns.TypeAXFR && qtype != dns.TypeIXFR {
// xfr didn't start any goroutines
select {}
}
i := 0
for {
select {
case ex := <-ch:
if i == len(qname)-1 {
os.Exit(0)
}
i++
r := ex.Reply
rtt := ex.Rtt
e := ex.Error
Redo:
if e != nil {
fmt.Printf(";; %s\n", e.Error())
@ -241,19 +256,19 @@ Flags:
o.Hdr.Rrtype = dns.TypeOPT
o.SetUDPSize(dns.DefaultMsgSize)
m.Extra = append(m.Extra, o)
r, rtt, e = c.ExchangeRtt(m, nameserver)
r, rtt, e = c.Exchange(m, nameserver)
*dnssec = true
goto Redo
} else {
// First EDNS, then TCP
fmt.Printf(";; Truncated, trying TCP\n")
c.Net = "tcp"
r, rtt, e = c.ExchangeRtt(m, nameserver)
r, rtt, e = c.Exchange(m, nameserver)
goto Redo
}
}
}
if r.MsgHdr.Truncated && !*fallback {
if ex.Reply.MsgHdr.Truncated && !*fallback {
fmt.Printf(";; Truncated\n")
}
if *check {
@ -266,13 +281,8 @@ Flags:
fmt.Printf("%v", r)
fmt.Printf("\n;; query time: %.3d µs, server: %s(%s), size: %d bytes\n", rtt/1e3, nameserver, c.Net, r.Size)
})
}
}
if qtype != dns.TypeAXFR && qtype != dns.TypeIXFR {
// xfr don't start any goroutines
select {}
}
}
func tsigKeyParse(s string) (algo, name, secret string, ok bool) {
@ -385,7 +395,7 @@ func getKey(name string, keytag uint16, server string, tcp bool) *dns.RR_DNSKEY
m := new(dns.Msg)
m.SetQuestion(name, dns.TypeDNSKEY)
m.SetEdns0(4096, true)
r, err := c.Exchange(m, server)
r, _, err := c.Exchange(m, server)
if err != nil {
return nil
}

View File

@ -125,7 +125,7 @@ func handleReflect(w dns.ResponseWriter, r *dns.Msg) {
if *printf {
fmt.Printf("%v\n", m.String())
}
w.Write(m)
w.WriteMsg(m)
}
func serve(net, name, secret string) {

View File

@ -11,7 +11,7 @@ func ExampleRR_MX() {
m := new(Msg)
m.SetQuestion("miek.nl.", TypeMX)
m.RecursionDesired = true
r, err := c.Exchange(m, config.Servers[0]+":"+config.Port)
r, _, err := c.Exchange(m, config.Servers[0]+":"+config.Port)
if err != nil {
return
}
@ -36,7 +36,7 @@ func ExampleToDs(zone string) {
}
m.SetQuestion(Fqdn(zone), TypeDNSKEY)
m.SetEdns0(4096, true)
r, err := c.Exchange(m, config.Servers[0]+":"+config.Port)
r, _, err := c.Exchange(m, config.Servers[0]+":"+config.Port)
if err != nil {
return
}

9
msg.go
View File

@ -99,11 +99,16 @@ var Rr_str = map[uint16]string{
TypeMX: "MX",
TypeWKS: "WKS",
TypeNS: "NS",
TypeNULL: "NULL",
TypeAFSDB: "AFSDB",
TypeX25: "X25",
TypeISDN: "ISDN",
TypePTR: "PTR",
TypeRT: "RT",
TypeSOA: "SOA",
TypeTXT: "TXT",
TypeSRV: "SRV",
TypeATMA: "ATMA",
TypeNAPTR: "NAPTR",
TypeKX: "KX",
TypeCERT: "CERT",
@ -115,6 +120,10 @@ var Rr_str = map[uint16]string{
TypeDS: "DS",
TypeDHCID: "DHCID",
TypeHIP: "HIP",
TypeNINFO: "NINFO",
TypeRKEY: "RKEY",
TypeCDS: "CDS",
TypeCAA: "CAA",
TypeIPSECKEY: "IPSECKEY",
TypeSSHFP: "SSHFP",
TypeRRSIG: "RRSIG",

141
nsecx.go
View File

@ -13,22 +13,6 @@ const (
_NSEC3_NODATA
)
// A Denialer is a record that performs denial
// of existence in DNSSEC. Currently there are
// two types NSEC and NSEC3.
type Denialer interface {
// HashNames hashes the owner and next domain name according
// to the hashing set in the record. For NSEC it is the identity function.
// The string domain is appended to the ownername in case of NSEC3
HashNames(domain string)
// Match checks if domain matches the (hashed) owner of name of the record.
Match(domain string) bool
// Cover checks if domain is covered by the NSEC(3) record
Cover(domain string) bool
// MatchType checks if the type is present in the bitmap
MatchType(rrtype uint16) bool
}
type saltWireFmt struct {
Salt string `dns:"size-hex"`
}
@ -135,128 +119,3 @@ func (rr *RR_NSEC3) Cover(domain string) bool {
func (rr *RR_NSEC) Cover(domain string) bool {
return false
}
// NsecVerify verifies an denial of existence response with NSECs
// NsecVerify returns nil when the NSECs in the message contain
// the correct proof. This function does not validates the NSECs.
func (m *Msg) NsecVerify(q Question) error {
return nil
}
// Nsec3Verify verifies an denial of existence response with NSEC3s.
// This function does not validate the NSEC3s.
func (m *Msg) Nsec3Verify(q Question) (int, error) {
var (
nsec3 []*RR_NSEC3
ncdenied = false // next closer denied
sodenied = false // source of synthesis denied
ce = "" // closest encloser
nc = "" // next closer
so = "" // source of synthesis
)
if len(m.Answer) > 0 && len(m.Ns) > 0 {
// Wildcard expansion
// Closest encloser inferred from SIG in authority and qname
// println("EXPANDED WILDCARD PROOF or DNAME CNAME")
// println("NODATA")
// I need to check the type bitmap
// wildcard bit not set?
// MM: No need to check the wildcard bit here:
// This response has only 1 NSEC4 and it does not match
// the closest encloser (it covers next closer).
}
if len(m.Answer) == 0 && len(m.Ns) > 0 {
// Maybe an NXDOMAIN or NODATA, we only know when we check
for _, n := range m.Ns {
if n.Header().Rrtype == TypeNSEC3 {
nsec3 = append(nsec3, n.(*RR_NSEC3))
}
}
if len(nsec3) == 0 {
return 0, ErrDenialNsec3
}
lastchopped := ""
labels := SplitLabels(q.Name)
// Find the closest encloser and create the next closer
for _, nsec := range nsec3 {
candidate := ""
for i := len(labels) - 1; i >= 0; i-- {
candidate = labels[i] + "." + candidate
if nsec.Match(candidate) {
ce = candidate
}
lastchopped = labels[i]
}
}
if ce == "" { // what about root label?
return 0, ErrDenialCe
}
nc = lastchopped + "." + ce
so = "*." + ce
// Check if the next closer is covered and thus denied
for _, nsec := range nsec3 {
if nsec.Cover(nc) {
ncdenied = true
break
}
}
if !ncdenied {
if m.Rcode == RcodeNameError {
// For NXDOMAIN this is a problem
return 0, ErrDenialNc // add next closer name here
}
goto NoData
}
// Check if the source of synthesis is covered and thus also denied
for _, nsec := range nsec3 {
if nsec.Cover(so) {
sodenied = true
break
}
}
if !sodenied {
return 0, ErrDenialSo
}
// The message headers claims something different!
if m.Rcode != RcodeNameError {
return 0, ErrDenialHdr
}
return _NSEC3_NXDOMAIN, nil
}
return 0, nil
NoData:
// For NODATA we need to to check if the matching nsec3 has to correct type bit map
// And we need to check that the wildcard does NOT exist
for _, nsec := range nsec3 {
if nsec.Cover(so) {
sodenied = true
break
}
}
if sodenied {
// Whoa, the closest encloser is denied, but there does exist
// a wildcard a that level. That's not good
return 0, ErrDenialWc
}
// The closest encloser MUST be the query name
for _, nsec := range nsec3 {
if nsec.Match(nc) {
// This nsec3 must NOT have the type bitmap set of the qtype. If it does have it, return an error
for _, t := range nsec.TypeBitMap {
if t == q.Qtype {
return 0, ErrDenialBit
}
}
}
}
if m.Rcode == RcodeNameError {
return 0, ErrDenialHdr
}
return _NSEC3_NODATA, nil
}

View File

@ -21,10 +21,10 @@ type Handler interface {
type ResponseWriter interface {
// RemoteAddr returns the net.Addr of the client that sent the current request.
RemoteAddr() net.Addr
// Write writes a reply back to the client.
Write(*Msg) error
// WriteBuf writes a raw buffer back to the client.
WriteBuf([]byte) error
// WriteMsg writes a reply back to the client.
WriteMsg(*Msg) error
// Write writes a raw buffer back to the client.
Write([]byte) (int, error)
// Close closes the connection.
Close() error
// TsigStatus returns the status of the Tsig.
@ -86,7 +86,7 @@ func HandleFailed(w ResponseWriter, r *Msg) {
m := new(Msg)
m.SetRcode(r, RcodeServerFailure)
// does not matter if this write fails
w.Write(m)
w.WriteMsg(m)
}
// AuthorHandler returns a HandlerFunc that returns the authors
@ -118,7 +118,7 @@ func HandleAuthors(w ResponseWriter, r *Msg) {
h := RR_Header{r.Question[0].Name, TypeTXT, ClassCHAOS, 0, 0}
m.Answer = append(m.Answer, &RR_TXT{h, []string{author}})
}
w.Write(m)
w.WriteMsg(m)
}
// VersionHandler returns a HandlerFunc that returns the version
@ -148,7 +148,7 @@ func HandleVersion(w ResponseWriter, r *Msg) {
m.SetReply(r)
h := RR_Header{r.Question[0].Name, TypeTXT, ClassCHAOS, 0, 0}
m.Answer = append(m.Answer, &RR_TXT{h, []string{Version}})
w.Write(m)
w.WriteMsg(m)
}
func authorHandler() Handler { return HandlerFunc(HandleAuthors) }
@ -375,7 +375,7 @@ func serve(a net.Addr, h Handler, m []byte, u *net.UDPConn, t *net.TCPConn, tsig
// Send a format error back
x := new(Msg)
x.SetRcodeFormatError(req)
w.Write(x)
w.WriteMsg(x)
break
}
@ -404,8 +404,8 @@ func serve(a net.Addr, h Handler, m []byte, u *net.UDPConn, t *net.TCPConn, tsig
return
}
// Write implements the ResponseWriter.Write method.
func (w *response) Write(m *Msg) (err error) {
// WriteMsg implements the ResponseWriter.WriteMsg method.
func (w *response) WriteMsg(m *Msg) (err error) {
var data []byte
if w.tsigSecret != nil { // if no secrets, dont check for the tsig (which is a longer check)
if t := m.IsTsig(); t != nil {
@ -413,46 +413,48 @@ func (w *response) Write(m *Msg) (err error) {
if err != nil {
return err
}
return w.WriteBuf(data)
_, err = w.Write(data)
return err
}
}
data, err = m.Pack()
if err != nil {
return err
}
return w.WriteBuf(data)
_, err = w.Write(data)
return err
}
// WriteBuf implements the ResponseWriter.WriteBuf method.
func (w *response) WriteBuf(m []byte) error {
// Write implements the ResponseWriter.Write method.
func (w *response) Write(m []byte) (int, error) {
switch {
case w._UDP != nil:
_, err := w._UDP.WriteTo(m, w.remoteAddr)
if err != nil {
return err
}
n, err := w._UDP.WriteTo(m, w.remoteAddr)
return n, err
case w._TCP != nil:
lm := len(m)
if len(m) > MaxMsgSize {
return &Error{Err: "message too large"}
return 0, &Error{Err: "message too large"}
}
l := make([]byte, 2)
l[0], l[1] = packUint16(uint16(len(m)))
l[0], l[1] = packUint16(uint16(lm))
m = append(l, m...)
n, err := w._TCP.Write(m)
if err != nil {
return err
return n, err
}
i := n
if i < len(m) {
j, err := w._TCP.Write(m[i:len(m)])
if i < lm {
j, err := w._TCP.Write(m[i:lm])
if err != nil {
return err
return i, err
}
i += j
}
n = i
return i, nil
}
return nil
panic("not reached")
}
// RemoteAddr implements the ResponseWriter.RemoteAddr method.

View File

@ -11,7 +11,7 @@ func HelloServer(w ResponseWriter, req *Msg) {
m.Extra = make([]RR, 1)
m.Extra[0] = &RR_TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
w.Write(m)
w.WriteMsg(m)
}
func AnotherHelloServer(w ResponseWriter, req *Msg) {
@ -20,7 +20,7 @@ func AnotherHelloServer(w ResponseWriter, req *Msg) {
m.Extra = make([]RR, 1)
m.Extra[0] = &RR_TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello example"}}
w.Write(m)
w.WriteMsg(m)
}
func TestServing(t *testing.T) {
@ -38,14 +38,14 @@ func TestServing(t *testing.T) {
m := new(Msg)
m.SetQuestion("miek.nl.", TypeTXT)
r, _ := c.Exchange(m, "127.0.0.1:8053")
r, _, _ := c.Exchange(m, "127.0.0.1:8053")
txt := r.Extra[0].(*RR_TXT).Txt[0]
if txt != "Hello world" {
t.Log("Unexpected result for miek.nl", txt, "!= Hello world")
t.Fail()
}
m.SetQuestion("example.com.", TypeTXT)
r, _ = c.Exchange(m, "127.0.0.1:8053")
r, _, _ = c.Exchange(m, "127.0.0.1:8053")
txt = r.Extra[0].(*RR_TXT).Txt[0]
if txt != "Hello example" {
t.Log("Unexpected result for example.com", txt, "!= Hello example")

132
types.go
View File

@ -37,6 +37,8 @@ const (
TypeTXT uint16 = 16
TypeRP uint16 = 17
TypeAFSDB uint16 = 18
TypeX25 uint16 = 19
TypeISDN uint16 = 20
TypeRT uint16 = 21
TypeSIG uint16 = 24
TypeKEY uint16 = 25
@ -44,6 +46,7 @@ const (
TypeLOC uint16 = 29
TypeNXT uint16 = 30
TypeSRV uint16 = 33
TypeATMA uint16 = 34
TypeNAPTR uint16 = 35
TypeKX uint16 = 36
TypeCERT uint16 = 37
@ -60,7 +63,10 @@ const (
TypeNSEC3PARAM uint16 = 51
TypeTLSA uint16 = 52
TypeHIP uint16 = 55
TypeNINFO uint16 = 56
TypeRKEY uint16 = 57
TypeTALINK uint16 = 58
TypeCDS uint16 = 59
TypeSPF uint16 = 99
TypeNID uint16 = 104
TypeL32 uint16 = 105
@ -77,6 +83,7 @@ const (
TypeANY uint16 = 255
TypeURI uint16 = 256
TypeCAA uint16 = 257
TypeTA uint16 = 32768
TypeDLV uint16 = 32769
@ -369,9 +376,9 @@ func (rr *RR_MD) Copy() RR {
}
type RR_MX struct {
Hdr RR_Header
Hdr RR_Header
Preference uint16
Mx string `dns:"cdomain-name"`
Mx string `dns:"cdomain-name"`
}
func (rr *RR_MX) Header() *RR_Header {
@ -414,6 +421,27 @@ func (rr *RR_AFSDB) Copy() RR {
return &RR_AFSDB{*rr.Hdr.CopyHeader(), rr.Subtype, rr.Hostname}
}
type RR_X25 struct {
Hdr RR_Header
PSDNAddress string
}
func (rr *RR_X25) Header() *RR_Header {
return &rr.Hdr
}
func (rr *RR_X25) String() string {
return rr.Hdr.String() + rr.PSDNAddress
}
func (rr *RR_X25) Len() int {
return rr.Hdr.Len() + len(rr.PSDNAddress)
}
func (rr *RR_X25) Copy() RR {
return &RR_X25{*rr.Hdr.CopyHeader(), rr.PSDNAddress}
}
type RR_RT struct {
Hdr RR_Header
Preference uint16
@ -634,7 +662,7 @@ func (rr *RR_SRV) Copy() RR {
type RR_NAPTR struct {
Hdr RR_Header
Order uint16
Preference uint16
Preference uint16
Flags string
Service string
Regexp string
@ -923,6 +951,33 @@ func (rr *RR_DS) Copy() RR {
return &RR_DS{*rr.Hdr.CopyHeader(), rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest}
}
type RR_CDS struct {
Hdr RR_Header
KeyTag uint16
Algorithm uint8
DigestType uint8
Digest string `dns:"hex"`
}
func (rr *RR_CDS) Header() *RR_Header {
return &rr.Hdr
}
func (rr *RR_CDS) String() string {
return rr.Hdr.String() + strconv.Itoa(int(rr.KeyTag)) +
" " + strconv.Itoa(int(rr.Algorithm)) +
" " + strconv.Itoa(int(rr.DigestType)) +
" " + strings.ToUpper(rr.Digest)
}
func (rr *RR_CDS) Len() int {
return rr.Hdr.Len() + 4 + len(rr.Digest)/2
}
func (rr *RR_CDS) Copy() RR {
return &RR_CDS{*rr.Hdr.CopyHeader(), rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest}
}
type RR_DLV struct {
Hdr RR_Header
KeyTag uint16
@ -951,9 +1006,9 @@ func (rr *RR_DLV) Copy() RR {
}
type RR_KX struct {
Hdr RR_Header
Preference uint16
Exchanger string `dns:"domain-name"`
Hdr RR_Header
Preference uint16
Exchanger string `dns:"domain-name"`
}
func (rr *RR_KX) Header() *RR_Header {
@ -1106,6 +1161,34 @@ func (rr *RR_DNSKEY) Copy() RR {
return &RR_DNSKEY{*rr.Hdr.CopyHeader(), rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey}
}
type RR_RKEY struct {
Hdr RR_Header
Flags uint16
Protocol uint8
Algorithm uint8
PublicKey string `dns:"base64"`
}
func (rr *RR_RKEY) Header() *RR_Header {
return &rr.Hdr
}
func (rr *RR_RKEY) String() string {
return rr.Hdr.String() + strconv.Itoa(int(rr.Flags)) +
" " + strconv.Itoa(int(rr.Protocol)) +
" " + strconv.Itoa(int(rr.Algorithm)) +
" " + rr.PublicKey
}
func (rr *RR_RKEY) Len() int {
return rr.Hdr.Len() + 4 +
base64.StdEncoding.DecodedLen(len(rr.PublicKey))
}
func (rr *RR_RKEY) Copy() RR {
return &RR_RKEY{*rr.Hdr.CopyHeader(), rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey}
}
type RR_NSEC3 struct {
Hdr RR_Header
Hash uint8
@ -1347,6 +1430,39 @@ func (rr *RR_HIP) Copy() RR {
return &RR_HIP{*rr.Hdr.CopyHeader(), rr.HitLength, rr.PublicKeyAlgorithm, rr.PublicKeyLength, rr.Hit, rr.PublicKey, rr.RendezvousServers}
}
type RR_NINFO struct {
Hdr RR_Header
ZSData []string `dns:"txt"`
}
func (rr *RR_NINFO) Header() *RR_Header {
return &rr.Hdr
}
func (rr *RR_NINFO) String() string {
s := rr.Hdr.String()
for i, s1 := range rr.ZSData {
if i > 0 {
s += " " + strconv.QuoteToASCII(s1)
} else {
s += strconv.QuoteToASCII(s1)
}
}
return s
}
func (rr *RR_NINFO) Len() int {
l := rr.Hdr.Len()
for _, t := range rr.ZSData {
l += len(t)
}
return l
}
func (rr *RR_NINFO) Copy() RR {
return &RR_NINFO{*rr.Hdr.CopyHeader(), rr.ZSData}
}
type RR_WKS struct {
Hdr RR_Header
Address net.IP `dns:"a"`
@ -1536,8 +1652,11 @@ var rr_mk = map[uint16]func() RR{
TypeMINFO: func() RR { return new(RR_MINFO) },
TypeRP: func() RR { return new(RR_RP) },
TypeAFSDB: func() RR { return new(RR_AFSDB) },
TypeX25: func() RR { return new(RR_X25) },
TypeMR: func() RR { return new(RR_MR) },
TypeMX: func() RR { return new(RR_MX) },
TypeRKEY: func() RR { return new(RR_RKEY) },
TypeNINFO: func() RR { return new(RR_NINFO) },
TypeNS: func() RR { return new(RR_NS) },
TypePTR: func() RR { return new(RR_PTR) },
TypeSOA: func() RR { return new(RR_SOA) },
@ -1552,6 +1671,7 @@ var rr_mk = map[uint16]func() RR{
TypeLOC: func() RR { return new(RR_LOC) },
TypeOPT: func() RR { return new(RR_OPT) },
TypeDS: func() RR { return new(RR_DS) },
TypeCDS: func() RR { return new(RR_CDS) },
TypeCERT: func() RR { return new(RR_CERT) },
TypeKX: func() RR { return new(RR_KX) },
TypeSPF: func() RR { return new(RR_SPF) },

2
xfr.go
View File

@ -183,7 +183,7 @@ func axfrSend(w ResponseWriter, req *Msg, c chan *XfrToken, e *error) {
for x := range c {
// assume it fits
rep.Answer = append(rep.Answer, x.RR...)
if err := w.Write(rep); e != nil {
if err := w.WriteMsg(rep); e != nil {
*e = err
return
}

View File

@ -46,6 +46,9 @@ func setRR(h RR_Header, c chan lex, o, f string) (RR, *ParseError) {
case TypeAFSDB:
r, e = setAFSDB(h, c, o, f)
goto Slurp
case TypeX25:
r, e = setX25(h,c, f)
goto Slurp
case TypeMX:
r, e = setMX(h, c, o, f)
goto Slurp
@ -95,6 +98,8 @@ func setRR(h RR_Header, c chan lex, o, f string) (RR, *ParseError) {
// newline. Thus there is no need to slurp the remainder, because there is none.
case TypeDNSKEY:
return setDNSKEY(h, c, f)
case TypeRKEY:
return setRKEY(h, c, f)
case TypeRRSIG:
return setRRSIG(h, c, o, f)
case TypeNSEC:
@ -107,6 +112,8 @@ func setRR(h RR_Header, c chan lex, o, f string) (RR, *ParseError) {
return setWKS(h, c, f)
case TypeDS:
return setDS(h, c, f)
case TypeCDS:
return setCDS(h, c, f)
case TypeDLV:
return setDLV(h, c, f)
case TypeTA:
@ -115,6 +122,8 @@ func setRR(h RR_Header, c chan lex, o, f string) (RR, *ParseError) {
return setTLSA(h, c, f)
case TypeTXT:
return setTXT(h, c, f)
case TypeNINFO:
return setNINFO(h, c, f)
case TypeHIP:
return setHIP(h, c, o, f)
case TypeSPF:
@ -465,6 +474,15 @@ func setAFSDB(h RR_Header, c chan lex, o, f string) (RR, *ParseError) {
return rr, nil
}
func setX25(h RR_Header, c chan lex, f string) (RR, *ParseError) {
rr := new(RR_X25)
rr.Hdr = h
l := <-c
rr.PSDNAddress = l.token
return rr, nil
}
func setKX(h RR_Header, c chan lex, o, f string) (RR, *ParseError) {
rr := new(RR_KX)
rr.Hdr = h
@ -1328,6 +1346,47 @@ func setDNSKEY(h RR_Header, c chan lex, f string) (RR, *ParseError) {
return rr, nil
}
func setRKEY(h RR_Header, c chan lex, f string) (RR, *ParseError) {
rr := new(RR_RKEY)
rr.Hdr = h
l := <-c
if i, e := strconv.Atoi(l.token); e != nil {
return nil, &ParseError{f, "bad RKEY Flags", l}
} else {
rr.Flags = uint16(i)
}
<-c // _BLANK
l = <-c // _STRING
if i, e := strconv.Atoi(l.token); e != nil {
return nil, &ParseError{f, "bad RKEY Protocol", l}
} else {
rr.Protocol = uint8(i)
}
<-c // _BLANK
l = <-c // _STRING
if i, e := strconv.Atoi(l.token); e != nil {
return nil, &ParseError{f, "bad RKEY Algorithm", l}
} else {
rr.Algorithm = uint8(i)
}
l = <-c
var s string
for l.value != _NEWLINE && l.value != _EOF {
switch l.value {
case _STRING:
s += l.token
case _BLANK:
// Ok
default:
return nil, &ParseError{f, "bad RKEY PublicKey", l}
}
l = <-c
}
rr.PublicKey = s
return rr, nil
}
func setDS(h RR_Header, c chan lex, f string) (RR, *ParseError) {
rr := new(RR_DS)
rr.Hdr = h
@ -1373,6 +1432,51 @@ func setDS(h RR_Header, c chan lex, f string) (RR, *ParseError) {
return rr, nil
}
func setCDS(h RR_Header, c chan lex, f string) (RR, *ParseError) {
rr := new(RR_CDS)
rr.Hdr = h
l := <-c
if i, e := strconv.Atoi(l.token); e != nil {
return nil, &ParseError{f, "bad CDS KeyTag", l}
} else {
rr.KeyTag = uint16(i)
}
<-c // _BLANK
l = <-c
if i, e := strconv.Atoi(l.token); e != nil {
if i, ok := Str_alg[strings.ToUpper(l.token)]; !ok {
return nil, &ParseError{f, "bad CDS Algorithm", l}
} else {
rr.Algorithm = i
}
} else {
rr.Algorithm = uint8(i)
}
<-c // _BLANK
l = <-c
if i, e := strconv.Atoi(l.token); e != nil {
return nil, &ParseError{f, "bad CDS DigestType", l}
} else {
rr.DigestType = uint8(i)
}
// There can be spaces here...
l = <-c
s := ""
for l.value != _NEWLINE && l.value != _EOF {
switch l.value {
case _STRING:
s += l.token
case _BLANK:
// Ok
default:
return nil, &ParseError{f, "bad CDS Digest", l}
}
l = <-c
}
rr.Digest = s
return rr, nil
}
func setDLV(h RR_Header, c chan lex, f string) (RR, *ParseError) {
rr := new(RR_DLV)
rr.Hdr = h
@ -1620,6 +1724,48 @@ func setTXT(h RR_Header, c chan lex, f string) (RR, *ParseError) {
return rr, nil
}
// identical to setTXT
func setNINFO(h RR_Header, c chan lex, f string) (RR, *ParseError) {
rr := new(RR_NINFO)
rr.Hdr = h
// Get the remaining data until we see a NEWLINE
quote := false
l := <-c
var s []string
switch l.value == _QUOTE {
case true: // A number of quoted string
s = make([]string, 0)
for l.value != _NEWLINE && l.value != _EOF {
switch l.value {
case _STRING:
s = append(s, l.token)
case _BLANK:
if quote {
// _BLANK can only be seen in between txt parts.
return nil, &ParseError{f, "bad NINFO ZSData", l}
}
case _QUOTE:
quote = !quote
default:
return nil, &ParseError{f, "bad NINFO ZSData", l}
}
l = <-c
}
if quote {
return nil, &ParseError{f, "bad NINFO ZSData", l}
}
case false: // Unquoted text record
s = make([]string, 1)
for l.value != _NEWLINE && l.value != _EOF {
s[0] += l.token
l = <-c
}
}
rr.ZSData = s
return rr, nil
}
func setURI(h RR_Header, c chan lex, f string) (RR, *ParseError) {
rr := new(RR_URI)
rr.Hdr = h