@decentnetwork/peer 0.1.4 → 0.1.5

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 +66 -5
  2. package/package.json +1 -1
package/dist/peer.js CHANGED
@@ -2788,10 +2788,73 @@ export class Peer {
2788
2788
  // residential ISPs where self-announce doesn't propagate).
2789
2789
  // Mirrors toxcore's send_dht_pk_packet which calls
2790
2790
  // tcp_copy_connected_relays to fill extras.
2791
- let extras = new Uint8Array(0);
2791
+ // Also pack OUR OWN UDP endpoint(s) as extras (family 0x02 =
2792
+ // UDP_FAMILY_IPV4). Without this, peers receiving our DHT-PK push
2793
+ // learn our TCP relays but have NO IDEA where to send UDP cookie
2794
+ // requests — so they never even try UDP holepunch and every
2795
+ // session stays on tcp-relay (~500ms+ RTT) instead of the
2796
+ // ~80ms UDP path. Observed in the wild: even same-LAN peers
2797
+ // (snoopy and mac-dev on the same /24) stuck on tcp-relay
2798
+ // forever because neither side ever announces a UDP endpoint
2799
+ // to the other.
2800
+ //
2801
+ // For peers behind NAT, the LAN address we advertise is
2802
+ // reachable by same-LAN peers and harmless for WAN peers
2803
+ // (they fall through to tcp-relay as before). For peers with
2804
+ // a public IP (e.g. a Vultr VPS), the LAN address IS the
2805
+ // reachable one; same-format works. STUN-style reflexive
2806
+ // discovery is the next layer but isn't required to fix the
2807
+ // LAN case immediately.
2808
+ const packed = [];
2809
+ const ourUdpPort = this.#udp?.localPort();
2810
+ if (ourUdpPort && ourUdpPort > 0) {
2811
+ const ourDhtPk = this.#keyPair?.publicKey;
2812
+ if (ourDhtPk && ourDhtPk.length === 32) {
2813
+ // Enumerate non-loopback IPv4 addresses on this host.
2814
+ // We use os.networkInterfaces() rather than guessing — this
2815
+ // covers a multi-homed peer with several reachable IPs.
2816
+ try {
2817
+ const ifaces = (await import("os")).networkInterfaces();
2818
+ const seenIps = new Set();
2819
+ for (const iface of Object.values(ifaces)) {
2820
+ if (!iface)
2821
+ continue;
2822
+ for (const addr of iface) {
2823
+ if (addr.family !== "IPv4")
2824
+ continue;
2825
+ if (addr.internal)
2826
+ continue;
2827
+ if (addr.address === "0.0.0.0")
2828
+ continue;
2829
+ if (seenIps.has(addr.address))
2830
+ continue;
2831
+ seenIps.add(addr.address);
2832
+ const parts = addr.address
2833
+ .split(".")
2834
+ .map((p) => Number.parseInt(p, 10));
2835
+ if (parts.length !== 4 || parts.some((n) => !(n >= 0 && n <= 255)))
2836
+ continue;
2837
+ const entry = new Uint8Array(1 + 4 + 2 + 32);
2838
+ entry[0] = 0x02; // UDP_FAMILY_IPV4
2839
+ entry[1] = parts[0];
2840
+ entry[2] = parts[1];
2841
+ entry[3] = parts[2];
2842
+ entry[4] = parts[3];
2843
+ entry[5] = (ourUdpPort >> 8) & 0xff;
2844
+ entry[6] = ourUdpPort & 0xff;
2845
+ entry.set(ourDhtPk, 7);
2846
+ packed.push(entry);
2847
+ }
2848
+ }
2849
+ }
2850
+ catch {
2851
+ // os import or interface enumeration failed; skip UDP extras
2852
+ // and let TCP relay carry the session as before.
2853
+ }
2854
+ }
2855
+ }
2792
2856
  if (this.#tcpRelays) {
2793
2857
  const relays = this.#tcpRelays.connectedRelays(3); // MAX_SHARED_RELAYS
2794
- const packed = [];
2795
2858
  for (const r of relays) {
2796
2859
  // Only IPv4 TCP entries supported here; matches what we parse.
2797
2860
  // Format per packed-nodes spec: family(1) + ipv4(4) + port(2 BE) + pk(32)
@@ -2809,10 +2872,8 @@ export class Peer {
2809
2872
  entry.set(r.serverPublicKey, 7);
2810
2873
  packed.push(entry);
2811
2874
  }
2812
- if (packed.length > 0) {
2813
- extras = concatBytes(packed);
2814
- }
2815
2875
  }
2876
+ const extras = packed.length > 0 ? concatBytes(packed) : new Uint8Array(0);
2816
2877
  const innerPayload = concatBytes([
2817
2878
  noReplayBytes,
2818
2879
  this.#keyPair.publicKey,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decentnetwork/peer",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
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",