From 426446bd15727b941c77c6775a9b5884c2e73012 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Mon, 5 Jul 2021 20:11:56 -0700 Subject: [PATCH] run both mDNS services in parallel --- examples/chat-with-mdns/mdns.go | 6 +- examples/go.sum | 5 + examples/ipfs-camp-2019/06-Pubsub/main.go | 4 +- examples/ipfs-camp-2019/07-Messaging/main.go | 2 +- examples/ipfs-camp-2019/08-End/main.go | 2 +- examples/ipfs-camp-2019/go.sum | 5 + examples/pubsub/chat/go.sum | 5 + examples/pubsub/chat/main.go | 2 +- go.mod | 1 + go.sum | 5 + p2p/discovery/mdns.go | 191 +++++++++++++++++++ p2p/discovery/mdns_legacy.go | 23 +-- p2p/discovery/mdns_legacy_test.go | 4 +- p2p/discovery/mdns_mult.go | 52 +++++ p2p/discovery/mdns_test.go | 123 ++++++++++++ 15 files changed, 402 insertions(+), 28 deletions(-) create mode 100644 p2p/discovery/mdns.go create mode 100644 p2p/discovery/mdns_mult.go create mode 100644 p2p/discovery/mdns_test.go diff --git a/examples/chat-with-mdns/mdns.go b/examples/chat-with-mdns/mdns.go index 1471fc819f..41953d7fc9 100644 --- a/examples/chat-with-mdns/mdns.go +++ b/examples/chat-with-mdns/mdns.go @@ -13,15 +13,15 @@ type discoveryNotifee struct { PeerChan chan peer.AddrInfo } -//interface to be called when new peer is found +// interface to be called when new peer is found func (n *discoveryNotifee) HandlePeerFound(pi peer.AddrInfo) { n.PeerChan <- pi } -//Initialize the MDNS service +// Initialize the MDNS service func initMDNS(ctx context.Context, peerhost host.Host, rendezvous string) chan peer.AddrInfo { // An hour might be a long long period in practical applications. But this is fine for us - ser, err := discovery.NewMdnsService(ctx, peerhost, time.Hour, rendezvous) + ser, err := discovery.NewMdnsService(ctx, peerhost, time.Hour) if err != nil { panic(err) } diff --git a/examples/go.sum b/examples/go.sum index 55b5184eac..46327abc1c 100644 --- a/examples/go.sum +++ b/examples/go.sum @@ -61,7 +61,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= @@ -486,6 +489,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= diff --git a/examples/ipfs-camp-2019/06-Pubsub/main.go b/examples/ipfs-camp-2019/06-Pubsub/main.go index 2522db877f..1ba551a3e5 100644 --- a/examples/ipfs-camp-2019/06-Pubsub/main.go +++ b/examples/ipfs-camp-2019/06-Pubsub/main.go @@ -19,7 +19,7 @@ import ( tls "github.com/libp2p/go-libp2p-tls" yamux "github.com/libp2p/go-libp2p-yamux" "github.com/libp2p/go-libp2p/p2p/discovery" - tcp "github.com/libp2p/go-tcp-transport" + "github.com/libp2p/go-tcp-transport" ws "github.com/libp2p/go-ws-transport" "github.com/multiformats/go-multiaddr" ) @@ -102,7 +102,7 @@ func main() { fmt.Println("Connected to", targetInfo.ID) } - mdns, err := discovery.NewMdnsService(ctx, host, time.Second*10, "") + mdns, err := discovery.NewMdnsService(ctx, host, time.Second*10) if err != nil { panic(err) } diff --git a/examples/ipfs-camp-2019/07-Messaging/main.go b/examples/ipfs-camp-2019/07-Messaging/main.go index 04ef4c5502..6464edf72e 100644 --- a/examples/ipfs-camp-2019/07-Messaging/main.go +++ b/examples/ipfs-camp-2019/07-Messaging/main.go @@ -110,7 +110,7 @@ func main() { fmt.Println("Connected to", targetInfo.ID) - mdns, err := discovery.NewMdnsService(ctx, host, time.Second*10, "") + mdns, err := discovery.NewMdnsService(ctx, host, time.Second*10) if err != nil { panic(err) } diff --git a/examples/ipfs-camp-2019/08-End/main.go b/examples/ipfs-camp-2019/08-End/main.go index d9384933e1..bd410d579f 100644 --- a/examples/ipfs-camp-2019/08-End/main.go +++ b/examples/ipfs-camp-2019/08-End/main.go @@ -109,7 +109,7 @@ func main() { fmt.Println("Connected to", targetInfo.ID) - mdns, err := discovery.NewMdnsService(ctx, host, time.Second*10, "") + mdns, err := discovery.NewMdnsService(ctx, host, time.Second*10) if err != nil { panic(err) } diff --git a/examples/ipfs-camp-2019/go.sum b/examples/ipfs-camp-2019/go.sum index 43f8dfd4ec..367f783c31 100644 --- a/examples/ipfs-camp-2019/go.sum +++ b/examples/ipfs-camp-2019/go.sum @@ -62,7 +62,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= @@ -490,6 +493,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= diff --git a/examples/pubsub/chat/go.sum b/examples/pubsub/chat/go.sum index b145f8729c..bd42d0162c 100644 --- a/examples/pubsub/chat/go.sum +++ b/examples/pubsub/chat/go.sum @@ -61,7 +61,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= @@ -450,6 +453,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= diff --git a/examples/pubsub/chat/main.go b/examples/pubsub/chat/main.go index 644d497369..e52483c945 100644 --- a/examples/pubsub/chat/main.go +++ b/examples/pubsub/chat/main.go @@ -107,7 +107,7 @@ func (n *discoveryNotifee) HandlePeerFound(pi peer.AddrInfo) { // This lets us automatically discover peers on the same LAN and connect to them. func setupDiscovery(ctx context.Context, h host.Host) error { // setup mDNS discovery to find local peers - disc, err := discovery.NewMdnsService(ctx, h, DiscoveryInterval, DiscoveryServiceTag) + disc, err := discovery.NewMdnsService(ctx, h, DiscoveryInterval) if err != nil { return err } diff --git a/go.mod b/go.mod index 38cf56d5cf..d370e33738 100644 --- a/go.mod +++ b/go.mod @@ -36,6 +36,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/multiformats/go-multiaddr v0.4.0 github.com/multiformats/go-multiaddr-dns v0.3.1 github.com/multiformats/go-multistream v0.2.2 diff --git a/go.sum b/go.sum index bfd2eacb34..6561c4ed4d 100644 --- a/go.sum +++ b/go.sum @@ -61,7 +61,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= @@ -523,6 +526,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= diff --git a/p2p/discovery/mdns.go b/p2p/discovery/mdns.go new file mode 100644 index 0000000000..08231ada27 --- /dev/null +++ b/p2p/discovery/mdns.go @@ -0,0 +1,191 @@ +package discovery + +import ( + "context" + "errors" + "net" + "strings" + "sync" + + "github.com/libp2p/go-libp2p-core/host" + "github.com/libp2p/go-libp2p-core/peer" + ma "github.com/multiformats/go-multiaddr" + manet "github.com/multiformats/go-multiaddr/net" + + "github.com/libp2p/zeroconf/v2" +) + +const ( + mdnsServiceName = "_p2p._udp" + mdnsDomain = "local" + dnsaddrPrefix = "dnsaddr=" +) + +type mdnsService struct { + host host.Host + + // This ctx is passed to the resolver. + // It is closed when Close() is called. + ctx context.Context + ctxCancel context.CancelFunc + + server *zeroconf.Server + + mutex sync.Mutex + notifees []Notifee +} + +func NewMdnsServiceNew(host host.Host) *mdnsService { + ctx, cancel := context.WithCancel(context.Background()) + s := &mdnsService{ + ctx: ctx, + ctxCancel: cancel, + host: host, + } + s.startServer() + s.startResolver() + return s +} + +func (s *mdnsService) Close() error { + s.ctxCancel() + if s.server != nil { + s.server.Shutdown() + } + 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(), + mdnsServiceName, + 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() error { + 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() + } + }() + return zeroconf.Browse(s.ctx, mdnsServiceName, mdnsDomain, entryChan) +} + +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:]...) + } +} diff --git a/p2p/discovery/mdns_legacy.go b/p2p/discovery/mdns_legacy.go index 44044c7842..82a7c17c50 100644 --- a/p2p/discovery/mdns_legacy.go +++ b/p2p/discovery/mdns_legacy.go @@ -3,7 +3,6 @@ package discovery import ( "context" "errors" - "io" "net" "sync" "time" @@ -26,16 +25,6 @@ var log = logging.Logger("mdns") const ServiceTag = "_ipfs-discovery._udp" -type Service interface { - io.Closer - RegisterNotifee(Notifee) - UnregisterNotifee(Notifee) -} - -type Notifee interface { - HandlePeerFound(peer.AddrInfo) -} - type mdnsServiceLegacy struct { server *mdns.Server service *mdns.MDNSService @@ -47,6 +36,8 @@ type mdnsServiceLegacy struct { interval time.Duration } +var _ Service = &mdnsServiceLegacy{} + func getDialableListenAddrs(ph host.Host) ([]*net.TCPAddr, error) { var out []*net.TCPAddr addrs, err := ph.Network().InterfaceListenAddresses() @@ -69,8 +60,7 @@ func getDialableListenAddrs(ph host.Host) ([]*net.TCPAddr, error) { return out, nil } -func NewMdnsService(ctx context.Context, peerhost host.Host, interval time.Duration, serviceTag string) (Service, error) { - +func NewMdnsServiceLegacy(ctx context.Context, peerhost host.Host, interval time.Duration) (Service, error) { var ipaddrs []net.IP port := 4001 @@ -87,10 +77,7 @@ func NewMdnsService(ctx context.Context, peerhost host.Host, interval time.Durat myid := peerhost.ID().Pretty() info := []string{myid} - if serviceTag == "" { - serviceTag = ServiceTag - } - service, err := mdns.NewMDNSService(myid, serviceTag, "", "", port, ipaddrs, info) + service, err := mdns.NewMDNSService(myid, ServiceTag, "", "", port, ipaddrs, info) if err != nil { return nil, err } @@ -106,7 +93,7 @@ func NewMdnsService(ctx context.Context, peerhost host.Host, interval time.Durat service: service, host: peerhost, interval: interval, - tag: serviceTag, + tag: ServiceTag, } go s.pollForEntries(ctx) diff --git a/p2p/discovery/mdns_legacy_test.go b/p2p/discovery/mdns_legacy_test.go index 8f1e5bec91..0d82357cc9 100644 --- a/p2p/discovery/mdns_legacy_test.go +++ b/p2p/discovery/mdns_legacy_test.go @@ -30,12 +30,12 @@ func TestMdnsDiscovery(t *testing.T) { a := bhost.New(swarmt.GenSwarm(t, ctx)) b := bhost.New(swarmt.GenSwarm(t, ctx)) - sa, err := NewMdnsService(ctx, a, time.Second, "someTag") + sa, err := NewMdnsService(ctx, a, time.Second) if err != nil { t.Fatal(err) } - sb, err := NewMdnsService(ctx, b, time.Second, "someTag") + sb, err := NewMdnsService(ctx, b, time.Second) if err != nil { t.Fatal(err) } diff --git a/p2p/discovery/mdns_mult.go b/p2p/discovery/mdns_mult.go new file mode 100644 index 0000000000..285e15fbdc --- /dev/null +++ b/p2p/discovery/mdns_mult.go @@ -0,0 +1,52 @@ +package discovery + +import ( + "context" + "io" + "time" + + "github.com/libp2p/go-libp2p-core/host" + "github.com/libp2p/go-libp2p-core/peer" +) + +type Notifee interface { + HandlePeerFound(peer.AddrInfo) +} + +type Service interface { + io.Closer + RegisterNotifee(Notifee) + UnregisterNotifee(Notifee) +} + +func NewMdnsService(ctx context.Context, peerhost host.Host, interval time.Duration) (Service, error) { + legacy, err := NewMdnsServiceLegacy(ctx, peerhost, interval) + if err != nil { + return nil, err + } + return &mdnsServiceMuxer{ + s1: legacy, + s2: NewMdnsServiceNew(peerhost), + }, nil +} + +type mdnsServiceMuxer struct { + s1, s2 Service +} + +var _ Service = &mdnsServiceMuxer{} + +func (m *mdnsServiceMuxer) Close() error { + m.s1.Close() + return m.s2.Close() +} + +func (m *mdnsServiceMuxer) RegisterNotifee(notifee Notifee) { + m.s1.RegisterNotifee(notifee) + m.s2.RegisterNotifee(notifee) +} + +func (m *mdnsServiceMuxer) UnregisterNotifee(notifee Notifee) { + m.s1.UnregisterNotifee(notifee) + m.s2.UnregisterNotifee(notifee) +} diff --git a/p2p/discovery/mdns_test.go b/p2p/discovery/mdns_test.go new file mode 100644 index 0000000000..72d0e71d4a --- /dev/null +++ b/p2p/discovery/mdns_test.go @@ -0,0 +1,123 @@ +package discovery + +import ( + "context" + "fmt" + "sync" + "testing" + "time" + + "github.com/libp2p/go-libp2p" + "github.com/libp2p/go-libp2p-core/host" + "github.com/libp2p/go-libp2p-core/peer" + + "github.com/stretchr/testify/assert" +) + +func setupMDNS(t *testing.T, notifee Notifee) (host.Host, *mdnsService) { + t.Helper() + host, err := libp2p.New(context.Background(), libp2p.ListenAddrStrings("/ip4/127.0.0.1/tcp/0")) + if err != nil { + t.Fatal(err) + } + s := NewMdnsServiceNew(host) + s.RegisterNotifee(notifee) + return host, s +} + +type notif struct { + mutex sync.Mutex + infos []peer.AddrInfo +} + +var _ Notifee = ¬if{} + +func (n *notif) HandlePeerFound(info peer.AddrInfo) { + n.mutex.Lock() + n.infos = append(n.infos, info) + fmt.Printf("Discovered %s at %v.\n", info.ID, info.Addrs) + n.mutex.Unlock() +} + +func (n *notif) GetPeers() []peer.AddrInfo { + n.mutex.Lock() + defer n.mutex.Unlock() + infos := make([]peer.AddrInfo, 0, len(n.infos)) + infos = append(infos, n.infos...) + return infos +} + +func TestSelfDiscovery(t *testing.T) { + notif := ¬if{} + host, s := setupMDNS(t, notif) + defer s.Close() + assert.Eventuallyf( + t, + func() bool { + var found bool + for _, info := range notif.GetPeers() { + if info.ID == host.ID() { + found = true + break + } + } + return found + }, + 5*time.Second, + 5*time.Millisecond, + "expected peer to find itself", + ) +} + +func TestOtherDiscovery(t *testing.T) { + const n = 4 + + notifs := make([]*notif, n) + hostIDs := make([]peer.ID, n) + for i := 0; i < n; i++ { + notif := ¬if{} + notifs[i] = notif + var s *mdnsService + host, s := setupMDNS(t, notif) + hostIDs[i] = host.ID() + defer s.Close() + time.Sleep(2 * time.Second) + } + // fmt.Println(hostIDs) + + containsAllHostIDs := func(ids []peer.ID) bool { + for _, id := range hostIDs { + var found bool + for _, i := range ids { + if id == i { + found = true + break + } + } + if !found { + return false + } + } + return true + } + + assert.Eventuallyf( + t, + func() bool { + for _, notif := range notifs { + infos := notif.GetPeers() + ids := make([]peer.ID, 0, len(infos)) + for _, info := range infos { + ids = append(ids, info.ID) + } + if !containsAllHostIDs(ids) { + return false + } + } + return true + }, + 25*time.Second, + 5*time.Millisecond, + "expected peer to find itself", + ) +}