Merge branch '5966'
This commit is contained in:
commit
2c3eafab12
|
@ -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
|
|
@ -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
|
|
@ -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.
|
|
@ -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
2
msg.go
|
@ -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",
|
||||
|
|
|
@ -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
169
server.go
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
24
types.go
24
types.go
|
@ -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) },
|
||||
|
|
26
zscan_rr.go
26
zscan_rr.go
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue