Merge branch '5966'

This commit is contained in:
Miek Gieben 2013-10-19 21:44:40 +01:00
commit 2c3eafab12
11 changed files with 174 additions and 83 deletions

View File

@ -105,6 +105,7 @@ Example programs can be found in the `github.com/miekg/exdns` repository.
* 5205 - HIP record
* 5702 - SHA2 in the DNS
* 5936 - AXFR
* 5966 - TCP implementation recommendations
* 6605 - ECDSA
* 6742 - ILNP DNS
* 6891 - EDNS0 update

View File

@ -1,12 +0,0 @@
# TODO
* Support for on-the-fly-signing or check how to do it
* Ratelimiting? server side (RRL)
## Nice to have
* Speed, we can always go faster. A simple reflect server now hits 75/80K qps on
moderate hardware
* privatekey.Precompute() when signing?
* Last remaining RRs: APL, NIMLOC & EID, ATMA, A6, KEY, SIG and NXT
* CAA parsing is broken

12
TODO.md Normal file
View File

@ -0,0 +1,12 @@
# TODO
* Support for on-the-fly-signing or check how to do it;
* Ratelimiting? server side (RRL).
## Nice to have
* Speed, we can always go faster. A simple reflect server now hits 75/80K qps on
moderate hardware;
* privatekey.Precompute() when signing?
* Last remaining RRs: APL, ATMA, A6, KEY, SIG and NXT;
* CAA parsing is broken.

View File

@ -13,6 +13,7 @@ import (
)
const dnsTimeout time.Duration = 2 * 1e9
const tcpIdleTimeout time.Duration = 1 * time.Minute
// A Conn represents a connection to a DNS server.
type Conn struct {

2
msg.go
View File

@ -94,6 +94,7 @@ var TypeToString = map[uint16]string{
TypeDNAME: "DNAME",
TypeDNSKEY: "DNSKEY",
TypeDS: "DS",
TypeEID: "EID",
TypeEUI48: "EUI48",
TypeEUI64: "EUI64",
TypeGID: "GID",
@ -118,6 +119,7 @@ var TypeToString = map[uint16]string{
TypeNAPTR: "NAPTR",
TypeNID: "NID",
TypeNINFO: "NINFO",
TypeNIMLOC: "NIMLOC",
TypeNS: "NS",
TypeNSAP: "NSAP",
TypeNSAPPTR: "NSAP-PTR",

View File

@ -591,13 +591,15 @@ func TestILNP(t *testing.T) {
}
}
func TestNSAPGPOS(t *testing.T) {
func TestNsapGposEidNimloc(t *testing.T) {
dt := map[string]string{
"foo.bar.com. IN NSAP 21 47000580ffff000000321099991111222233334444": "foo.bar.com.\t3600\tIN\tNSAP\t21 47000580ffff000000321099991111222233334444",
"host.school.de IN NSAP 17 39276f3100111100002222333344449876": "host.school.de.\t3600\tIN\tNSAP\t17 39276f3100111100002222333344449876",
"444433332222111199990123000000ff. NSAP-PTR foo.bar.com.": "444433332222111199990123000000ff.\t3600\tIN\tNSAP-PTR\tfoo.bar.com.",
"lillee. IN GPOS -32.6882 116.8652 10.0": "lillee.\t3600\tIN\tGPOS\t-32.6882 116.8652 10.0",
"hinault. IN GPOS -22.6882 116.8652 250.0": "hinault.\t3600\tIN\tGPOS\t-22.6882 116.8652 250.0",
"VENERA. IN NIMLOC 75234159EAC457800920": "VENERA.\t3600\tIN\tNIMLOC\t75234159EAC457800920",
"VAXA. IN EID 3141592653589793": "VAXA.\t3600\tIN\tEID\t3141592653589793",
}
for i, o := range dt {
rr, e := NewRR(i)
@ -753,7 +755,7 @@ func TestTXT(t *testing.T) {
func TestRR(t *testing.T) {
rr, err := NewRR("example.com IN TYPE1234 \\# 4 aabbccdd")
if err == nil {
t.Log("%s\n", rr.String())
t.Logf("%s\n", rr.String())
} else {
t.Error("Failed to parse TYPE1234 RR: ", err.Error())
}

169
server.go
View File

@ -87,7 +87,7 @@ func HandleFailed(w ResponseWriter, r *Msg) {
w.WriteMsg(m)
}
func failedHandler() Handler { return HandlerFunc(HandleFailed) }
func failedHandler() Handler { return HandlerFunc(HandleFailed) }
// Start a server on addresss and network speficied. Invoke handler
// for incoming queries.
@ -189,13 +189,14 @@ func HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) {
// A Server defines parameters for running an DNS server.
type Server struct {
Addr string // address to listen on, ":dns" if empty
Net string // if "tcp" it will invoke a TCP listener, otherwise an UDP one
Handler Handler // handler to invoke, dns.DefaultServeMux if nil
UDPSize int // default buffer size to use to read incoming UDP messages
ReadTimeout time.Duration // the net.Conn.SetReadTimeout value for new connections
WriteTimeout time.Duration // the net.Conn.SetWriteTimeout value for new connections
TsigSecret map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>
Addr string // address to listen on, ":dns" if empty
Net string // if "tcp" it will invoke a TCP listener, otherwise an UDP one
Handler Handler // handler to invoke, dns.DefaultServeMux if nil
UDPSize int // default buffer size to use to read incoming UDP messages
ReadTimeout time.Duration // the net.Conn.SetReadTimeout value for new connections
WriteTimeout time.Duration // the net.Conn.SetWriteTimeout value for new connections
IdleTimeout func() time.Duration // TCP idle timeout for multilpe queries, if nil, defaults to 1 time.Minute (RFC 5966)
TsigSecret map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>
}
// ListenAndServe starts a nameserver on the configured address in *Server.
@ -237,43 +238,20 @@ func (srv *Server) serveTCP(l *net.TCPListener) error {
if handler == nil {
handler = DefaultServeMux
}
forever:
rtimeout := dnsTimeout
if srv.ReadTimeout != 0 {
rtimeout = srv.ReadTimeout
}
for {
rw, e := l.AcceptTCP()
if e != nil {
// don't bail out, but wait for a new request
continue
}
if srv.ReadTimeout != 0 {
rw.SetReadDeadline(time.Now().Add(srv.ReadTimeout))
}
if srv.WriteTimeout != 0 {
rw.SetWriteDeadline(time.Now().Add(srv.WriteTimeout))
}
l := make([]byte, 2)
n, err := rw.Read(l)
if err != nil || n != 2 {
m, e := srv.readTCP(rw, rtimeout)
if e != nil {
continue
}
length, _ := unpackUint16(l, 0)
if length == 0 {
continue
}
m := make([]byte, int(length))
n, err = rw.Read(m[:int(length)])
if err != nil || n == 0 {
continue
}
i := n
for i < int(length) {
j, err := rw.Read(m[i:int(length)])
if err != nil {
continue forever
}
i += j
}
n = i
go serve(rw.RemoteAddr(), handler, m, nil, rw, srv.TsigSecret)
go srv.serve(rw.RemoteAddr(), handler, m, nil, rw)
}
panic("dns: not reached")
}
@ -289,62 +267,119 @@ func (srv *Server) serveUDP(l *net.UDPConn) error {
if srv.UDPSize == 0 {
srv.UDPSize = MinMsgSize
}
rtimeout := dnsTimeout
if srv.ReadTimeout != 0 {
rtimeout = srv.ReadTimeout
}
for {
if srv.ReadTimeout != 0 {
l.SetReadDeadline(time.Now().Add(srv.ReadTimeout))
}
if srv.WriteTimeout != 0 {
l.SetWriteDeadline(time.Now().Add(srv.WriteTimeout))
}
m := make([]byte, srv.UDPSize)
n, a, e := l.ReadFromUDP(m)
if e != nil || n == 0 {
// don't bail out, but wait for a new request
m, a, e := srv.readUDP(l, rtimeout)
if e != nil {
continue
}
m = m[:n]
go serve(a, handler, m, l, nil, srv.TsigSecret)
go srv.serve(a, handler, m, l, nil)
}
panic("dns: not reached")
}
// Serve a new connection.
func serve(a net.Addr, h Handler, m []byte, u *net.UDPConn, t *net.TCPConn, tsigSecret map[string]string) {
// Request has been read in serveUDP or serveTCP
w := &response{tsigSecret: tsigSecret, udp: u, tcp: t, remoteAddr: a}
func (srv *Server) serve(a net.Addr, h Handler, m []byte, u *net.UDPConn, t *net.TCPConn) {
w := &response{tsigSecret: srv.TsigSecret, udp: u, tcp: t, remoteAddr: a}
q := 0
Redo:
req := new(Msg)
if req.Unpack(m) != nil {
// Send a format error back
if req.Unpack(m) != nil { // Send a FormatError back
x := new(Msg)
x.SetRcodeFormatError(req)
w.WriteMsg(x)
w.Close()
return
goto Exit
}
defer func() {
if w.hijacked {
// client takes care of the connection, i.e. calls Close()
return
}
w.Close()
}()
w.tsigStatus = nil
if w.tsigSecret != nil {
if t := req.IsTsig(); t != nil {
secret := t.Hdr.Name
if _, ok := tsigSecret[secret]; !ok {
if _, ok := w.tsigSecret[secret]; !ok {
w.tsigStatus = ErrKeyAlg
}
w.tsigStatus = TsigVerify(m, tsigSecret[secret], "", false)
w.tsigStatus = TsigVerify(m, w.tsigSecret[secret], "", false)
w.tsigTimersOnly = false
w.tsigRequestMAC = req.Extra[len(req.Extra)-1].(*TSIG).MAC
}
}
h.ServeDNS(w, req) // this does the writing back to the client
h.ServeDNS(w, req) // Writes back to the client
Exit:
if w.hijacked {
return // client calls Close()
}
if u != nil { // UDP, "close" and return
w.Close()
return
}
idleTimeout := tcpIdleTimeout
if srv.IdleTimeout != nil {
idleTimeout = srv.IdleTimeout()
}
m, e := srv.readTCP(w.tcp, idleTimeout)
if e == nil {
q++
// TODO(miek): make this number configurable?
if q > 128 { // close socket after this many queries
w.Close()
return
}
goto Redo
}
w.Close()
return
}
func (srv *Server) readTCP(conn *net.TCPConn, timeout time.Duration) ([]byte, error) {
conn.SetReadDeadline(time.Now().Add(timeout))
l := make([]byte, 2)
n, err := conn.Read(l)
if err != nil || n != 2 {
if err != nil {
return nil, err
}
return nil, ErrConn
}
length, _ := unpackUint16(l, 0)
if length == 0 {
return nil, ErrConn
}
m := make([]byte, int(length))
n, err = conn.Read(m[:int(length)])
if err != nil || n == 0 {
if err != nil {
return nil, err
}
return nil, ErrConn
}
i := n
for i < int(length) {
j, err := conn.Read(m[i:int(length)])
if err != nil {
return nil, err
}
i += j
}
n = i
m = m[:n]
return m, nil
}
func (srv *Server) readUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, net.Addr, error) {
conn.SetReadDeadline(time.Now().Add(timeout))
m := make([]byte, srv.UDPSize)
n, a, e := conn.ReadFromUDP(m)
if e != nil || n == 0 {
return nil, nil, ErrConn
}
m = m[:n]
return m, a, nil
}
// WriteMsg implements the ResponseWriter.WriteMsg method.
func (w *response) WriteMsg(m *Msg) (err error) {
var data []byte

View File

@ -37,7 +37,7 @@ func TestServing(t *testing.T) {
err := ListenAndServe(":8053", "udp", nil)
if err != nil {
t.Log("ListenAndServe: ", err.Error())
t.Fail()
t.Fatal()
}
}()
time.Sleep(4e8)
@ -57,7 +57,7 @@ func TestServing(t *testing.T) {
t.Log("Unexpected result for example.com", txt, "!= Hello example")
t.Fail()
}
// Test Mixes cased as notices by Ask.
// Test Mixes cased as noticed by Ask.
m.SetQuestion("eXaMplE.cOm.", TypeTXT)
r, _, _ = c.Exchange(m, "127.0.0.1:8053")
txt = r.Extra[0].(*TXT).Txt[0]

View File

@ -55,6 +55,8 @@ const (
TypeAAAA uint16 = 28
TypeLOC uint16 = 29
TypeNXT uint16 = 30
TypeEID uint16 = 31
TypeNIMLOC uint16 = 32
TypeSRV uint16 = 33
TypeATMA uint16 = 34
TypeNAPTR uint16 = 35
@ -1489,6 +1491,26 @@ func (rr *UINFO) copy() RR { return &UINFO{*rr.Hdr.copyHeader(), rr.Ui
func (rr *UINFO) String() string { return rr.Hdr.String() + strconv.QuoteToASCII(rr.Uinfo) }
func (rr *UINFO) len() int { return rr.Hdr.len() + len(rr.Uinfo) + 1 }
type EID struct {
Hdr RR_Header
Endpoint string `dns:"hex"`
}
func (rr *EID) Header() *RR_Header { return &rr.Hdr }
func (rr *EID) copy() RR { return &EID{*rr.Hdr.copyHeader(), rr.Endpoint} }
func (rr *EID) String() string { return rr.Hdr.String() + strings.ToUpper(rr.Endpoint) }
func (rr *EID) len() int { return rr.Hdr.len() + len(rr.Endpoint)/2 }
type NIMLOC struct {
Hdr RR_Header
Locator string `dns:"hex"`
}
func (rr *NIMLOC) Header() *RR_Header { return &rr.Hdr }
func (rr *NIMLOC) copy() RR { return &NIMLOC{*rr.Hdr.copyHeader(), rr.Locator} }
func (rr *NIMLOC) String() string { return rr.Hdr.String() + strings.ToUpper(rr.Locator) }
func (rr *NIMLOC) len() int { return rr.Hdr.len() + len(rr.Locator)/2 }
// TimeToString translates the RRSIG's incep. and expir. times to the
// string representation used when printing the record.
// It takes serial arithmetic (RFC 1982) into account.
@ -1574,6 +1596,7 @@ var rr_mk = map[uint16]func() RR{
TypeEUI64: func() RR { return new(EUI64) },
TypeGID: func() RR { return new(GID) },
TypeGPOS: func() RR { return new(GPOS) },
TypeEID: func() RR { return new(EID) },
TypeHINFO: func() RR { return new(HINFO) },
TypeHIP: func() RR { return new(HIP) },
TypeKX: func() RR { return new(KX) },
@ -1591,6 +1614,7 @@ var rr_mk = map[uint16]func() RR{
TypeNAPTR: func() RR { return new(NAPTR) },
TypeNID: func() RR { return new(NID) },
TypeNINFO: func() RR { return new(NINFO) },
TypeNIMLOC: func() RR { return new(NIMLOC) },
TypeNS: func() RR { return new(NS) },
TypeNSAP: func() RR { return new(NSAP) },
TypeNSAPPTR: func() RR { return new(NSAPPTR) },

View File

@ -134,6 +134,10 @@ func setRR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
// These types have a variable ending: either chunks of txt or chunks/base64 or hex.
// They need to search for the end of the RR themselves, hence they look for the ending
// newline. Thus there is no need to slurp the remainder, because there is none.
case TypeEID:
return setEID(h, c, f)
case TypeNIMLOC:
return setNIMLOC(h, c, f)
case TypeNSAP:
return setNSAP(h, c, f)
case TypeDNSKEY:
@ -1561,6 +1565,28 @@ func setDS(h RR_Header, c chan lex, f string) (RR, *ParseError, string) {
return rr, nil, c1
}
func setEID(h RR_Header, c chan lex, f string) (RR, *ParseError, string) {
rr := new(EID)
rr.Hdr = h
s, e, c1 := endingToString(c, "bad EID Endpoint", f)
if e != nil {
return nil, e, c1
}
rr.Endpoint = s
return rr, nil, c1
}
func setNIMLOC(h RR_Header, c chan lex, f string) (RR, *ParseError, string) {
rr := new(NIMLOC)
rr.Hdr = h
s, e, c1 := endingToString(c, "bad NIMLOC Locator", f)
if e != nil {
return nil, e, c1
}
rr.Locator = s
return rr, nil, c1
}
func setNSAP(h RR_Header, c chan lex, f string) (RR, *ParseError, string) {
rr := new(NSAP)
rr.Hdr = h