@@ -3,7 +3,9 @@ import { isIPv4 } from '@chainsafe/is-ip'
3
3
import { peerIdFromString } from '@libp2p/peer-id'
4
4
import { debounce } from '@libp2p/utils/debounce'
5
5
import { createScalableCuckooFilter } from '@libp2p/utils/filters'
6
+ import { isPrivateIp } from '@libp2p/utils/private-ip'
6
7
import { multiaddr } from '@multiformats/multiaddr'
8
+ import { QUICV1 , TCP , WebSockets , WebSocketsSecure } from '@multiformats/multiaddr-matcher'
7
9
import { DNSMappings } from './dns-mappings.js'
8
10
import { IPMappings } from './ip-mappings.js'
9
11
import { ObservedAddresses } from './observed-addresses.js'
@@ -249,20 +251,42 @@ export class AddressManager implements AddressManagerInterface {
249
251
addr = stripPeerId ( addr , this . components . peerId )
250
252
let startingConfidence = true
251
253
252
- if ( options ?. type === 'observed' || this . observed . has ( addr ) ) {
253
- startingConfidence = this . observed . confirm ( addr , options ?. ttl ?? this . addressVerificationTTL )
254
- }
255
-
256
254
if ( options ?. type === 'transport' || this . transportAddresses . has ( addr ) ) {
257
- startingConfidence = this . transportAddresses . confirm ( addr , options ?. ttl ?? this . addressVerificationTTL )
255
+ const transportStartingConfidence = this . transportAddresses . confirm ( addr , options ?. ttl ?? this . addressVerificationTTL )
256
+
257
+ if ( ! transportStartingConfidence && startingConfidence ) {
258
+ startingConfidence = false
259
+ }
258
260
}
259
261
260
262
if ( options ?. type === 'dns-mapping' || this . dnsMappings . has ( addr ) ) {
261
- startingConfidence = this . dnsMappings . confirm ( addr , options ?. ttl ?? this . addressVerificationTTL )
263
+ const dnsMapingStartingConfidence = this . dnsMappings . confirm ( addr , options ?. ttl ?? this . addressVerificationTTL )
264
+
265
+ if ( ! dnsMapingStartingConfidence && startingConfidence ) {
266
+ startingConfidence = false
267
+ }
262
268
}
263
269
264
270
if ( options ?. type === 'ip-mapping' || this . ipMappings . has ( addr ) ) {
265
- startingConfidence = this . ipMappings . confirm ( addr , options ?. ttl ?? this . addressVerificationTTL )
271
+ const ipMapingStartingConfidence = this . ipMappings . confirm ( addr , options ?. ttl ?? this . addressVerificationTTL )
272
+
273
+ if ( ! ipMapingStartingConfidence && startingConfidence ) {
274
+ startingConfidence = false
275
+ }
276
+ }
277
+
278
+ if ( options ?. type === 'observed' || this . observed . has ( addr ) ) {
279
+ // try to match up observed address with local transport listener
280
+ if ( this . maybeUpgradeToIPMapping ( addr ) ) {
281
+ this . ipMappings . confirm ( addr , options ?. ttl ?? this . addressVerificationTTL )
282
+ startingConfidence = false
283
+ } else {
284
+ const observedStartingConfidence = this . observed . confirm ( addr , options ?. ttl ?? this . addressVerificationTTL )
285
+
286
+ if ( ! observedStartingConfidence && startingConfidence ) {
287
+ startingConfidence = false
288
+ }
289
+ }
266
290
}
267
291
268
292
// only trigger the 'self:peer:update' event if our confidence in an address has changed
@@ -277,19 +301,35 @@ export class AddressManager implements AddressManagerInterface {
277
301
let startingConfidence = false
278
302
279
303
if ( this . observed . has ( addr ) ) {
280
- startingConfidence = this . observed . remove ( addr )
304
+ const observedStartingConfidence = this . observed . remove ( addr )
305
+
306
+ if ( ! observedStartingConfidence && startingConfidence ) {
307
+ startingConfidence = false
308
+ }
281
309
}
282
310
283
311
if ( this . transportAddresses . has ( addr ) ) {
284
- startingConfidence = this . transportAddresses . unconfirm ( addr , options ?. ttl ?? this . addressVerificationRetry )
312
+ const transportStartingConfidence = this . transportAddresses . unconfirm ( addr , options ?. ttl ?? this . addressVerificationRetry )
313
+
314
+ if ( ! transportStartingConfidence && startingConfidence ) {
315
+ startingConfidence = false
316
+ }
285
317
}
286
318
287
319
if ( this . dnsMappings . has ( addr ) ) {
288
- startingConfidence = this . dnsMappings . unconfirm ( addr , options ?. ttl ?? this . addressVerificationRetry )
320
+ const dnsMapingStartingConfidence = this . dnsMappings . unconfirm ( addr , options ?. ttl ?? this . addressVerificationRetry )
321
+
322
+ if ( ! dnsMapingStartingConfidence && startingConfidence ) {
323
+ startingConfidence = false
324
+ }
289
325
}
290
326
291
327
if ( this . ipMappings . has ( addr ) ) {
292
- startingConfidence = this . ipMappings . unconfirm ( addr , options ?. ttl ?? this . addressVerificationRetry )
328
+ const ipMapingStartingConfidence = this . ipMappings . unconfirm ( addr , options ?. ttl ?? this . addressVerificationRetry )
329
+
330
+ if ( ! ipMapingStartingConfidence && startingConfidence ) {
331
+ startingConfidence = false
332
+ }
293
333
}
294
334
295
335
// only trigger the 'self:peer:update' event if our confidence in an address has changed
@@ -410,4 +450,82 @@ export class AddressManager implements AddressManagerInterface {
410
450
this . _updatePeerStoreAddresses ( )
411
451
}
412
452
}
453
+
454
+ /**
455
+ * Where an external service (router, gateway, etc) is forwarding traffic to
456
+ * us, attempt to add an IP mapping for the external address - this will
457
+ * include the observed mapping in the address list where we also have a DNS
458
+ * mapping for the external IP.
459
+ *
460
+ * Returns true if we added a new mapping
461
+ */
462
+ private maybeUpgradeToIPMapping ( ma : Multiaddr ) : boolean {
463
+ // this address is already mapped
464
+ if ( this . ipMappings . has ( ma ) ) {
465
+ return false
466
+ }
467
+
468
+ const maOptions = ma . toOptions ( )
469
+
470
+ // only public IPv4 addresses
471
+ if ( maOptions . family === 6 || maOptions . host === '127.0.0.1' || isPrivateIp ( maOptions . host ) === true ) {
472
+ return false
473
+ }
474
+
475
+ const listeners = this . components . transportManager . getListeners ( )
476
+
477
+ const transportMatchers : Array < ( ma : Multiaddr ) => boolean > = [
478
+ ( ma : Multiaddr ) => WebSockets . exactMatch ( ma ) || WebSocketsSecure . exactMatch ( ma ) ,
479
+ ( ma : Multiaddr ) => TCP . exactMatch ( ma ) ,
480
+ ( ma : Multiaddr ) => QUICV1 . exactMatch ( ma )
481
+ ]
482
+
483
+ for ( const matcher of transportMatchers ) {
484
+ // is the incoming address the same type as the matcher
485
+ if ( ! matcher ( ma ) ) {
486
+ continue
487
+ }
488
+
489
+ // get the listeners for this transport
490
+ const transportListeners = listeners . filter ( listener => {
491
+ return listener . getAddrs ( ) . filter ( ma => {
492
+ // only IPv4 addresses of the matcher type
493
+ return ma . toOptions ( ) . family === 4 && matcher ( ma )
494
+ } ) . length > 0
495
+ } )
496
+
497
+ // because the NAT mapping could be forwarding different external ports to
498
+ // internal ones, we can only be sure enough to add a mapping if there is
499
+ // a single listener
500
+ if ( transportListeners . length !== 1 ) {
501
+ continue
502
+ }
503
+
504
+ // we have one listener which listens on one port so whatever the external
505
+ // NAT port mapping is, it should be for this listener
506
+ const linkLocalAddr = transportListeners [ 0 ] . getAddrs ( ) . filter ( ma => {
507
+ return ma . toOptions ( ) . host !== '127.0.0.1'
508
+ } ) . pop ( )
509
+
510
+ if ( linkLocalAddr == null ) {
511
+ continue
512
+ }
513
+
514
+ const linkLocalOptions = linkLocalAddr . toOptions ( )
515
+
516
+ // upgrade observed address to IP mapping
517
+ this . observed . remove ( ma )
518
+ this . ipMappings . add (
519
+ linkLocalOptions . host ,
520
+ linkLocalOptions . port ,
521
+ maOptions . host ,
522
+ maOptions . port ,
523
+ maOptions . transport
524
+ )
525
+
526
+ return true
527
+ }
528
+
529
+ return false
530
+ }
413
531
}
0 commit comments