First stab at server support
Cherry pick GRONG for the server stuff (only server.go) seems interesting. Still working on the responder idea of GRONG and how to implement it in godns
This commit is contained in:
parent
586337bf57
commit
57b1e2f714
2
Makefile
2
Makefile
|
@ -20,11 +20,13 @@ include $(GOROOT)/src/Make.pkg
|
|||
|
||||
all: package
|
||||
gomake -C resolver package
|
||||
gomake -C responder package
|
||||
# gomake -C strconv package
|
||||
|
||||
dnstest:
|
||||
gotest
|
||||
gomake -C resolver test
|
||||
gomake -C responder test
|
||||
# gomake -C strconv test
|
||||
|
||||
_examples:
|
||||
|
|
51
dns.go
51
dns.go
|
@ -27,6 +27,8 @@ package dns
|
|||
|
||||
import (
|
||||
"strconv"
|
||||
"os"
|
||||
"net"
|
||||
)
|
||||
|
||||
const Year68 = 2 << (32 - 1)
|
||||
|
@ -91,11 +93,54 @@ func (h *RR_Header) String() string {
|
|||
return s
|
||||
}
|
||||
|
||||
// Or expose the pack/unpack functions??
|
||||
func SendTCP(c net.Conn, m []byte) os.Error {
|
||||
l := make([]byte, 2)
|
||||
l[0] = byte(len(m) >> 8)
|
||||
l[1] = byte(len(m))
|
||||
// First we send the length
|
||||
_, err := c.Write(l)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// And the the message
|
||||
_, err = c.Write(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func RecvTCP(c net.Conn) ([]byte, os.Error) {
|
||||
l := make([]byte, 2) // receiver length
|
||||
// The server replies with two bytes length
|
||||
_, err := c.Read(l)
|
||||
if err != nil {
|
||||
return nil,err
|
||||
}
|
||||
length := uint16(l[0])<<8 | uint16(l[1])
|
||||
if length == 0 {
|
||||
return nil, &Error{Error: "received nil msg length", Server: c.RemoteAddr().String()}
|
||||
}
|
||||
m := make([]byte, length)
|
||||
n, cerr := c.Read(m)
|
||||
if cerr != nil {
|
||||
return nil, cerr
|
||||
}
|
||||
i := n
|
||||
if i < int(length) {
|
||||
n, err = c.Read(m[i:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
i += n
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Or expose the pack/unpack functions??
|
||||
// These were created for dnssec.go, but now that
|
||||
// this package is included again in dns, they are
|
||||
// not really needed
|
||||
// that package is included again in dns, they are
|
||||
// not really needed. These will be removed asap.
|
||||
// Return the wiredata of rdata portion of a RR.
|
||||
func WireRdata(r RR) ([]byte, bool) {
|
||||
buf := make([]byte, 4096) // Too large, need to FIX TODO(mg)
|
||||
|
|
|
@ -55,7 +55,7 @@ type Resolver struct {
|
|||
Attempts int // lost packets before giving up on server
|
||||
Rotate bool // round robin among servers
|
||||
Tcp bool // use TCP
|
||||
Mangle func([]byte) []byte // Mangle the packet
|
||||
Mangle func([]byte) []byte // mangle the packet
|
||||
}
|
||||
|
||||
// Start a new resolver as a goroutine, return the communication channel
|
||||
|
@ -68,10 +68,12 @@ func (res *Resolver) NewQuerier() (ch chan DnsMsg) {
|
|||
// The query function.
|
||||
func query(res *Resolver, msg chan DnsMsg) {
|
||||
// error checking, robustness
|
||||
var c net.Conn
|
||||
var err os.Error
|
||||
var in *dns.Msg
|
||||
var port string
|
||||
var (
|
||||
c net.Conn
|
||||
err os.Error
|
||||
in *dns.Msg
|
||||
port string
|
||||
)
|
||||
// len(res.Server) == 0 can be perfectly valid, when setting up the resolver
|
||||
if res.Port == "" {
|
||||
port = "53"
|
||||
|
@ -91,10 +93,10 @@ func query(res *Resolver, msg chan DnsMsg) {
|
|||
|
||||
var cerr os.Error
|
||||
//if len(name) >= 256 {
|
||||
if out.Dns.Id == 0 {
|
||||
// No Id sed, set it
|
||||
out.Dns.SetId()
|
||||
}
|
||||
if out.Dns.Id == 0 {
|
||||
// No Id sed, set it
|
||||
out.Dns.SetId()
|
||||
}
|
||||
sending, ok := out.Dns.Pack()
|
||||
if !ok {
|
||||
msg <- DnsMsg{nil, &dns.Error{Error: packErr}}
|
||||
|
@ -113,9 +115,9 @@ func query(res *Resolver, msg chan DnsMsg) {
|
|||
continue
|
||||
}
|
||||
if res.Tcp {
|
||||
in, err = exchangeTcp(c, sending, res, true)
|
||||
in, err = exchangeTCP(c, sending, res, true)
|
||||
} else {
|
||||
in, err = exchangeUdp(c, sending, res, true)
|
||||
in, err = exchangeUDP(c, sending, res, true)
|
||||
}
|
||||
|
||||
// Check id in.id != out.id, should be checked in the client!
|
||||
|
@ -180,18 +182,18 @@ func axfr(res *Resolver, msg chan DnsMsg) {
|
|||
// Start the AXFR
|
||||
for {
|
||||
if first {
|
||||
in, cerr = exchangeTcp(c, sending, res, true)
|
||||
in, cerr = exchangeTCP(c, sending, res, true)
|
||||
} else {
|
||||
in, cerr = exchangeTcp(c, sending, res, false)
|
||||
in, cerr = exchangeTCP(c, sending, res, false)
|
||||
}
|
||||
|
||||
if cerr != nil {
|
||||
// Failed to send, try the next
|
||||
err = cerr
|
||||
c.Close()
|
||||
c.Close()
|
||||
continue SERVER
|
||||
}
|
||||
// if in.Dns.Id != out.Id // error
|
||||
// if in.Dns.Id != out.Id // error
|
||||
if first {
|
||||
if !checkSOA(in, true) {
|
||||
// SOA record not there...
|
||||
|
@ -215,7 +217,7 @@ func axfr(res *Resolver, msg chan DnsMsg) {
|
|||
}
|
||||
}
|
||||
}
|
||||
println("Should never be reached")
|
||||
println("Should never be reached")
|
||||
return
|
||||
}
|
||||
msg <- DnsMsg{nil, err}
|
||||
|
@ -226,10 +228,9 @@ func axfr(res *Resolver, msg chan DnsMsg) {
|
|||
return
|
||||
}
|
||||
|
||||
|
||||
// Send a request on the connection and hope for a reply.
|
||||
// Up to res.Attempts attempts.
|
||||
func exchangeUdp(c net.Conn, m []byte, r *Resolver, send bool) (*dns.Msg, os.Error) {
|
||||
func exchangeUDP(c net.Conn, m []byte, r *Resolver, send bool) (*dns.Msg, os.Error) {
|
||||
var timeout int64
|
||||
var attempts int
|
||||
if r.Mangle != nil {
|
||||
|
@ -247,26 +248,24 @@ func exchangeUdp(c net.Conn, m []byte, r *Resolver, send bool) (*dns.Msg, os.Err
|
|||
}
|
||||
for a := 0; a < attempts; a++ {
|
||||
if send {
|
||||
_, err := c.Write(m)
|
||||
err := sendUDP(c, m)
|
||||
if err != nil {
|
||||
if e, ok := err.(net.Error); ok && e.Timeout() {
|
||||
continue
|
||||
}
|
||||
if e, ok := err.(net.Error); ok && e.Timeout() {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
c.SetReadTimeout(timeout * 1e9) // nanoseconds
|
||||
buf := make([]byte, dns.DefaultMsgSize) // More than enough???
|
||||
n, err := c.Read(buf)
|
||||
c.SetReadTimeout(timeout * 1e9) // nanoseconds
|
||||
buf, err := recvUDP(c)
|
||||
if err != nil {
|
||||
// If timeout try the next
|
||||
if e, ok := err.(net.Error); ok && e.Timeout() {
|
||||
continue
|
||||
}
|
||||
if e, ok := err.(net.Error); ok && e.Timeout() {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
buf = buf[0:n]
|
||||
|
||||
in := new(dns.Msg)
|
||||
if !in.Unpack(buf) {
|
||||
continue
|
||||
|
@ -277,9 +276,9 @@ func exchangeUdp(c net.Conn, m []byte, r *Resolver, send bool) (*dns.Msg, os.Err
|
|||
}
|
||||
|
||||
// Up to res.Attempts attempts.
|
||||
func exchangeTcp(c net.Conn, m []byte, r *Resolver, send bool) (*dns.Msg, os.Error) {
|
||||
func exchangeTCP(c net.Conn, m []byte, r *Resolver, send bool) (*dns.Msg, os.Error) {
|
||||
var timeout int64
|
||||
var attempts, n int
|
||||
var attempts int
|
||||
if r.Mangle != nil {
|
||||
m = r.Mangle(m)
|
||||
}
|
||||
|
@ -294,65 +293,26 @@ func exchangeTcp(c net.Conn, m []byte, r *Resolver, send bool) (*dns.Msg, os.Err
|
|||
attempts = r.Attempts
|
||||
}
|
||||
|
||||
ls := make([]byte, 2) // sender length
|
||||
lr := make([]byte, 2) // receiver length
|
||||
var length uint16
|
||||
ls[0] = byte(len(m) >> 8)
|
||||
ls[1] = byte(len(m))
|
||||
for a := 0; a < attempts; a++ {
|
||||
// only send something when told so
|
||||
if send {
|
||||
// With DNS over TCP we first send the length
|
||||
_, err := c.Write(ls)
|
||||
err := sendTCP(c, m)
|
||||
if err != nil {
|
||||
if e, ok := err.(net.Error); ok && e.Timeout() {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// And then send the message
|
||||
_, err = c.Write(m)
|
||||
if err != nil {
|
||||
if e, ok := err.(net.Error); ok && e.Timeout() {
|
||||
continue
|
||||
}
|
||||
if e, ok := err.(net.Error); ok && e.Timeout() {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
c.SetReadTimeout(timeout * 1e9) // nanoseconds
|
||||
// The server replies with two bytes length
|
||||
_, err := c.Read(lr)
|
||||
buf, err := recvTCP(c)
|
||||
if err != nil {
|
||||
if e, ok := err.(net.Error); ok && e.Timeout() {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
length = uint16(lr[0])<<8 | uint16(lr[1])
|
||||
if length == 0 {
|
||||
return nil, &dns.Error{Error: "received nil msg length", Server: c.RemoteAddr().String()}
|
||||
}
|
||||
buf := make([]byte, length)
|
||||
|
||||
n, err = c.Read(buf)
|
||||
if err != nil {
|
||||
if e, ok := err.(net.Error); ok && e.Timeout() {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
i := n
|
||||
if i < int(length) {
|
||||
n, err = c.Read(buf[i:])
|
||||
if err != nil {
|
||||
if e, ok := err.(net.Error); ok && e.Timeout() {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
if e, ok := err.(net.Error); ok && e.Timeout() {
|
||||
continue
|
||||
}
|
||||
i += n
|
||||
return nil, err
|
||||
}
|
||||
in := new(dns.Msg)
|
||||
if !in.Unpack(buf) {
|
||||
|
@ -363,6 +323,69 @@ func exchangeTcp(c net.Conn, m []byte, r *Resolver, send bool) (*dns.Msg, os.Err
|
|||
return nil, &dns.Error{Error: servErr}
|
||||
}
|
||||
|
||||
|
||||
func sendUDP(c net.Conn, m []byte) os.Error {
|
||||
_, err := c.Write(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func recvUDP(c net.Conn) ([]byte, os.Error) {
|
||||
m := make([]byte, dns.DefaultMsgSize) // More than enough???
|
||||
n, err := c.Read(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m = m[:n]
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func sendTCP(c net.Conn, m []byte) os.Error {
|
||||
l := make([]byte, 2)
|
||||
l[0] = byte(len(m) >> 8)
|
||||
l[1] = byte(len(m))
|
||||
// First we send the length
|
||||
_, err := c.Write(l)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// And the the message
|
||||
_, err = c.Write(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func recvTCP(c net.Conn) ([]byte, os.Error) {
|
||||
l := make([]byte, 2) // receiver length
|
||||
// The server replies with two bytes length
|
||||
_, err := c.Read(l)
|
||||
if err != nil {
|
||||
return nil,err
|
||||
}
|
||||
length := uint16(l[0])<<8 | uint16(l[1])
|
||||
if length == 0 {
|
||||
return nil, &dns.Error{Error: "received nil msg length", Server: c.RemoteAddr().String()}
|
||||
}
|
||||
m := make([]byte, length)
|
||||
n, cerr := c.Read(m)
|
||||
if cerr != nil {
|
||||
return nil, cerr
|
||||
}
|
||||
i := n
|
||||
if i < int(length) {
|
||||
n, err = c.Read(m[i:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
i += n
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Check if he SOA record exists in the Answer section of
|
||||
// the packet. If first is true the first RR must be a soa
|
||||
// if false, the last one should be a SOA
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
# Copyright 2009 The Go Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
include $(GOROOT)/src/Make.inc
|
||||
|
||||
TARG=dns/responder
|
||||
GOFILES=\
|
||||
responder.go\
|
||||
|
||||
DEPS=..
|
||||
include $(GOROOT)/src/Make.pkg
|
|
@ -0,0 +1,98 @@
|
|||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// DNS server
|
||||
|
||||
// For every reply the resolver answers by sending the
|
||||
// received packet (with a possible error) back on the channel.
|
||||
package responder
|
||||
|
||||
import (
|
||||
"os"
|
||||
"net"
|
||||
"dns"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Responder struct {
|
||||
Addresses []string // interfaces to use
|
||||
Port string // what port to use
|
||||
Timeout int // seconds before giving up on packet
|
||||
Tcp bool // use TCP
|
||||
Mangle func([]byte) []byte // mangle the packet, before sending
|
||||
}
|
||||
|
||||
// When communicating with a resolver, we use this structure
|
||||
// to send packets to it, for sending Error must be nil.
|
||||
// A resolver responds with a reply packet and a possible error.
|
||||
// Sending a nil message instructs to resolver to stop.
|
||||
type DnsMsg struct {
|
||||
Dns *dns.Msg
|
||||
Error os.Error
|
||||
}
|
||||
|
||||
// This is a NAMESERVER
|
||||
// Communicate withit via a channel
|
||||
func (res *Responder) NewResponder() bool {
|
||||
var port string
|
||||
if len(res.Addresses) == 0 {
|
||||
// We cannot start responding with an addresss
|
||||
return false
|
||||
}
|
||||
if res.Port == "" {
|
||||
port = "53"
|
||||
} else {
|
||||
port = res.Port
|
||||
}
|
||||
// TODO(mg) handle multiple addresses
|
||||
switch res.Tcp {
|
||||
case true:
|
||||
|
||||
case false:
|
||||
udpaddr, _ := net.ResolveUDPAddr(res.Addresses[0] + ":" + port)
|
||||
c, _ := net.ListenUDP("udp", udpaddr)
|
||||
m := make([]byte, 4096)
|
||||
n, raddr, err := c.ReadFrom(m)
|
||||
if err != nil {
|
||||
//continue
|
||||
}
|
||||
m = m[:n]
|
||||
// If I don't pick off the remote addr, but do it in the Go routine
|
||||
// I've created a race condition?? TODO(mg)
|
||||
handlerUDP(res, c, raddr, m)
|
||||
c.Close()
|
||||
}
|
||||
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
func handlerUDP(res *Responder, c *net.UDPConn, raddr net.Addr, in []byte) {
|
||||
// don't care what you've read, just blap a default, but put in the
|
||||
// correct Id
|
||||
fmt.Printf("handlerUDP called!")
|
||||
|
||||
inmsg := new(dns.Msg)
|
||||
inmsg.Unpack(in)
|
||||
fmt.Printf("%v\n", inmsg)
|
||||
|
||||
m := new(dns.Msg)
|
||||
m.MsgHdr.Id = inmsg.MsgHdr.Id
|
||||
m.MsgHdr.Authoritative = true
|
||||
m.MsgHdr.Response = true
|
||||
m.MsgHdr.Rcode = dns.RcodeSuccess
|
||||
m.Question = make([]dns.Question, 1)
|
||||
m.Question[0] = dns.Question{"miek.nl.", dns.TypeTXT, dns.ClassINET}
|
||||
m.Answer = make([]dns.RR, 1)
|
||||
a := new(dns.RR_TXT)
|
||||
a.Hdr = dns.RR_Header{Name: "miek.nl.", Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: 3600}
|
||||
a.Txt = "dit dan"
|
||||
m.Answer[0] = a
|
||||
out, _ := m.Pack()
|
||||
c.WriteTo(out, raddr)
|
||||
}
|
||||
|
||||
func handlerTCP(res *Responder, c net.Conn, raddr net.Addr, in []byte) {
|
||||
|
||||
}
|
Loading…
Reference in New Issue