@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/src/client.ts CHANGED
@@ -1,16 +1,23 @@
1
- import { InvalidParametersError, ProtocolError, serviceCapabilities, serviceDependencies } from '@libp2p/interface'
1
+ import { ProtocolError, serviceCapabilities, serviceDependencies } from '@libp2p/interface'
2
2
  import { peerSet } from '@libp2p/peer-collections'
3
- import { createScalableCuckooFilter, isGlobalUnicast, isPrivate, PeerQueue, repeatingTask, trackedMap, pbStream, getNetConfig } from '@libp2p/utils'
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, Stream } from '@libp2p/interface'
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, RepeatingTask } from '@libp2p/utils'
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, (stream, connection) => {
151
- void this.handleDialBackStream(stream, connection)
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 (stream: Stream, connection: Connection): Promise<void> {
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 = getNetConfig(addr.multiaddr)
298
+ const options = addr.multiaddr.toOptions()
292
299
 
293
- if (options.type === 'ip6') {
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 getNetConfig(multiaddr).type === 'ip6'
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 %a to verify multiaddrs %s', connection.remoteAddr, unverifiedAddresses)
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
- for (let i = 0; i < unverifiedAddresses.length; i++) {
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 = getNetConfig(ma)
618
+ const options = ma.toOptions()
612
619
 
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
- }
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, pbStream } from '@libp2p/utils'
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, this.handleDialRequestStream, {
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 (stream: Stream, connection: Connection): Promise<void> {
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
- const connectionIp = getIpAddress(connection.remoteAddr)
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
- const { dialRequest } = await messages.read({
81
- signal
82
- })
81
+ if (connectionIp == null) {
82
+ throw new ProtocolError(`Could not find IP address in connection address "${data.connection.remoteAddr}"`)
83
+ }
83
84
 
84
- if (dialRequest == null) {
85
- throw new ProtocolError('Did not receive DialRequest message on incoming dial request stream')
86
- }
85
+ const { dialRequest } = await messages.read({
86
+ signal
87
+ })
87
88
 
88
- if (dialRequest.addrs.length === 0) {
89
- throw new ProtocolError('Did not receive any addresses to dial')
90
- }
89
+ if (dialRequest == null) {
90
+ throw new ProtocolError('Did not receive DialRequest message on incoming dial request stream')
91
+ }
91
92
 
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
- })
93
+ if (dialRequest.addrs.length === 0) {
94
+ throw new ProtocolError('Did not receive any addresses to dial')
95
+ }
98
96
 
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
- }, {
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
- continue
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
- const ip = getIpAddress(ma)
118
+ const ip = getIpAddress(ma)
114
119
 
115
- if (ip == null) {
116
- throw new ProtocolError(`Could not find IP address in requested address "${ma}"`)
117
- }
120
+ if (ip == null) {
121
+ throw new ProtocolError(`Could not find IP address in requested address "${ma}"`)
122
+ }
118
123
 
119
- if (isPrivateIp(ip)) {
120
- throw new ProtocolError(`Requested address had private IP "${ma}"`)
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
- 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, {
136
+ const dialStatus = await this.dialClientBack(ma, dialRequest.nonce, {
127
137
  signal
128
138
  })
129
- }
130
139
 
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)
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
- await stream.close({
150
- signal
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: ProtobufMessageStream<Message, Stream>, index: number, options: AbortOptions): Promise<void> {
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, options)
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')