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