@libp2p/upnp-nat 2.0.12 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,16 +1,15 @@
1
1
  import { NotStartedError, start, stop } from '@libp2p/interface'
2
2
  import { repeatingTask } from '@libp2p/utils/repeating-task'
3
- import { multiaddr } from '@multiformats/multiaddr'
4
3
  import pDefer from 'p-defer'
5
4
  import { raceSignal } from 'race-signal'
6
- import type { NatAPI } from '@achingbrain/nat-port-mapper'
5
+ import type { Gateway } from '@achingbrain/nat-port-mapper'
7
6
  import type { AbortOptions, ComponentLogger, Logger, Startable } from '@libp2p/interface'
8
7
  import type { AddressManager } from '@libp2p/interface-internal'
9
8
  import type { RepeatingTask } from '@libp2p/utils/repeating-task'
10
9
  import type { DeferredPromise } from 'p-defer'
11
10
 
12
11
  export interface ExternalAddressCheckerComponents {
13
- client: NatAPI
12
+ gateway: Gateway
14
13
  addressManager: AddressManager
15
14
  logger: ComponentLogger
16
15
  }
@@ -18,7 +17,7 @@ export interface ExternalAddressCheckerComponents {
18
17
  export interface ExternalAddressCheckerInit {
19
18
  interval?: number
20
19
  timeout?: number
21
- autoConfirmAddress?: boolean
20
+ onExternalAddressChange?(newExternalAddress: string): void
22
21
  }
23
22
 
24
23
  export interface ExternalAddress {
@@ -30,19 +29,19 @@ export interface ExternalAddress {
30
29
  */
31
30
  class ExternalAddressChecker implements ExternalAddress, Startable {
32
31
  private readonly log: Logger
33
- private readonly client: NatAPI
32
+ private readonly gateway: Gateway
34
33
  private readonly addressManager: AddressManager
35
34
  private started: boolean
36
35
  private lastPublicIp?: string
37
36
  private readonly lastPublicIpPromise: DeferredPromise<string>
38
37
  private readonly check: RepeatingTask
39
- private readonly autoConfirmAddress: boolean
38
+ private readonly onExternalAddressChange?: (newExternalAddress: string) => void
40
39
 
41
- constructor (components: ExternalAddressCheckerComponents, init: ExternalAddressCheckerInit = {}) {
40
+ constructor (components: ExternalAddressCheckerComponents, init: ExternalAddressCheckerInit) {
42
41
  this.log = components.logger.forComponent('libp2p:upnp-nat:external-address-check')
43
- this.client = components.client
42
+ this.gateway = components.gateway
44
43
  this.addressManager = components.addressManager
45
- this.autoConfirmAddress = init.autoConfirmAddress ?? false
44
+ this.onExternalAddressChange = init.onExternalAddressChange
46
45
  this.started = false
47
46
 
48
47
  this.checkExternalAddress = this.checkExternalAddress.bind(this)
@@ -61,14 +60,11 @@ class ExternalAddressChecker implements ExternalAddress, Startable {
61
60
  }
62
61
 
63
62
  await start(this.check)
64
-
65
- this.check.start()
66
63
  this.started = true
67
64
  }
68
65
 
69
66
  async stop (): Promise<void> {
70
67
  await stop(this.check)
71
-
72
68
  this.started = false
73
69
  }
74
70
 
@@ -87,37 +83,21 @@ class ExternalAddressChecker implements ExternalAddress, Startable {
87
83
 
88
84
  private async checkExternalAddress (options?: AbortOptions): Promise<void> {
89
85
  try {
90
- const externalAddress = await this.client.externalIp(options)
86
+ const externalAddress = await this.gateway.externalIp(options)
91
87
 
92
88
  // check if our public address has changed
93
89
  if (this.lastPublicIp != null && externalAddress !== this.lastPublicIp) {
94
90
  this.log('external address changed from %s to %s', this.lastPublicIp, externalAddress)
95
91
 
96
- for (const ma of this.addressManager.getAddresses()) {
97
- const addrString = ma.toString()
98
-
99
- if (!addrString.includes(this.lastPublicIp)) {
100
- continue
101
- }
102
-
103
- // create a new version of the multiaddr with the new public IP
104
- const newAddress = multiaddr(addrString.replace(this.lastPublicIp, externalAddress))
105
-
106
- // remove the old address and add the new one
107
- this.addressManager.removeObservedAddr(ma)
108
- this.addressManager.confirmObservedAddr(newAddress)
109
-
110
- if (this.autoConfirmAddress) {
111
- this.addressManager.confirmObservedAddr(newAddress)
112
- } else {
113
- this.addressManager.addObservedAddr(newAddress)
114
- }
115
- }
92
+ // notify listeners that the address has changed
93
+ this.onExternalAddressChange?.(externalAddress)
116
94
  }
117
95
 
118
96
  this.lastPublicIp = externalAddress
119
97
  this.lastPublicIpPromise.resolve(externalAddress)
120
98
  } catch (err: any) {
99
+ this.log.error('could not resolve external address - %e', err)
100
+
121
101
  if (this.lastPublicIp != null) {
122
102
  // ignore the error if we've previously run successfully
123
103
  return
@@ -128,7 +108,7 @@ class ExternalAddressChecker implements ExternalAddress, Startable {
128
108
  }
129
109
  }
130
110
 
131
- export function dynamicExternalAddress (components: ExternalAddressCheckerComponents, init: ExternalAddressCheckerInit = {}): ExternalAddress {
111
+ export function dynamicExternalAddress (components: ExternalAddressCheckerComponents, init: ExternalAddressCheckerInit): ExternalAddress {
132
112
  return new ExternalAddressChecker(components, init)
133
113
  }
134
114
 
@@ -0,0 +1,2 @@
1
+ export const DEFAULT_GATEWAY_SEARCH_TIMEOUT = 60_000
2
+ export const DEFAULT_GATEWAY_SEARCH_INTERVAL = 300_000
@@ -0,0 +1,75 @@
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({
38
+ ...options,
39
+ searchInterval: 10000
40
+ })) {
41
+ if (this.gateways.some(g => {
42
+ return g.id === gateway.id && g.family === gateway.family
43
+ })) {
44
+ // already seen this gateway
45
+ continue
46
+ }
47
+
48
+ this.gateways.push(gateway)
49
+ this.safeDispatchEvent('gateway', {
50
+ detail: gateway
51
+ })
52
+ }
53
+ }, DEFAULT_GATEWAY_SEARCH_INTERVAL, {
54
+ runImmediately: true,
55
+ timeout: DEFAULT_GATEWAY_SEARCH_TIMEOUT
56
+ })
57
+ }
58
+
59
+ async start (): Promise<void> {
60
+ if (this.started) {
61
+ return
62
+ }
63
+
64
+ this.started = true
65
+ await start(this.findGateways)
66
+ }
67
+
68
+ /**
69
+ * Stops the NAT manager
70
+ */
71
+ async stop (): Promise<void> {
72
+ await stop(this.findGateways)
73
+ this.started = false
74
+ }
75
+ }
package/src/index.ts CHANGED
@@ -35,11 +35,12 @@
35
35
  * ```
36
36
  */
37
37
 
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'
39
40
  import type { ComponentLogger, Libp2pEvents, NodeInfo, PeerId, TypedEventTarget } from '@libp2p/interface'
40
41
  import type { AddressManager } from '@libp2p/interface-internal'
41
42
 
42
- export type { NatAPI, MapPortOptions }
43
+ export type { UPnPNATClient, MapPortOptions }
43
44
 
44
45
  export interface PMPOptions {
45
46
  /**
@@ -49,12 +50,6 @@ export interface PMPOptions {
49
50
  }
50
51
 
51
52
  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
-
58
53
  /**
59
54
  * Check if the external address has changed this often in ms. Ignored if an
60
55
  * external address is specified.
@@ -71,55 +66,38 @@ export interface UPnPNATInit {
71
66
  */
72
67
  externalAddressCheckTimeout?: number
73
68
 
74
- /**
75
- * Pass a value to use instead of auto-detection
76
- */
77
- localAddress?: string
78
-
79
69
  /**
80
70
  * A string value to use for the port mapping description on the gateway
81
71
  */
82
- description?: string
83
-
84
- /**
85
- * How long UPnP port mappings should last for in seconds (minimum 1200)
86
- */
87
- ttl?: number
72
+ portMappingDescription?: string
88
73
 
89
74
  /**
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
96
78
  */
97
- gateway?: string
79
+ portMappingTTL?: number
98
80
 
99
81
  /**
100
- * How long in ms to wait before giving up trying to auto-detect a
101
- * `urn:schemas-upnp-org:device:InternetGatewayDevice:1` device on the local
102
- * network
82
+ * Whether to automatically refresh UPnP port mappings when their TTL is
83
+ * reached
103
84
  *
104
- * @default 10000
85
+ * @default true
105
86
  */
106
- gatewayDetectionTimeout?: number
87
+ portMappingAutoRefresh?: boolean
107
88
 
108
89
  /**
109
- * Ports are mapped when the `self:peer:update` event fires, which happens
110
- * when the node's addresses change. To avoid starting to map ports while
111
- * multiple addresses are being added, the mapping function is debounced by
112
- * this number of ms
90
+ * How long before a port mapping expires to refresh it in ms
113
91
  *
114
- * @default 5000
92
+ * @default 60_000
115
93
  */
116
- delay?: number
94
+ portMappingRefreshThreshold?: number
117
95
 
118
96
  /**
119
97
  * A preconfigured instance of a NatAPI client can be passed as an option,
120
98
  * otherwise one will be created
121
99
  */
122
- client?: NatAPI
100
+ portMappingClient?: UPnPNATClient
123
101
 
124
102
  /**
125
103
  * Any mapped addresses are added to the observed address list. These
@@ -142,7 +120,7 @@ export interface UPnPNATComponents {
142
120
  }
143
121
 
144
122
  export interface UPnPNAT {
145
- client: NatAPI
123
+ portMappingClient: UPnPNATClient
146
124
  }
147
125
 
148
126
  export function uPnPNAT (init: UPnPNATInit = {}): (components: UPnPNATComponents) => UPnPNAT {
package/src/upnp-nat.ts CHANGED
@@ -1,84 +1,55 @@
1
1
  import { upnpNat } from '@achingbrain/nat-port-mapper'
2
- import { isIPv4, isIPv6 } from '@chainsafe/is-ip'
3
- import { InvalidParametersError, serviceCapabilities, serviceDependencies, start, stop } from '@libp2p/interface'
2
+ import { serviceCapabilities, serviceDependencies, setMaxListeners, start, stop } from '@libp2p/interface'
4
3
  import { debounce } from '@libp2p/utils/debounce'
5
- import { isLoopback } from '@libp2p/utils/multiaddr/is-loopback'
6
- import { isPrivate } from '@libp2p/utils/multiaddr/is-private'
7
- import { isPrivateIp } from '@libp2p/utils/private-ip'
8
- import { multiaddr } from '@multiformats/multiaddr'
9
- import { QUICV1, TCP, WebSockets, WebSocketsSecure, WebTransport } from '@multiformats/multiaddr-matcher'
10
- import { raceSignal } from 'race-signal'
11
- import { dynamicExternalAddress, staticExternalAddress } from './check-external-address.js'
12
- import { DoubleNATError, InvalidIPAddressError } from './errors.js'
13
- import type { ExternalAddress } from './check-external-address.js'
4
+ import { GatewayFinder } from './gateway-finder.js'
5
+ import { UPnPPortMapper } from './upnp-port-mapper.js'
14
6
  import type { UPnPNATComponents, UPnPNATInit, UPnPNAT as UPnPNATInterface } from './index.js'
15
- import type { NatAPI, MapPortOptions } from '@achingbrain/nat-port-mapper'
16
- import type { Libp2pEvents, Logger, Startable, TypedEventTarget } from '@libp2p/interface'
17
- import type { AddressManager } from '@libp2p/interface-internal'
7
+ import type { Gateway, UPnPNAT as UPnPNATClient } from '@achingbrain/nat-port-mapper'
8
+ import type { Logger, Startable } from '@libp2p/interface'
18
9
  import type { DebouncedFunction } from '@libp2p/utils/debounce'
19
- import type { Multiaddr } from '@multiformats/multiaddr'
20
-
21
- const DEFAULT_TTL = 7200
22
-
23
- export type { NatAPI, MapPortOptions }
24
10
 
25
11
  export class UPnPNAT implements Startable, UPnPNATInterface {
26
- public client: NatAPI
27
- private readonly addressManager: AddressManager
28
- private readonly events: TypedEventTarget<Libp2pEvents>
29
- private readonly externalAddress: ExternalAddress
30
- private readonly localAddress?: string
31
- private readonly description: string
32
- private readonly ttl: number
33
- private readonly keepAlive: boolean
34
- private readonly gateway?: string
35
- private started: boolean
36
12
  private readonly log: Logger
37
- private readonly gatewayDetectionTimeout: number
38
- private readonly mappedPorts: Map<number, number>
39
- private readonly onSelfPeerUpdate: DebouncedFunction
13
+ private readonly components: UPnPNATComponents
14
+ private readonly init: UPnPNATInit
15
+ private started: boolean
16
+ public portMappingClient: UPnPNATClient
17
+ private shutdownController?: AbortController
18
+ private readonly mapIpAddressesDebounced: DebouncedFunction
19
+ private readonly gatewayFinder: GatewayFinder
20
+ private readonly portMappers: UPnPPortMapper[]
40
21
  private readonly autoConfirmAddress: boolean
41
22
 
42
23
  constructor (components: UPnPNATComponents, init: UPnPNATInit) {
43
24
  this.log = components.logger.forComponent('libp2p:upnp-nat')
44
- this.addressManager = components.addressManager
45
- this.events = components.events
25
+ this.components = components
26
+ this.init = init
46
27
  this.started = false
47
- this.localAddress = init.localAddress
48
- this.description = init.description ?? `${components.nodeInfo.name}@${components.nodeInfo.version} ${components.peerId.toString()}`
49
- this.ttl = init.ttl ?? DEFAULT_TTL
50
- this.keepAlive = init.keepAlive ?? true
51
- this.gateway = init.gateway
52
- this.gatewayDetectionTimeout = init.gatewayDetectionTimeout ?? 10000
28
+ this.portMappers = []
53
29
  this.autoConfirmAddress = init.autoConfirmAddress ?? false
54
- this.mappedPorts = new Map()
55
30
 
56
- if (this.ttl < DEFAULT_TTL) {
57
- throw new InvalidParametersError(`NatManager ttl should be at least ${DEFAULT_TTL} seconds`)
58
- }
59
-
60
- this.client = init.client ?? upnpNat({
61
- description: this.description,
62
- ttl: this.ttl,
63
- keepAlive: this.keepAlive,
64
- gateway: this.gateway
31
+ this.portMappingClient = init.portMappingClient ?? upnpNat({
32
+ description: init.portMappingDescription ?? `${components.nodeInfo.name}@${components.nodeInfo.version} ${components.peerId.toString()}`,
33
+ ttl: init.portMappingTTL,
34
+ autoRefresh: init.portMappingAutoRefresh,
35
+ refreshThreshold: init.portMappingRefreshThreshold
65
36
  })
66
37
 
67
- this.onSelfPeerUpdate = debounce(this._onSelfPeerUpdate.bind(this), init.delay ?? 5000)
38
+ // trigger update when our addresses change
39
+ this.mapIpAddressesDebounced = debounce(async () => {
40
+ try {
41
+ await this.mapIpAddresses()
42
+ } catch (err: any) {
43
+ this.log.error('error mapping IP addresses - %e', err)
44
+ }
45
+ }, 5_000)
68
46
 
69
- if (typeof init.externalAddress === 'string') {
70
- this.externalAddress = staticExternalAddress(init.externalAddress)
71
- } else {
72
- this.externalAddress = dynamicExternalAddress({
73
- client: this.client,
74
- addressManager: this.addressManager,
75
- logger: components.logger
76
- }, {
77
- autoConfirmAddress: init.autoConfirmAddress,
78
- interval: init.externalAddressCheckInterval,
79
- timeout: init.externalAddressCheckTimeout
80
- })
81
- }
47
+ // trigger update when we discovery gateways on the network
48
+ this.gatewayFinder = new GatewayFinder(components, {
49
+ portMappingClient: this.portMappingClient
50
+ })
51
+
52
+ this.onGatewayDiscovered = this.onGatewayDiscovered.bind(this)
82
53
  }
83
54
 
84
55
  readonly [Symbol.toStringTag] = '@libp2p/upnp-nat'
@@ -107,158 +78,48 @@ export class UPnPNAT implements Startable, UPnPNATInterface {
107
78
  }
108
79
 
109
80
  this.started = true
110
- this.events.addEventListener('self:peer:update', this.onSelfPeerUpdate)
111
- await start(this.externalAddress, this.onSelfPeerUpdate)
81
+ this.shutdownController = new AbortController()
82
+ setMaxListeners(Infinity, this.shutdownController.signal)
83
+ this.components.events.addEventListener('self:peer:update', this.mapIpAddressesDebounced)
84
+ this.gatewayFinder.addEventListener('gateway', this.onGatewayDiscovered)
85
+ await start(this.mapIpAddressesDebounced, this.gatewayFinder, ...this.portMappers)
112
86
  }
113
87
 
114
88
  /**
115
89
  * Stops the NAT manager
116
90
  */
117
91
  async stop (): Promise<void> {
118
- try {
119
- await this.client?.close()
120
- } catch (err: any) {
121
- this.log.error(err)
122
- }
123
-
124
- this.events.removeEventListener('self:peer:update', this.onSelfPeerUpdate)
125
- await stop(this.externalAddress, this.onSelfPeerUpdate)
92
+ this.shutdownController?.abort()
93
+ this.components.events.removeEventListener('self:peer:update', this.mapIpAddressesDebounced)
94
+ this.gatewayFinder.removeEventListener('gateway', this.onGatewayDiscovered)
95
+ await stop(this.mapIpAddressesDebounced, this.gatewayFinder, ...this.portMappers)
126
96
  this.started = false
127
97
  }
128
98
 
129
- _onSelfPeerUpdate (): void {
130
- this.mapIpAddresses()
131
- .catch(err => {
132
- this.log.error('error mapping IP addresses - %e', err)
133
- })
134
- }
135
-
136
- private getUnmappedAddresses (multiaddrs: Multiaddr[], ipType: 4 | 6): Multiaddr[] {
137
- const output: Multiaddr[] = []
138
-
139
- for (const ma of multiaddrs) {
140
- // ignore public addresses
141
- if (!isPrivate(ma)) {
142
- continue
143
- }
144
-
145
- // ignore loopback
146
- if (isLoopback(ma)) {
147
- continue
148
- }
149
-
150
- // only IP based addresses
151
- if (!(
152
- TCP.exactMatch(ma) ||
153
- WebSockets.exactMatch(ma) ||
154
- WebSocketsSecure.exactMatch(ma) ||
155
- QUICV1.exactMatch(ma) ||
156
- WebTransport.exactMatch(ma)
157
- )) {
158
- continue
159
- }
160
-
161
- const { port, family } = ma.toOptions()
162
-
163
- if (family !== ipType) {
164
- continue
165
- }
166
-
167
- if (this.mappedPorts.has(port)) {
168
- continue
169
- }
170
-
171
- output.push(ma)
172
- }
173
-
174
- return output
175
- }
176
-
177
- async mapIpAddresses (): Promise<void> {
178
- if (this.externalAddress == null) {
179
- this.log('discovering public address')
180
- }
181
-
182
- const publicIp = await this.externalAddress.getPublicIp({
183
- signal: AbortSignal.timeout(this.gatewayDetectionTimeout)
99
+ onGatewayDiscovered (event: CustomEvent<Gateway>): void {
100
+ const mapper = new UPnPPortMapper(this.components, {
101
+ ...this.init,
102
+ gateway: event.detail
184
103
  })
185
104
 
186
- this.externalAddress ?? await raceSignal(this.client.externalIp(), AbortSignal.timeout(this.gatewayDetectionTimeout), {
187
- errorMessage: `Did not discover a "urn:schemas-upnp-org:device:InternetGatewayDevice:1" device on the local network after ${this.gatewayDetectionTimeout}ms - UPnP may not be configured on your router correctly`
188
- })
189
-
190
- let ipType: 4 | 6 = 4
191
-
192
- if (isIPv4(publicIp)) {
193
- ipType = 4
194
- } else if (isIPv6(publicIp)) {
195
- ipType = 6
196
- } else {
197
- throw new InvalidIPAddressError(`Public address ${publicIp} was not an IPv4 address`)
198
- }
199
-
200
- // filter addresses to get private, non-relay, IP based addresses that we
201
- // haven't mapped yet
202
- const addresses = this.getUnmappedAddresses(this.addressManager.getAddresses(), ipType)
105
+ this.portMappers.push(mapper)
203
106
 
204
- if (addresses.length === 0) {
205
- this.log('no private, non-relay, unmapped, IP based addresses found')
206
- return
207
- }
208
-
209
- this.log('%s public IP %s', this.externalAddress != null ? 'using configured' : 'discovered', publicIp)
210
-
211
- this.assertNotBehindDoubleNAT(publicIp)
212
-
213
- for (const addr of addresses) {
214
- // try to open uPnP ports for each thin waist address
215
- const { family, host, port, transport } = addr.toOptions()
216
-
217
- if (family === 6) {
218
- // only support IPv4 addresses
219
- continue
220
- }
221
-
222
- if (this.mappedPorts.has(port)) {
223
- // already mapped this port
224
- continue
225
- }
226
-
227
- const protocol = transport.toUpperCase()
228
-
229
- this.log(`creating mapping of ${host}:${port}`)
230
-
231
- const mappedPort = await this.client.map(port, {
232
- localAddress: host,
233
- protocol: protocol === 'TCP' ? 'TCP' : 'UDP'
107
+ start(mapper)
108
+ .then(() => {
109
+ this.mapIpAddressesDebounced()
234
110
  })
235
-
236
- this.mappedPorts.set(port, mappedPort)
237
-
238
- const ma = multiaddr(addr.toString().replace(`/ip${family}/${host}/${transport}/${port}`, `/ip${family}/${publicIp}/${transport}/${mappedPort}`))
239
-
240
- this.log(`created mapping of ${publicIp}:${mappedPort} to ${host}:${port} as %a`, ma)
241
-
242
- if (this.autoConfirmAddress) {
243
- this.addressManager.confirmObservedAddr(ma)
244
- } else {
245
- this.addressManager.addObservedAddr(ma)
246
- }
247
- }
111
+ .catch(() => {})
248
112
  }
249
113
 
250
- /**
251
- * Some ISPs have double-NATs, there's not much we can do with them
252
- */
253
- private assertNotBehindDoubleNAT (publicIp: string): void {
254
- const isPrivate = isPrivateIp(publicIp)
255
-
256
- if (isPrivate === true) {
257
- throw new DoubleNATError(`${publicIp} is private - please set config.nat.externalIp to an externally routable IP or ensure you are not behind a double NAT`)
258
- }
259
-
260
- if (isPrivate == null) {
261
- throw new InvalidParametersError(`${publicIp} is not an IP address`)
114
+ async mapIpAddresses (): Promise<void> {
115
+ try {
116
+ await Promise.all(
117
+ this.portMappers.map(async mapper => mapper.mapIpAddresses({
118
+ autoConfirmAddress: this.autoConfirmAddress
119
+ }))
120
+ )
121
+ } catch (err: any) {
122
+ this.log.error('error mapping IP addresses - %e', err)
262
123
  }
263
124
  }
264
125
  }