@libp2p/autonat 2.0.12-b248eefc0 → 2.0.12-d19974d93
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/README.md +1 -1
- package/dist/index.min.js +1 -1
- package/dist/src/autonat.d.ts +29 -7
- package/dist/src/autonat.d.ts.map +1 -1
- package/dist/src/autonat.js +303 -205
- package/dist/src/autonat.js.map +1 -1
- package/dist/src/constants.d.ts +0 -2
- package/dist/src/constants.d.ts.map +1 -1
- package/dist/src/constants.js +0 -2
- package/dist/src/constants.js.map +1 -1
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js.map +1 -1
- package/package.json +20 -18
- package/src/autonat.ts +378 -233
- package/src/constants.ts +0 -2
- package/src/index.ts +2 -1
- package/LICENSE +0 -4
package/dist/src/autonat.js
CHANGED
|
@@ -1,30 +1,32 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { serviceCapabilities, serviceDependencies, setMaxListeners } from '@libp2p/interface';
|
|
2
|
+
import { peerSet } from '@libp2p/peer-collections';
|
|
2
3
|
import { peerIdFromMultihash } from '@libp2p/peer-id';
|
|
3
|
-
import {
|
|
4
|
+
import { createScalableCuckooFilter } from '@libp2p/utils/filters';
|
|
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';
|
|
4
8
|
import { multiaddr, protocols } from '@multiformats/multiaddr';
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import map from 'it-map';
|
|
8
|
-
import parallel from 'it-parallel';
|
|
9
|
-
import { pipe } from 'it-pipe';
|
|
9
|
+
import { anySignal } from 'any-signal';
|
|
10
|
+
import { pbStream } from 'it-protobuf-stream';
|
|
10
11
|
import * as Digest from 'multiformats/hashes/digest';
|
|
11
|
-
import { MAX_INBOUND_STREAMS, MAX_OUTBOUND_STREAMS, PROTOCOL_NAME, PROTOCOL_PREFIX, PROTOCOL_VERSION,
|
|
12
|
+
import { MAX_INBOUND_STREAMS, MAX_OUTBOUND_STREAMS, PROTOCOL_NAME, PROTOCOL_PREFIX, PROTOCOL_VERSION, TIMEOUT } from './constants.js';
|
|
12
13
|
import { Message } from './pb/index.js';
|
|
13
14
|
// if more than 3 peers manage to dial us on what we believe to be our external
|
|
14
15
|
// IP then we are convinced that it is, in fact, our external IP
|
|
15
|
-
// https://github.com/libp2p/specs/blob/master/autonat/
|
|
16
|
+
// https://github.com/libp2p/specs/blob/master/autonat/autonat-v1.md#autonat-protocol
|
|
16
17
|
const REQUIRED_SUCCESSFUL_DIALS = 4;
|
|
17
18
|
export class AutoNATService {
|
|
18
19
|
components;
|
|
19
|
-
startupDelay;
|
|
20
|
-
refreshInterval;
|
|
21
20
|
protocol;
|
|
22
21
|
timeout;
|
|
23
22
|
maxInboundStreams;
|
|
24
23
|
maxOutboundStreams;
|
|
25
|
-
verifyAddressTimeout;
|
|
26
24
|
started;
|
|
27
25
|
log;
|
|
26
|
+
topologyId;
|
|
27
|
+
dialResults;
|
|
28
|
+
findPeers;
|
|
29
|
+
addressFilter;
|
|
28
30
|
constructor(components, init) {
|
|
29
31
|
this.components = components;
|
|
30
32
|
this.log = components.logger.forComponent('libp2p:autonat');
|
|
@@ -33,14 +35,19 @@ export class AutoNATService {
|
|
|
33
35
|
this.timeout = init.timeout ?? TIMEOUT;
|
|
34
36
|
this.maxInboundStreams = init.maxInboundStreams ?? MAX_INBOUND_STREAMS;
|
|
35
37
|
this.maxOutboundStreams = init.maxOutboundStreams ?? MAX_OUTBOUND_STREAMS;
|
|
36
|
-
this.
|
|
37
|
-
this.
|
|
38
|
-
this.
|
|
38
|
+
this.dialResults = new Map();
|
|
39
|
+
this.findPeers = repeatingTask(this.findRandomPeers.bind(this), 60_000);
|
|
40
|
+
this.addressFilter = createScalableCuckooFilter(1024);
|
|
39
41
|
}
|
|
40
42
|
[Symbol.toStringTag] = '@libp2p/autonat';
|
|
41
43
|
[serviceCapabilities] = [
|
|
42
44
|
'@libp2p/autonat'
|
|
43
45
|
];
|
|
46
|
+
get [serviceDependencies]() {
|
|
47
|
+
return [
|
|
48
|
+
'@libp2p/identify'
|
|
49
|
+
];
|
|
50
|
+
}
|
|
44
51
|
isStarted() {
|
|
45
52
|
return this.started;
|
|
46
53
|
}
|
|
@@ -51,79 +58,94 @@ export class AutoNATService {
|
|
|
51
58
|
await this.components.registrar.handle(this.protocol, (data) => {
|
|
52
59
|
void this.handleIncomingAutonatStream(data)
|
|
53
60
|
.catch(err => {
|
|
54
|
-
this.log.error('error handling incoming autonat stream', err);
|
|
61
|
+
this.log.error('error handling incoming autonat stream - %e', err);
|
|
55
62
|
});
|
|
56
63
|
}, {
|
|
57
64
|
maxInboundStreams: this.maxInboundStreams,
|
|
58
65
|
maxOutboundStreams: this.maxOutboundStreams
|
|
59
66
|
});
|
|
60
|
-
this.
|
|
67
|
+
this.topologyId = await this.components.registrar.register(this.protocol, {
|
|
68
|
+
onConnect: (peerId, connection) => {
|
|
69
|
+
this.verifyExternalAddresses(connection)
|
|
70
|
+
.catch(err => {
|
|
71
|
+
this.log.error('could not verify addresses - %e', err);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
this.findPeers.start();
|
|
61
76
|
this.started = true;
|
|
62
77
|
}
|
|
63
78
|
async stop() {
|
|
64
79
|
await this.components.registrar.unhandle(this.protocol);
|
|
65
|
-
|
|
80
|
+
if (this.topologyId != null) {
|
|
81
|
+
await this.components.registrar.unhandle(this.topologyId);
|
|
82
|
+
}
|
|
83
|
+
this.dialResults.clear();
|
|
84
|
+
this.findPeers.stop();
|
|
66
85
|
this.started = false;
|
|
67
86
|
}
|
|
87
|
+
allAddressesAreVerified() {
|
|
88
|
+
return this.components.addressManager.getAddressesWithMetadata().every(addr => addr.verified);
|
|
89
|
+
}
|
|
90
|
+
async findRandomPeers(options) {
|
|
91
|
+
// skip if all addresses are verified
|
|
92
|
+
if (this.allAddressesAreVerified()) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const signal = anySignal([
|
|
96
|
+
AbortSignal.timeout(10_000),
|
|
97
|
+
options?.signal
|
|
98
|
+
]);
|
|
99
|
+
// spend a few seconds finding random peers - dial them which will run
|
|
100
|
+
// identify to trigger the topology callbacks and run AutoNAT
|
|
101
|
+
try {
|
|
102
|
+
this.log('starting random walk to find peers to run AutoNAT');
|
|
103
|
+
for await (const peer of this.components.randomWalk.walk({ signal })) {
|
|
104
|
+
if (!(await this.components.connectionManager.isDialable(peer.multiaddrs))) {
|
|
105
|
+
this.log.trace('random peer %p was not dialable %s', peer.id, peer.multiaddrs.map(ma => ma.toString()).join(', '));
|
|
106
|
+
// skip peers we can't dial
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
this.log.trace('dial random peer %p', peer.id);
|
|
111
|
+
await this.components.connectionManager.openConnection(peer.multiaddrs, {
|
|
112
|
+
signal
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
catch { }
|
|
116
|
+
if (this.allAddressesAreVerified()) {
|
|
117
|
+
this.log('stopping random walk, all addresses are verified');
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch { }
|
|
123
|
+
}
|
|
68
124
|
/**
|
|
69
125
|
* Handle an incoming AutoNAT request
|
|
70
126
|
*/
|
|
71
127
|
async handleIncomingAutonatStream(data) {
|
|
72
128
|
const signal = AbortSignal.timeout(this.timeout);
|
|
73
|
-
const onAbort = () => {
|
|
74
|
-
data.stream.abort(new AbortError());
|
|
75
|
-
};
|
|
76
|
-
signal.addEventListener('abort', onAbort, { once: true });
|
|
77
|
-
// this controller may be used while dialing lots of peers so prevent MaxListenersExceededWarning
|
|
78
|
-
// appearing in the console
|
|
79
129
|
setMaxListeners(Infinity, signal);
|
|
130
|
+
const messages = pbStream(data.stream).pb(Message);
|
|
80
131
|
try {
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
let request;
|
|
96
|
-
try {
|
|
97
|
-
request = Message.decode(buf);
|
|
98
|
-
}
|
|
99
|
-
catch (err) {
|
|
100
|
-
self.log.error('could not decode message', err);
|
|
101
|
-
yield Message.encode({
|
|
102
|
-
type: Message.MessageType.DIAL_RESPONSE,
|
|
103
|
-
dialResponse: {
|
|
104
|
-
status: Message.ResponseStatus.E_BAD_REQUEST,
|
|
105
|
-
statusText: 'Could not decode message'
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
yield Message.encode(await self.handleAutonatMessage(request, data.connection, {
|
|
111
|
-
signal
|
|
112
|
-
}));
|
|
113
|
-
}, (source) => lp.encode(source), data.stream);
|
|
132
|
+
const request = await messages.read({
|
|
133
|
+
signal
|
|
134
|
+
});
|
|
135
|
+
const response = await this.handleAutonatMessage(request, data.connection, {
|
|
136
|
+
signal
|
|
137
|
+
});
|
|
138
|
+
await messages.write(response, {
|
|
139
|
+
signal
|
|
140
|
+
});
|
|
141
|
+
await messages.unwrap().unwrap().close({
|
|
142
|
+
signal
|
|
143
|
+
});
|
|
114
144
|
}
|
|
115
145
|
catch (err) {
|
|
116
|
-
this.log.error('error handling incoming autonat stream', err);
|
|
146
|
+
this.log.error('error handling incoming autonat stream - %e', err);
|
|
147
|
+
data.stream.abort(err);
|
|
117
148
|
}
|
|
118
|
-
finally {
|
|
119
|
-
signal.removeEventListener('abort', onAbort);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
_verifyExternalAddresses() {
|
|
123
|
-
void this.verifyExternalAddresses()
|
|
124
|
-
.catch(err => {
|
|
125
|
-
this.log.error('error verifying external address', err);
|
|
126
|
-
});
|
|
127
149
|
}
|
|
128
150
|
async handleAutonatMessage(message, connection, options) {
|
|
129
151
|
const ourHosts = this.components.addressManager.getAddresses()
|
|
@@ -156,7 +178,7 @@ export class AutoNATService {
|
|
|
156
178
|
peerId = peerIdFromMultihash(digest);
|
|
157
179
|
}
|
|
158
180
|
catch (err) {
|
|
159
|
-
this.log.error('invalid PeerId', err);
|
|
181
|
+
this.log.error('invalid PeerId - %e', err);
|
|
160
182
|
return {
|
|
161
183
|
type: Message.MessageType.DIAL_RESPONSE,
|
|
162
184
|
dialResponse: {
|
|
@@ -187,9 +209,8 @@ export class AutoNATService {
|
|
|
187
209
|
return isFromSameHost;
|
|
188
210
|
})
|
|
189
211
|
.filter(ma => {
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
this.log.trace('host %s was public %s', host, isPublicIp);
|
|
212
|
+
const isPublicIp = !isPrivate(ma);
|
|
213
|
+
this.log.trace('%a was public %s', ma, isPublicIp);
|
|
193
214
|
// don't try to dial private addresses
|
|
194
215
|
return isPublicIp;
|
|
195
216
|
})
|
|
@@ -236,7 +257,7 @@ export class AutoNATService {
|
|
|
236
257
|
this.log.error('tried to dial %a but dialed %a', multiaddr, connection.remoteAddr);
|
|
237
258
|
throw new Error('Unexpected remote address');
|
|
238
259
|
}
|
|
239
|
-
this.log('
|
|
260
|
+
this.log('successfully dialed %p via %a', peerId, multiaddr);
|
|
240
261
|
return {
|
|
241
262
|
type: Message.MessageType.DIAL_RESPONSE,
|
|
242
263
|
dialResponse: {
|
|
@@ -246,7 +267,7 @@ export class AutoNATService {
|
|
|
246
267
|
};
|
|
247
268
|
}
|
|
248
269
|
catch (err) {
|
|
249
|
-
this.log('could not dial %p', peerId, err);
|
|
270
|
+
this.log('could not dial %p - %e', peerId, err);
|
|
250
271
|
errorMessage = err.message;
|
|
251
272
|
}
|
|
252
273
|
finally {
|
|
@@ -264,157 +285,234 @@ export class AutoNATService {
|
|
|
264
285
|
}
|
|
265
286
|
};
|
|
266
287
|
}
|
|
288
|
+
/**
|
|
289
|
+
* The AutoNAT v1 server is only required to send us the address that it
|
|
290
|
+
* dialed successfully.
|
|
291
|
+
*
|
|
292
|
+
* When addresses fail, it can be because they are NATed, or because the peer
|
|
293
|
+
* did't support the transport, we have no way of knowing, so just send them
|
|
294
|
+
* one address so we can treat the response as:
|
|
295
|
+
*
|
|
296
|
+
* - OK - the dial request worked and the address is not NATed
|
|
297
|
+
* - E_DIAL_ERROR - the dial request failed and the address may be NATed
|
|
298
|
+
* - E_DIAL_REFUSED/E_BAD_REQUEST/E_INTERNAL_ERROR - the remote didn't dial the address
|
|
299
|
+
*/
|
|
300
|
+
getFirstUnverifiedMultiaddr(segment) {
|
|
301
|
+
const addrs = this.components.addressManager.getAddressesWithMetadata()
|
|
302
|
+
.sort((a, b) => {
|
|
303
|
+
// sort addresses, prioritize DNS/IP mapped addresses over observed ones
|
|
304
|
+
if (a.type === 'dns-mapping') {
|
|
305
|
+
return -1;
|
|
306
|
+
}
|
|
307
|
+
if (b.type === 'dns-mapping') {
|
|
308
|
+
return 1;
|
|
309
|
+
}
|
|
310
|
+
if (a.type === 'ip-mapping') {
|
|
311
|
+
return -1;
|
|
312
|
+
}
|
|
313
|
+
if (b.type === 'ip-mapping') {
|
|
314
|
+
return 1;
|
|
315
|
+
}
|
|
316
|
+
return 0;
|
|
317
|
+
})
|
|
318
|
+
.filter(addr => {
|
|
319
|
+
if (addr.verified && addr.expires > Date.now()) {
|
|
320
|
+
// skip verified addresses within their TTL
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
if (isPrivate(addr.multiaddr)) {
|
|
324
|
+
// skip private addresses
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
return true;
|
|
328
|
+
});
|
|
329
|
+
for (const addr of addrs) {
|
|
330
|
+
const addrString = addr.multiaddr.toString();
|
|
331
|
+
let results = this.dialResults.get(addrString);
|
|
332
|
+
if (results != null) {
|
|
333
|
+
if (results.networkSegments.includes(segment)) {
|
|
334
|
+
this.log.trace('%a already has a network segment result from %s', results.multiaddr, segment);
|
|
335
|
+
// skip this address if we already have a dial result from the
|
|
336
|
+
// network segment the peer is in
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
if (results.queue.size > 10) {
|
|
340
|
+
this.log.trace('%a already has enough peers queued', results.multiaddr);
|
|
341
|
+
// already have enough peers verifying this address, skip on to the
|
|
342
|
+
// next one
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
// will include this multiaddr, ensure we have a results object
|
|
347
|
+
if (results == null) {
|
|
348
|
+
const needsRevalidating = addr.expires < Date.now();
|
|
349
|
+
// allow re-validating addresses that worked previously
|
|
350
|
+
if (needsRevalidating) {
|
|
351
|
+
this.addressFilter.remove?.(addrString);
|
|
352
|
+
}
|
|
353
|
+
if (this.addressFilter.has(addrString)) {
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
// only try to validate the address once
|
|
357
|
+
this.addressFilter.add(addrString);
|
|
358
|
+
this.log.trace('creating dial result %s %s', needsRevalidating ? 'to revalidate' : 'for', addrString);
|
|
359
|
+
results = {
|
|
360
|
+
multiaddr: addr.multiaddr,
|
|
361
|
+
success: 0,
|
|
362
|
+
failure: 0,
|
|
363
|
+
networkSegments: [],
|
|
364
|
+
verifyingPeers: peerSet(),
|
|
365
|
+
queue: new PeerQueue({
|
|
366
|
+
concurrency: 3,
|
|
367
|
+
maxSize: 50
|
|
368
|
+
})
|
|
369
|
+
};
|
|
370
|
+
this.dialResults.set(addrString, results);
|
|
371
|
+
}
|
|
372
|
+
return results;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Removes any multiaddr result objects created for old multiaddrs that we are
|
|
377
|
+
* no longer waiting on
|
|
378
|
+
*/
|
|
379
|
+
removeOutdatedMultiaddrResults() {
|
|
380
|
+
const unverifiedMultiaddrs = new Set(this.components.addressManager.getAddressesWithMetadata()
|
|
381
|
+
.filter(({ verified }) => !verified)
|
|
382
|
+
.map(({ multiaddr }) => multiaddr.toString()));
|
|
383
|
+
for (const multiaddr of this.dialResults.keys()) {
|
|
384
|
+
if (!unverifiedMultiaddrs.has(multiaddr)) {
|
|
385
|
+
this.log.trace('remove results for %a', multiaddr);
|
|
386
|
+
this.dialResults.delete(multiaddr);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
267
390
|
/**
|
|
268
391
|
* Our multicodec topology noticed a new peer that supports autonat
|
|
269
392
|
*/
|
|
270
|
-
async verifyExternalAddresses() {
|
|
271
|
-
|
|
272
|
-
// Do not try to push if we are not running
|
|
393
|
+
async verifyExternalAddresses(connection) {
|
|
394
|
+
// do nothing if we are not running
|
|
273
395
|
if (!this.isStarted()) {
|
|
274
396
|
return;
|
|
275
397
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
this.log('no public addresses found, not requesting verification');
|
|
284
|
-
this.verifyAddressTimeout = setTimeout(this._verifyExternalAddresses, this.refreshInterval);
|
|
398
|
+
// perform cleanup
|
|
399
|
+
this.removeOutdatedMultiaddrResults();
|
|
400
|
+
// get multiaddrs this peer is eligible to verify
|
|
401
|
+
const segment = this.getNetworkSegment(connection.remoteAddr);
|
|
402
|
+
let results = this.getFirstUnverifiedMultiaddr(segment);
|
|
403
|
+
if (results == null) {
|
|
404
|
+
this.log.trace('no unverified public addresses found for peer %p to verify, not requesting verification', connection.remotePeer);
|
|
285
405
|
return;
|
|
286
406
|
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
id: this.components.peerId.toMultihash().bytes,
|
|
299
|
-
addrs: multiaddrs.map(map => map.bytes)
|
|
300
|
-
}
|
|
301
|
-
}
|
|
407
|
+
results.queue.add(async (options) => {
|
|
408
|
+
results = this.dialResults.get(options.multiaddr.toString());
|
|
409
|
+
if (results == null) {
|
|
410
|
+
this.log('%a was verified while %p was queued', options.multiaddr, connection.remotePeer);
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
const signal = AbortSignal.timeout(this.timeout);
|
|
414
|
+
setMaxListeners(Infinity, signal);
|
|
415
|
+
this.log.trace('asking %p to verify multiaddr %s', connection.remotePeer, options.multiaddr);
|
|
416
|
+
const stream = await connection.newStream(this.protocol, {
|
|
417
|
+
signal
|
|
302
418
|
});
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
signal
|
|
314
|
-
});
|
|
315
|
-
onAbort = () => { stream.abort(new AbortError()); };
|
|
316
|
-
signal.addEventListener('abort', onAbort, { once: true });
|
|
317
|
-
const buf = await pipe([request], (source) => lp.encode(source), stream, (source) => lp.decode(source), async (stream) => first(stream));
|
|
318
|
-
if (buf == null) {
|
|
319
|
-
this.log('no response received from %p', connection.remotePeer);
|
|
320
|
-
return undefined;
|
|
321
|
-
}
|
|
322
|
-
const response = Message.decode(buf);
|
|
323
|
-
if (response.type !== Message.MessageType.DIAL_RESPONSE || response.dialResponse == null) {
|
|
324
|
-
this.log('invalid autonat response from %p', connection.remotePeer);
|
|
325
|
-
return undefined;
|
|
326
|
-
}
|
|
327
|
-
if (response.dialResponse.status === Message.ResponseStatus.OK) {
|
|
328
|
-
// make sure we use different network segments
|
|
329
|
-
const options = connection.remoteAddr.toOptions();
|
|
330
|
-
let segment;
|
|
331
|
-
if (options.family === 4) {
|
|
332
|
-
const octets = options.host.split('.');
|
|
333
|
-
segment = octets[0];
|
|
334
|
-
}
|
|
335
|
-
else if (options.family === 6) {
|
|
336
|
-
const octets = options.host.split(':');
|
|
337
|
-
segment = octets[0];
|
|
338
|
-
}
|
|
339
|
-
else {
|
|
340
|
-
this.log('remote address "%s" was not IP4 or IP6?', options.host);
|
|
341
|
-
return undefined;
|
|
342
|
-
}
|
|
343
|
-
if (networkSegments.includes(segment)) {
|
|
344
|
-
this.log('already have response from network segment %d - %s', segment, options.host);
|
|
345
|
-
return undefined;
|
|
419
|
+
try {
|
|
420
|
+
const messages = pbStream(stream).pb(Message);
|
|
421
|
+
const [, response] = await Promise.all([
|
|
422
|
+
messages.write({
|
|
423
|
+
type: Message.MessageType.DIAL,
|
|
424
|
+
dial: {
|
|
425
|
+
peer: {
|
|
426
|
+
id: this.components.peerId.toMultihash().bytes,
|
|
427
|
+
addrs: [options.multiaddr.bytes]
|
|
428
|
+
}
|
|
346
429
|
}
|
|
347
|
-
|
|
348
|
-
}
|
|
349
|
-
|
|
430
|
+
}, { signal }),
|
|
431
|
+
messages.read({ signal })
|
|
432
|
+
]);
|
|
433
|
+
if (response.type !== Message.MessageType.DIAL_RESPONSE || response.dialResponse == null) {
|
|
434
|
+
this.log('invalid autonat response from %p - %j', connection.remotePeer, response);
|
|
435
|
+
return;
|
|
350
436
|
}
|
|
351
|
-
|
|
352
|
-
|
|
437
|
+
const status = response.dialResponse.status;
|
|
438
|
+
this.log.trace('autonat response from %p for %a is %s', connection.remotePeer, options.multiaddr, status);
|
|
439
|
+
if (status !== Message.ResponseStatus.OK && status !== Message.ResponseStatus.E_DIAL_ERROR) {
|
|
440
|
+
return;
|
|
353
441
|
}
|
|
354
|
-
|
|
355
|
-
|
|
442
|
+
results = this.dialResults.get(options.multiaddr.toString());
|
|
443
|
+
if (results == null) {
|
|
444
|
+
this.log.trace('peer reported %a as %s but there is no result object', options.multiaddr, response.dialResponse.status);
|
|
445
|
+
return;
|
|
356
446
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
447
|
+
if (results.networkSegments.includes(segment)) {
|
|
448
|
+
this.log.trace('%a results included network segment %s', options.multiaddr, segment);
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
if (results.result != null) {
|
|
452
|
+
this.log.trace('already resolved result for %a, ignoring response from', options.multiaddr, connection.remotePeer);
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
if (results.verifyingPeers.has(connection.remotePeer)) {
|
|
456
|
+
this.log.trace('peer %p has already verified %a, ignoring response', connection.remotePeer, options.multiaddr);
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
results.verifyingPeers.add(connection.remotePeer);
|
|
460
|
+
results.networkSegments.push(segment);
|
|
461
|
+
if (status === Message.ResponseStatus.OK) {
|
|
462
|
+
results.success++;
|
|
463
|
+
}
|
|
464
|
+
else if (status === Message.ResponseStatus.E_DIAL_ERROR) {
|
|
465
|
+
results.failure++;
|
|
466
|
+
}
|
|
467
|
+
this.log('%a success %d failure %d', results.multiaddr, results.success, results.failure);
|
|
468
|
+
if (results.success === REQUIRED_SUCCESSFUL_DIALS) {
|
|
469
|
+
// we are now convinced
|
|
470
|
+
this.log('%a is externally dialable', results.multiaddr);
|
|
471
|
+
this.components.addressManager.confirmObservedAddr(results.multiaddr);
|
|
472
|
+
this.dialResults.delete(results.multiaddr.toString());
|
|
473
|
+
// abort & remove any outstanding verification jobs for this multiaddr
|
|
474
|
+
results.result = true;
|
|
475
|
+
results.queue.abort();
|
|
476
|
+
}
|
|
477
|
+
if (results.failure === REQUIRED_SUCCESSFUL_DIALS) {
|
|
478
|
+
// we are now unconvinced
|
|
479
|
+
this.log('%a is not externally dialable', results.multiaddr);
|
|
480
|
+
this.components.addressManager.removeObservedAddr(results.multiaddr);
|
|
481
|
+
this.dialResults.delete(results.multiaddr.toString());
|
|
482
|
+
// abort & remove any outstanding verification jobs for this multiaddr
|
|
483
|
+
results.result = false;
|
|
484
|
+
results.queue.abort();
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
finally {
|
|
364
488
|
try {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
}
|
|
368
|
-
// they either told us which address worked/didn't work, or we only sent them one address
|
|
369
|
-
const addr = dialResponse.addr == null ? multiaddrs[0] : multiaddr(dialResponse.addr);
|
|
370
|
-
this.log('autonat response for %a is %s', addr, dialResponse.status);
|
|
371
|
-
if (dialResponse.status === Message.ResponseStatus.E_BAD_REQUEST) {
|
|
372
|
-
// the remote could not parse our request
|
|
373
|
-
continue;
|
|
374
|
-
}
|
|
375
|
-
if (dialResponse.status === Message.ResponseStatus.E_DIAL_REFUSED) {
|
|
376
|
-
// the remote could not honour our request
|
|
377
|
-
continue;
|
|
378
|
-
}
|
|
379
|
-
if (dialResponse.addr == null && multiaddrs.length > 1) {
|
|
380
|
-
// we sent the remote multiple addrs but they didn't tell us which ones worked/didn't work
|
|
381
|
-
continue;
|
|
382
|
-
}
|
|
383
|
-
if (!multiaddrs.some(ma => ma.equals(addr))) {
|
|
384
|
-
this.log('peer reported %a as %s but it was not in our observed address list', addr, dialResponse.status);
|
|
385
|
-
continue;
|
|
386
|
-
}
|
|
387
|
-
const addrStr = addr.toString();
|
|
388
|
-
if (results[addrStr] == null) {
|
|
389
|
-
results[addrStr] = { success: 0, failure: 0 };
|
|
390
|
-
}
|
|
391
|
-
if (dialResponse.status === Message.ResponseStatus.OK) {
|
|
392
|
-
results[addrStr].success++;
|
|
393
|
-
}
|
|
394
|
-
else if (dialResponse.status === Message.ResponseStatus.E_DIAL_ERROR) {
|
|
395
|
-
results[addrStr].failure++;
|
|
396
|
-
}
|
|
397
|
-
if (results[addrStr].success === REQUIRED_SUCCESSFUL_DIALS) {
|
|
398
|
-
// we are now convinced
|
|
399
|
-
this.log('%a is externally dialable', addr);
|
|
400
|
-
addressManager.confirmObservedAddr(addr);
|
|
401
|
-
return;
|
|
402
|
-
}
|
|
403
|
-
if (results[addrStr].failure === REQUIRED_SUCCESSFUL_DIALS) {
|
|
404
|
-
// we are now unconvinced
|
|
405
|
-
this.log('%a is not externally dialable', addr);
|
|
406
|
-
addressManager.removeObservedAddr(addr);
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
489
|
+
await stream.close({
|
|
490
|
+
signal
|
|
491
|
+
});
|
|
409
492
|
}
|
|
410
493
|
catch (err) {
|
|
411
|
-
|
|
494
|
+
stream.abort(err);
|
|
412
495
|
}
|
|
413
496
|
}
|
|
497
|
+
}, {
|
|
498
|
+
peerId: connection.remotePeer,
|
|
499
|
+
multiaddr: results.multiaddr
|
|
500
|
+
})
|
|
501
|
+
.catch(err => {
|
|
502
|
+
if (results?.result == null) {
|
|
503
|
+
this.log.error('error from %p verifying address %a - %e', connection.remotePeer, results?.multiaddr, err);
|
|
504
|
+
}
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
getNetworkSegment(ma) {
|
|
508
|
+
// make sure we use different network segments
|
|
509
|
+
const options = ma.toOptions();
|
|
510
|
+
if (options.family === 4) {
|
|
511
|
+
const octets = options.host.split('.');
|
|
512
|
+
return octets[0].padStart(3, '0');
|
|
414
513
|
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
}
|
|
514
|
+
const octets = options.host.split(':');
|
|
515
|
+
return octets[0].padStart(4, '0');
|
|
418
516
|
}
|
|
419
517
|
}
|
|
420
518
|
//# sourceMappingURL=autonat.js.map
|