Do not invoke the mDNS lookups from the server: they are used from the Zone database now...
Improved DNS server: - simplify the handlers code, removing duplicated code - more configuration parameters (eg, we can now disable the cache) - we can now bind to port 0 and let the server get a free port (so we can get rid of the mess we had in the unit tests...)
This commit is contained in:
1 parent
a23462e830
commit
07d15092aa
5 files changed
+231
-181
No files matched your search
@@ -32,6 +32,7 @@ type ZoneObservable interface {
|
||||
ObserveInaddr(inaddr string, observer ZoneRecordObserver) error
|
||||
}
|
||||
|
||||
type ZoneObserverFunc func(string, ZoneRecordObserver) error
|
||||
type ZoneRecordObserver func()
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -33,6 +33,8 @@ func makeTruncatedReply(r *dns.Msg) *dns.Msg {
|
||||
return reply
|
||||
}
|
||||
|
||||
type DNSResponseBuilder func(r *dns.Msg, q *dns.Question, addrs []ZoneRecord) *dns.Msg
|
||||
|
||||
func makeAddressReply(r *dns.Msg, q *dns.Question, addrs []ZoneRecord) *dns.Msg {
|
||||
answers := make([]dns.RR, len(addrs))
|
||||
header := makeHeader(r, q)
|
||||
|
||||
+184
-142
@@ -3,36 +3,48 @@ package nameserver
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/benbjohnson/clock"
|
||||
"github.com/miekg/dns"
|
||||
. "github.com/weaveworks/weave/common"
|
||||
"net"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultServerPort = 53 // The default server port
|
||||
DefaultCLICfgFile = "/etc/resolv.conf" // default "resolv.conf" file to try to load
|
||||
DefaultUDPBuflen = 4096 // bigger than the default 512
|
||||
DefaultCacheLen = 8192 // default cache capacity
|
||||
DefaultResolvWorkers = 8 // default number of resolution workers
|
||||
DefaultTimeout = 5 // default timeout for DNS resolutions
|
||||
DefaultServerPort = 53 // The default server port
|
||||
DefaultCLICfgFile = "/etc/resolv.conf" // default "resolv.conf" file to try to load
|
||||
DefaultUDPBuflen = 4096 // bigger than the default 512
|
||||
DefaultCacheLen = 8192 // default cache capacity
|
||||
DefaultTimeout = 5 // default timeout for DNS resolutions
|
||||
)
|
||||
|
||||
type DNSServerConfig struct {
|
||||
// The zone
|
||||
Zone Zone
|
||||
// (Optional) client config file for resolving upstream servers
|
||||
UpstreamCfgFile string
|
||||
// (Optional) DNS client config for the fallback server(s)
|
||||
UpstreamCfg *dns.ClientConfig
|
||||
// (Optional) port number (for TCP and UDP)
|
||||
Port int
|
||||
// (Optional) local domain (ie, "weave.local.")
|
||||
LocalDomain string
|
||||
// (Optional) cache size
|
||||
CacheLen int
|
||||
// (Optional) disable the cache
|
||||
CacheDisabled bool
|
||||
// (Optional) timeout for DNS queries
|
||||
Timeout int
|
||||
// (Optional) UDP buffer length
|
||||
UDPBufLen int
|
||||
// (Optional) force a specific cache
|
||||
Cache ZoneCache
|
||||
// (Optional) TTL for negative results in the local domain
|
||||
CacheNegLocalTTL int
|
||||
// (Optional) for a specific clock provider
|
||||
Clock clock.Clock
|
||||
// (Optional) Listening socket read timeout (in milliseconds)
|
||||
ListenReadTimeout int
|
||||
}
|
||||
|
||||
type dnsProtocol uint8
|
||||
@@ -53,51 +65,64 @@ func (proto dnsProtocol) String() string {
|
||||
}
|
||||
|
||||
// get a new dns.Client for a protocol
|
||||
func (proto dnsProtocol) GetNewClient(bufsize int) *dns.Client {
|
||||
func (proto dnsProtocol) GetNewClient(bufsize int, timeout time.Duration) *dns.Client {
|
||||
switch proto {
|
||||
case protTCP:
|
||||
return &dns.Client{Net: "tcp"}
|
||||
return &dns.Client{Net: "tcp", ReadTimeout: timeout}
|
||||
case protUDP:
|
||||
return &dns.Client{Net: "udp", UDPSize: uint16(bufsize)}
|
||||
return &dns.Client{Net: "udp", ReadTimeout: timeout, UDPSize: uint16(bufsize)}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// a DNS server
|
||||
type DNSServer struct {
|
||||
Zone Zone
|
||||
Iface *net.Interface
|
||||
Upstream *dns.ClientConfig
|
||||
udpSrv *dns.Server
|
||||
tcpSrv *dns.Server
|
||||
mdnsCli *MDNSClient
|
||||
mdnsSrv *MDNSServer
|
||||
cache *Cache
|
||||
timeout int
|
||||
udpBuf int
|
||||
listenersWg *sync.WaitGroup
|
||||
|
||||
Zone Zone
|
||||
Upstream *dns.ClientConfig
|
||||
Domain string // the local domain
|
||||
ListenAddr string // the address the server is listening at
|
||||
|
||||
udpSrv *dns.Server
|
||||
tcpSrv *dns.Server
|
||||
pc net.PacketConn
|
||||
lst net.Listener
|
||||
cache ZoneCache
|
||||
cacheDisabled bool
|
||||
negLocalTTL int
|
||||
timeout time.Duration
|
||||
readTimeout time.Duration
|
||||
udpBuf int
|
||||
listenersWg *sync.WaitGroup
|
||||
clock clock.Clock
|
||||
}
|
||||
|
||||
// Creates a new DNS server
|
||||
func NewDNSServer(config DNSServerConfig, zone Zone, iface *net.Interface) (s *DNSServer, err error) {
|
||||
func NewDNSServer(config DNSServerConfig) (s *DNSServer, err error) {
|
||||
s = &DNSServer{
|
||||
Zone: zone,
|
||||
Iface: iface,
|
||||
listenersWg: new(sync.WaitGroup),
|
||||
|
||||
Zone: config.Zone,
|
||||
Domain: DefaultLocalDomain,
|
||||
ListenAddr: fmt.Sprintf(":%d", DefaultServerPort),
|
||||
ListenAddr: fmt.Sprintf(":%d", config.Port),
|
||||
|
||||
listenersWg: new(sync.WaitGroup),
|
||||
timeout: time.Duration(config.Timeout) * time.Millisecond,
|
||||
readTimeout: time.Duration(config.ListenReadTimeout) * time.Millisecond,
|
||||
cacheDisabled: false,
|
||||
negLocalTTL: negLocalTTL,
|
||||
clock: config.Clock,
|
||||
}
|
||||
|
||||
// check some basic parameters are valid
|
||||
if s.Zone == nil {
|
||||
return nil, fmt.Errorf("No valid Zone provided in server initialization")
|
||||
}
|
||||
if len(s.Domain) == 0 {
|
||||
return nil, fmt.Errorf("No valid Domain provided in server initialization")
|
||||
}
|
||||
if s.clock == nil {
|
||||
s.clock = clock.New()
|
||||
}
|
||||
|
||||
// fill empty parameters with defaults...
|
||||
if config.Port != 0 {
|
||||
s.ListenAddr = fmt.Sprintf(":%d", config.Port)
|
||||
}
|
||||
if len(config.LocalDomain) > 0 {
|
||||
s.Domain = config.LocalDomain
|
||||
}
|
||||
if config.UpstreamCfg != nil {
|
||||
s.Upstream = config.UpstreamCfg
|
||||
} else {
|
||||
@@ -110,88 +135,97 @@ func NewDNSServer(config DNSServerConfig, zone Zone, iface *net.Interface) (s *D
|
||||
}
|
||||
}
|
||||
if config.Timeout > 0 {
|
||||
s.timeout = config.Timeout
|
||||
s.timeout = time.Duration(config.Timeout) * time.Millisecond
|
||||
}
|
||||
if config.UDPBufLen > 0 {
|
||||
s.udpBuf = config.UDPBufLen
|
||||
}
|
||||
s.mdnsCli, err = NewMDNSClient()
|
||||
if err != nil {
|
||||
return
|
||||
if config.CacheNegLocalTTL > 0 {
|
||||
s.negLocalTTL = config.CacheNegLocalTTL
|
||||
}
|
||||
s.mdnsSrv, err = NewMDNSServer(s.Zone, false)
|
||||
if err != nil {
|
||||
return
|
||||
if config.CacheDisabled {
|
||||
s.cacheDisabled = true
|
||||
}
|
||||
cacheLen := DefaultCacheLen
|
||||
if config.CacheLen > 0 {
|
||||
cacheLen = config.CacheLen
|
||||
if !s.cacheDisabled {
|
||||
if config.Cache != nil {
|
||||
s.cache = config.Cache
|
||||
} else {
|
||||
cacheLen := DefaultCacheLen
|
||||
if config.CacheLen > 0 {
|
||||
cacheLen = config.CacheLen
|
||||
}
|
||||
if s.cache, err = NewCache(cacheLen, s.clock); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
Debug.Printf("[dns] Initializing cache: %d entries", config.CacheLen)
|
||||
s.cache, err = NewCache(cacheLen, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// create two DNS request multiplexerers, depending on the protocol used by clients
|
||||
// (we use the same protocol for asking upstream servers)
|
||||
mux := func(proto dnsProtocol) *dns.ServeMux {
|
||||
m := dns.NewServeMux()
|
||||
m.HandleFunc(s.Domain, s.queryHandler(proto))
|
||||
m.HandleFunc(RDNSDomain, s.rdnsHandler(proto))
|
||||
m.HandleFunc(".", s.notUsHandler(proto))
|
||||
return m
|
||||
}
|
||||
|
||||
s.udpSrv = &dns.Server{Addr: s.ListenAddr, Net: "udp", Handler: mux(protUDP)}
|
||||
s.tcpSrv = &dns.Server{Addr: s.ListenAddr, Net: "tcp", Handler: mux(protTCP)}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Start the DNS server
|
||||
func (s *DNSServer) Start() error {
|
||||
Info.Printf("Using mDNS on %v", s.Iface)
|
||||
err := s.mdnsCli.Start(s.Iface)
|
||||
CheckFatal(err)
|
||||
err = s.mdnsSrv.Start(s.Iface)
|
||||
CheckFatal(err)
|
||||
Info.Printf("[dns] Upstream server(s): %+v", s.Upstream)
|
||||
if s.cacheDisabled {
|
||||
Info.Printf("[dns] Cache: disabled")
|
||||
} else {
|
||||
Info.Printf("[dns] Cache: %d entries", s.cache.Capacity())
|
||||
}
|
||||
|
||||
pc, err := net.ListenPacket("udp", s.ListenAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.pc = pc
|
||||
|
||||
_, port, err := net.SplitHostPort(pc.LocalAddr().String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.ListenAddr = fmt.Sprintf(":%s", port)
|
||||
s.udpSrv = &dns.Server{PacketConn: s.pc, Handler: s.createMux(protUDP), ReadTimeout: s.readTimeout}
|
||||
|
||||
// Bind the TCP socket at the same port, aborting otherwise
|
||||
l, err := net.Listen("tcp", s.ListenAddr)
|
||||
if err != nil {
|
||||
s.Stop()
|
||||
return err
|
||||
}
|
||||
s.lst = l
|
||||
s.tcpSrv = &dns.Server{Listener: l, Handler: s.createMux(protTCP), ReadTimeout: s.readTimeout}
|
||||
|
||||
s.listenersWg.Add(2)
|
||||
|
||||
go func() {
|
||||
defer s.listenersWg.Done()
|
||||
|
||||
Debug.Printf("Listening for DNS on %s (UDP)", s.ListenAddr)
|
||||
err = s.udpSrv.ListenAndServe()
|
||||
Info.Printf("[dns] Listening for DNS on %s (UDP)", s.ListenAddr)
|
||||
err := s.udpSrv.ActivateAndServe()
|
||||
CheckFatal(err)
|
||||
Debug.Printf("DNS UDP server exiting...")
|
||||
Debug.Printf("[dns] DNS UDP server exiting...")
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer s.listenersWg.Done()
|
||||
|
||||
Debug.Printf("Listening for DNS on %s (TCP)", s.ListenAddr)
|
||||
err = s.tcpSrv.ListenAndServe()
|
||||
Info.Printf("[dns] Listening for DNS on %s (TCP)", s.ListenAddr)
|
||||
err := s.tcpSrv.ActivateAndServe()
|
||||
CheckFatal(err)
|
||||
Debug.Printf("DNS TCP server exiting...")
|
||||
Debug.Printf("[dns] DNS TCP server exiting...")
|
||||
}()
|
||||
|
||||
// Waiting for all goroutines to finish (otherwise they die as main routine dies)
|
||||
s.listenersWg.Wait()
|
||||
|
||||
Info.Printf("WeaveDNS server exiting...")
|
||||
Info.Printf("[dns] Server exiting...")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Return status string
|
||||
func (s *DNSServer) Status() string {
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprintln(&buf, "Local domain", s.Domain)
|
||||
fmt.Fprintln(&buf, "Listen address", s.ListenAddr)
|
||||
fmt.Fprintln(&buf, "mDNS interface", s.Iface)
|
||||
fmt.Fprintln(&buf, "Fallback DNS config", s.Upstream)
|
||||
fmt.Fprintf(&buf, "Zone database:\n%s", s.Zone)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
@@ -206,111 +240,110 @@ func (s *DNSServer) Stop() error {
|
||||
}
|
||||
s.listenersWg.Wait()
|
||||
|
||||
// shutdown the mDNS server
|
||||
s.mdnsSrv.Stop()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *DNSServer) queryHandler(proto dnsProtocol) dns.HandlerFunc {
|
||||
zoneLookup := func(lookup ZoneLookup, q *dns.Question, r *dns.Msg) (*dns.Msg, []ZoneRecord, error) {
|
||||
ips, err := lookup.LookupName(q.Name)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return makeAddressReply(r, q, ips), ips, nil
|
||||
}
|
||||
|
||||
fallback := func(w dns.ResponseWriter, r *dns.Msg) {
|
||||
// Create a multiplexer for requests
|
||||
// We must create two DNS request multiplexers, depending on the protocol used by
|
||||
// clients (as we use the same protocol for asking upstream servers)
|
||||
func (s *DNSServer) createMux(proto dnsProtocol) *dns.ServeMux {
|
||||
failFallback := func(w dns.ResponseWriter, r *dns.Msg) {
|
||||
w.WriteMsg(makeDNSFailResponse(r))
|
||||
}
|
||||
|
||||
return s.commonQueryHandler(proto, "Query", dns.TypeA, zoneLookup, fallback)
|
||||
}
|
||||
|
||||
func (s *DNSServer) rdnsHandler(proto dnsProtocol) dns.HandlerFunc {
|
||||
zoneLookup := func(lookup ZoneLookup, q *dns.Question, r *dns.Msg) (*dns.Msg, []ZoneRecord, error) {
|
||||
names, err := lookup.LookupInaddr(q.Name)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return makePTRReply(r, q, names), names, nil
|
||||
}
|
||||
|
||||
notUsHandler := s.notUsHandler(proto)
|
||||
fallback := func(w dns.ResponseWriter, r *dns.Msg) {
|
||||
notUsFallback := func(w dns.ResponseWriter, r *dns.Msg) {
|
||||
Info.Printf("[dns msgid %d] -> sending to fallback server", r.MsgHdr.Id)
|
||||
notUsHandler(w, r)
|
||||
}
|
||||
|
||||
return s.commonQueryHandler(proto, "Reverse query", dns.TypePTR, zoneLookup, fallback)
|
||||
// create the multiplexer
|
||||
m := dns.NewServeMux()
|
||||
m.HandleFunc(s.Zone.Domain(), s.localHandler(proto, "Query", dns.TypeA,
|
||||
s.Zone.DomainLookupName, makeAddressReply, s.Zone.ObserveName, failFallback))
|
||||
m.HandleFunc(RDNSDomain, s.localHandler(proto, "Reverse query", dns.TypePTR,
|
||||
s.Zone.DomainLookupInaddr, makePTRReply, s.Zone.ObserveInaddr, notUsFallback))
|
||||
m.HandleFunc(".", s.notUsHandler(proto))
|
||||
return m
|
||||
}
|
||||
|
||||
func (s *DNSServer) commonQueryHandler(proto dnsProtocol, kind string, qtype uint16,
|
||||
zoneLookup func(ZoneLookup, *dns.Question, *dns.Msg) (*dns.Msg, []ZoneRecord, error), fallback dns.HandlerFunc) dns.HandlerFunc {
|
||||
// Process a request for a name/address in the '.weave.local.' zone
|
||||
func (s *DNSServer) localHandler(proto dnsProtocol, kind string, qtype uint16,
|
||||
lookup func(inaddr string) ([]ZoneRecord, error),
|
||||
msgBuilder DNSResponseBuilder, observer ZoneObserverFunc, fallback dns.HandlerFunc) dns.HandlerFunc {
|
||||
|
||||
return func(w dns.ResponseWriter, r *dns.Msg) {
|
||||
q := r.Question[0]
|
||||
Debug.Printf("[dns msgid %d] %s: %+v", r.MsgHdr.Id, kind, q)
|
||||
maxLen := getMaxReplyLen(r, proto)
|
||||
lookups := []ZoneLookup{s.Zone, s.mdnsCli}
|
||||
|
||||
reply, err := s.cache.Get(r, maxLen)
|
||||
if err != nil {
|
||||
if err == errNoLocalReplies {
|
||||
Debug.Printf("[dns msgid %d] Cached 'no local replies' - skipping local lookup", r.MsgHdr.Id)
|
||||
lookups = []ZoneLookup{s.Zone}
|
||||
} else {
|
||||
Debug.Printf("[dns msgid %d] Error from cache: %s", r.MsgHdr.Id, err)
|
||||
w.WriteMsg(makeDNSFailResponse(r))
|
||||
return
|
||||
// cache a reponse if the cache is enabled, and observe the name/IP
|
||||
maybeCache := func(m *dns.Msg, ttl int, flags uint8) {
|
||||
if !s.cacheDisabled {
|
||||
s.cache.Put(r, m, ttl, flags)
|
||||
// any change in the Zone database for this IP/name will lead to this
|
||||
// cache entry being removed...
|
||||
// TODO: this closure results in unnecessary `Remove`s and some wasted
|
||||
// mem... but we can live with that.
|
||||
observer(q.Name, func() { s.cache.Remove(&q) })
|
||||
}
|
||||
}
|
||||
if reply != nil {
|
||||
Debug.Printf("[dns msgid %d] Returning reply from cache: %s/%d answers",
|
||||
r.MsgHdr.Id, dns.RcodeToString[reply.MsgHdr.Rcode], len(reply.Answer))
|
||||
w.WriteMsg(reply)
|
||||
return
|
||||
|
||||
if !s.cacheDisabled {
|
||||
reply, err := s.cache.Get(r, maxLen)
|
||||
if err != nil {
|
||||
if err == errNoLocalReplies {
|
||||
Debug.Printf("[dns msgid %d] Cached no-local-replies", r.MsgHdr.Id)
|
||||
fallback(w, r)
|
||||
return
|
||||
} else {
|
||||
Debug.Printf("[dns msgid %d] Error from cache: %s", r.MsgHdr.Id, err)
|
||||
w.WriteMsg(makeDNSFailResponse(r))
|
||||
return
|
||||
}
|
||||
}
|
||||
if reply != nil {
|
||||
Debug.Printf("[dns msgid %d] Returning reply from cache: %s/%d answers",
|
||||
r.MsgHdr.Id, dns.RcodeToString[reply.MsgHdr.Rcode], len(reply.Answer))
|
||||
w.WriteMsg(reply)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// catch unsupported queries
|
||||
if q.Qtype != qtype {
|
||||
Debug.Printf("[dns msgid %d] Unsupported query type %s", r.MsgHdr.Id, dns.TypeToString[q.Qtype])
|
||||
m := makeDNSFailResponse(r)
|
||||
s.cache.Put(r, m, negLocalTTL, 0)
|
||||
maybeCache(m, s.negLocalTTL, 0)
|
||||
w.WriteMsg(m)
|
||||
return
|
||||
}
|
||||
|
||||
for _, lookup := range lookups {
|
||||
if m, answers, err := zoneLookup(lookup, &q, r); err == nil {
|
||||
m.Authoritative = true
|
||||
Debug.Printf("[dns msgid %d] Caching response for type %s query for '%s': %s [code:%s]",
|
||||
m.MsgHdr.Id, dns.TypeToString[q.Qtype], q.Name, answers, dns.RcodeToString[m.Rcode])
|
||||
s.cache.Put(r, m, nullTTL, 0)
|
||||
w.WriteMsg(m)
|
||||
return
|
||||
}
|
||||
if answers, err := lookup(q.Name); err != nil {
|
||||
Info.Printf("[dns msgid %d] No results for type %s query for '%s' [caching no-local]",
|
||||
r.MsgHdr.Id, dns.TypeToString[q.Qtype], q.Name)
|
||||
maybeCache(nil, s.negLocalTTL, CacheNoLocalReplies)
|
||||
fallback(w, r)
|
||||
} else {
|
||||
m := msgBuilder(r, &q, answers)
|
||||
m.Authoritative = true
|
||||
Debug.Printf("[dns msgid %d] Sending response: %s/%s:%s [code:%s]",
|
||||
m.MsgHdr.Id, dns.TypeToString[q.Qtype], q.Name, answers, dns.RcodeToString[m.Rcode])
|
||||
maybeCache(m, nullTTL, 0)
|
||||
w.WriteMsg(m)
|
||||
}
|
||||
|
||||
Info.Printf("[dns msgid %d] No results for type %s query for '%s' [caching no-local]",
|
||||
r.MsgHdr.Id, dns.TypeToString[q.Qtype], q.Name)
|
||||
s.cache.Put(r, nil, negLocalTTL, CacheNoLocalReplies)
|
||||
|
||||
fallback(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
// When we receive a request for a name outside of our '.weave.local.'
|
||||
// domain, ask the configured DNS server as a fallback.
|
||||
func (s *DNSServer) notUsHandler(proto dnsProtocol) dns.HandlerFunc {
|
||||
dnsClient := proto.GetNewClient(DefaultUDPBuflen)
|
||||
dnsClient := proto.GetNewClient(DefaultUDPBuflen, s.timeout)
|
||||
|
||||
return func(w dns.ResponseWriter, r *dns.Msg) {
|
||||
maxLen := getMaxReplyLen(r, proto)
|
||||
q := r.Question[0]
|
||||
|
||||
// create a request where we announce our max payload size
|
||||
// announce our max payload size as the max payload our client supports
|
||||
maxLen := getMaxReplyLen(r, proto)
|
||||
rcopy := r
|
||||
rcopy.SetEdns0(uint16(maxLen), false)
|
||||
|
||||
@@ -336,3 +369,12 @@ func (s *DNSServer) notUsHandler(proto dnsProtocol) dns.HandlerFunc {
|
||||
w.WriteMsg(makeDNSFailResponse(r))
|
||||
}
|
||||
}
|
||||
|
||||
// Get the listen port
|
||||
func (s *DNSServer) GetPort() (int, error) {
|
||||
_, portS, err := net.SplitHostPort(s.ListenAddr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return strconv.Atoi(portS)
|
||||
}
|
||||
+37
-34
@@ -18,10 +18,6 @@ const (
|
||||
testUDPBufSize = 16384
|
||||
)
|
||||
|
||||
var (
|
||||
testPort = 17625
|
||||
)
|
||||
|
||||
func setupForTest(t *testing.T) {
|
||||
// fail early if we cannot find a default multicast interface
|
||||
multicast, err := LinkLocalMulticastListener(nil)
|
||||
@@ -29,18 +25,17 @@ func setupForTest(t *testing.T) {
|
||||
t.Fatalf("Unable to create multicast listener: %s. No default multicast interface?", err)
|
||||
}
|
||||
multicast.Close()
|
||||
|
||||
testPort++
|
||||
}
|
||||
|
||||
func TestUDPDNSServer(t *testing.T) {
|
||||
setupForTest(t)
|
||||
|
||||
const (
|
||||
containerID = "foobar"
|
||||
successTestName = "test1.weave.local."
|
||||
failTestName = "test2.weave.local."
|
||||
failTestName = "fail.weave.local."
|
||||
nonLocalName = "weave.works."
|
||||
testAddr1 = "10.2.2.1"
|
||||
containerID = "somecontainer"
|
||||
)
|
||||
testCIDR1 := testAddr1 + "/24"
|
||||
|
||||
@@ -80,15 +75,17 @@ func TestUDPDNSServer(t *testing.T) {
|
||||
}
|
||||
|
||||
// Run another DNS server for fallback
|
||||
s, fallbackAddr, err := runLocalUDPServer("127.0.0.1:0", fallbackHandler)
|
||||
fallback, err := newMockedFallback(fallbackHandler, nil)
|
||||
wt.AssertNoErr(t, err)
|
||||
defer s.Shutdown()
|
||||
fallback.Start()
|
||||
defer fallback.Stop()
|
||||
|
||||
_, fallbackPort, err := net.SplitHostPort(fallbackAddr)
|
||||
wt.AssertNoErr(t, err)
|
||||
|
||||
config := &dns.ClientConfig{Servers: []string{"127.0.0.1"}, Port: fallbackPort}
|
||||
srv, err := NewDNSServer(DNSServerConfig{UpstreamCfg: config, Port: testPort}, zone, nil)
|
||||
srv, err := NewDNSServer(DNSServerConfig{
|
||||
Zone: zone,
|
||||
UpstreamCfg: fallback.CliConfig,
|
||||
CacheDisabled: true,
|
||||
ListenReadTimeout: testSocketTimeout,
|
||||
})
|
||||
wt.AssertNoErr(t, err)
|
||||
defer srv.Stop()
|
||||
go srv.Start()
|
||||
@@ -96,6 +93,10 @@ func TestUDPDNSServer(t *testing.T) {
|
||||
|
||||
var r *dns.Msg
|
||||
|
||||
testPort, err := srv.GetPort()
|
||||
wt.AssertNoErr(t, err)
|
||||
wt.AssertNotEqualInt(t, testPort, 0, "invalid listen port")
|
||||
|
||||
_, r = assertExchange(t, successTestName, dns.TypeA, testPort, 1, 1, 0)
|
||||
wt.AssertType(t, r.Answer[0], (*dns.A)(nil), "DNS record")
|
||||
wt.AssertEqualString(t, r.Answer[0].(*dns.A).A.String(), testAddr1, "IP address")
|
||||
@@ -126,11 +127,11 @@ func TestUDPDNSServer(t *testing.T) {
|
||||
|
||||
func TestTCPDNSServer(t *testing.T) {
|
||||
setupForTest(t)
|
||||
|
||||
const (
|
||||
numAnswers = 512
|
||||
nonLocalName = "weave.works."
|
||||
)
|
||||
dnsAddr := fmt.Sprintf("localhost:%d", testPort)
|
||||
|
||||
InitDefaultLogging(testing.Verbose())
|
||||
Info.Println("TestTCPDNSServer starting")
|
||||
@@ -140,13 +141,14 @@ func TestTCPDNSServer(t *testing.T) {
|
||||
err = zone.Start()
|
||||
wt.AssertNoErr(t, err)
|
||||
defer zone.Stop()
|
||||
|
||||
|
||||
// generate a list of `numAnswers` IP addresses
|
||||
var addrs []ZoneRecord
|
||||
bs := make([]byte, 4)
|
||||
for i := 0; i < numAnswers; i++ {
|
||||
binary.LittleEndian.PutUint32(bs, uint32(i))
|
||||
addrs = append(addrs, Record{"", net.IPv4(bs[0], bs[1], bs[2], bs[3]), 0, 0, 0})
|
||||
ip := net.IPv4(bs[0], bs[1], bs[2], bs[3])
|
||||
addrs = append(addrs, ZoneRecord(Record{"", ip, 0, 0, 0}))
|
||||
}
|
||||
|
||||
// handler for the fallback server: it will just return a very long response
|
||||
@@ -173,27 +175,28 @@ func TestTCPDNSServer(t *testing.T) {
|
||||
}
|
||||
|
||||
t.Logf("Running a DNS fallback server with UDP")
|
||||
us, fallbackUDPAddr, err := runLocalUDPServer("127.0.0.1:0", fallbackUDPHandler)
|
||||
fallback, err := newMockedFallback(fallbackUDPHandler, fallbackTCPHandler)
|
||||
wt.AssertNoErr(t, err)
|
||||
defer us.Shutdown()
|
||||
fallback.Start()
|
||||
defer fallback.Stop()
|
||||
|
||||
_, fallbackPort, err := net.SplitHostPort(fallbackUDPAddr)
|
||||
wt.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Starting another fallback server, with TCP, on the same port as the UDP server")
|
||||
fallbackTCPAddr := fmt.Sprintf("127.0.0.1:%s", fallbackPort)
|
||||
ts, fallbackTCPAddr, err := runLocalTCPServer(fallbackTCPAddr, fallbackTCPHandler)
|
||||
wt.AssertNoErr(t, err)
|
||||
defer ts.Shutdown()
|
||||
|
||||
t.Logf("Creating a WeaveDNS server instance, falling back to 127.0.0.1:%s", fallbackPort)
|
||||
config := &dns.ClientConfig{Servers: []string{"127.0.0.1"}, Port: fallbackPort}
|
||||
srv, err := NewDNSServer(DNSServerConfig{UpstreamCfg: config, Port: testPort}, zone, nil)
|
||||
t.Logf("Creating a WeaveDNS server instance, falling back to 127.0.0.1:%s", fallback.Port)
|
||||
srv, err := NewDNSServer(DNSServerConfig{
|
||||
Zone: zone,
|
||||
UpstreamCfg: fallback.CliConfig,
|
||||
CacheDisabled: true,
|
||||
ListenReadTimeout: testSocketTimeout,
|
||||
})
|
||||
wt.AssertNoErr(t, err)
|
||||
defer srv.Stop()
|
||||
go srv.Start()
|
||||
time.Sleep(100 * time.Millisecond) // Allow sever goroutine to start
|
||||
|
||||
testPort, err := srv.GetPort()
|
||||
wt.AssertNoErr(t, err)
|
||||
wt.AssertNotEqualInt(t, testPort, 0, "listen port")
|
||||
dnsAddr := fmt.Sprintf("127.0.0.1:%d", testPort)
|
||||
|
||||
t.Logf("Creating a UDP and a TCP client")
|
||||
uc := new(dns.Client)
|
||||
uc.UDPSize = minUDPSize
|
||||
@@ -205,8 +208,8 @@ func TestTCPDNSServer(t *testing.T) {
|
||||
m.RecursionDesired = true
|
||||
m.SetQuestion(nonLocalName, dns.TypeA)
|
||||
|
||||
t.Logf("Checking the fallback server at %s returns a truncated response with UDP", fallbackUDPAddr)
|
||||
r, _, err := uc.Exchange(m, fallbackUDPAddr)
|
||||
t.Logf("Checking the fallback server at %s returns a truncated response with UDP", fallback.Addr)
|
||||
r, _, err := uc.Exchange(m, fallback.Addr)
|
||||
t.Logf("Got response from fallback server (UDP) with %d answers", len(r.Answer))
|
||||
t.Logf("Response:\n%+v\n", r)
|
||||
wt.AssertNoErr(t, err)
|
||||
|
||||
Reference in new issue
Block a user