@@ -4,16 +4,15 @@ import (
4
4
"context"
5
5
"errors"
6
6
"fmt"
7
+ "slices"
7
8
"sync"
8
9
"time"
9
10
10
11
logging "github.com/ipfs/go-log/v2"
11
- "github.com/libp2p/go-libp2p/core/event"
12
12
"github.com/libp2p/go-libp2p/core/host"
13
13
"github.com/libp2p/go-libp2p/core/network"
14
14
"github.com/libp2p/go-libp2p/core/peer"
15
15
"github.com/libp2p/go-libp2p/core/protocol"
16
- "github.com/libp2p/go-libp2p/p2p/host/eventbus"
17
16
"github.com/libp2p/go-libp2p/p2p/protocol/holepunch/pb"
18
17
"github.com/libp2p/go-libp2p/p2p/protocol/identify"
19
18
"github.com/libp2p/go-msgio/pbio"
@@ -47,7 +46,13 @@ type Service struct {
47
46
ctxCancel context.CancelFunc
48
47
49
48
host host.Host
50
- ids identify.IDService
49
+ // ids helps with connection reversal. We wait for identify to complete and attempt
50
+ // a direct connection to the peer if it's publicly reachable.
51
+ ids identify.IDService
52
+ // listenAddrs provides the addresses for the host to be used for hole punching. We use this
53
+ // and not host.Addrs because host.Addrs might remove public unreachable address and only advertise
54
+ // publicly reachable relay addresses.
55
+ listenAddrs func () []ma.Multiaddr
51
56
52
57
holePuncherMx sync.Mutex
53
58
holePuncher * holePuncher
@@ -65,7 +70,7 @@ type Service struct {
65
70
// no matter if they are behind a NAT / firewall or not.
66
71
// The Service handles DCUtR streams (which are initiated from the node behind
67
72
// a NAT / Firewall once we establish a connection to them through a relay.
68
- func NewService (h host.Host , ids identify.IDService , opts ... Option ) (* Service , error ) {
73
+ func NewService (h host.Host , ids identify.IDService , listenAddrs func () []ma. Multiaddr , opts ... Option ) (* Service , error ) {
69
74
if ids == nil {
70
75
return nil , errors .New ("identify service can't be nil" )
71
76
}
@@ -76,6 +81,7 @@ func NewService(h host.Host, ids identify.IDService, opts ...Option) (*Service,
76
81
ctxCancel : cancel ,
77
82
host : h ,
78
83
ids : ids ,
84
+ listenAddrs : listenAddrs ,
79
85
hasPublicAddrsChan : make (chan struct {}),
80
86
}
81
87
@@ -88,18 +94,18 @@ func NewService(h host.Host, ids identify.IDService, opts ...Option) (*Service,
88
94
s .tracer .Start ()
89
95
90
96
s .refCount .Add (1 )
91
- go s .watchForPublicAddr ()
97
+ go s .waitForPublicAddr ()
92
98
93
99
return s , nil
94
100
}
95
101
96
- func (s * Service ) watchForPublicAddr () {
102
+ func (s * Service ) waitForPublicAddr () {
97
103
defer s .refCount .Done ()
98
104
99
105
log .Debug ("waiting until we have at least one public address" , "peer" , s .host .ID ())
100
106
101
107
// TODO: We should have an event here that fires when identify discovers a new
102
- // address (and when autonat confirms that address) .
108
+ // address.
103
109
// As we currently don't have an event like this, just check our observed addresses
104
110
// regularly (exponential backoff starting at 250 ms, capped at 5s).
105
111
duration := 250 * time .Millisecond
@@ -125,44 +131,27 @@ func (s *Service) watchForPublicAddr() {
125
131
}
126
132
}
127
133
128
- // Only start the holePuncher if we're behind a NAT / firewall.
129
- sub , err := s .host .EventBus ().Subscribe (& event.EvtLocalReachabilityChanged {}, eventbus .Name ("holepunch" ))
130
- if err != nil {
131
- log .Debugf ("failed to subscripe to Reachability event: %s" , err )
134
+ s .holePuncherMx .Lock ()
135
+ if s .ctx .Err () != nil {
136
+ // service is closed
132
137
return
133
138
}
134
- defer sub .Close ()
135
- for {
136
- select {
137
- case <- s .ctx .Done ():
138
- return
139
- case e , ok := <- sub .Out ():
140
- if ! ok {
141
- return
142
- }
143
- if e .(event.EvtLocalReachabilityChanged ).Reachability != network .ReachabilityPrivate {
144
- continue
145
- }
146
- s .holePuncherMx .Lock ()
147
- s .holePuncher = newHolePuncher (s .host , s .ids , s .tracer , s .filter )
148
- s .holePuncherMx .Unlock ()
149
- close (s .hasPublicAddrsChan )
150
- return
151
- }
152
- }
139
+ s .holePuncher = newHolePuncher (s .host , s .ids , s .listenAddrs , s .tracer , s .filter )
140
+ s .holePuncherMx .Unlock ()
141
+ close (s .hasPublicAddrsChan )
153
142
}
154
143
155
144
// Close closes the Hole Punch Service.
156
145
func (s * Service ) Close () error {
157
146
var err error
147
+ s .ctxCancel ()
158
148
s .holePuncherMx .Lock ()
159
149
if s .holePuncher != nil {
160
150
err = s .holePuncher .Close ()
161
151
}
162
152
s .holePuncherMx .Unlock ()
163
153
s .tracer .Close ()
164
154
s .host .RemoveStreamHandler (Protocol )
165
- s .ctxCancel ()
166
155
s .refCount .Wait ()
167
156
return err
168
157
}
@@ -172,7 +161,7 @@ func (s *Service) incomingHolePunch(str network.Stream) (rtt time.Duration, remo
172
161
if ! isRelayAddress (str .Conn ().RemoteMultiaddr ()) {
173
162
return 0 , nil , nil , fmt .Errorf ("received hole punch stream: %s" , str .Conn ().RemoteMultiaddr ())
174
163
}
175
- ownAddrs = s .getPublicAddrs ()
164
+ ownAddrs = s .listenAddrs ()
176
165
if s .filter != nil {
177
166
ownAddrs = s .filter .FilterLocal (str .Conn ().RemotePeer (), ownAddrs )
178
167
}
@@ -277,25 +266,7 @@ func (s *Service) handleNewStream(str network.Stream) {
277
266
278
267
// getPublicAddrs returns public observed and interface addresses
279
268
func (s * Service ) getPublicAddrs () []ma.Multiaddr {
280
- addrs := removeRelayAddrs (s .ids .OwnObservedAddrs ())
281
-
282
- interfaceListenAddrs , err := s .host .Network ().InterfaceListenAddresses ()
283
- if err != nil {
284
- log .Debugf ("failed to get to get InterfaceListenAddresses: %s" , err )
285
- } else {
286
- addrs = append (addrs , interfaceListenAddrs ... )
287
- }
288
-
289
- addrs = ma .Unique (addrs )
290
-
291
- publicAddrs := make ([]ma.Multiaddr , 0 , len (addrs ))
292
-
293
- for _ , addr := range addrs {
294
- if manet .IsPublicAddr (addr ) {
295
- publicAddrs = append (publicAddrs , addr )
296
- }
297
- }
298
- return publicAddrs
269
+ return slices .DeleteFunc (s .listenAddrs (), func (a ma.Multiaddr ) bool { return ! manet .IsPublicAddr (a ) })
299
270
}
300
271
301
272
// DirectConnect is only exposed for testing purposes.
0 commit comments