Skip to content

Commit

Permalink
use libp2p/zeroconf for mDNS discovery
Browse files Browse the repository at this point in the history
  • Loading branch information
marten-seemann committed Aug 17, 2021
1 parent bb33361 commit 0325789
Show file tree
Hide file tree
Showing 13 changed files with 361 additions and 20 deletions.
6 changes: 3 additions & 3 deletions examples/chat-with-mdns/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,12 @@ func handleStream(stream net.Stream) {

3. **Find peers nearby using mdns**

Start [mdns discovery](https://godoc.org/github.com/libp2p/go-libp2p/p2p/discovery_legacy#NewMdnsService) service in host.
Start [mdns discovery](https://godoc.org/github.com/libp2p/go-libp2p/p2p/discovery#NewMdnsService) service in host.

```go
ser, err := discovery.NewMdnsService(ctx, peerhost, time.Hour, rendezvous)
ser, err := discovery.NewMdnsService(peerhost, rendezvous)
```
register [Notifee interface](https://godoc.org/github.com/libp2p/go-libp2p/p2p/discovery_legacy#Notifee) with service so that we get notified about peer discovery
register [Notifee interface](https://godoc.org/github.com/libp2p/go-libp2p/p2p/discovery#Notifee) with service so that we get notified about peer discovery

```go
n := &discoveryNotifee{}
Expand Down
1 change: 1 addition & 0 deletions examples/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ require (
github.com/libp2p/go-libp2p-quic-transport v0.11.2
github.com/libp2p/go-libp2p-swarm v0.5.3
github.com/libp2p/go-libp2p-tls v0.2.0
github.com/libp2p/zeroconf/v2 v2.0.0 // indirect
github.com/multiformats/go-multiaddr v0.4.0
)

Expand Down
5 changes: 5 additions & 0 deletions examples/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,10 @@ github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtE
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ=
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
Expand Down Expand Up @@ -558,6 +561,8 @@ github.com/libp2p/go-yamux v1.4.1 h1:P1Fe9vF4th5JOxxgQvfbOHkrGqIZniTLf+ddhZp8YTI
github.com/libp2p/go-yamux v1.4.1/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE=
github.com/libp2p/go-yamux/v2 v2.2.0 h1:RwtpYZ2/wVviZ5+3pjC8qdQ4TKnrak0/E01N1UWoAFU=
github.com/libp2p/go-yamux/v2 v2.2.0/go.mod h1:3So6P6TV6r75R9jiBpiIKgU/66lOarCZjqROGxzPpPQ=
github.com/libp2p/zeroconf/v2 v2.0.0 h1:qYAHAqUVh4hMSfu+iDTZNqH07wLGAvb1+DW4Tx/qUoQ=
github.com/libp2p/zeroconf/v2 v2.0.0/go.mod h1:J85R/d9joD8u8F9aHM8pBXygtG9W02enEwS+wWeL6yo=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/lucas-clemente/quic-go v0.21.2 h1:8LqqL7nBQFDUINadW0fHV/xSaCQJgmJC0Gv+qUnjd78=
Expand Down
2 changes: 1 addition & 1 deletion examples/ipfs-camp-2019/06-Pubsub/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
mplex "github.com/libp2p/go-libp2p-mplex"
tls "github.com/libp2p/go-libp2p-tls"
yamux "github.com/libp2p/go-libp2p-yamux"
discovery "github.com/libp2p/go-libp2p/p2p/discovery_legacy"
"github.com/libp2p/go-libp2p/p2p/discovery"
tcp "github.com/libp2p/go-tcp-transport"
ws "github.com/libp2p/go-ws-transport"
"github.com/multiformats/go-multiaddr"
Expand Down
2 changes: 1 addition & 1 deletion examples/ipfs-camp-2019/07-Messaging/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
pubsub "github.com/libp2p/go-libp2p-pubsub"
tls "github.com/libp2p/go-libp2p-tls"
yamux "github.com/libp2p/go-libp2p-yamux"
discovery "github.com/libp2p/go-libp2p/p2p/discovery_legacy"
"github.com/libp2p/go-libp2p/p2p/discovery"
"github.com/libp2p/go-tcp-transport"
ws "github.com/libp2p/go-ws-transport"
"github.com/multiformats/go-multiaddr"
Expand Down
2 changes: 1 addition & 1 deletion examples/ipfs-camp-2019/08-End/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
pubsub "github.com/libp2p/go-libp2p-pubsub"
tls "github.com/libp2p/go-libp2p-tls"
yamux "github.com/libp2p/go-libp2p-yamux"
discovery "github.com/libp2p/go-libp2p/p2p/discovery_legacy"
"github.com/libp2p/go-libp2p/p2p/discovery"
"github.com/libp2p/go-tcp-transport"
ws "github.com/libp2p/go-ws-transport"
"github.com/multiformats/go-multiaddr"
Expand Down
2 changes: 1 addition & 1 deletion examples/pubsub/chat/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (

"github.com/libp2p/go-libp2p"
"github.com/libp2p/go-libp2p-core/peer"
discovery "github.com/libp2p/go-libp2p/p2p/discovery_legacy"
"github.com/libp2p/go-libp2p/p2p/discovery"

"github.com/libp2p/go-libp2p-core/host"

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ require (
github.com/libp2p/go-stream-muxer-multistream v0.3.0
github.com/libp2p/go-tcp-transport v0.2.7
github.com/libp2p/go-ws-transport v0.5.0
github.com/libp2p/zeroconf/v2 v2.0.0
github.com/miekg/dns v1.1.43 // indirect
github.com/multiformats/go-multiaddr v0.4.0
github.com/multiformats/go-multiaddr-dns v0.3.1
Expand Down
5 changes: 5 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,10 @@ github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtE
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ=
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
Expand Down Expand Up @@ -598,6 +601,8 @@ github.com/libp2p/go-yamux v1.4.1 h1:P1Fe9vF4th5JOxxgQvfbOHkrGqIZniTLf+ddhZp8YTI
github.com/libp2p/go-yamux v1.4.1/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE=
github.com/libp2p/go-yamux/v2 v2.2.0 h1:RwtpYZ2/wVviZ5+3pjC8qdQ4TKnrak0/E01N1UWoAFU=
github.com/libp2p/go-yamux/v2 v2.2.0/go.mod h1:3So6P6TV6r75R9jiBpiIKgU/66lOarCZjqROGxzPpPQ=
github.com/libp2p/zeroconf/v2 v2.0.0 h1:qYAHAqUVh4hMSfu+iDTZNqH07wLGAvb1+DW4Tx/qUoQ=
github.com/libp2p/zeroconf/v2 v2.0.0/go.mod h1:J85R/d9joD8u8F9aHM8pBXygtG9W02enEwS+wWeL6yo=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/lucas-clemente/quic-go v0.21.2 h1:8LqqL7nBQFDUINadW0fHV/xSaCQJgmJC0Gv+qUnjd78=
Expand Down
218 changes: 218 additions & 0 deletions p2p/discovery/mdns.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
package discovery

import (
"context"
"errors"
"io"
"net"
"strings"
"sync"

logging "github.com/ipfs/go-log/v2"
"github.com/libp2p/go-libp2p-core/host"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/libp2p/zeroconf/v2"

ma "github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr/net"
)

const (
ServiceName = "_p2p._udp"
mdnsDomain = "local"
dnsaddrPrefix = "dnsaddr="
)

var log = logging.Logger("mdns")

type Service interface {
io.Closer
RegisterNotifee(Notifee)
UnregisterNotifee(Notifee)
}

type Notifee interface {
HandlePeerFound(peer.AddrInfo)
}

type mdnsService struct {
host host.Host
serviceName string

// This ctx is passed to the resolver.
// It is closed when Close() is called.
ctx context.Context
ctxCancel context.CancelFunc

resolverRunning chan struct{}
server *zeroconf.Server

mutex sync.Mutex
notifees []Notifee
}

func NewMdnsService(host host.Host, serviceName string) *mdnsService {
ctx, cancel := context.WithCancel(context.Background())
if serviceName == "" {
serviceName = ServiceName
}
s := &mdnsService{
ctx: ctx,
ctxCancel: cancel,
resolverRunning: make(chan struct{}),
host: host,
serviceName: serviceName,
}
s.startServer()
s.startResolver()
return s
}

func (s *mdnsService) Close() error {
s.ctxCancel()
if s.server != nil {
s.server.Shutdown()
}
<-s.resolverRunning
return nil
}

// We don't really care about the IP addresses, but the spec (and various routers / firewalls) require us
// to send A and AAAA records.
func (s *mdnsService) getIPs(addrs []ma.Multiaddr) ([]string, error) {
var ip4, ip6 string
for _, addr := range addrs {
network, hostport, err := manet.DialArgs(addr)
if err != nil {
continue
}
host, _, err := net.SplitHostPort(hostport)
if err != nil {
continue
}
if ip4 == "" && (network == "udp4" || network == "tcp4") {
ip4 = host
} else if ip6 == "" && (network == "udp6" || network == "tcp6") {
ip6 = host
}
}
ips := make([]string, 0, 2)
if ip4 != "" {
ips = append(ips, ip4)
}
if ip6 != "" {
ips = append(ips, ip6)
}
if len(ips) == 0 {
return nil, errors.New("didn't find any IP addresses")
}
return ips, nil
}

func (s *mdnsService) mdnsInstance() string {
return string(s.host.ID())
}

func (s *mdnsService) startServer() error {
interfaceAddrs, err := s.host.Network().InterfaceListenAddresses()
if err != nil {
return err
}
addrs, err := peer.AddrInfoToP2pAddrs(&peer.AddrInfo{
ID: s.host.ID(),
Addrs: interfaceAddrs,
})
if err != nil {
return err
}
var txts []string
for _, addr := range addrs {
if manet.IsThinWaist(addr) { // don't announce circuit addresses
txts = append(txts, dnsaddrPrefix+addr.String())
}
}

ips, err := s.getIPs(addrs)
if err != nil {
return err
}

server, err := zeroconf.RegisterProxy(
s.mdnsInstance(),
s.serviceName,
mdnsDomain,
4001,
s.host.ID().Pretty(), // TODO: deals with peer IDs longer than 63 characters
ips,
txts,
nil,
)
if err != nil {
return err
}
s.server = server
return nil
}

func (s *mdnsService) startResolver() {
entryChan := make(chan *zeroconf.ServiceEntry, 1000)
go func() {
for entry := range entryChan {
// We only care about the TXT records.
// Ignore A, AAAA and PTR.
addrs := make([]ma.Multiaddr, 0, len(entry.Text)) // assume that all TXT records are dnsaddrs
for _, s := range entry.Text {
if !strings.HasPrefix(s, dnsaddrPrefix) {
log.Debug("missing dnsaddr prefix")
continue
}
addr, err := ma.NewMultiaddr(s[len(dnsaddrPrefix):])
if err != nil {
log.Debugf("failed to parse multiaddr: %s", err)
continue
}
addrs = append(addrs, addr)
}
infos, err := peer.AddrInfosFromP2pAddrs(addrs...)
if err != nil {
log.Debugf("failed to get peer info: %s", err)
continue
}
s.mutex.Lock()
for _, info := range infos {
for _, notif := range s.notifees {
go notif.HandlePeerFound(info)
}
}
s.mutex.Unlock()
}
}()
go func() {
defer close(s.resolverRunning)
if err := zeroconf.Browse(s.ctx, s.serviceName, mdnsDomain, entryChan); err != nil {
log.Debugf("zeroconf browsing failed: %s", err)
}
}()
}

func (s *mdnsService) RegisterNotifee(n Notifee) {
s.mutex.Lock()
s.notifees = append(s.notifees, n)
s.mutex.Unlock()
}

func (s *mdnsService) UnregisterNotifee(n Notifee) {
s.mutex.Lock()
defer s.mutex.Unlock()

found := -1
for i, notif := range s.notifees {
if notif == n {
found = i
break
}
}
if found != -1 {
s.notifees = append(s.notifees[:found], s.notifees[found+1:]...)
}
}
Loading

0 comments on commit 0325789

Please sign in to comment.