Merge branch 'weave-362' of https://github.com/inercia/weave into pr392

This commit is contained in:
Michael Bridgen committed 2015-02-24 16:43:56 +00:00
commit 540f84af3d
6 files changed
+305 -93

No files matched your search

+13
View File
@@ -0,0 +1,13 @@
package common
func CheckFatal(e error) {
if e != nil {
Error.Fatal(e)
}
}
func CheckWarn(e error) {
if e != nil {
Warning.Println(e)
}
}
+4 -3
View File
@@ -2,6 +2,7 @@ package nameserver
import (
"github.com/miekg/dns"
. "github.com/zettio/weave/common"
wt "github.com/zettio/weave/testing"
"log"
"net"
@@ -20,18 +21,18 @@ func minimalServer(w dns.ResponseWriter, req *dns.Msg) {
sendAnswer := func(m *dns.Msg) {
buf, err := m.Pack()
checkFatal(err)
CheckFatal(err)
if buf == nil {
log.Fatal("Nil buffer")
}
//log.Println("minimalServer sending:", buf)
// This is a bit of a kludge - per the RFC we should send responses from 5353, but that doesn't seem to work
sendconn, err := net.DialUDP("udp", nil, ipv4Addr)
checkFatal(err)
CheckFatal(err)
_, err = sendconn.Write(buf)
sendconn.Close()
checkFatal(err)
CheckFatal(err)
}
//log.Println("minimalServer received:", req)
+102 -60
View File
@@ -5,6 +5,7 @@ import (
"github.com/miekg/dns"
. "github.com/zettio/weave/common"
"net"
"sync"
)
const (
@@ -12,18 +13,6 @@ const (
UDPBufSize = 4096 // bigger than the default 512
)
func checkFatal(e error) {
if e != nil {
Error.Fatal(e)
}
}
func checkWarn(e error) {
if e != nil {
Warning.Println(e)
}
}
func makeDNSFailResponse(r *dns.Msg) *dns.Msg {
m := new(dns.Msg)
m.SetReply(r)
@@ -32,7 +21,100 @@ func makeDNSFailResponse(r *dns.Msg) *dns.Msg {
return m
}
func queryHandler(lookups []Lookup) dns.HandlerFunc {
type DNSServer struct {
config *dns.ClientConfig
zone Zone
iface *net.Interface
port int
udpSrv *dns.Server
tcpSrv *dns.Server
}
// Creates a new DNS server with a given config
func NewDNSServer(config *dns.ClientConfig, zone Zone, iface *net.Interface, port int) *DNSServer {
return &DNSServer{
config: config,
zone: zone,
iface: iface,
port: port,
}
}
// Start the DNS server
func (s *DNSServer) Start() error {
Info.Printf("WeaveDNS server starting...")
mdnsClient, err := NewMDNSClient()
CheckFatal(err)
ifaceName := "default interface"
if s.iface != nil {
ifaceName = s.iface.Name
}
Info.Printf("Using mDNS on %s", ifaceName)
err = mdnsClient.Start(s.iface)
CheckFatal(err)
// create two DNS request multiplexerers, depending on the protocol used by clients
// (we use the same protocol for asking upstream servers)
mux := func(client *dns.Client) *dns.ServeMux {
m := dns.NewServeMux()
m.HandleFunc(LOCAL_DOMAIN, s.queryHandler([]Lookup{s.zone, mdnsClient}))
m.HandleFunc(RDNS_DOMAIN, s.rdnsHandler([]Lookup{s.zone, mdnsClient}, client))
m.HandleFunc(".", s.notUsHandler(client))
return m
}
mdnsServer, err := NewMDNSServer(s.zone)
CheckFatal(err)
err = mdnsServer.Start(s.iface)
CheckFatal(err)
address := fmt.Sprintf(":%d", s.port)
s.udpSrv = &dns.Server{Addr: address, Net: "udp", Handler: mux(&dns.Client{Net: "udp", UDPSize: UDPBufSize})}
s.tcpSrv = &dns.Server{Addr: address, Net: "tcp", Handler: mux(&dns.Client{Net: "tcp"})}
wg := new(sync.WaitGroup)
wg.Add(2)
go func() {
defer wg.Done()
Debug.Printf("Listening for DNS on %s (UDP)", address)
err = s.udpSrv.ListenAndServe()
CheckFatal(err)
Debug.Printf("DNS UDP server exiting...")
}()
go func() {
defer wg.Done()
Debug.Printf("Listening for DNS on %s (TCP)", address)
err = s.tcpSrv.ListenAndServe()
CheckFatal(err)
Debug.Printf("DNS TCP server exiting...")
}()
// Waiting for all goroutines to finish (otherwise they die as main routine dies)
wg.Wait()
Info.Printf("WeaveDNS server exiting...")
return nil
}
// Perform a graceful shutdown
func (s *DNSServer) Stop() error {
if err := s.tcpSrv.Shutdown(); err != nil {
return err
}
if err := s.udpSrv.Shutdown(); err != nil {
return err
}
return nil
}
func (s *DNSServer) queryHandler(lookups []Lookup) dns.HandlerFunc {
return func(w dns.ResponseWriter, r *dns.Msg) {
q := r.Question[0]
Debug.Printf("Query: %+v", q)
@@ -51,8 +133,8 @@ func queryHandler(lookups []Lookup) dns.HandlerFunc {
}
}
func rdnsHandler(config *dns.ClientConfig, lookups []Lookup) dns.HandlerFunc {
fallback := notUsHandler(config)
func (s *DNSServer) rdnsHandler(lookups []Lookup, client *dns.Client) dns.HandlerFunc {
fallback := s.notUsHandler(client)
return func(w dns.ResponseWriter, r *dns.Msg) {
q := r.Question[0]
Debug.Printf("Reverse query: %+v", q)
@@ -72,17 +154,14 @@ func rdnsHandler(config *dns.ClientConfig, lookups []Lookup) dns.HandlerFunc {
}
}
/* When we receive a request for a name outside of our '.weave.local.'
domain, ask the configured DNS server as a fallback.
*/
func notUsHandler(config *dns.ClientConfig) dns.HandlerFunc {
dnsClient := new(dns.Client)
dnsClient.UDPSize = UDPBufSize
// 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(dnsClient *dns.Client) dns.HandlerFunc {
return func(w dns.ResponseWriter, r *dns.Msg) {
q := r.Question[0]
Debug.Printf("[dns msgid %d] Fallback query: %+v", r.MsgHdr.Id, q)
for _, server := range config.Servers {
reply, _, err := dnsClient.Exchange(r, fmt.Sprintf("%s:%s", server, config.Port))
for _, server := range s.config.Servers {
reply, _, err := dnsClient.Exchange(r, fmt.Sprintf("%s:%s", server, s.config.Port))
if err != nil {
Debug.Printf("[dns msgid %d] Network error trying %s (%s)",
r.MsgHdr.Id, server, err)
@@ -103,40 +182,3 @@ func notUsHandler(config *dns.ClientConfig) dns.HandlerFunc {
w.WriteMsg(makeDNSFailResponse(r))
}
}
func StartServer(zone Zone, iface *net.Interface, dnsPort int, wait int) error {
config, err := dns.ClientConfigFromFile("/etc/resolv.conf")
checkFatal(err)
return startServerWithConfig(config, zone, iface, dnsPort, wait)
}
func startServerWithConfig(config *dns.ClientConfig, zone Zone, iface *net.Interface, dnsPort int, wait int) error {
mdnsClient, err := NewMDNSClient()
checkFatal(err)
ifaceName := "default interface"
if iface != nil {
ifaceName = iface.Name
}
Info.Printf("Using mDNS on %s", ifaceName)
err = mdnsClient.Start(iface)
checkFatal(err)
LocalServeMux := dns.NewServeMux()
LocalServeMux.HandleFunc(LOCAL_DOMAIN, queryHandler([]Lookup{zone, mdnsClient}))
LocalServeMux.HandleFunc(RDNS_DOMAIN, rdnsHandler(config, []Lookup{zone, mdnsClient}))
LocalServeMux.HandleFunc(".", notUsHandler(config))
mdnsServer, err := NewMDNSServer(zone)
checkFatal(err)
err = mdnsServer.Start(iface)
checkFatal(err)
address := fmt.Sprintf(":%d", dnsPort)
Info.Printf("Listening for DNS on %s", address)
err = dns.ListenAndServe(address, "udp", LocalServeMux)
checkFatal(err)
return nil
}
+146 -28
View File
@@ -1,9 +1,10 @@
package nameserver
import (
"encoding/binary"
"fmt"
"github.com/miekg/dns"
"github.com/zettio/weave/common"
. "github.com/zettio/weave/common"
wt "github.com/zettio/weave/testing"
"net"
"testing"
@@ -16,7 +17,7 @@ const (
testRDNSnonlocal = "8.8.8.8.in-addr.arpa."
)
func TestDNSServer(t *testing.T) {
func TestUDPDNSServer(t *testing.T) {
const (
port = 17625
successTestName = "test1.weave.local."
@@ -27,13 +28,37 @@ func TestDNSServer(t *testing.T) {
dnsAddr := fmt.Sprintf("localhost:%d", port)
testCIDR1 := testAddr1 + "/24"
common.InitDefaultLogging(true)
InitDefaultLogging(true)
var zone = new(ZoneDb)
ip, _, _ := net.ParseCIDR(testCIDR1)
zone.AddRecord(containerID, successTestName, ip)
fallbackHandler := func(w dns.ResponseWriter, req *dns.Msg) {
m := new(dns.Msg)
m.SetReply(req)
if len(req.Question) == 1 {
q := req.Question[0]
if q.Name == "weave.works." && q.Qtype == dns.TypeMX {
m.Answer = make([]dns.RR, 1)
m.Answer[0] = &dns.MX{Hdr: dns.RR_Header{Name: m.Question[0].Name, Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 0}, Mx: "mail.weave.works."}
} else if q.Name == "weave.works." && q.Qtype == dns.TypeANY {
const N = 10
m.Extra = make([]dns.RR, N)
for i, _ := range m.Extra {
m.Extra[i] = &dns.TXT{Hdr: dns.RR_Header{Name: m.Question[0].Name, Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: 0}, Txt: []string{"Lots and lots and lots and lots and lots and lots and lots and lots and lots of data"}}
}
} else if q.Name == testRDNSnonlocal && q.Qtype == dns.TypePTR {
m.Answer = make([]dns.RR, 1)
m.Answer[0] = &dns.PTR{Hdr: dns.RR_Header{Name: m.Question[0].Name, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: 0}, Ptr: "ns1.google.com."}
} else if q.Name == testRDNSfail && q.Qtype == dns.TypePTR {
m.Rcode = dns.RcodeNameError
}
}
w.WriteMsg(m)
}
// Run another DNS server for fallback
s, fallbackAddr, err := RunLocalUDPServer("127.0.0.1:0")
s, fallbackAddr, err := RunLocalUDPServer(t, "127.0.0.1:0", fallbackHandler)
wt.AssertNoErr(t, err)
defer s.Shutdown()
@@ -41,7 +66,9 @@ func TestDNSServer(t *testing.T) {
wt.AssertNoErr(t, err)
config := &dns.ClientConfig{Servers: []string{"127.0.0.1"}, Port: fallbackPort}
go startServerWithConfig(config, zone, nil, port, 0)
srv := NewDNSServer(config, zone, nil, port)
defer srv.Stop()
go srv.Start()
time.Sleep(100 * time.Millisecond) // Allow sever goroutine to start
c := new(dns.Client)
@@ -49,6 +76,7 @@ func TestDNSServer(t *testing.T) {
m := new(dns.Msg)
m.SetQuestion(successTestName, dns.TypeA)
m.RecursionDesired = true
r, _, err := c.Exchange(m, dnsAddr)
wt.AssertNoErr(t, err)
wt.AssertStatus(t, r.Rcode, dns.RcodeSuccess, "DNS response code")
@@ -112,41 +140,131 @@ func TestDNSServer(t *testing.T) {
// needs two servers, each listening on its own address
}
func fallbackHandler(w dns.ResponseWriter, req *dns.Msg) {
m := new(dns.Msg)
m.SetReply(req)
if len(req.Question) == 1 {
q := req.Question[0]
if q.Name == "weave.works." && q.Qtype == dns.TypeMX {
m.Answer = make([]dns.RR, 1)
m.Answer[0] = &dns.MX{Hdr: dns.RR_Header{Name: m.Question[0].Name, Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 0}, Mx: "mail.weave.works."}
} else if q.Name == "weave.works." && q.Qtype == dns.TypeANY {
const N = 10
m.Extra = make([]dns.RR, N)
for i, _ := range m.Extra {
m.Extra[i] = &dns.TXT{Hdr: dns.RR_Header{Name: m.Question[0].Name, Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: 0}, Txt: []string{"Lots and lots and lots and lots and lots and lots and lots and lots and lots of data"}}
}
} else if q.Name == testRDNSnonlocal && q.Qtype == dns.TypePTR {
m.Answer = make([]dns.RR, 1)
m.Answer[0] = &dns.PTR{Hdr: dns.RR_Header{Name: m.Question[0].Name, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: 0}, Ptr: "ns1.google.com."}
} else if q.Name == testRDNSfail && q.Qtype == dns.TypePTR {
m.Rcode = dns.RcodeNameError
}
func TestTCPDNSServer(t *testing.T) {
const (
port = 17625
numAnswers = 512
nonLocalName = "weave.works."
)
dnsAddr := fmt.Sprintf("localhost:%d", port)
InitDefaultLogging(true)
var zone = new(ZoneDb)
var addrs []net.IP
bs := make([]byte, 4)
for i := 0; i < numAnswers; i++ {
binary.LittleEndian.PutUint32(bs, uint32(i))
addrs = append(addrs, net.IPv4(bs[0], bs[1], bs[2], bs[3]))
}
w.WriteMsg(m)
// handler for the fallback server: it will just return a very long response
fallbackUDPHandler := func(w dns.ResponseWriter, req *dns.Msg) {
t.Logf("Fallback UDP server got asked: returning %d answers", numAnswers)
q := req.Question[0]
m := makeAddressReply(req, &q, addrs)
m.Truncated = true
w.WriteMsg(m)
}
fallbackTCPHandler := func(w dns.ResponseWriter, req *dns.Msg) {
t.Logf("Fallback TCP server got asked: returning %d answers", numAnswers)
q := req.Question[0]
m := makeAddressReply(req, &q, addrs)
w.WriteMsg(m)
}
// Run another DNS server for fallback
us, fallbackUdpAddr, err := RunLocalUDPServer(t, "127.0.0.1:0", fallbackUDPHandler)
wt.AssertNoErr(t, err)
defer us.Shutdown()
_, fallbackPort, err := net.SplitHostPort(fallbackUdpAddr)
wt.AssertNoErr(t, err)
// start the TCP server on the same port as the UDP server
fallbackTcpAddr := fmt.Sprintf("127.0.0.1:%s", fallbackPort)
ts, fallbackTcpAddr, err := RunLocalTCPServer(t, 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 := NewDNSServer(config, zone, nil, port)
defer srv.Stop()
go srv.Start()
time.Sleep(100 * time.Millisecond) // Allow sever goroutine to start
t.Logf("Creating a UDP and a TCP client")
uc := new(dns.Client)
uc.UDPSize = UDPBufSize
tc := new(dns.Client)
tc.Net = "tcp"
t.Logf("Creating DNS query message")
m := new(dns.Msg)
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("Got response from fallback server (UDP) with %d answers", len(r.Answer))
t.Logf("Response:\n%+v\n", r)
wt.AssertNoErr(t, err)
wt.AssertTrue(t, r.MsgHdr.Truncated, "DNS truncated reponse flag")
wt.AssertNotEqualInt(t, len(r.Answer), numAnswers, "number of answers (UDP)")
t.Logf("Checking the WeaveDNS server at %s returns a truncated reponse with UDP", dnsAddr)
r, _, err = uc.Exchange(m, dnsAddr)
t.Logf("Got response from WeaveDNS (UDP) with %d answers", len(r.Answer))
t.Logf("Response:\n%+v\n", r)
wt.AssertNoErr(t, err)
wt.AssertTrue(t, r.MsgHdr.Truncated, "DNS truncated reponse flag")
wt.AssertNotEqualInt(t, len(r.Answer), numAnswers, "number of answers (UDP)")
t.Logf("Checking the WeaveDNS server at %s does not return a truncated reponse with TCP", dnsAddr)
r, _, err = tc.Exchange(m, dnsAddr)
t.Logf("Got response from WeaveDNS (TCP) with %d answers", len(r.Answer))
t.Logf("Response:\n%+v\n", r)
wt.AssertNoErr(t, err)
wt.AssertFalse(t, r.MsgHdr.Truncated, "DNS truncated response flag")
wt.AssertEqualInt(t, len(r.Answer), numAnswers, "number of answers (TCP)")
}
func RunLocalUDPServer(laddr string) (*dns.Server, string, error) {
func RunLocalUDPServer(t *testing.T, laddr string, handler dns.HandlerFunc) (*dns.Server, string, error) {
t.Logf("Starting fallback UDP server at %s", laddr)
pc, err := net.ListenPacket("udp", laddr)
if err != nil {
return nil, "", err
}
server := &dns.Server{PacketConn: pc, Handler: dns.HandlerFunc(fallbackHandler)}
server := &dns.Server{PacketConn: pc, Handler: handler}
go func() {
server.ActivateAndServe()
pc.Close()
}()
t.Logf("Fallback UDP server listening at %s", pc.LocalAddr())
return server, pc.LocalAddr().String(), nil
}
func RunLocalTCPServer(t *testing.T, laddr string, handler dns.HandlerFunc) (*dns.Server, string, error) {
t.Logf("Starting fallback TCP server at %s", laddr)
laddrTcp, err := net.ResolveTCPAddr("tcp", laddr)
if err != nil {
return nil, "", err
}
l, err := net.ListenTCP("tcp", laddrTcp)
if err != nil {
return nil, "", err
}
server := &dns.Server{Listener: l, Handler: handler}
go func() {
server.ActivateAndServe()
l.Close()
}()
t.Logf("Fallback TCP server listening at %s", l.Addr().String())
return server, l.Addr().String(), nil
}
+18
View File
@@ -7,6 +7,18 @@ import (
"time"
)
func AssertTrue(t *testing.T, cond bool, desc string) {
if !cond {
Fatalf(t, "Expected %s to be true", desc)
}
}
func AssertFalse(t *testing.T, cond bool, desc string) {
if cond {
Fatalf(t, "Expected %s to be false", desc)
}
}
func AssertNoErr(t *testing.T, err error) {
if err != nil {
Fatalf(t, "Unexpected error: %s", err)
@@ -25,6 +37,12 @@ func AssertEqualInt(t *testing.T, got, wanted int, desc string) {
}
}
func AssertNotEqualInt(t *testing.T, got, wanted int, desc string) {
if got == wanted {
Fatalf(t, "Expected %s %d to be different to %d", desc, wanted, got)
}
}
func AssertEqualString(t *testing.T, got, wanted string, desc string) {
if got != wanted {
Fatalf(t, "Expected %s '%s' but got '%s'", desc, wanted, got)
+22 -2
View File
@@ -3,6 +3,7 @@ package main
import (
"flag"
"fmt"
"github.com/miekg/dns"
. "github.com/zettio/weave/common"
weavedns "github.com/zettio/weave/nameserver"
weavenet "github.com/zettio/weave/net"
@@ -18,19 +19,22 @@ func main() {
justVersion bool
ifaceName string
apiPath string
fallback string
dnsPort int
httpPort int
wait int
watch bool
debug bool
err error
)
flag.BoolVar(&justVersion, "version", false, "print version and exit")
flag.StringVar(&ifaceName, "iface", "", "name of interface to use for multicast")
flag.StringVar(&apiPath, "api", "unix:///var/run/docker.sock", "Path to Docker API socket")
flag.IntVar(&wait, "wait", 0, "number of seconds to wait for interface to be created and come up")
flag.IntVar(&dnsPort, "dnsport", 53, "port to listen to dns requests")
flag.IntVar(&dnsPort, "dnsport", 53, "port to listen to DNS requests")
flag.IntVar(&httpPort, "httpport", 6785, "port to listen to HTTP requests")
flag.StringVar(&fallback, "fallback", "", "Fallback server and port (ie, '8.8.8.8:53')")
flag.BoolVar(&watch, "watch", true, "watch the docker socket for container events")
flag.BoolVar(&debug, "debug", false, "output debugging info to stderr")
flag.Parse()
@@ -63,8 +67,24 @@ func main() {
}
}
var config *dns.ClientConfig
if len(fallback) > 0 {
fallbackHost, fallbackPort, err := net.SplitHostPort(fallback)
if err != nil {
Error.Fatal("Fould not parse fallback host and port", err)
}
config = &dns.ClientConfig{Servers: []string{fallbackHost}, Port: fallbackPort}
Debug.Printf("DNS fallback at %s:%s", fallbackHost, fallbackPort)
} else {
config, err = dns.ClientConfigFromFile("/etc/resolv.conf")
if err != nil {
Error.Fatal("Failed obtain DNS client configuration", err)
}
}
go weavedns.ListenHttp(weavedns.LOCAL_DOMAIN, zone, httpPort)
err := weavedns.StartServer(zone, iface, dnsPort, wait)
srv := weavedns.NewDNSServer(config, zone, iface, dnsPort)
err = srv.Start()
if err != nil {
Error.Fatal("Failed to start server", err)
}