Files
weave/nameserver/server.go
T

158 lines
4.0 KiB
Go

package nameserver
import (
"fmt"
"github.com/miekg/dns"
. "github.com/zettio/weave/common"
"net"
)
const (
LOCAL_DOMAIN = "weave.local."
UDPBufSize = 4096 // bigger than the default 512
)
func makeDNSFailResponse(r *dns.Msg) *dns.Msg {
m := new(dns.Msg)
m.SetReply(r)
m.RecursionAvailable = true
m.Rcode = dns.RcodeNameError
return m
}
type DNSServer struct {
config *dns.ClientConfig
zone Zone
iface *net.Interface
udpPort int
tcpPort int
}
// Creates a new DNS server
func NewDNSServer(zone Zone, iface *net.Interface, udpPort int, tcpPort int) (*DNSServer, error) {
config, err := dns.ClientConfigFromFile("/etc/resolv.conf")
if err != nil {
return nil, err
}
return &DNSServer{
config: config,
zone: zone,
iface: iface,
udpPort: udpPort,
tcpPort: tcpPort,
}, nil
}
// Start the DNS server
func (s *DNSServer) Start() error {
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)
LocalServeMux := dns.NewServeMux()
LocalServeMux.HandleFunc(LOCAL_DOMAIN, s.queryHandler([]Lookup{s.zone, mdnsClient}))
LocalServeMux.HandleFunc(RDNS_DOMAIN, s.rdnsHandler([]Lookup{s.zone, mdnsClient}))
LocalServeMux.HandleFunc(".", s.notUsHandler())
mdnsServer, err := NewMDNSServer(s.zone)
checkFatal(err)
err = mdnsServer.Start(s.iface)
checkFatal(err)
go func() {
udpAddress := fmt.Sprintf(":%d", s.udpPort)
Info.Printf("Listening for DNS on %s (UDP)", udpAddress)
err = dns.ListenAndServe(udpAddress, "udp", LocalServeMux)
checkFatal(err)
}()
go func() {
tcpAddress := fmt.Sprintf(":%d", s.tcpPort)
Info.Printf("Listening for DNS on %s (TCP)", tcpAddress)
err = dns.ListenAndServe(tcpAddress, "tcp", LocalServeMux)
checkFatal(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)
if q.Qtype == dns.TypeA {
for _, lookup := range lookups {
if ip, err := lookup.LookupName(q.Name); err == nil {
m := makeAddressReply(r, &q, []net.IP{ip})
w.WriteMsg(m)
return
}
}
}
Info.Printf("[dns msgid %d] No results for type %s query %s",
r.MsgHdr.Id, dns.TypeToString[q.Qtype], q.Name)
w.WriteMsg(makeDNSFailResponse(r))
}
}
func (s *DNSServer) rdnsHandler(lookups []Lookup) dns.HandlerFunc {
fallback := s.notUsHandler()
return func(w dns.ResponseWriter, r *dns.Msg) {
q := r.Question[0]
Debug.Printf("Reverse query: %+v", q)
if q.Qtype == dns.TypePTR {
for _, lookup := range lookups {
if name, err := lookup.LookupInaddr(q.Name); err == nil {
m := makePTRReply(r, &q, []string{name})
w.WriteMsg(m)
return
}
}
fallback(w, r)
return
}
Warning.Printf("[dns msgid %d] Unexpected reverse query type %s: %+v",
r.MsgHdr.Id, dns.TypeToString[q.Qtype], q)
}
}
// 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() dns.HandlerFunc {
dnsClient := new(dns.Client)
dnsClient.UDPSize = UDPBufSize
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 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)
continue
}
if reply != nil && reply.Rcode != dns.RcodeSuccess {
Debug.Printf("[dns msgid %d] Failure reported by %s for query %s",
r.MsgHdr.Id, server, q.Name)
continue
}
Debug.Printf("[dns msgid %d] Given answer by %s for query %s",
r.MsgHdr.Id, server, q.Name)
w.WriteMsg(reply)
return
}
Warning.Printf("[dns msgid %d] Failed lookup for external name %s",
r.MsgHdr.Id, q.Name)
w.WriteMsg(makeDNSFailResponse(r))
}
}