Skip to content

Commit b02ea9b

Browse files
authored
deps: update nat-port-mapper to v3 (#2843)
Updates nat-port-mapper to try to map ports on all available gateways instead of just the first one that is discovered on the network.
1 parent bc90b4f commit b02ea9b

File tree

8 files changed

+408
-286
lines changed

8 files changed

+408
-286
lines changed

packages/upnp-nat/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
"test:electron-main": "aegir test -t electron-main"
5151
},
5252
"dependencies": {
53-
"@achingbrain/nat-port-mapper": "^2.0.1",
53+
"@achingbrain/nat-port-mapper": "^3.0.1",
5454
"@chainsafe/is-ip": "^2.0.2",
5555
"@libp2p/interface": "^2.2.1",
5656
"@libp2p/interface-internal": "^2.1.1",

packages/upnp-nat/src/check-external-address.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ import { NotStartedError, start, stop } from '@libp2p/interface'
22
import { repeatingTask } from '@libp2p/utils/repeating-task'
33
import pDefer from 'p-defer'
44
import { raceSignal } from 'race-signal'
5-
import type { NatAPI } from '@achingbrain/nat-port-mapper'
5+
import type { Gateway } from '@achingbrain/nat-port-mapper'
66
import type { AbortOptions, ComponentLogger, Logger, Startable } from '@libp2p/interface'
77
import type { AddressManager } from '@libp2p/interface-internal'
88
import type { RepeatingTask } from '@libp2p/utils/repeating-task'
99
import type { DeferredPromise } from 'p-defer'
1010

1111
export interface ExternalAddressCheckerComponents {
12-
client: NatAPI
12+
gateway: Gateway
1313
addressManager: AddressManager
1414
logger: ComponentLogger
1515
}
@@ -29,7 +29,7 @@ export interface ExternalAddress {
2929
*/
3030
class ExternalAddressChecker implements ExternalAddress, Startable {
3131
private readonly log: Logger
32-
private readonly client: NatAPI
32+
private readonly gateway: Gateway
3333
private readonly addressManager: AddressManager
3434
private started: boolean
3535
private lastPublicIp?: string
@@ -39,7 +39,7 @@ class ExternalAddressChecker implements ExternalAddress, Startable {
3939

4040
constructor (components: ExternalAddressCheckerComponents, init: ExternalAddressCheckerInit) {
4141
this.log = components.logger.forComponent('libp2p:upnp-nat:external-address-check')
42-
this.client = components.client
42+
this.gateway = components.gateway
4343
this.addressManager = components.addressManager
4444
this.onExternalAddressChange = init.onExternalAddressChange
4545
this.started = false
@@ -60,14 +60,11 @@ class ExternalAddressChecker implements ExternalAddress, Startable {
6060
}
6161

6262
await start(this.check)
63-
64-
this.check.start()
6563
this.started = true
6664
}
6765

6866
async stop (): Promise<void> {
6967
await stop(this.check)
70-
7168
this.started = false
7269
}
7370

@@ -86,7 +83,7 @@ class ExternalAddressChecker implements ExternalAddress, Startable {
8683

8784
private async checkExternalAddress (options?: AbortOptions): Promise<void> {
8885
try {
89-
const externalAddress = await this.client.externalIp(options)
86+
const externalAddress = await this.gateway.externalIp(options)
9087

9188
// check if our public address has changed
9289
if (this.lastPublicIp != null && externalAddress !== this.lastPublicIp) {
@@ -99,6 +96,8 @@ class ExternalAddressChecker implements ExternalAddress, Startable {
9996
this.lastPublicIp = externalAddress
10097
this.lastPublicIpPromise.resolve(externalAddress)
10198
} catch (err: any) {
99+
this.log.error('could not resolve external address - %e', err)
100+
102101
if (this.lastPublicIp != null) {
103102
// ignore the error if we've previously run successfully
104103
return

packages/upnp-nat/src/constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const DEFAULT_PORT_MAPPING_TTL = 720_000
2+
export const DEFAULT_GATEWAY_SEARCH_TIMEOUT = 60_000
3+
export const DEFAULT_GATEWAY_SEARCH_INTERVAL = 300_000
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { TypedEventEmitter, start, stop } from '@libp2p/interface'
2+
import { repeatingTask } from '@libp2p/utils/repeating-task'
3+
import { DEFAULT_GATEWAY_SEARCH_INTERVAL, DEFAULT_GATEWAY_SEARCH_TIMEOUT } from './constants.js'
4+
import type { Gateway, UPnPNAT } from '@achingbrain/nat-port-mapper'
5+
import type { ComponentLogger, Logger } from '@libp2p/interface'
6+
import type { RepeatingTask } from '@libp2p/utils/repeating-task'
7+
8+
export interface GatewayFinderComponents {
9+
logger: ComponentLogger
10+
}
11+
12+
export interface GatewayFinderInit {
13+
portMappingClient: UPnPNAT
14+
}
15+
16+
export interface GatewayFinderEvents {
17+
'gateway': CustomEvent<Gateway>
18+
}
19+
20+
export class GatewayFinder extends TypedEventEmitter<GatewayFinderEvents> {
21+
private readonly log: Logger
22+
private readonly gateways: Gateway[]
23+
private readonly findGateways: RepeatingTask
24+
private readonly portMappingClient: UPnPNAT
25+
private started: boolean
26+
27+
constructor (components: GatewayFinderComponents, init: GatewayFinderInit) {
28+
super()
29+
30+
this.log = components.logger.forComponent('libp2p:upnp-nat')
31+
this.portMappingClient = init.portMappingClient
32+
this.started = false
33+
this.gateways = []
34+
35+
// every five minutes, search for network gateways for one minute
36+
this.findGateways = repeatingTask(async (options) => {
37+
for await (const gateway of this.portMappingClient.findGateways(options)) {
38+
if (this.gateways.some(g => g.id === gateway.id)) {
39+
// already seen this gateway
40+
continue
41+
}
42+
43+
this.gateways.push(gateway)
44+
this.safeDispatchEvent('gateway', {
45+
detail: gateway
46+
})
47+
}
48+
}, DEFAULT_GATEWAY_SEARCH_INTERVAL, {
49+
runImmediately: true,
50+
timeout: DEFAULT_GATEWAY_SEARCH_TIMEOUT
51+
})
52+
}
53+
54+
async start (): Promise<void> {
55+
if (this.started) {
56+
return
57+
}
58+
59+
this.started = true
60+
await start(this.findGateways)
61+
}
62+
63+
/**
64+
* Stops the NAT manager
65+
*/
66+
async stop (): Promise<void> {
67+
await stop(this.findGateways)
68+
this.started = false
69+
}
70+
}

packages/upnp-nat/src/index.ts

Lines changed: 14 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,12 @@
3535
* ```
3636
*/
3737

38-
import { UPnPNAT as UPnPNATClass, type NatAPI, type MapPortOptions } from './upnp-nat.js'
38+
import { UPnPNAT as UPnPNATClass } from './upnp-nat.js'
39+
import type { UPnPNAT as UPnPNATClient, MapPortOptions } from '@achingbrain/nat-port-mapper'
3940
import type { ComponentLogger, Libp2pEvents, NodeInfo, PeerId, TypedEventTarget } from '@libp2p/interface'
4041
import type { AddressManager } from '@libp2p/interface-internal'
4142

42-
export type { NatAPI, MapPortOptions }
43+
export type { UPnPNATClient, MapPortOptions }
4344

4445
export interface PMPOptions {
4546
/**
@@ -49,12 +50,6 @@ export interface PMPOptions {
4950
}
5051

5152
export interface UPnPNATInit {
52-
/**
53-
* Pass a string to hard code the external address, otherwise it will be
54-
* auto-detected
55-
*/
56-
externalAddress?: string
57-
5853
/**
5954
* Check if the external address has changed this often in ms. Ignored if an
6055
* external address is specified.
@@ -71,46 +66,31 @@ export interface UPnPNATInit {
7166
*/
7267
externalAddressCheckTimeout?: number
7368

74-
/**
75-
* Pass a value to use instead of auto-detection
76-
*/
77-
localAddress?: string
78-
7969
/**
8070
* A string value to use for the port mapping description on the gateway
8171
*/
82-
description?: string
72+
portMappingDescription?: string
8373

8474
/**
85-
* How long UPnP port mappings should last for in seconds (minimum 1200)
86-
*/
87-
ttl?: number
88-
89-
/**
90-
* Whether to automatically refresh UPnP port mappings when their TTL is reached
91-
*/
92-
keepAlive?: boolean
93-
94-
/**
95-
* Pass a value to use instead of auto-detection
75+
* How long UPnP port mappings should last for in ms
76+
*
77+
* @default 720_000
9678
*/
97-
gateway?: string
79+
portMappingTTL?: number
9880

9981
/**
100-
* Ports are mapped when the `self:peer:update` event fires, which happens
101-
* when the node's addresses change. To avoid starting to map ports while
102-
* multiple addresses are being added, the mapping function is debounced by
103-
* this number of ms
82+
* Whether to automatically refresh UPnP port mappings when their TTL is
83+
* reached
10484
*
105-
* @default 5000
85+
* @default true
10686
*/
107-
delay?: number
87+
portMappingAutoRefresh?: boolean
10888

10989
/**
11090
* A preconfigured instance of a NatAPI client can be passed as an option,
11191
* otherwise one will be created
11292
*/
113-
client?: NatAPI
93+
portMappingClient?: UPnPNATClient
11494
}
11595

11696
export interface UPnPNATComponents {
@@ -122,7 +102,7 @@ export interface UPnPNATComponents {
122102
}
123103

124104
export interface UPnPNAT {
125-
client: NatAPI
105+
portMappingClient: UPnPNATClient
126106
}
127107

128108
export function uPnPNAT (init: UPnPNATInit = {}): (components: UPnPNATComponents) => UPnPNAT {

0 commit comments

Comments
 (0)