@libp2p/upnp-nat 2.0.12-a82b07d8c → 2.0.12-b02ea9b6e
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.
- package/dist/src/check-external-address.d.ts +2 -2
- package/dist/src/check-external-address.d.ts.map +1 -1
- package/dist/src/check-external-address.js +4 -4
- package/dist/src/check-external-address.js.map +1 -1
- package/dist/src/constants.d.ts +4 -0
- package/dist/src/constants.d.ts.map +1 -0
- package/dist/src/constants.js +4 -0
- package/dist/src/constants.js.map +1 -0
- package/dist/src/gateway-finder.d.ts +26 -0
- package/dist/src/gateway-finder.d.ts.map +1 -0
- package/dist/src/gateway-finder.js +48 -0
- package/dist/src/gateway-finder.js.map +1 -0
- package/dist/src/index.d.ts +13 -30
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/upnp-nat.d.ts +9 -24
- package/dist/src/upnp-nat.d.ts.map +1 -1
- package/dist/src/upnp-nat.js +52 -165
- package/dist/src/upnp-nat.js.map +1 -1
- package/dist/src/upnp-port-mapper.d.ts +38 -0
- package/dist/src/upnp-port-mapper.d.ts.map +1 -0
- package/dist/src/upnp-port-mapper.js +166 -0
- package/dist/src/upnp-port-mapper.js.map +1 -0
- package/package.json +8 -8
- package/src/check-external-address.ts +7 -8
- package/src/constants.ts +3 -0
- package/src/gateway-finder.ts +70 -0
- package/src/index.ts +14 -34
- package/src/upnp-nat.ts +57 -209
- package/src/upnp-port-mapper.ts +223 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Gateway } from '@achingbrain/nat-port-mapper';
|
|
2
|
+
import type { ComponentLogger } from '@libp2p/interface';
|
|
3
|
+
import type { AddressManager } from '@libp2p/interface-internal';
|
|
4
|
+
export interface UPnPPortMapperInit {
|
|
5
|
+
gateway: Gateway;
|
|
6
|
+
externalAddressCheckInterval?: number;
|
|
7
|
+
externalAddressCheckTimeout?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface UPnPPortMapperComponents {
|
|
10
|
+
logger: ComponentLogger;
|
|
11
|
+
addressManager: AddressManager;
|
|
12
|
+
}
|
|
13
|
+
export declare class UPnPPortMapper {
|
|
14
|
+
private readonly gateway;
|
|
15
|
+
private readonly externalAddress;
|
|
16
|
+
private readonly addressManager;
|
|
17
|
+
private readonly log;
|
|
18
|
+
private readonly mappedPorts;
|
|
19
|
+
private started;
|
|
20
|
+
constructor(components: UPnPPortMapperComponents, init: UPnPPortMapperInit);
|
|
21
|
+
start(): Promise<void>;
|
|
22
|
+
stop(): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Update the local address mappings when the gateway's external interface
|
|
25
|
+
* address changes
|
|
26
|
+
*/
|
|
27
|
+
private remapPorts;
|
|
28
|
+
/**
|
|
29
|
+
* Return any eligible multiaddrs that are not mapped on the detected gateway
|
|
30
|
+
*/
|
|
31
|
+
private getUnmappedAddresses;
|
|
32
|
+
mapIpAddresses(): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Some ISPs have double-NATs, there's not much we can do with them
|
|
35
|
+
*/
|
|
36
|
+
private assertNotBehindDoubleNAT;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=upnp-port-mapper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upnp-port-mapper.d.ts","sourceRoot":"","sources":["../../src/upnp-port-mapper.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,8BAA8B,CAAA;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAU,MAAM,mBAAmB,CAAA;AAChE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAA;AAGhE,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAA;IAChB,4BAA4B,CAAC,EAAE,MAAM,CAAA;IACrC,2BAA2B,CAAC,EAAE,MAAM,CAAA;CACrC;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,eAAe,CAAA;IACvB,cAAc,EAAE,cAAc,CAAA;CAC/B;AAOD,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAiB;IACjD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAgB;IAC/C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAQ;IAC5B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA0B;IACtD,OAAO,CAAC,OAAO,CAAS;gBAEX,UAAU,EAAE,wBAAwB,EAAE,IAAI,EAAE,kBAAkB;IAkBrE,KAAK,IAAK,OAAO,CAAC,IAAI,CAAC;IASvB,IAAI,IAAK,OAAO,CAAC,IAAI,CAAC;IAe5B;;;OAGG;IACH,OAAO,CAAC,UAAU;IAalB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAyCtB,cAAc,IAAK,OAAO,CAAC,IAAI,CAAC;IAmEtC;;OAEG;IACH,OAAO,CAAC,wBAAwB;CAWjC"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { isIPv4, isIPv6 } from '@chainsafe/is-ip';
|
|
2
|
+
import { InvalidParametersError, start, stop } from '@libp2p/interface';
|
|
3
|
+
import { isLoopback } from '@libp2p/utils/multiaddr/is-loopback';
|
|
4
|
+
import { isPrivate } from '@libp2p/utils/multiaddr/is-private';
|
|
5
|
+
import { isPrivateIp } from '@libp2p/utils/private-ip';
|
|
6
|
+
import { QUICV1, TCP, WebSockets, WebSocketsSecure, WebTransport } from '@multiformats/multiaddr-matcher';
|
|
7
|
+
import { dynamicExternalAddress } from './check-external-address.js';
|
|
8
|
+
import { DoubleNATError, InvalidIPAddressError } from './errors.js';
|
|
9
|
+
export class UPnPPortMapper {
|
|
10
|
+
gateway;
|
|
11
|
+
externalAddress;
|
|
12
|
+
addressManager;
|
|
13
|
+
log;
|
|
14
|
+
mappedPorts;
|
|
15
|
+
started;
|
|
16
|
+
constructor(components, init) {
|
|
17
|
+
this.log = components.logger.forComponent(`libp2p:upnp-nat:gateway:${init.gateway.id}`);
|
|
18
|
+
this.addressManager = components.addressManager;
|
|
19
|
+
this.gateway = init.gateway;
|
|
20
|
+
this.externalAddress = dynamicExternalAddress({
|
|
21
|
+
gateway: this.gateway,
|
|
22
|
+
addressManager: this.addressManager,
|
|
23
|
+
logger: components.logger
|
|
24
|
+
}, {
|
|
25
|
+
interval: init.externalAddressCheckInterval,
|
|
26
|
+
timeout: init.externalAddressCheckTimeout,
|
|
27
|
+
onExternalAddressChange: this.remapPorts.bind(this)
|
|
28
|
+
});
|
|
29
|
+
this.gateway = init.gateway;
|
|
30
|
+
this.mappedPorts = new Map();
|
|
31
|
+
this.started = false;
|
|
32
|
+
}
|
|
33
|
+
async start() {
|
|
34
|
+
if (this.started) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
await start(this.externalAddress);
|
|
38
|
+
this.started = true;
|
|
39
|
+
}
|
|
40
|
+
async stop() {
|
|
41
|
+
try {
|
|
42
|
+
const shutdownTimeout = AbortSignal.timeout(1000);
|
|
43
|
+
await this.gateway.stop({
|
|
44
|
+
signal: shutdownTimeout
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
this.log.error('error closing gateway - %e', err);
|
|
49
|
+
}
|
|
50
|
+
await stop(this.externalAddress);
|
|
51
|
+
this.started = false;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Update the local address mappings when the gateway's external interface
|
|
55
|
+
* address changes
|
|
56
|
+
*/
|
|
57
|
+
remapPorts(newExternalHost) {
|
|
58
|
+
for (const [key, { externalHost, externalPort }] of this.mappedPorts.entries()) {
|
|
59
|
+
const [host, port, transport] = key.split('-');
|
|
60
|
+
this.addressManager.removePublicAddressMapping(host, parseInt(port), externalHost, externalPort, transport === 'tcp' ? 'tcp' : 'udp');
|
|
61
|
+
this.addressManager.addPublicAddressMapping(host, parseInt(port), newExternalHost, externalPort, transport === 'tcp' ? 'tcp' : 'udp');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Return any eligible multiaddrs that are not mapped on the detected gateway
|
|
66
|
+
*/
|
|
67
|
+
getUnmappedAddresses(multiaddrs, ipType) {
|
|
68
|
+
const output = [];
|
|
69
|
+
for (const ma of multiaddrs) {
|
|
70
|
+
// ignore public addresses
|
|
71
|
+
if (!isPrivate(ma)) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
// ignore loopback
|
|
75
|
+
if (isLoopback(ma)) {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
// only IP based addresses
|
|
79
|
+
if (!(TCP.exactMatch(ma) ||
|
|
80
|
+
WebSockets.exactMatch(ma) ||
|
|
81
|
+
WebSocketsSecure.exactMatch(ma) ||
|
|
82
|
+
QUICV1.exactMatch(ma) ||
|
|
83
|
+
WebTransport.exactMatch(ma))) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const { port, host, family, transport } = ma.toOptions();
|
|
87
|
+
if (family !== ipType) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (this.mappedPorts.has(`${host}-${port}-${transport}`)) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
output.push(ma);
|
|
94
|
+
}
|
|
95
|
+
return output;
|
|
96
|
+
}
|
|
97
|
+
async mapIpAddresses() {
|
|
98
|
+
try {
|
|
99
|
+
const externalHost = await this.externalAddress.getPublicIp();
|
|
100
|
+
let ipType = 4;
|
|
101
|
+
if (isIPv4(externalHost)) {
|
|
102
|
+
ipType = 4;
|
|
103
|
+
}
|
|
104
|
+
else if (isIPv6(externalHost)) {
|
|
105
|
+
ipType = 6;
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
throw new InvalidIPAddressError(`Public address ${externalHost} was not an IPv4 address`);
|
|
109
|
+
}
|
|
110
|
+
// filter addresses to get private, non-relay, IP based addresses that we
|
|
111
|
+
// haven't mapped yet
|
|
112
|
+
const addresses = this.getUnmappedAddresses(this.addressManager.getAddresses(), ipType);
|
|
113
|
+
if (addresses.length === 0) {
|
|
114
|
+
this.log('no private, non-relay, unmapped, IP based addresses found');
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
this.log('%s public IP %s', this.externalAddress != null ? 'using configured' : 'discovered', externalHost);
|
|
118
|
+
this.assertNotBehindDoubleNAT(externalHost);
|
|
119
|
+
for (const addr of addresses) {
|
|
120
|
+
// try to open uPnP ports for each thin waist address
|
|
121
|
+
const { family, host, port, transport } = addr.toOptions();
|
|
122
|
+
if (family === 6) {
|
|
123
|
+
// only support IPv4 addresses
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (this.mappedPorts.has(`${host}-${port}-${transport}`)) {
|
|
127
|
+
// already mapped this port
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
const key = `${host}-${port}-${transport}`;
|
|
132
|
+
this.log('creating mapping of key %s', key);
|
|
133
|
+
const externalPort = await this.gateway.map(port, {
|
|
134
|
+
localAddress: host,
|
|
135
|
+
protocol: transport === 'tcp' ? 'tcp' : 'udp'
|
|
136
|
+
});
|
|
137
|
+
this.mappedPorts.set(key, {
|
|
138
|
+
externalHost,
|
|
139
|
+
externalPort
|
|
140
|
+
});
|
|
141
|
+
this.log('created mapping of %s:%s to %s:%s', externalHost, externalPort, host, port);
|
|
142
|
+
this.addressManager.addPublicAddressMapping(host, port, externalHost, externalPort, transport === 'tcp' ? 'tcp' : 'udp');
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
this.log.error('failed to create mapping of %s:%s - %e', host, port, err);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
this.log.error('error finding gateways - %e', err);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Some ISPs have double-NATs, there's not much we can do with them
|
|
155
|
+
*/
|
|
156
|
+
assertNotBehindDoubleNAT(publicIp) {
|
|
157
|
+
const isPrivate = isPrivateIp(publicIp);
|
|
158
|
+
if (isPrivate === true) {
|
|
159
|
+
throw new DoubleNATError(`${publicIp} is private - please init uPnPNAT with 'externalAddress' set to an externally routable IP or ensure you are not behind a double NAT`);
|
|
160
|
+
}
|
|
161
|
+
if (isPrivate == null) {
|
|
162
|
+
throw new InvalidParametersError(`${publicIp} is not an IP address`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=upnp-port-mapper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upnp-port-mapper.js","sourceRoot":"","sources":["../../src/upnp-port-mapper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AACjD,OAAO,EAAE,sBAAsB,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAA;AACvE,OAAO,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAA;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,oCAAoC,CAAA;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AACtD,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAA;AACzG,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAA;AACpE,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAuBnE,MAAM,OAAO,cAAc;IACR,OAAO,CAAS;IAChB,eAAe,CAAiB;IAChC,cAAc,CAAgB;IAC9B,GAAG,CAAQ;IACX,WAAW,CAA0B;IAC9C,OAAO,CAAS;IAExB,YAAa,UAAoC,EAAE,IAAwB;QACzE,IAAI,CAAC,GAAG,GAAG,UAAU,CAAC,MAAM,CAAC,YAAY,CAAC,2BAA2B,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,CAAA;QACvF,IAAI,CAAC,cAAc,GAAG,UAAU,CAAC,cAAc,CAAA;QAC/C,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAA;QAC3B,IAAI,CAAC,eAAe,GAAG,sBAAsB,CAAC;YAC5C,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,cAAc,EAAE,IAAI,CAAC,cAAc;YACnC,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,EAAE;YACD,QAAQ,EAAE,IAAI,CAAC,4BAA4B;YAC3C,OAAO,EAAE,IAAI,CAAC,2BAA2B;YACzC,uBAAuB,EAAE,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;SACpD,CAAC,CAAA;QACF,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAA;QAC3B,IAAI,CAAC,WAAW,GAAG,IAAI,GAAG,EAAE,CAAA;QAC5B,IAAI,CAAC,OAAO,GAAG,KAAK,CAAA;IACtB,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,OAAM;QACR,CAAC;QAED,MAAM,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAA;QACjC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAA;IACrB,CAAC;IAED,KAAK,CAAC,IAAI;QACR,IAAI,CAAC;YACH,MAAM,eAAe,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;YAEjD,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;gBACtB,MAAM,EAAE,eAAe;aACxB,CAAC,CAAA;QACJ,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,4BAA4B,EAAE,GAAG,CAAC,CAAA;QACnD,CAAC;QAED,MAAM,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,CAAA;QAChC,IAAI,CAAC,OAAO,GAAG,KAAK,CAAA;IACtB,CAAC;IAED;;;OAGG;IACK,UAAU,CAAE,eAAuB;QACzC,KAAK,MAAM,CAAC,GAAG,EAAE,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC,IAAI,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,EAAE,CAAC;YAC/E,MAAM,CACJ,IAAI,EACJ,IAAI,EACJ,SAAS,CACV,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;YAElB,IAAI,CAAC,cAAc,CAAC,0BAA0B,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;YACrI,IAAI,CAAC,cAAc,CAAC,uBAAuB,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,eAAe,EAAE,YAAY,EAAE,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;QACvI,CAAC;IACH,CAAC;IAED;;OAEG;IACK,oBAAoB,CAAE,UAAuB,EAAE,MAAa;QAClE,MAAM,MAAM,GAAgB,EAAE,CAAA;QAE9B,KAAK,MAAM,EAAE,IAAI,UAAU,EAAE,CAAC;YAC5B,0BAA0B;YAC1B,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC;gBACnB,SAAQ;YACV,CAAC;YAED,kBAAkB;YAClB,IAAI,UAAU,CAAC,EAAE,CAAC,EAAE,CAAC;gBACnB,SAAQ;YACV,CAAC;YAED,0BAA0B;YAC1B,IAAI,CAAC,CACH,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;gBAClB,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;gBACzB,gBAAgB,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC/B,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;gBACrB,YAAY,CAAC,UAAU,CAAC,EAAE,CAAC,CAC5B,EAAE,CAAC;gBACF,SAAQ;YACV,CAAC;YAED,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,SAAS,EAAE,CAAA;YAExD,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;gBACtB,SAAQ;YACV,CAAC;YAED,IAAI,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,IAAI,IAAI,IAAI,IAAI,SAAS,EAAE,CAAC,EAAE,CAAC;gBACzD,SAAQ;YACV,CAAC;YAED,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QACjB,CAAC;QAED,OAAO,MAAM,CAAA;IACf,CAAC;IAED,KAAK,CAAC,cAAc;QAClB,IAAI,CAAC;YACH,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,WAAW,EAAE,CAAA;YAE7D,IAAI,MAAM,GAAU,CAAC,CAAA;YAErB,IAAI,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC;gBACzB,MAAM,GAAG,CAAC,CAAA;YACZ,CAAC;iBAAM,IAAI,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC;gBAChC,MAAM,GAAG,CAAC,CAAA;YACZ,CAAC;iBAAM,CAAC;gBACN,MAAM,IAAI,qBAAqB,CAAC,kBAAkB,YAAY,0BAA0B,CAAC,CAAA;YAC3F,CAAC;YAED,yEAAyE;YACzE,qBAAqB;YACrB,MAAM,SAAS,GAAG,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,cAAc,CAAC,YAAY,EAAE,EAAE,MAAM,CAAC,CAAA;YAEvF,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC3B,IAAI,CAAC,GAAG,CAAC,2DAA2D,CAAC,CAAA;gBACrE,OAAM;YACR,CAAC;YAED,IAAI,CAAC,GAAG,CAAC,iBAAiB,EAAE,IAAI,CAAC,eAAe,IAAI,IAAI,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,YAAY,EAAE,YAAY,CAAC,CAAA;YAE3G,IAAI,CAAC,wBAAwB,CAAC,YAAY,CAAC,CAAA;YAE3C,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;gBAC7B,qDAAqD;gBACrD,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAA;gBAE1D,IAAI,MAAM,KAAK,CAAC,EAAE,CAAC;oBACjB,8BAA8B;oBAC9B,SAAQ;gBACV,CAAC;gBAED,IAAI,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,IAAI,IAAI,IAAI,IAAI,SAAS,EAAE,CAAC,EAAE,CAAC;oBACzD,2BAA2B;oBAC3B,SAAQ;gBACV,CAAC;gBAED,IAAI,CAAC;oBACH,MAAM,GAAG,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,SAAS,EAAE,CAAA;oBAC1C,IAAI,CAAC,GAAG,CAAC,4BAA4B,EAAE,GAAG,CAAC,CAAA;oBAE3C,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE;wBAChD,YAAY,EAAE,IAAI;wBAClB,QAAQ,EAAE,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK;qBAC9C,CAAC,CAAA;oBAEF,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE;wBACxB,YAAY;wBACZ,YAAY;qBACb,CAAC,CAAA;oBAEF,IAAI,CAAC,GAAG,CAAC,mCAAmC,EAAE,YAAY,EAAE,YAAY,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA;oBAErF,IAAI,CAAC,cAAc,CAAC,uBAAuB,CAAC,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;gBAC1H,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,wCAAwC,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,CAAA;gBAC3E,CAAC;YACH,CAAC;QACH,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAA;QACpD,CAAC;IACH,CAAC;IAED;;OAEG;IACK,wBAAwB,CAAE,QAAgB;QAChD,MAAM,SAAS,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAA;QAEvC,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;YACvB,MAAM,IAAI,cAAc,CAAC,GAAG,QAAQ,qIAAqI,CAAC,CAAA;QAC5K,CAAC;QAED,IAAI,SAAS,IAAI,IAAI,EAAE,CAAC;YACtB,MAAM,IAAI,sBAAsB,CAAC,GAAG,QAAQ,uBAAuB,CAAC,CAAA;QACtE,CAAC;IACH,CAAC;CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@libp2p/upnp-nat",
|
|
3
|
-
"version": "2.0.12-
|
|
3
|
+
"version": "2.0.12-b02ea9b6e",
|
|
4
4
|
"description": "UPnP NAT hole punching",
|
|
5
5
|
"license": "Apache-2.0 OR MIT",
|
|
6
6
|
"homepage": "https://github.com/libp2p/js-libp2p/tree/main/packages/upnp-nat#readme",
|
|
@@ -50,20 +50,20 @@
|
|
|
50
50
|
"test:electron-main": "aegir test -t electron-main"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@achingbrain/nat-port-mapper": "^
|
|
53
|
+
"@achingbrain/nat-port-mapper": "^3.0.1",
|
|
54
54
|
"@chainsafe/is-ip": "^2.0.2",
|
|
55
|
-
"@libp2p/interface": "2.2.1-
|
|
56
|
-
"@libp2p/interface-internal": "2.1.1-
|
|
57
|
-
"@libp2p/utils": "6.2.1-
|
|
55
|
+
"@libp2p/interface": "2.2.1-b02ea9b6e",
|
|
56
|
+
"@libp2p/interface-internal": "2.1.1-b02ea9b6e",
|
|
57
|
+
"@libp2p/utils": "6.2.1-b02ea9b6e",
|
|
58
58
|
"@multiformats/multiaddr": "^12.2.3",
|
|
59
59
|
"@multiformats/multiaddr-matcher": "^1.4.0",
|
|
60
60
|
"p-defer": "^4.0.1",
|
|
61
61
|
"race-signal": "^1.1.0"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
|
-
"@libp2p/crypto": "5.0.7-
|
|
65
|
-
"@libp2p/logger": "5.1.4-
|
|
66
|
-
"@libp2p/peer-id": "5.0.8-
|
|
64
|
+
"@libp2p/crypto": "5.0.7-b02ea9b6e",
|
|
65
|
+
"@libp2p/logger": "5.1.4-b02ea9b6e",
|
|
66
|
+
"@libp2p/peer-id": "5.0.8-b02ea9b6e",
|
|
67
67
|
"aegir": "^44.0.1",
|
|
68
68
|
"sinon-ts": "^2.0.0"
|
|
69
69
|
},
|
|
@@ -2,14 +2,14 @@ import { NotStartedError, start, stop } from '@libp2p/interface'
|
|
|
2
2
|
import { repeatingTask } from '@libp2p/utils/repeating-task'
|
|
3
3
|
import pDefer from 'p-defer'
|
|
4
4
|
import { raceSignal } from 'race-signal'
|
|
5
|
-
import type {
|
|
5
|
+
import type { Gateway } from '@achingbrain/nat-port-mapper'
|
|
6
6
|
import type { AbortOptions, ComponentLogger, Logger, Startable } from '@libp2p/interface'
|
|
7
7
|
import type { AddressManager } from '@libp2p/interface-internal'
|
|
8
8
|
import type { RepeatingTask } from '@libp2p/utils/repeating-task'
|
|
9
9
|
import type { DeferredPromise } from 'p-defer'
|
|
10
10
|
|
|
11
11
|
export interface ExternalAddressCheckerComponents {
|
|
12
|
-
|
|
12
|
+
gateway: Gateway
|
|
13
13
|
addressManager: AddressManager
|
|
14
14
|
logger: ComponentLogger
|
|
15
15
|
}
|
|
@@ -29,7 +29,7 @@ export interface ExternalAddress {
|
|
|
29
29
|
*/
|
|
30
30
|
class ExternalAddressChecker implements ExternalAddress, Startable {
|
|
31
31
|
private readonly log: Logger
|
|
32
|
-
private readonly
|
|
32
|
+
private readonly gateway: Gateway
|
|
33
33
|
private readonly addressManager: AddressManager
|
|
34
34
|
private started: boolean
|
|
35
35
|
private lastPublicIp?: string
|
|
@@ -39,7 +39,7 @@ class ExternalAddressChecker implements ExternalAddress, Startable {
|
|
|
39
39
|
|
|
40
40
|
constructor (components: ExternalAddressCheckerComponents, init: ExternalAddressCheckerInit) {
|
|
41
41
|
this.log = components.logger.forComponent('libp2p:upnp-nat:external-address-check')
|
|
42
|
-
this.
|
|
42
|
+
this.gateway = components.gateway
|
|
43
43
|
this.addressManager = components.addressManager
|
|
44
44
|
this.onExternalAddressChange = init.onExternalAddressChange
|
|
45
45
|
this.started = false
|
|
@@ -60,14 +60,11 @@ class ExternalAddressChecker implements ExternalAddress, Startable {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
await start(this.check)
|
|
63
|
-
|
|
64
|
-
this.check.start()
|
|
65
63
|
this.started = true
|
|
66
64
|
}
|
|
67
65
|
|
|
68
66
|
async stop (): Promise<void> {
|
|
69
67
|
await stop(this.check)
|
|
70
|
-
|
|
71
68
|
this.started = false
|
|
72
69
|
}
|
|
73
70
|
|
|
@@ -86,7 +83,7 @@ class ExternalAddressChecker implements ExternalAddress, Startable {
|
|
|
86
83
|
|
|
87
84
|
private async checkExternalAddress (options?: AbortOptions): Promise<void> {
|
|
88
85
|
try {
|
|
89
|
-
const externalAddress = await this.
|
|
86
|
+
const externalAddress = await this.gateway.externalIp(options)
|
|
90
87
|
|
|
91
88
|
// check if our public address has changed
|
|
92
89
|
if (this.lastPublicIp != null && externalAddress !== this.lastPublicIp) {
|
|
@@ -99,6 +96,8 @@ class ExternalAddressChecker implements ExternalAddress, Startable {
|
|
|
99
96
|
this.lastPublicIp = externalAddress
|
|
100
97
|
this.lastPublicIpPromise.resolve(externalAddress)
|
|
101
98
|
} catch (err: any) {
|
|
99
|
+
this.log.error('could not resolve external address - %e', err)
|
|
100
|
+
|
|
102
101
|
if (this.lastPublicIp != null) {
|
|
103
102
|
// ignore the error if we've previously run successfully
|
|
104
103
|
return
|
package/src/constants.ts
ADDED
|
@@ -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
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -35,11 +35,12 @@
|
|
|
35
35
|
* ```
|
|
36
36
|
*/
|
|
37
37
|
|
|
38
|
-
import { UPnPNAT as UPnPNATClass
|
|
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 {
|
|
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,46 +66,31 @@ 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
|
-
|
|
72
|
+
portMappingDescription?: string
|
|
83
73
|
|
|
84
74
|
/**
|
|
85
|
-
* How long UPnP port mappings should last for in
|
|
86
|
-
|
|
87
|
-
|
|
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
|
|
96
78
|
*/
|
|
97
|
-
|
|
79
|
+
portMappingTTL?: number
|
|
98
80
|
|
|
99
81
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
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
|
|
104
84
|
*
|
|
105
|
-
* @default
|
|
85
|
+
* @default true
|
|
106
86
|
*/
|
|
107
|
-
|
|
87
|
+
portMappingAutoRefresh?: boolean
|
|
108
88
|
|
|
109
89
|
/**
|
|
110
90
|
* A preconfigured instance of a NatAPI client can be passed as an option,
|
|
111
91
|
* otherwise one will be created
|
|
112
92
|
*/
|
|
113
|
-
|
|
93
|
+
portMappingClient?: UPnPNATClient
|
|
114
94
|
}
|
|
115
95
|
|
|
116
96
|
export interface UPnPNATComponents {
|
|
@@ -122,7 +102,7 @@ export interface UPnPNATComponents {
|
|
|
122
102
|
}
|
|
123
103
|
|
|
124
104
|
export interface UPnPNAT {
|
|
125
|
-
|
|
105
|
+
portMappingClient: UPnPNATClient
|
|
126
106
|
}
|
|
127
107
|
|
|
128
108
|
export function uPnPNAT (init: UPnPNATInit = {}): (components: UPnPNATComponents) => UPnPNAT {
|