@@ -3,7 +3,9 @@ import { isIPv4 } from '@chainsafe/is-ip'
33import { peerIdFromString } from '@libp2p/peer-id'
44import { debounce } from '@libp2p/utils/debounce'
55import { createScalableCuckooFilter } from '@libp2p/utils/filters'
6+ import { isPrivateIp } from '@libp2p/utils/private-ip'
67import { multiaddr } from '@multiformats/multiaddr'
8+ import { QUICV1 , TCP , WebSockets , WebSocketsSecure } from '@multiformats/multiaddr-matcher'
79import { DNSMappings } from './dns-mappings.js'
810import { IPMappings } from './ip-mappings.js'
911import { ObservedAddresses } from './observed-addresses.js'
@@ -249,20 +251,42 @@ export class AddressManager implements AddressManagerInterface {
249251 addr = stripPeerId ( addr , this . components . peerId )
250252 let startingConfidence = true
251253
252- if ( options ?. type === 'observed' || this . observed . has ( addr ) ) {
253- startingConfidence = this . observed . confirm ( addr , options ?. ttl ?? this . addressVerificationTTL )
254- }
255-
256254 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+ }
258260 }
259261
260262 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+ }
262268 }
263269
264270 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+ }
266290 }
267291
268292 // only trigger the 'self:peer:update' event if our confidence in an address has changed
@@ -277,19 +301,35 @@ export class AddressManager implements AddressManagerInterface {
277301 let startingConfidence = false
278302
279303 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+ }
281309 }
282310
283311 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+ }
285317 }
286318
287319 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+ }
289325 }
290326
291327 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+ }
293333 }
294334
295335 // only trigger the 'self:peer:update' event if our confidence in an address has changed
@@ -410,4 +450,82 @@ export class AddressManager implements AddressManagerInterface {
410450 this . _updatePeerStoreAddresses ( )
411451 }
412452 }
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+ }
413531}
0 commit comments