@decentnetwork/peer 0.1.114 → 0.1.116
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/peer.js +63 -2
- package/package.json +1 -1
package/dist/peer.js
CHANGED
|
@@ -841,7 +841,24 @@ export class Peer {
|
|
|
841
841
|
// reply) makes the hot distance-sort + memory grow without limit; a few
|
|
842
842
|
// hundred nodes is plenty to stay findable and to announce on the closest.
|
|
843
843
|
const merged = dedupeNodes([...nodes, ...this.#knownNodes]);
|
|
844
|
-
|
|
844
|
+
if (merged.length > MAX_KNOWN_NODES) {
|
|
845
|
+
const kept = merged.slice(0, MAX_KNOWN_NODES);
|
|
846
|
+
// Pin the bootstrap nodes. They are the only guaranteed-public,
|
|
847
|
+
// onion-capable nodes we have; newest-first truncation on a busy DHT
|
|
848
|
+
// evicted them within hours, leaving the onion hop pool with only
|
|
849
|
+
// NAT'd/CGNAT peers — every onion path died at hop A, so announce and
|
|
850
|
+
// DHTPK delivery failed for days ("no onion path" / "self announce
|
|
851
|
+
// step1 no response" floods) and natives could never (re)discover us.
|
|
852
|
+
const keptIds = new Set(kept.map((node) => `${node.host}:${node.port}`));
|
|
853
|
+
for (const bn of this.#opts.bootstrapNodes) {
|
|
854
|
+
if (!keptIds.has(`${bn.host}:${bn.port}`))
|
|
855
|
+
kept.push(bn);
|
|
856
|
+
}
|
|
857
|
+
this.#knownNodes = kept;
|
|
858
|
+
}
|
|
859
|
+
else {
|
|
860
|
+
this.#knownNodes = merged;
|
|
861
|
+
}
|
|
845
862
|
}
|
|
846
863
|
knownNodes() {
|
|
847
864
|
return [...this.#knownNodes];
|
|
@@ -1808,6 +1825,20 @@ export class Peer {
|
|
|
1808
1825
|
if (!fromTcp) {
|
|
1809
1826
|
this.#cacheFriendRemote(senderId, remote.address, remote.port, request.senderRealPublicKey, request.senderDhtPublicKey);
|
|
1810
1827
|
}
|
|
1828
|
+
else if (request.senderDhtPublicKey && request.senderDhtPublicKey.length === 32) {
|
|
1829
|
+
// TCP arrival: skip the endpoint, but STILL learn the sender's DHT
|
|
1830
|
+
// key. Natives rotate it every app restart and the relay routes
|
|
1831
|
+
// CRYPTO_DATA by it — a relay-only native (its UDP unreachable to
|
|
1832
|
+
// us) otherwise kept its stale key here, every routed send went to
|
|
1833
|
+
// a key the relay didn't know, and the peer — never receiving our
|
|
1834
|
+
// data — re-keyed every 8s forever (the iPhone churn loop).
|
|
1835
|
+
const session = this.#friendSessions.get(senderId);
|
|
1836
|
+
if (session && !(session.friendDhtPublicKey && Buffer.from(session.friendDhtPublicKey).equals(Buffer.from(request.senderDhtPublicKey)))) {
|
|
1837
|
+
this.#debugLog(`dht_key_from_tcp_cookie friend=${senderId} new=${carrierIdFromPublicKey(request.senderDhtPublicKey)}`);
|
|
1838
|
+
session.friendDhtPublicKey = request.senderDhtPublicKey;
|
|
1839
|
+
}
|
|
1840
|
+
this.#learnFriendDhtKey(senderId, request.senderDhtPublicKey);
|
|
1841
|
+
}
|
|
1811
1842
|
const response = createCookieResponse({
|
|
1812
1843
|
request,
|
|
1813
1844
|
receiverDhtSecretKey: this.#keyPair.secretKey,
|
|
@@ -2071,8 +2102,18 @@ export class Peer {
|
|
|
2071
2102
|
else {
|
|
2072
2103
|
if (!state.friendRealPublicKey)
|
|
2073
2104
|
state.friendRealPublicKey = hs.senderRealPublicKey;
|
|
2074
|
-
|
|
2105
|
+
// ALWAYS take the handshake's DHT key, not only when empty: the
|
|
2106
|
+
// handshake authenticates the sender's real key, and natives rotate
|
|
2107
|
+
// their DHT key every restart. Keeping a stale key here sent every
|
|
2108
|
+
// relay-routed CRYPTO_DATA packet to a key the relay didn't know —
|
|
2109
|
+
// silent drop, peer re-keys every 8s (iPhone churn loop).
|
|
2110
|
+
if (hs.senderDhtPublicKey && hs.senderDhtPublicKey.length === 32 &&
|
|
2111
|
+
!(state.friendDhtPublicKey && Buffer.from(state.friendDhtPublicKey).equals(Buffer.from(hs.senderDhtPublicKey)))) {
|
|
2112
|
+
if (state.friendDhtPublicKey) {
|
|
2113
|
+
this.#debugLog(`dht_key_from_hs friend=${friendId} new=${carrierIdFromPublicKey(hs.senderDhtPublicKey)}`);
|
|
2114
|
+
}
|
|
2075
2115
|
state.friendDhtPublicKey = hs.senderDhtPublicKey;
|
|
2116
|
+
}
|
|
2076
2117
|
}
|
|
2077
2118
|
if (hs.senderDhtPublicKey && hs.senderDhtPublicKey.length === 32) {
|
|
2078
2119
|
this.#learnFriendDhtKey(friendId, hs.senderDhtPublicKey);
|
|
@@ -5512,6 +5553,17 @@ export class Peer {
|
|
|
5512
5553
|
}
|
|
5513
5554
|
if (!friendId)
|
|
5514
5555
|
return;
|
|
5556
|
+
// If ANY established session already uses this DHT key, the key's owner
|
|
5557
|
+
// is known and it is not `friendId` — a shared own-host target (the
|
|
5558
|
+
// simulator) answers EVERY prober's probe, and without this check each
|
|
5559
|
+
// stranded friend in turn "adopted" the sim's key during its slot,
|
|
5560
|
+
// endlessly poisoning its own routing.
|
|
5561
|
+
for (const [fid, s] of this.#friendSessions) {
|
|
5562
|
+
if (fid === friendId || !s.established || !s.friendDhtPublicKey)
|
|
5563
|
+
continue;
|
|
5564
|
+
if (Buffer.from(s.friendDhtPublicKey).equals(Buffer.from(senderDhtPk)))
|
|
5565
|
+
return;
|
|
5566
|
+
}
|
|
5515
5567
|
// The sender pk currently maps to a DIFFERENT friend. If that friend's
|
|
5516
5568
|
// session is ESTABLISHED, the key genuinely belongs to them — don't
|
|
5517
5569
|
// cross-wire. If it is NOT established, the mapping itself may be a
|
|
@@ -6149,6 +6201,15 @@ export class Peer {
|
|
|
6149
6201
|
if (id === nodeDId || node.host === nodeD.host || seenHosts.has(node.host)) {
|
|
6150
6202
|
continue;
|
|
6151
6203
|
}
|
|
6204
|
+
// Onion hops must be PUBLICLY routable: they forward our request onward
|
|
6205
|
+
// and route the response back. A CGNAT (Tailscale 100.64/10), RFC1918,
|
|
6206
|
+
// or own-host address cannot do either from the open internet — yet
|
|
6207
|
+
// such peer endpoints leak into #knownNodes and were being picked as
|
|
6208
|
+
// hop A (observed: 1900+ onion initials sent via a friend's Tailscale
|
|
6209
|
+
// 100.76.x address, all black-holed).
|
|
6210
|
+
if (isPrivateAddress(node.host) || isCgnatAddress(node.host) || isOwnAddress(node.host)) {
|
|
6211
|
+
continue;
|
|
6212
|
+
}
|
|
6152
6213
|
// Skip blacklisted relays — onion paths through dead nodes
|
|
6153
6214
|
// silently swallow the request and the announce times out.
|
|
6154
6215
|
if (this.#isNodeBlacklisted(id)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decentnetwork/peer",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.116",
|
|
4
4
|
"description": "Pure TypeScript port of Elastos Carrier (toxcore-derived) P2P messaging. DHT, onion routing, TCP relay, FlatBuffers app payloads, Express offline relay. Wire-compatible with iOS Beagle and the Carrier C SDK.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|