@decentnetwork/peer 0.1.57 → 0.1.59
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 +55 -6
- package/package.json +1 -1
package/dist/peer.js
CHANGED
|
@@ -82,6 +82,17 @@ const FRIEND_PING_INTERVAL_MS = readEnvInt("DECENT_FRIEND_PING_INTERVAL_MS", 400
|
|
|
82
82
|
// slow enough not to burn CPU on idle peers. Tunable via env.
|
|
83
83
|
const FRIEND_CONNECTION_LOOP_MS = readEnvInt("DECENT_FRIEND_CONNECTION_LOOP_MS", 250);
|
|
84
84
|
const FRIEND_TIMEOUT_MS = readEnvInt("DECENT_FRIEND_TIMEOUT_MS", 32000);
|
|
85
|
+
// Half-open breaker: if the peer proposes a NEW session key this many times over
|
|
86
|
+
// this long while our session looks "alive", accept it (the peer can't decrypt
|
|
87
|
+
// our current session — a desynced half-open session, observed as a flood of
|
|
88
|
+
// "no session matched"). Each handshake carries a fresh cookie so these are
|
|
89
|
+
// genuine attempts, not replays. Bounded so it never fires on a healthy session.
|
|
90
|
+
const REHS_ACCEPT_COUNT = readEnvInt("DECENT_REHS_ACCEPT_COUNT", 4);
|
|
91
|
+
const REHS_ACCEPT_MS = readEnvInt("DECENT_REHS_ACCEPT_MS", 2500);
|
|
92
|
+
const REHS_WINDOW_MS = readEnvInt("DECENT_REHS_WINDOW_MS", 8000);
|
|
93
|
+
// Re-handshake when we keep receiving undecryptable crypto data from a known
|
|
94
|
+
// friend's endpoint (desynced sessions), rate-limited per friend.
|
|
95
|
+
const REINIT_ON_DESYNC_MS = readEnvInt("DECENT_REINIT_ON_DESYNC_MS", 2000);
|
|
85
96
|
// How long a relay path stays "confirmed" (carrying bulk data on its own, no
|
|
86
97
|
// tcp-relay fan-out) after the last packet received over it. The relay
|
|
87
98
|
// keepalive refreshes this every ~4s on a healthy path, so a generous window
|
|
@@ -1436,12 +1447,30 @@ export class Peer {
|
|
|
1436
1447
|
const lastAlive = state.lastPingRecvMs ?? state.sessionEstablishedAtMs;
|
|
1437
1448
|
const currentSessionAlive = Date.now() - lastAlive < FRIEND_TIMEOUT_MS;
|
|
1438
1449
|
if (sinceEstablished > 1000 && currentSessionAlive) {
|
|
1439
|
-
//
|
|
1440
|
-
//
|
|
1441
|
-
//
|
|
1442
|
-
|
|
1443
|
-
|
|
1450
|
+
// Half-open breaker. Our session looks alive — but "alive" only means
|
|
1451
|
+
// WE are receiving (possibly only over the relay). If the peer KEEPS
|
|
1452
|
+
// proposing a new key, THEIR side can't decrypt our packets: a desynced
|
|
1453
|
+
// half-open session (observed snoopy↔mac on one LAN as 288k "no session
|
|
1454
|
+
// matched"). Rejecting forever deadlocks. So count sustained attempts
|
|
1455
|
+
// and, past the threshold, accept the new key to re-converge. Bounded
|
|
1456
|
+
// to sustained attempts so it never disturbs a healthy session.
|
|
1457
|
+
const now = Date.now();
|
|
1458
|
+
if (state.rehsFirstMs === undefined || now - (state.rehsLastMs ?? 0) > REHS_WINDOW_MS) {
|
|
1459
|
+
state.rehsFirstMs = now;
|
|
1460
|
+
state.rehsCount = 0;
|
|
1461
|
+
}
|
|
1462
|
+
state.rehsLastMs = now;
|
|
1463
|
+
state.rehsCount = (state.rehsCount ?? 0) + 1;
|
|
1464
|
+
const stuck = state.rehsCount >= REHS_ACCEPT_COUNT && now - state.rehsFirstMs >= REHS_ACCEPT_MS;
|
|
1465
|
+
if (!stuck) {
|
|
1466
|
+
this.#debugVerboseLog(`hs_recv ignored friend=${friendId} (new session pubkey on live session, attempt ${state.rehsCount}, ${sinceEstablished}ms after establish)`);
|
|
1467
|
+
return;
|
|
1468
|
+
}
|
|
1469
|
+
this.#debugLog(`hs_recv accepting re-handshake friend=${friendId} (half-open breaker: ${state.rehsCount} new-key attempts over ${now - state.rehsFirstMs}ms — peer can't decrypt our session)`);
|
|
1444
1470
|
}
|
|
1471
|
+
// Converging on a fresh session — clear the breaker counter.
|
|
1472
|
+
state.rehsCount = 0;
|
|
1473
|
+
state.rehsFirstMs = undefined;
|
|
1445
1474
|
this.#debugLog(`hs_recv accepting re-handshake friend=${friendId} (current session wedged/dead, ` +
|
|
1446
1475
|
`lastPingRecv=${state.lastPingRecvMs ? Date.now() - state.lastPingRecvMs + "ms ago" : "never"})`);
|
|
1447
1476
|
}
|
|
@@ -1799,7 +1828,27 @@ export class Peer {
|
|
|
1799
1828
|
this.#debugVerboseLog(`unknown messenger packet kind ${kind} from ${friendId}`);
|
|
1800
1829
|
return;
|
|
1801
1830
|
}
|
|
1802
|
-
|
|
1831
|
+
// We received crypto data we can't decrypt from a KNOWN friend's endpoint:
|
|
1832
|
+
// our two sessions are desynced (different keys) and NEITHER side will
|
|
1833
|
+
// re-handshake on its own, because each believes it is still established
|
|
1834
|
+
// (the half-open breaker can't help — no handshakes are being exchanged).
|
|
1835
|
+
// Kick a fresh handshake so we re-converge. Rate-limited per friend so a
|
|
1836
|
+
// flood of undecryptable packets doesn't spam cookie requests.
|
|
1837
|
+
if (!this.#remoteIsTcp(remote) && !viaRelay) {
|
|
1838
|
+
for (const [fid, st] of this.#friendSessions.entries()) {
|
|
1839
|
+
const matches = (st.remote?.host === remote.address && st.remote?.port === remote.port) ||
|
|
1840
|
+
(st.endpointCandidates ?? []).some((c) => c.host === remote.address && c.port === remote.port);
|
|
1841
|
+
if (!matches)
|
|
1842
|
+
continue;
|
|
1843
|
+
if (Date.now() - (st.lastReinitMs ?? 0) > REINIT_ON_DESYNC_MS) {
|
|
1844
|
+
st.lastReinitMs = Date.now();
|
|
1845
|
+
this.#debugLog(`no-session-matched from ${remote.address}:${remote.port} (friend ${fid}) — desynced, re-initiating handshake`);
|
|
1846
|
+
void this.#initiateSession(fid).catch(() => undefined);
|
|
1847
|
+
}
|
|
1848
|
+
break;
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
this.#debugVerboseLog(`crypto data received from ${remote.address}:${remote.port} but no session matched`);
|
|
1803
1852
|
return;
|
|
1804
1853
|
}
|
|
1805
1854
|
if (packet[0] === NET_PACKET_ONION_DATA_RESPONSE && this.#announceDataKey) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decentnetwork/peer",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.59",
|
|
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",
|