13 "golang.org/x/net/ipv4"
14 "golang.org/x/net/ipv6"
18 "github.com/miekg/dns"
22 // Number of Multicast responses sent for a query message (default: 1 < x < 9)
23 multicastRepetitions = 2
26 // Register a service by given arguments. This call will take the system's hostname
27 // and lookup IP by that hostname.
28 func Register(instance, service, domain string, port int, text []string, ifaces []net.Interface) (*Server, error) {
29 entry := NewServiceEntry(instance, service, domain)
33 if entry.Instance == "" {
34 return nil, fmt.Errorf("Missing service instance name")
36 if entry.Service == "" {
37 return nil, fmt.Errorf("Missing service name")
39 if entry.Domain == "" {
40 entry.Domain = "local."
43 return nil, fmt.Errorf("Missing port")
47 if entry.HostName == "" {
48 entry.HostName, err = os.Hostname()
50 return nil, fmt.Errorf("Could not determine host")
54 if !strings.HasSuffix(trimDot(entry.HostName), entry.Domain) {
55 entry.HostName = fmt.Sprintf("%s.%s.", trimDot(entry.HostName), trimDot(entry.Domain))
59 ifaces = listMulticastInterfaces()
62 for _, iface := range ifaces {
63 v4, v6 := addrsForInterface(&iface)
64 entry.AddrIPv4 = append(entry.AddrIPv4, v4...)
65 entry.AddrIPv6 = append(entry.AddrIPv6, v6...)
68 if entry.AddrIPv4 == nil && entry.AddrIPv6 == nil {
69 return nil, fmt.Errorf("Could not determine host IP addresses")
72 s, err := newServer(ifaces)
84 // RegisterProxy registers a service proxy. This call will skip the hostname/IP lookup and
85 // will use the provided values.
86 func RegisterProxy(instance, service, domain string, port int, host string, ips []string, text []string, ifaces []net.Interface) (*Server, error) {
87 entry := NewServiceEntry(instance, service, domain)
92 if entry.Instance == "" {
93 return nil, fmt.Errorf("Missing service instance name")
95 if entry.Service == "" {
96 return nil, fmt.Errorf("Missing service name")
98 if entry.HostName == "" {
99 return nil, fmt.Errorf("Missing host name")
101 if entry.Domain == "" {
102 entry.Domain = "local"
105 return nil, fmt.Errorf("Missing port")
108 if !strings.HasSuffix(trimDot(entry.HostName), entry.Domain) {
109 entry.HostName = fmt.Sprintf("%s.%s.", trimDot(entry.HostName), trimDot(entry.Domain))
112 for _, ip := range ips {
113 ipAddr := net.ParseIP(ip)
115 return nil, fmt.Errorf("Failed to parse given IP: %v", ip)
116 } else if ipv4 := ipAddr.To4(); ipv4 != nil {
117 entry.AddrIPv4 = append(entry.AddrIPv4, ipAddr)
118 } else if ipv6 := ipAddr.To16(); ipv6 != nil {
119 entry.AddrIPv6 = append(entry.AddrIPv6, ipAddr)
121 return nil, fmt.Errorf("The IP is neither IPv4 nor IPv6: %#v", ipAddr)
125 if len(ifaces) == 0 {
126 ifaces = listMulticastInterfaces()
129 s, err := newServer(ifaces)
142 qClassCacheFlush uint16 = 1 << 15
145 // Server structure encapsulates both IPv4/IPv6 UDP connections
147 service *ServiceEntry
148 ipv4conn *ipv4.PacketConn
149 ipv6conn *ipv6.PacketConn
150 ifaces []net.Interface
152 shouldShutdown chan struct{}
153 shutdownLock sync.Mutex
154 shutdownEnd sync.WaitGroup
159 // Constructs server structure
160 func newServer(ifaces []net.Interface) (*Server, error) {
161 ipv4conn, err4 := joinUdp4Multicast(ifaces)
163 log.Printf("[zeroconf] no suitable IPv4 interface: %s", err4.Error())
165 ipv6conn, err6 := joinUdp6Multicast(ifaces)
167 log.Printf("[zeroconf] no suitable IPv6 interface: %s", err6.Error())
169 if err4 != nil && err6 != nil {
170 // No supported interface left.
171 return nil, fmt.Errorf("No supported interface")
179 shouldShutdown: make(chan struct{}),
185 // Start listeners and waits for the shutdown signal from exit channel
186 func (s *Server) mainloop() {
187 if s.ipv4conn != nil {
188 go s.recv4(s.ipv4conn)
190 if s.ipv6conn != nil {
191 go s.recv6(s.ipv6conn)
195 // Shutdown closes all udp connections and unregisters the service
196 func (s *Server) Shutdown() {
200 // SetText updates and announces the TXT records
201 func (s *Server) SetText(text []string) {
202 s.service.Text = text
206 // TTL sets the TTL for DNS replies
207 func (s *Server) TTL(ttl uint32) {
211 // Shutdown server will close currently open connections & channel
212 func (s *Server) shutdown() error {
213 s.shutdownLock.Lock()
214 defer s.shutdownLock.Unlock()
216 return errors.New("Server is already shutdown")
219 err := s.unregister()
224 close(s.shouldShutdown)
226 if s.ipv4conn != nil {
229 if s.ipv6conn != nil {
233 // Wait for connection and routines to be closed
240 // recv is a long running routine to receive packets from an interface
241 func (s *Server) recv4(c *ipv4.PacketConn) {
245 buf := make([]byte, 65536)
247 defer s.shutdownEnd.Done()
250 case <-s.shouldShutdown:
254 n, cm, from, err := c.ReadFrom(buf)
261 if err := s.parsePacket(buf[:n], ifIndex, from); err != nil {
262 // log.Printf("[ERR] zeroconf: failed to handle query v4: %v", err)
268 // recv is a long running routine to receive packets from an interface
269 func (s *Server) recv6(c *ipv6.PacketConn) {
273 buf := make([]byte, 65536)
275 defer s.shutdownEnd.Done()
278 case <-s.shouldShutdown:
282 n, cm, from, err := c.ReadFrom(buf)
289 if err := s.parsePacket(buf[:n], ifIndex, from); err != nil {
290 // log.Printf("[ERR] zeroconf: failed to handle query v6: %v", err)
296 // parsePacket is used to parse an incoming packet
297 func (s *Server) parsePacket(packet []byte, ifIndex int, from net.Addr) error {
299 if err := msg.Unpack(packet); err != nil {
300 // log.Printf("[ERR] zeroconf: Failed to unpack packet: %v", err)
303 return s.handleQuery(&msg, ifIndex, from)
306 // handleQuery is used to handle an incoming query
307 func (s *Server) handleQuery(query *dns.Msg, ifIndex int, from net.Addr) error {
308 // Ignore questions with authoritative section for now
309 if len(query.Ns) > 0 {
313 // Handle each question
315 for _, q := range query.Question {
319 resp.RecursionDesired = false
320 resp.Authoritative = true
321 resp.Question = nil // RFC6762 section 6 "responses MUST NOT contain any questions"
322 resp.Answer = []dns.RR{}
323 resp.Extra = []dns.RR{}
324 if err = s.handleQuestion(q, &resp, query, ifIndex); err != nil {
325 // log.Printf("[ERR] zeroconf: failed to handle question %v: %v", q, err)
328 // Check if there is an answer
329 if len(resp.Answer) == 0 {
333 if isUnicastQuestion(q) {
335 if e := s.unicastResponse(&resp, ifIndex, from); e != nil {
340 if e := s.multicastResponse(&resp, ifIndex); e != nil {
349 // RFC6762 7.1. Known-Answer Suppression
350 func isKnownAnswer(resp *dns.Msg, query *dns.Msg) bool {
351 if len(resp.Answer) == 0 || len(query.Answer) == 0 {
355 if resp.Answer[0].Header().Rrtype != dns.TypePTR {
358 answer := resp.Answer[0].(*dns.PTR)
360 for _, known := range query.Answer {
361 hdr := known.Header()
362 if hdr.Rrtype != answer.Hdr.Rrtype {
365 ptr := known.(*dns.PTR)
366 if ptr.Ptr == answer.Ptr && hdr.Ttl >= answer.Hdr.Ttl/2 {
367 // log.Printf("skipping known answer: %v", ptr)
375 // handleQuestion is used to handle an incoming question
376 func (s *Server) handleQuestion(q dns.Question, resp *dns.Msg, query *dns.Msg, ifIndex int) error {
377 if s.service == nil {
382 case s.service.ServiceTypeName():
383 s.serviceTypeName(resp, s.ttl)
384 if isKnownAnswer(resp, query) {
388 case s.service.ServiceName():
389 s.composeBrowsingAnswers(resp, ifIndex)
390 if isKnownAnswer(resp, query) {
394 case s.service.ServiceInstanceName():
395 s.composeLookupAnswers(resp, s.ttl, ifIndex, false)
401 func (s *Server) composeBrowsingAnswers(resp *dns.Msg, ifIndex int) {
404 Name: s.service.ServiceName(),
406 Class: dns.ClassINET,
409 Ptr: s.service.ServiceInstanceName(),
411 resp.Answer = append(resp.Answer, ptr)
415 Name: s.service.ServiceInstanceName(),
417 Class: dns.ClassINET,
424 Name: s.service.ServiceInstanceName(),
426 Class: dns.ClassINET,
431 Port: uint16(s.service.Port),
432 Target: s.service.HostName,
434 resp.Extra = append(resp.Extra, srv, txt)
436 resp.Extra = s.appendAddrs(resp.Extra, s.ttl, ifIndex, false)
439 func (s *Server) composeLookupAnswers(resp *dns.Msg, ttl uint32, ifIndex int, flushCache bool) {
441 // The most significant bit of the rrclass for a record in the Answer
442 // Section of a response message is the Multicast DNS cache-flush bit
443 // and is discussed in more detail below in Section 10.2, "Announcements
444 // to Flush Outdated Cache Entries".
447 Name: s.service.ServiceName(),
449 Class: dns.ClassINET,
452 Ptr: s.service.ServiceInstanceName(),
456 Name: s.service.ServiceInstanceName(),
458 Class: dns.ClassINET | qClassCacheFlush,
463 Port: uint16(s.service.Port),
464 Target: s.service.HostName,
468 Name: s.service.ServiceInstanceName(),
470 Class: dns.ClassINET | qClassCacheFlush,
477 Name: s.service.ServiceTypeName(),
479 Class: dns.ClassINET,
482 Ptr: s.service.ServiceName(),
484 resp.Answer = append(resp.Answer, srv, txt, ptr, dnssd)
486 resp.Answer = s.appendAddrs(resp.Answer, ttl, ifIndex, flushCache)
489 func (s *Server) serviceTypeName(resp *dns.Msg, ttl uint32) {
491 // 9. Service Type Enumeration
493 // For this purpose, a special meta-query is defined. A DNS query for
494 // PTR records with the name "_services._dns-sd._udp.<Domain>" yields a
495 // set of PTR records, where the rdata of each PTR record is the two-
496 // label <Service> name, plus the same domain, e.g.,
497 // "_http._tcp.<Domain>".
500 Name: s.service.ServiceTypeName(),
502 Class: dns.ClassINET,
505 Ptr: s.service.ServiceName(),
507 resp.Answer = append(resp.Answer, dnssd)
510 // Perform probing & announcement
511 //TODO: implement a proper probing & conflict resolution
512 func (s *Server) probe() {
514 q.SetQuestion(s.service.ServiceInstanceName(), dns.TypePTR)
515 q.RecursionDesired = false
519 Name: s.service.ServiceInstanceName(),
521 Class: dns.ClassINET,
526 Port: uint16(s.service.Port),
527 Target: s.service.HostName,
531 Name: s.service.ServiceInstanceName(),
533 Class: dns.ClassINET,
538 q.Ns = []dns.RR{srv, txt}
540 randomizer := rand.New(rand.NewSource(time.Now().UnixNano()))
542 for i := 0; i < multicastRepetitions; i++ {
543 if err := s.multicastResponse(q, 0); err != nil {
544 log.Println("[ERR] zeroconf: failed to send probe:", err.Error())
546 time.Sleep(time.Duration(randomizer.Intn(250)) * time.Millisecond)
550 // The Multicast DNS responder MUST send at least two unsolicited
551 // responses, one second apart. To provide increased robustness against
552 // packet loss, a responder MAY send up to eight unsolicited responses,
553 // provided that the interval between unsolicited responses increases by
554 // at least a factor of two with every response sent.
555 timeout := 1 * time.Second
556 for i := 0; i < multicastRepetitions; i++ {
557 for _, intf := range s.ifaces {
559 resp.MsgHdr.Response = true
560 // TODO: make response authoritative if we are the publisher
562 resp.Answer = []dns.RR{}
563 resp.Extra = []dns.RR{}
564 s.composeLookupAnswers(resp, s.ttl, intf.Index, true)
565 if err := s.multicastResponse(resp, intf.Index); err != nil {
566 log.Println("[ERR] zeroconf: failed to send announcement:", err.Error())
574 // announceText sends a Text announcement with cache flush enabled
575 func (s *Server) announceText() {
577 resp.MsgHdr.Response = true
581 Name: s.service.ServiceInstanceName(),
583 Class: dns.ClassINET | qClassCacheFlush,
589 resp.Answer = []dns.RR{txt}
590 s.multicastResponse(resp, 0)
593 func (s *Server) unregister() error {
595 resp.MsgHdr.Response = true
596 resp.Answer = []dns.RR{}
597 resp.Extra = []dns.RR{}
598 s.composeLookupAnswers(resp, 0, 0, true)
599 return s.multicastResponse(resp, 0)
602 func (s *Server) appendAddrs(list []dns.RR, ttl uint32, ifIndex int, flushCache bool) []dns.RR {
603 v4 := s.service.AddrIPv4
604 v6 := s.service.AddrIPv6
605 if len(v4) == 0 && len(v6) == 0 {
606 iface, _ := net.InterfaceByIndex(ifIndex)
608 a4, a6 := addrsForInterface(iface)
609 v4 = append(v4, a4...)
610 v6 = append(v6, a6...)
614 // RFC6762 Section 10 says A/AAAA records SHOULD
615 // use TTL of 120s, to account for network interface
616 // and IP address changes.
619 var cacheFlushBit uint16
621 cacheFlushBit = qClassCacheFlush
623 for _, ipv4 := range v4 {
626 Name: s.service.HostName,
628 Class: dns.ClassINET | cacheFlushBit,
633 list = append(list, a)
635 for _, ipv6 := range v6 {
638 Name: s.service.HostName,
639 Rrtype: dns.TypeAAAA,
640 Class: dns.ClassINET | cacheFlushBit,
645 list = append(list, aaaa)
650 func addrsForInterface(iface *net.Interface) ([]net.IP, []net.IP) {
651 var v4, v6, v6local []net.IP
652 addrs, _ := iface.Addrs()
653 for _, address := range addrs {
654 if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
655 if ipnet.IP.To4() != nil {
656 v4 = append(v4, ipnet.IP)
658 switch ip := ipnet.IP.To16(); ip != nil {
659 case ip.IsGlobalUnicast():
660 v6 = append(v6, ipnet.IP)
661 case ip.IsLinkLocalUnicast():
662 v6local = append(v6local, ipnet.IP)
673 // unicastResponse is used to send a unicast response packet
674 func (s *Server) unicastResponse(resp *dns.Msg, ifIndex int, from net.Addr) error {
675 buf, err := resp.Pack()
679 addr := from.(*net.UDPAddr)
680 if addr.IP.To4() != nil {
682 var wcm ipv4.ControlMessage
683 wcm.IfIndex = ifIndex
684 _, err = s.ipv4conn.WriteTo(buf, &wcm, addr)
686 _, err = s.ipv4conn.WriteTo(buf, nil, addr)
691 var wcm ipv6.ControlMessage
692 wcm.IfIndex = ifIndex
693 _, err = s.ipv6conn.WriteTo(buf, &wcm, addr)
695 _, err = s.ipv6conn.WriteTo(buf, nil, addr)
701 // multicastResponse us used to send a multicast response packet
702 func (s *Server) multicastResponse(msg *dns.Msg, ifIndex int) error {
703 buf, err := msg.Pack()
707 if s.ipv4conn != nil {
708 var wcm ipv4.ControlMessage
710 wcm.IfIndex = ifIndex
711 s.ipv4conn.WriteTo(buf, &wcm, ipv4Addr)
713 for _, intf := range s.ifaces {
714 wcm.IfIndex = intf.Index
715 s.ipv4conn.WriteTo(buf, &wcm, ipv4Addr)
720 if s.ipv6conn != nil {
721 var wcm ipv6.ControlMessage
723 wcm.IfIndex = ifIndex
724 s.ipv6conn.WriteTo(buf, &wcm, ipv6Addr)
726 for _, intf := range s.ifaces {
727 wcm.IfIndex = intf.Index
728 s.ipv6conn.WriteTo(buf, &wcm, ipv6Addr)
735 func isUnicastQuestion(q dns.Question) bool {
737 // 18.12. Repurposing of Top Bit of qclass in Question Section
739 // In the Question Section of a Multicast DNS query, the top bit of the
740 // qclass field is used to indicate that unicast responses are preferred
741 // for this particular question. (See Section 5.4.)
742 return q.Qclass&qClassCacheFlush != 0