@decentnetwork/peer 0.1.124 → 0.1.126

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.
Files changed (2) hide show
  1. package/dist/peer.js +33 -12
  2. package/package.json +1 -1
package/dist/peer.js CHANGED
@@ -1901,7 +1901,7 @@ export class Peer {
1901
1901
  this.#debugLog(`cookie response: no tcp relay accepted oob send for ${senderId}`);
1902
1902
  }
1903
1903
  }
1904
- else if (this.#isUnroutableSelfSource(remote.address, remote.port)) {
1904
+ else if (this.#isUnroutableSelfSource(remote.address, remote.port, true)) {
1905
1905
  // The request arrived sourced from our own TUN address (or our own
1906
1906
  // socket) — an overlay routing artifact. Replying there loops into
1907
1907
  // ourselves and dies. Answer over the relay OOB path instead, which
@@ -1996,7 +1996,7 @@ export class Peer {
1996
1996
  // poison the UDP send-back path.
1997
1997
  const fromTcp = this.#remoteIsTcp(remote);
1998
1998
  if (!fromTcp) {
1999
- this.#adoptRemote(matchedSession, remote.address, remote.port);
1999
+ this.#adoptRemote(matchedSession, remote.address, remote.port, true);
2000
2000
  }
2001
2001
  else {
2002
2002
  matchedSession.hasTcpRoute = true;
@@ -2070,7 +2070,7 @@ export class Peer {
2070
2070
  state.hasTcpRoute = true;
2071
2071
  }
2072
2072
  else {
2073
- this.#adoptRemote(state, remote.address, remote.port);
2073
+ this.#adoptRemote(state, remote.address, remote.port, true);
2074
2074
  }
2075
2075
  // CONVERGENCE RESYNC: the peer is retransmitting the SAME session pubkey
2076
2076
  // we already hold, so OUR view of the session looks fine. But if we've
@@ -2267,7 +2267,7 @@ export class Peer {
2267
2267
  // Don't set state.remote — there's no UDP endpoint for this peer.
2268
2268
  }
2269
2269
  else {
2270
- this.#adoptRemote(state, remote.address, remote.port);
2270
+ this.#adoptRemote(state, remote.address, remote.port, true);
2271
2271
  }
2272
2272
  if (isNewSession) {
2273
2273
  state.sendPacketNumber = 0;
@@ -2456,7 +2456,7 @@ export class Peer {
2456
2456
  }
2457
2457
  state.lastRelayRecvMs = Date.now();
2458
2458
  }
2459
- else if (this.#isUnroutableSelfSource(remote.address, remote.port)) {
2459
+ else if (this.#isUnroutableSelfSource(remote.address, remote.port, true)) {
2460
2460
  // Sourced from our own TUN address / own socket (overlay routing
2461
2461
  // artifact): the payload is real — deliver it — but this is NOT a
2462
2462
  // usable bidirectional UDP path. Don't adopt it (replies loop into
@@ -2469,7 +2469,7 @@ export class Peer {
2469
2469
  // Tailscale-exit hairpin source. Cheap string compare, NO interface
2470
2470
  // syscall (that was the CCTV CPU regression) — safe at CCTV packet
2471
2471
  // rates.
2472
- this.#adoptRemote(state, remote.address, remote.port);
2472
+ this.#adoptRemote(state, remote.address, remote.port, true);
2473
2473
  // A successfully decrypted packet arriving from a private address is
2474
2474
  // stronger proof than an endpoint hint: it demonstrates that this
2475
2475
  // exact host:port already reaches us. Do not require the address to be
@@ -2484,7 +2484,7 @@ export class Peer {
2484
2484
  if (!state.lanRemoteHost &&
2485
2485
  isPrivateAddress(remote.address) &&
2486
2486
  !isCgnatAddress(remote.address) &&
2487
- !this.#isUnroutableSelfSource(remote.address, remote.port)) {
2487
+ !this.#isUnroutableSelfSource(remote.address, remote.port, true)) {
2488
2488
  state.lanRemoteHost = remote.address;
2489
2489
  this.#rememberEndpointCandidate(state, remote.address, remote.port);
2490
2490
  this.#debugLog(`lan_lock_confirmed friend=${friendId} host=${remote.address}:${remote.port} (decrypted UDP source)`);
@@ -4704,7 +4704,10 @@ export class Peer {
4704
4704
  }
4705
4705
  // An unroutable self-source is never the friend's endpoint (see
4706
4706
  // #adoptRemote) — don't let it poison the in-memory record either.
4707
- if (this.#isUnroutableSelfSource(host, port)) {
4707
+ // observed=false: this path takes SPECULATIVE hints (DHT-PK extras,
4708
+ // sendnodes-derived knownNodes), so a self-address here is a claim, not
4709
+ // proof of a same-host peer.
4710
+ if (this.#isUnroutableSelfSource(host, port, false)) {
4708
4711
  return;
4709
4712
  }
4710
4713
  // Important: do NOT persist this candidate to disk. Speculative endpoint
@@ -4755,19 +4758,37 @@ export class Peer {
4755
4758
  * router and loop), or it's literally our own UDP socket. A self
4756
4759
  * PHYSICAL address with a DIFFERENT port is routable — that's a
4757
4760
  * same-host peer (e.g. the iOS simulator running on this Mac). */
4758
- #isUnroutableSelfSource(host, port) {
4761
+ #isUnroutableSelfSource(host, port, observed) {
4759
4762
  if (isOwnVirtualAddress(host))
4760
4763
  return true;
4761
4764
  if (!isOwnAddress(host))
4762
4765
  return false;
4763
4766
  const ourPort = this.#udp?.localPort();
4764
- return ourPort !== undefined && port === ourPort;
4767
+ if (ourPort !== undefined && port === ourPort)
4768
+ return true;
4769
+ // Our own physical IP on a DIFFERENT port is a same-host peer (the iOS
4770
+ // simulator on this Mac) — but only when we actually RECEIVED a packet
4771
+ // from there. A peer merely CLAIMING that address in its candidate list
4772
+ // is not evidence: private ranges are reused everywhere, so two machines
4773
+ // on unrelated networks routinely share one. Seen in the field between
4774
+ // ubuntu-aaec and air-wli, both sitting on a 172.29/20: aaec adopted its
4775
+ // OWN eth0 address as air-wli's endpoint and sent every IP packet to
4776
+ // itself — friend "online", chat fine, ping 100% loss, and not a single
4777
+ // drop recorded because nothing was dropped, just delivered to the wrong
4778
+ // machine.
4779
+ return !observed;
4765
4780
  }
4766
- #adoptRemote(session, host, port) {
4781
+ /**
4782
+ * @param observed true when host:port is the source of a datagram we
4783
+ * actually received, false when it is an address the peer advertised.
4784
+ * The distinction matters for self-addresses — see
4785
+ * {@link #isUnroutableSelfSource}.
4786
+ */
4787
+ #adoptRemote(session, host, port, observed = false) {
4767
4788
  // NEVER adopt an unroutable self-source as a friend's remote — replies
4768
4789
  // would loop into our own interface and die, the peer never hears back
4769
4790
  // and re-handshakes forever.
4770
- if (this.#isUnroutableSelfSource(host, port)) {
4791
+ if (this.#isUnroutableSelfSource(host, port, observed)) {
4771
4792
  return;
4772
4793
  }
4773
4794
  if (session.lanRemoteHost &&
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decentnetwork/peer",
3
- "version": "0.1.124",
3
+ "version": "0.1.126",
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",