The Local was redundant (and inaccurate in the case of the mDNS client implementation), and otherwise they are too nondescript.
117 lines
3.0 KiB
Go
117 lines
3.0 KiB
Go
package nameserver
|
|
|
|
import (
|
|
"github.com/miekg/dns"
|
|
. "github.com/zettio/weave/common"
|
|
"net"
|
|
)
|
|
|
|
type MDNSServer struct {
|
|
localAddrs []net.Addr
|
|
sendconn *net.UDPConn
|
|
zone Zone
|
|
}
|
|
|
|
func NewMDNSServer(zone Zone) (*MDNSServer, error) {
|
|
// 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.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &MDNSServer{sendconn: sendconn, zone: zone}, nil
|
|
}
|
|
|
|
// Return true if testaddr is a UDP address with IP matching my local i/f
|
|
func (s *MDNSServer) addrIsLocal(testaddr net.Addr) bool {
|
|
if udpaddr, ok := testaddr.(*net.UDPAddr); ok {
|
|
for _, localaddr := range s.localAddrs {
|
|
if ipnetlocal, ok := localaddr.(*net.IPNet); ok {
|
|
if ipnetlocal.IP.Equal(udpaddr.IP) {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (s *MDNSServer) Start(ifi *net.Interface) error {
|
|
conn, err := LinkLocalMulticastListener(ifi)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if ifi == nil {
|
|
s.localAddrs, err = net.InterfaceAddrs()
|
|
} else {
|
|
s.localAddrs, err = ifi.Addrs()
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
handleLocal := s.makeHandler(dns.TypeA,
|
|
func(zone Lookup, r *dns.Msg, q *dns.Question) *dns.Msg {
|
|
if ip, err := zone.LookupName(q.Name); err == nil {
|
|
return makeAddressReply(r, q, []net.IP{ip})
|
|
} else {
|
|
return nil
|
|
}
|
|
})
|
|
|
|
handleReverse := s.makeHandler(dns.TypePTR,
|
|
func(zone Lookup, r *dns.Msg, q *dns.Question) *dns.Msg {
|
|
if name, err := zone.LookupInaddr(q.Name); err == nil {
|
|
return makePTRReply(r, q, []string{name})
|
|
} else {
|
|
return nil
|
|
}
|
|
})
|
|
|
|
mux := dns.NewServeMux()
|
|
mux.HandleFunc(LOCAL_DOMAIN, handleLocal)
|
|
mux.HandleFunc(RDNS_DOMAIN, handleReverse)
|
|
|
|
go dns.ActivateAndServe(nil, conn, mux)
|
|
return err
|
|
}
|
|
|
|
type LookupFunc func(Lookup, *dns.Msg, *dns.Question) *dns.Msg
|
|
|
|
func (s *MDNSServer) makeHandler(qtype uint16, lookup LookupFunc) dns.HandlerFunc {
|
|
return func(_ dns.ResponseWriter, r *dns.Msg) {
|
|
// Handle only questions, ignore answers. We might also ignore
|
|
// questions that arise locally (i.e., that come from an IP we
|
|
// think is local), but in the interest of avoiding
|
|
// complication, and easier testing, this is elided on the
|
|
// assumption that the client wouldn't ask if it already knew
|
|
// the answer, and if it does ask, it'll be happy to get an
|
|
// answer.
|
|
if len(r.Answer) == 0 && len(r.Question) > 0 {
|
|
q := &r.Question[0]
|
|
if q.Qtype == qtype {
|
|
if m := lookup(s.zone, r, q); m != nil {
|
|
Debug.Printf("[mdns msgid %d] Found local answer to mDNS query %s",
|
|
r.MsgHdr.Id, q.Name)
|
|
if err := s.sendResponse(m); err != nil {
|
|
Warning.Printf("[mdns msgid %d] Error writing to %s",
|
|
r.MsgHdr.Id, s.sendconn)
|
|
}
|
|
} else {
|
|
Debug.Printf("[mdns msgid %d] No local answer for mDNS query %s",
|
|
r.MsgHdr.Id, q.Name)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *MDNSServer) sendResponse(m *dns.Msg) error {
|
|
buf, err := m.Pack()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = s.sendconn.WriteTo(buf, ipv4Addr)
|
|
return err
|
|
}
|