@libp2p/autonat-v2 1.0.1 → 2.0.0-049bfa0fa
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/index.min.js +1 -1
- package/dist/index.min.js.map +4 -4
- package/dist/src/client.d.ts +2 -2
- package/dist/src/client.d.ts.map +1 -1
- package/dist/src/client.js +26 -25
- package/dist/src/client.js.map +1 -1
- package/dist/src/server.d.ts +2 -2
- package/dist/src/server.d.ts.map +1 -1
- package/dist/src/server.js +60 -71
- package/dist/src/server.js.map +1 -1
- package/package.json +16 -17
- package/src/client.ts +28 -29
- package/src/server.ts +71 -80
- package/dist/typedoc-urls.json +0 -8
package/src/client.ts
CHANGED
|
@@ -1,23 +1,16 @@
|
|
|
1
|
-
import { ProtocolError, serviceCapabilities, serviceDependencies } from '@libp2p/interface'
|
|
1
|
+
import { InvalidParametersError, ProtocolError, serviceCapabilities, serviceDependencies } from '@libp2p/interface'
|
|
2
2
|
import { peerSet } from '@libp2p/peer-collections'
|
|
3
|
-
import { createScalableCuckooFilter } from '@libp2p/utils
|
|
4
|
-
import { isGlobalUnicast } from '@libp2p/utils/multiaddr/is-global-unicast'
|
|
5
|
-
import { isPrivate } from '@libp2p/utils/multiaddr/is-private'
|
|
6
|
-
import { PeerQueue } from '@libp2p/utils/peer-queue'
|
|
7
|
-
import { repeatingTask } from '@libp2p/utils/repeating-task'
|
|
8
|
-
import { trackedMap } from '@libp2p/utils/tracked-map'
|
|
3
|
+
import { createScalableCuckooFilter, isGlobalUnicast, isPrivate, PeerQueue, repeatingTask, trackedMap, pbStream, getNetConfig } from '@libp2p/utils'
|
|
9
4
|
import { anySignal } from 'any-signal'
|
|
10
|
-
import { pbStream } from 'it-protobuf-stream'
|
|
11
5
|
import { setMaxListeners } from 'main-event'
|
|
12
6
|
import { DEFAULT_CONNECTION_THRESHOLD, DIAL_DATA_CHUNK_SIZE, MAX_DIAL_DATA_BYTES, MAX_INBOUND_STREAMS, MAX_MESSAGE_SIZE, MAX_OUTBOUND_STREAMS, TIMEOUT } from './constants.ts'
|
|
13
7
|
import { DialBack, DialBackResponse, DialResponse, DialStatus, Message } from './pb/index.ts'
|
|
14
8
|
import { randomNumber } from './utils.ts'
|
|
15
9
|
import type { AutoNATv2Components, AutoNATv2ServiceInit } from './index.ts'
|
|
16
|
-
import type { Logger, Connection, Startable, AbortOptions,
|
|
10
|
+
import type { Logger, Connection, Startable, AbortOptions, Stream } from '@libp2p/interface'
|
|
17
11
|
import type { AddressType } from '@libp2p/interface-internal'
|
|
18
12
|
import type { PeerSet } from '@libp2p/peer-collections'
|
|
19
|
-
import type { Filter } from '@libp2p/utils
|
|
20
|
-
import type { RepeatingTask } from '@libp2p/utils/repeating-task'
|
|
13
|
+
import type { Filter, RepeatingTask } from '@libp2p/utils'
|
|
21
14
|
import type { Multiaddr } from '@multiformats/multiaddr'
|
|
22
15
|
|
|
23
16
|
// if more than 3 peers manage to dial us on what we believe to be our external
|
|
@@ -154,8 +147,8 @@ export class AutoNATv2Client implements Startable {
|
|
|
154
147
|
}
|
|
155
148
|
})
|
|
156
149
|
|
|
157
|
-
await this.components.registrar.handle(this.dialBackProtocol, (
|
|
158
|
-
void this.handleDialBackStream(
|
|
150
|
+
await this.components.registrar.handle(this.dialBackProtocol, (stream, connection) => {
|
|
151
|
+
void this.handleDialBackStream(stream, connection)
|
|
159
152
|
.catch(err => {
|
|
160
153
|
this.log.error('error handling incoming autonat stream - %e', err)
|
|
161
154
|
})
|
|
@@ -239,11 +232,11 @@ export class AutoNATv2Client implements Startable {
|
|
|
239
232
|
/**
|
|
240
233
|
* Handle an incoming AutoNAT request
|
|
241
234
|
*/
|
|
242
|
-
async handleDialBackStream (
|
|
235
|
+
async handleDialBackStream (stream: Stream, connection: Connection): Promise<void> {
|
|
243
236
|
const signal = AbortSignal.timeout(this.timeout)
|
|
244
237
|
setMaxListeners(Infinity, signal)
|
|
245
238
|
|
|
246
|
-
const messages = pbStream(
|
|
239
|
+
const messages = pbStream(stream, {
|
|
247
240
|
maxDataLength: this.maxMessageSize
|
|
248
241
|
})
|
|
249
242
|
|
|
@@ -264,12 +257,12 @@ export class AutoNATv2Client implements Startable {
|
|
|
264
257
|
status: DialBackResponse.DialBackStatus.OK
|
|
265
258
|
}, DialBackResponse)
|
|
266
259
|
|
|
267
|
-
await
|
|
260
|
+
await stream.close({
|
|
268
261
|
signal
|
|
269
262
|
})
|
|
270
263
|
} catch (err: any) {
|
|
271
264
|
this.log.error('error handling incoming dial back stream - %e', err)
|
|
272
|
-
|
|
265
|
+
stream.abort(err)
|
|
273
266
|
}
|
|
274
267
|
}
|
|
275
268
|
|
|
@@ -295,9 +288,9 @@ export class AutoNATv2Client implements Startable {
|
|
|
295
288
|
return false
|
|
296
289
|
}
|
|
297
290
|
|
|
298
|
-
const options = addr.multiaddr
|
|
291
|
+
const options = getNetConfig(addr.multiaddr)
|
|
299
292
|
|
|
300
|
-
if (options.
|
|
293
|
+
if (options.type === 'ip6') {
|
|
301
294
|
// do not send IPv6 addresses to peers without IPv6 addresses
|
|
302
295
|
if (!supportsIPv6) {
|
|
303
296
|
return false
|
|
@@ -409,7 +402,7 @@ export class AutoNATv2Client implements Startable {
|
|
|
409
402
|
// if the remote peer has IPv6 addresses, we can probably send them an IPv6
|
|
410
403
|
// address to verify, otherwise only send them IPv4 addresses
|
|
411
404
|
const supportsIPv6 = peer.addresses.some(({ multiaddr }) => {
|
|
412
|
-
return multiaddr
|
|
405
|
+
return getNetConfig(multiaddr).type === 'ip6'
|
|
413
406
|
})
|
|
414
407
|
|
|
415
408
|
// get multiaddrs this peer is eligible to verify
|
|
@@ -465,7 +458,7 @@ export class AutoNATv2Client implements Startable {
|
|
|
465
458
|
return
|
|
466
459
|
}
|
|
467
460
|
|
|
468
|
-
this.log.trace('asking %
|
|
461
|
+
this.log.trace('asking %a to verify multiaddrs %s', connection.remoteAddr, unverifiedAddresses)
|
|
469
462
|
|
|
470
463
|
const stream = await connection.newStream(this.dialRequestProtocol, options)
|
|
471
464
|
|
|
@@ -478,7 +471,7 @@ export class AutoNATv2Client implements Startable {
|
|
|
478
471
|
}
|
|
479
472
|
}, options)
|
|
480
473
|
|
|
481
|
-
|
|
474
|
+
for (let i = 0; i < unverifiedAddresses.length; i++) {
|
|
482
475
|
let response = await messages.read(options)
|
|
483
476
|
|
|
484
477
|
if (response.dialDataRequest != null) {
|
|
@@ -615,14 +608,20 @@ export class AutoNATv2Client implements Startable {
|
|
|
615
608
|
|
|
616
609
|
private getNetworkSegment (ma: Multiaddr): string {
|
|
617
610
|
// make sure we use different network segments
|
|
618
|
-
const options = ma
|
|
611
|
+
const options = getNetConfig(ma)
|
|
619
612
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
613
|
+
switch (options.type) {
|
|
614
|
+
case 'ip4': {
|
|
615
|
+
const octets = options.host.split('.')
|
|
616
|
+
return octets[0].padStart(3, '0')
|
|
617
|
+
}
|
|
618
|
+
case 'ip6': {
|
|
619
|
+
const octets = options.host.split(':')
|
|
620
|
+
return octets[0].padStart(4, '0')
|
|
621
|
+
}
|
|
622
|
+
default: {
|
|
623
|
+
throw new InvalidParametersError(`Remote address ${ma} was not an IPv4 or Ipv6 address`)
|
|
624
|
+
}
|
|
623
625
|
}
|
|
624
|
-
|
|
625
|
-
const octets = options.host.split(':')
|
|
626
|
-
return octets[0].padStart(4, '0')
|
|
627
626
|
}
|
|
628
627
|
}
|
package/src/server.ts
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { ProtocolError } from '@libp2p/interface'
|
|
2
|
-
import { isPrivateIp } from '@libp2p/utils
|
|
2
|
+
import { isPrivateIp, pbStream } from '@libp2p/utils'
|
|
3
3
|
import { CODE_IP4, CODE_IP6, multiaddr } from '@multiformats/multiaddr'
|
|
4
|
-
import { pbStream } from 'it-protobuf-stream'
|
|
5
4
|
import { setMaxListeners } from 'main-event'
|
|
6
5
|
import { MAX_INBOUND_STREAMS, MAX_MESSAGE_SIZE, MAX_OUTBOUND_STREAMS, TIMEOUT } from './constants.ts'
|
|
7
6
|
import { DialBack, DialBackResponse, DialResponse, DialStatus, Message } from './pb/index.ts'
|
|
8
7
|
import { randomNumber } from './utils.ts'
|
|
9
8
|
import type { AutoNATv2Components, AutoNATv2ServiceInit } from './index.ts'
|
|
10
|
-
import type { Logger, Connection, Startable, AbortOptions,
|
|
9
|
+
import type { Logger, Connection, Startable, AbortOptions, Stream } from '@libp2p/interface'
|
|
10
|
+
import type { ProtobufMessageStream } from '@libp2p/utils'
|
|
11
11
|
import type { Multiaddr } from '@multiformats/multiaddr'
|
|
12
|
-
import type { MessageStream } from 'it-protobuf-stream'
|
|
13
12
|
|
|
14
13
|
export interface AutoNATv2ServerInit extends AutoNATv2ServiceInit {
|
|
15
14
|
dialRequestProtocol: string
|
|
@@ -37,6 +36,8 @@ export class AutoNATv2Server implements Startable {
|
|
|
37
36
|
this.maxInboundStreams = init.maxInboundStreams ?? MAX_INBOUND_STREAMS
|
|
38
37
|
this.maxOutboundStreams = init.maxOutboundStreams ?? MAX_OUTBOUND_STREAMS
|
|
39
38
|
this.maxMessageSize = init.maxMessageSize ?? MAX_MESSAGE_SIZE
|
|
39
|
+
|
|
40
|
+
this.handleDialRequestStream = this.handleDialRequestStream.bind(this)
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
async start (): Promise<void> {
|
|
@@ -45,12 +46,7 @@ export class AutoNATv2Server implements Startable {
|
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
// AutoNat server
|
|
48
|
-
await this.components.registrar.handle(this.dialRequestProtocol,
|
|
49
|
-
void this.handleDialRequestStream(data)
|
|
50
|
-
.catch(err => {
|
|
51
|
-
this.log.error('error handling incoming autonat stream - %e', err)
|
|
52
|
-
})
|
|
53
|
-
}, {
|
|
49
|
+
await this.components.registrar.handle(this.dialRequestProtocol, this.handleDialRequestStream, {
|
|
54
50
|
maxInboundStreams: this.maxInboundStreams,
|
|
55
51
|
maxOutboundStreams: this.maxOutboundStreams
|
|
56
52
|
})
|
|
@@ -67,100 +63,95 @@ export class AutoNATv2Server implements Startable {
|
|
|
67
63
|
/**
|
|
68
64
|
* Handle an incoming AutoNAT request
|
|
69
65
|
*/
|
|
70
|
-
async handleDialRequestStream (
|
|
66
|
+
async handleDialRequestStream (stream: Stream, connection: Connection): Promise<void> {
|
|
71
67
|
const signal = AbortSignal.timeout(this.timeout)
|
|
72
68
|
setMaxListeners(Infinity, signal)
|
|
73
69
|
|
|
74
|
-
const messages = pbStream(
|
|
70
|
+
const messages = pbStream(stream, {
|
|
75
71
|
maxDataLength: this.maxMessageSize
|
|
76
72
|
}).pb(Message)
|
|
77
73
|
|
|
78
|
-
|
|
79
|
-
const connectionIp = getIpAddress(data.connection.remoteAddr)
|
|
74
|
+
const connectionIp = getIpAddress(connection.remoteAddr)
|
|
80
75
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
76
|
+
if (connectionIp == null) {
|
|
77
|
+
throw new ProtocolError(`Could not find IP address in connection address "${connection.remoteAddr}"`)
|
|
78
|
+
}
|
|
84
79
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
80
|
+
const { dialRequest } = await messages.read({
|
|
81
|
+
signal
|
|
82
|
+
})
|
|
88
83
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
84
|
+
if (dialRequest == null) {
|
|
85
|
+
throw new ProtocolError('Did not receive DialRequest message on incoming dial request stream')
|
|
86
|
+
}
|
|
92
87
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
88
|
+
if (dialRequest.addrs.length === 0) {
|
|
89
|
+
throw new ProtocolError('Did not receive any addresses to dial')
|
|
90
|
+
}
|
|
96
91
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
92
|
+
for (let i = 0; i < dialRequest.addrs.length; i++) {
|
|
93
|
+
try {
|
|
94
|
+
const ma = multiaddr(dialRequest.addrs[i])
|
|
95
|
+
const isDialable = await this.components.connectionManager.isDialable(ma, {
|
|
96
|
+
signal
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
if (!isDialable) {
|
|
100
|
+
await messages.write({
|
|
101
|
+
dialResponse: {
|
|
102
|
+
addrIdx: i,
|
|
103
|
+
status: DialResponse.ResponseStatus.E_DIAL_REFUSED,
|
|
104
|
+
dialStatus: DialStatus.UNUSED
|
|
105
|
+
}
|
|
106
|
+
}, {
|
|
101
107
|
signal
|
|
102
108
|
})
|
|
103
109
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
dialResponse: {
|
|
107
|
-
addrIdx: i,
|
|
108
|
-
status: DialResponse.ResponseStatus.E_DIAL_REFUSED,
|
|
109
|
-
dialStatus: DialStatus.UNUSED
|
|
110
|
-
}
|
|
111
|
-
}, {
|
|
112
|
-
signal
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
continue
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const ip = getIpAddress(ma)
|
|
119
|
-
|
|
120
|
-
if (ip == null) {
|
|
121
|
-
throw new ProtocolError(`Could not find IP address in requested address "${ma}"`)
|
|
122
|
-
}
|
|
110
|
+
continue
|
|
111
|
+
}
|
|
123
112
|
|
|
124
|
-
|
|
125
|
-
throw new ProtocolError(`Requested address had private IP "${ma}"`)
|
|
126
|
-
}
|
|
113
|
+
const ip = getIpAddress(ma)
|
|
127
114
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
await this.preventAmplificationAttack(messages, i, {
|
|
132
|
-
signal
|
|
133
|
-
})
|
|
134
|
-
}
|
|
115
|
+
if (ip == null) {
|
|
116
|
+
throw new ProtocolError(`Could not find IP address in requested address "${ma}"`)
|
|
117
|
+
}
|
|
135
118
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
119
|
+
if (isPrivateIp(ip)) {
|
|
120
|
+
throw new ProtocolError(`Requested address had private IP "${ma}"`)
|
|
121
|
+
}
|
|
139
122
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
dialStatus
|
|
145
|
-
}
|
|
146
|
-
}, {
|
|
123
|
+
if (ip !== connectionIp) {
|
|
124
|
+
// amplification attack protection - request the client sends us a
|
|
125
|
+
// random number of bytes before we'll dial the address
|
|
126
|
+
await this.preventAmplificationAttack(messages, i, {
|
|
147
127
|
signal
|
|
148
128
|
})
|
|
149
|
-
} catch (err) {
|
|
150
|
-
this.log.error('could not parse multiaddr - %e', err)
|
|
151
129
|
}
|
|
152
|
-
}
|
|
153
130
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
131
|
+
const dialStatus = await this.dialClientBack(ma, dialRequest.nonce, {
|
|
132
|
+
signal
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
await messages.write({
|
|
136
|
+
dialResponse: {
|
|
137
|
+
addrIdx: i,
|
|
138
|
+
status: DialResponse.ResponseStatus.OK,
|
|
139
|
+
dialStatus
|
|
140
|
+
}
|
|
141
|
+
}, {
|
|
142
|
+
signal
|
|
143
|
+
})
|
|
144
|
+
} catch (err) {
|
|
145
|
+
this.log.error('error handling incoming dialback request - %e', err)
|
|
146
|
+
}
|
|
160
147
|
}
|
|
148
|
+
|
|
149
|
+
await stream.close({
|
|
150
|
+
signal
|
|
151
|
+
})
|
|
161
152
|
}
|
|
162
153
|
|
|
163
|
-
private async preventAmplificationAttack (messages:
|
|
154
|
+
private async preventAmplificationAttack (messages: ProtobufMessageStream<Message, Stream>, index: number, options: AbortOptions): Promise<void> {
|
|
164
155
|
const numBytes = randomNumber(30_000, 100_000)
|
|
165
156
|
|
|
166
157
|
await messages.write({
|
|
@@ -207,7 +198,7 @@ export class AutoNATv2Server implements Startable {
|
|
|
207
198
|
nonce
|
|
208
199
|
}, DialBack, options)
|
|
209
200
|
|
|
210
|
-
const response = await dialBackMessages.read(DialBackResponse)
|
|
201
|
+
const response = await dialBackMessages.read(DialBackResponse, options)
|
|
211
202
|
|
|
212
203
|
if (response.status !== DialBackResponse.DialBackStatus.OK) {
|
|
213
204
|
throw new ProtocolError('DialBackResponse status was not OK')
|
package/dist/typedoc-urls.json
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"AutoNATv2Components": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_autonat-v2.AutoNATv2Components.html",
|
|
3
|
-
".:AutoNATv2Components": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_autonat-v2.AutoNATv2Components.html",
|
|
4
|
-
"AutoNATv2ServiceInit": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_autonat-v2.AutoNATv2ServiceInit.html",
|
|
5
|
-
".:AutoNATv2ServiceInit": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_autonat-v2.AutoNATv2ServiceInit.html",
|
|
6
|
-
"autoNATv2": "https://libp2p.github.io/js-libp2p/functions/_libp2p_autonat-v2.autoNATv2.html",
|
|
7
|
-
".:autoNATv2": "https://libp2p.github.io/js-libp2p/functions/_libp2p_autonat-v2.autoNATv2.html"
|
|
8
|
-
}
|