Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

General Fixes #85

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 14 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
ZeroConf: Service Discovery with mDNS
=====================================
# ZeroConf: Service Discovery with mDNS

ZeroConf is a pure Golang library that employs Multicast DNS-SD for

* browsing and resolving services in your network
Expand All @@ -13,17 +13,20 @@ It basically implements aspects of the standards
Though it does not support all requirements yet, the aim is to provide a compliant solution in the long-term with the community.

By now, it should be compatible to [Avahi](http://avahi.org/) (tested) and Apple's Bonjour (untested).
Target environments: private LAN/Wifi, small or isolated networks.
Target environments: private LAN/WiFi, small or isolated networks.

[![GoDoc](https://godoc.org/github.com/grandcat/zeroconf?status.svg)](https://godoc.org/github.com/grandcat/zeroconf)
[![Go Report Card](https://goreportcard.com/badge/github.com/grandcat/zeroconf)](https://goreportcard.com/report/github.com/grandcat/zeroconf)
[![Build Status](https://travis-ci.com/grandcat/zeroconf.svg?branch=master)](https://travis-ci.com/grandcat/zeroconf)

## Install

Nothing is as easy as that:

```bash
$ go get -u github.com/grandcat/zeroconf
go get -u github.com/grandcat/zeroconf
```

This package requires **Go 1.7** (context in std lib) or later.

## Browse for services in your local network
Expand Down Expand Up @@ -52,9 +55,10 @@ if err != nil {

<-ctx.Done()
```

A subtype may added to service name to narrow the set of results. E.g. to browse `_workstation._tcp` with subtype `_windows`, use`_workstation._tcp,_windows`.

See https://github.com/grandcat/zeroconf/blob/master/examples/resolv/client.go.
See [examples/resolv/client.go](examples/resolv/client.go).

## Lookup a specific service instance

Expand Down Expand Up @@ -83,11 +87,13 @@ case <-time.After(time.Second * 120):

log.Println("Shutting down.")
```

Multiple subtypes may be added to service name, separated by commas. E.g `_workstation._tcp,_windows` has subtype `_windows`.

See https://github.com/grandcat/zeroconf/blob/master/examples/register/server.go.
See [examples/register/server.go](examples/register/server.go).

## Features and ToDo's

This list gives a quick impression about the state of this library.
See what needs to be done and submit a pull request :)

Expand All @@ -103,11 +109,12 @@ _Notes:_
Some tests showed improvements in overall robustness and performance with the features enabled.

## Credits

Great thanks to [hashicorp](https://github.com/hashicorp/mdns) and to [oleksandr](https://github.com/oleksandr/bonjour) and all contributing authors for the code this projects bases upon.
Large parts of the code are still the same.

However, there are several reasons why I decided to create a fork of the original project:
The previous project seems to be unmaintained. There are several useful pull requests waiting. I merged most of them in this project.
Still, the implementation has some bugs and lacks some other features that make it quite unreliable in real LAN environments when running continously.
Still, the implementation has some bugs and lacks some other features that make it quite unreliable in real LAN environments when running continuously.
Last but not least, the aim for this project is to build a solution that targets standard conformance in the long term with the support of the community.
Though, resiliency should remain a top goal.
28 changes: 15 additions & 13 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
)

// IPType specifies the IP traffic the client listens for.
// This does not guarantee that only mDNS entries of this sepcific
// This does not guarantee that only mDNS entries of this specific
// type passes. E.g. typical mDNS packets distributed via IPv4, often contain
// both DNS A and AAAA entries.
type IPType uint8
Expand All @@ -31,12 +31,12 @@ type clientOpts struct {
ifaces []net.Interface
}

// ClientOption fills the option struct to configure intefaces, etc.
// ClientOption fills the option struct to configure interfaces, etc.
type ClientOption func(*clientOpts)

// SelectIPTraffic selects the type of IP packets (IPv4, IPv6, or both) this
// instance listens for.
// This does not guarantee that only mDNS entries of this sepcific
// This does not guarantee that only mDNS entries of this specific
// type passes. E.g. typical mDNS packets distributed via IPv4, may contain
// both DNS A and AAAA entries.
func SelectIPTraffic(t IPType) ClientOption {
Expand Down Expand Up @@ -154,7 +154,7 @@ func newClient(opts clientOpts) (*client, error) {
var ipv4conn *ipv4.PacketConn
if (opts.listenOn & IPv4) > 0 {
var err error
ipv4conn, err = joinUdp4Multicast(ifaces)
ipv4conn, err = joinUDP4Multicast(ifaces)
if err != nil {
return nil, err
}
Expand All @@ -163,7 +163,7 @@ func newClient(opts clientOpts) (*client, error) {
var ipv6conn *ipv6.PacketConn
if (opts.listenOn & IPv6) > 0 {
var err error
ipv6conn, err = joinUdp6Multicast(ifaces)
ipv6conn, err = joinUDP6Multicast(ifaces)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -295,8 +295,6 @@ func (c *client) mainloop(ctx context.Context, params *LookupParams) {
sentEntries[k] = e
params.disableProbing()
}
// reset entries
entries = make(map[string]*ServiceEntry)
}
}
}
Expand Down Expand Up @@ -413,8 +411,8 @@ func (c *client) query(params *LookupParams) error {
if params.Instance != "" { // service instance name lookup
serviceInstanceName = fmt.Sprintf("%s.%s", params.Instance, serviceName)
m.Question = []dns.Question{
dns.Question{serviceInstanceName, dns.TypeSRV, dns.ClassINET},
dns.Question{serviceInstanceName, dns.TypeTXT, dns.ClassINET},
{Name: serviceInstanceName, Qtype: dns.TypeSRV, Qclass: dns.ClassINET},
{Name: serviceInstanceName, Qtype: dns.TypeTXT, Qclass: dns.ClassINET},
}
} else if len(params.Subtypes) > 0 { // service subtype browse
m.SetQuestion(params.Subtypes[0], dns.TypePTR)
Expand All @@ -438,15 +436,19 @@ func (c *client) sendQuery(msg *dns.Msg) error {
if c.ipv4conn != nil {
var wcm ipv4.ControlMessage
for ifi := range c.ifaces {
wcm.IfIndex = c.ifaces[ifi].Index
c.ipv4conn.WriteTo(buf, &wcm, ipv4Addr)
if c.ifaces[ifi].Flags&net.FlagUp != 0 {
wcm.IfIndex = c.ifaces[ifi].Index
c.ipv4conn.WriteTo(buf, &wcm, ipv4Addr)
}
}
}
if c.ipv6conn != nil {
var wcm ipv6.ControlMessage
for ifi := range c.ifaces {
wcm.IfIndex = c.ifaces[ifi].Index
c.ipv6conn.WriteTo(buf, &wcm, ipv6Addr)
if c.ifaces[ifi].Flags&net.FlagUp != 0 {
wcm.IfIndex = c.ifaces[ifi].Index
c.ipv6conn.WriteTo(buf, &wcm, ipv6Addr)
}
}
}
return nil
Expand Down
16 changes: 11 additions & 5 deletions connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,18 @@ var (
}
)

func joinUdp6Multicast(interfaces []net.Interface) (*ipv6.PacketConn, error) {
func joinUDP6Multicast(interfaces []net.Interface) (*ipv6.PacketConn, error) {
udpConn, err := net.ListenUDP("udp6", mdnsWildcardAddrIPv6)
if err != nil {
return nil, err
}

// Join multicast groups to receive announcements
pkConn := ipv6.NewPacketConn(udpConn)
pkConn.SetControlMessage(ipv6.FlagInterface, true)
err = pkConn.SetControlMessage(ipv6.FlagInterface, true)
if err != nil {
return nil, err
}

if len(interfaces) == 0 {
interfaces = listMulticastInterfaces()
Expand All @@ -65,16 +68,19 @@ func joinUdp6Multicast(interfaces []net.Interface) (*ipv6.PacketConn, error) {
return pkConn, nil
}

func joinUdp4Multicast(interfaces []net.Interface) (*ipv4.PacketConn, error) {
func joinUDP4Multicast(interfaces []net.Interface) (*ipv4.PacketConn, error) {
udpConn, err := net.ListenUDP("udp4", mdnsWildcardAddrIPv4)
if err != nil {
// log.Printf("[ERR] bonjour: Failed to bind to udp4 mutlicast: %v", err)
// log.Printf("[ERR] bonjour: Failed to bind to udp4 multicast: %v", err)
return nil, err
}

// Join multicast groups to receive announcements
pkConn := ipv4.NewPacketConn(udpConn)
pkConn.SetControlMessage(ipv4.FlagInterface, true)
err = pkConn.SetControlMessage(ipv4.FlagInterface, true)
if err != nil {
return nil, err
}

if len(interfaces) == 0 {
interfaces = listMulticastInterfaces()
Expand Down
2 changes: 1 addition & 1 deletion doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// RFC 6762 (mDNS) and
// RFC 6763 (DNS-SD).
// Though it does not support all requirements yet, the aim is to provide a
// complient solution in the long-term with the community.
// compliant solution in the long-term with the community.
//
// By now, it should be compatible to [Avahi](http://avahi.org/) (tested) and
// Apple's Bonjour (untested). Should work in the most office, home and private
Expand Down
58 changes: 29 additions & 29 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,23 @@ func Register(instance, service, domain string, port int, text []string, ifaces
entry.Text = text

if entry.Instance == "" {
return nil, fmt.Errorf("Missing service instance name")
return nil, fmt.Errorf("missing service instance name")
}
if entry.Service == "" {
return nil, fmt.Errorf("Missing service name")
return nil, fmt.Errorf("missing service name")
}
if entry.Domain == "" {
entry.Domain = "local."
}
if entry.Port == 0 {
return nil, fmt.Errorf("Missing port")
return nil, fmt.Errorf("missing port")
}

var err error
if entry.HostName == "" {
entry.HostName, err = os.Hostname()
if err != nil {
return nil, fmt.Errorf("Could not determine host")
return nil, fmt.Errorf("could not determine host")
}
}

Expand All @@ -64,7 +64,7 @@ func Register(instance, service, domain string, port int, text []string, ifaces
}

if entry.AddrIPv4 == nil && entry.AddrIPv6 == nil {
return nil, fmt.Errorf("Could not determine host IP addresses")
return nil, fmt.Errorf("could not determine host IP addresses")
}

s, err := newServer(ifaces)
Expand All @@ -88,19 +88,19 @@ func RegisterProxy(instance, service, domain string, port int, host string, ips
entry.HostName = host

if entry.Instance == "" {
return nil, fmt.Errorf("Missing service instance name")
return nil, fmt.Errorf("missing service instance name")
}
if entry.Service == "" {
return nil, fmt.Errorf("Missing service name")
return nil, fmt.Errorf("missing service name")
}
if entry.HostName == "" {
return nil, fmt.Errorf("Missing host name")
return nil, fmt.Errorf("missing host name")
}
if entry.Domain == "" {
entry.Domain = "local"
}
if entry.Port == 0 {
return nil, fmt.Errorf("Missing port")
return nil, fmt.Errorf("missing port")
}

if !strings.HasSuffix(trimDot(entry.HostName), entry.Domain) {
Expand All @@ -110,13 +110,13 @@ func RegisterProxy(instance, service, domain string, port int, host string, ips
for _, ip := range ips {
ipAddr := net.ParseIP(ip)
if ipAddr == nil {
return nil, fmt.Errorf("Failed to parse given IP: %v", ip)
return nil, fmt.Errorf("failed to parse given IP: %v", ip)
} else if ipv4 := ipAddr.To4(); ipv4 != nil {
entry.AddrIPv4 = append(entry.AddrIPv4, ipAddr)
} else if ipv6 := ipAddr.To16(); ipv6 != nil {
entry.AddrIPv6 = append(entry.AddrIPv6, ipAddr)
} else {
return nil, fmt.Errorf("The IP is neither IPv4 nor IPv6: %#v", ipAddr)
return nil, fmt.Errorf("the IP is neither IPv4 nor IPv6: %#v", ipAddr)
}
}

Expand Down Expand Up @@ -156,17 +156,17 @@ type Server struct {

// Constructs server structure
func newServer(ifaces []net.Interface) (*Server, error) {
ipv4conn, err4 := joinUdp4Multicast(ifaces)
ipv4conn, err4 := joinUDP4Multicast(ifaces)
if err4 != nil {
log.Printf("[zeroconf] no suitable IPv4 interface: %s", err4.Error())
}
ipv6conn, err6 := joinUdp6Multicast(ifaces)
ipv6conn, err6 := joinUDP6Multicast(ifaces)
if err6 != nil {
log.Printf("[zeroconf] no suitable IPv6 interface: %s", err6.Error())
}
if err4 != nil && err6 != nil {
// No supported interface left.
return nil, fmt.Errorf("No supported interface")
return nil, fmt.Errorf("no supported interface")
}

s := &Server{
Expand All @@ -191,14 +191,14 @@ func (s *Server) mainloop() {
}

// Shutdown closes all udp connections and unregisters the service
func (s *Server) Shutdown() {
s.shutdown()
func (s *Server) Shutdown() error {
return s.shutdown()
}

// SetText updates and announces the TXT records
func (s *Server) SetText(text []string) {
func (s *Server) SetText(text []string) error {
s.service.Text = text
s.announceText()
return s.announceText()
}

// TTL sets the TTL for DNS replies
Expand Down Expand Up @@ -331,7 +331,7 @@ func (s *Server) handleQuery(query *dns.Msg, ifIndex int, from net.Addr) error {
err = e
}
} else {
// Send mulicast
// Send multicast
if e := s.multicastResponse(&resp, ifIndex); e != nil {
err = e
}
Expand Down Expand Up @@ -592,7 +592,7 @@ func (s *Server) probe() {
}

// announceText sends a Text announcement with cache flush enabled
func (s *Server) announceText() {
func (s *Server) announceText() error {
resp := new(dns.Msg)
resp.MsgHdr.Response = true

Expand All @@ -607,7 +607,7 @@ func (s *Server) announceText() {
}

resp.Answer = []dns.RR{txt}
s.multicastResponse(resp, 0)
return s.multicastResponse(resp, 0)
}

func (s *Server) unregister() error {
Expand Down Expand Up @@ -706,16 +706,16 @@ func (s *Server) unicastResponse(resp *dns.Msg, ifIndex int, from net.Addr) erro
_, err = s.ipv4conn.WriteTo(buf, nil, addr)
}
return err
}
if ifIndex != 0 {
var wcm ipv6.ControlMessage
wcm.IfIndex = ifIndex
_, err = s.ipv6conn.WriteTo(buf, &wcm, addr)
} else {
if ifIndex != 0 {
var wcm ipv6.ControlMessage
wcm.IfIndex = ifIndex
_, err = s.ipv6conn.WriteTo(buf, &wcm, addr)
} else {
_, err = s.ipv6conn.WriteTo(buf, nil, addr)
}
return err
_, err = s.ipv6conn.WriteTo(buf, nil, addr)
}
return err

}

// multicastResponse us used to send a multicast response packet
Expand Down
Loading