@decentnetwork/peer 0.1.38 → 0.1.39
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 +70 -3
- package/package.json +1 -1
package/dist/peer.js
CHANGED
|
@@ -3058,7 +3058,7 @@ export class Peer {
|
|
|
3058
3058
|
// "direct" option we advertise is our srflx (public) address, which the
|
|
3059
3059
|
// NAT must hairpin back onto the LAN — most don't, so two machines in one
|
|
3060
3060
|
// office silently fall back to the TURN relay (~260ms for a <1ms hop).
|
|
3061
|
-
const lanIp =
|
|
3061
|
+
const lanIp = getPhysicalLanAddresses().find((ip) => isPrivateAddress(ip));
|
|
3062
3062
|
const lanOctets = lanIp ? lanIp.split(".").map((s) => parseInt(s, 10)) : undefined;
|
|
3063
3063
|
const lanPort = this.#udp.localPort() ?? 0;
|
|
3064
3064
|
const lanValid = !!lanOctets && lanOctets.length === 4 && !lanOctets.some((o) => Number.isNaN(o)) && lanPort > 0;
|
|
@@ -3147,8 +3147,9 @@ export class Peer {
|
|
|
3147
3147
|
const lanPort = ((payload[16] << 8) | payload[17]) >>> 0;
|
|
3148
3148
|
const sameLan = lanPort !== 0 &&
|
|
3149
3149
|
isPrivateAddress(lanHost) &&
|
|
3150
|
-
|
|
3151
|
-
|
|
3150
|
+
!isCgnatAddress(lanHost) &&
|
|
3151
|
+
getPhysicalLanSubnets().some((s) => isInIpv4Subnet(lanHost, s)) &&
|
|
3152
|
+
!(getPhysicalLanAddresses().includes(lanHost) && this.#udp.localPort() === lanPort);
|
|
3152
3153
|
if (sameLan) {
|
|
3153
3154
|
this.#rememberEndpointCandidate(session, lanHost, lanPort);
|
|
3154
3155
|
this.#debugLog(`udp-endpoint LAN candidate from ${friendId}: ${lanHost}:${lanPort} (same subnet) — punching`);
|
|
@@ -4433,6 +4434,72 @@ function isInIpv4Subnet(host, subnet) {
|
|
|
4433
4434
|
return false;
|
|
4434
4435
|
return ((ip & subnet.maskBits) >>> 0) === subnet.networkBits;
|
|
4435
4436
|
}
|
|
4437
|
+
// Interface names that are VIRTUAL / overlay networks, not a physical LAN:
|
|
4438
|
+
// Tailscale & utun tunnels (utun* on macOS, tailscale*/tun* on Linux),
|
|
4439
|
+
// WireGuard (wg*), ZeroTier (zt*), the decentlan TUN itself (agentnet*), and
|
|
4440
|
+
// assorted macOS virtual links (awdl/llw/gif/stf/bridge). A LAN-direct host
|
|
4441
|
+
// candidate MUST be a physical-LAN address: advertising/punching an overlay
|
|
4442
|
+
// address (e.g. Tailscale 100.x, or our own 10.86.x mesh IP) sends the "direct"
|
|
4443
|
+
// path onto the overlay or loops into our own mesh, then silently falls back to
|
|
4444
|
+
// the relay — the snoopy/lili "lastUdpRecv=never -> bufferbloat" path. (lili
|
|
4445
|
+
// runs Tailscale; that's exactly this.)
|
|
4446
|
+
const VIRTUAL_IFACE_RE = /^(utun|tun|tap|wg|tailscale|zt|ham|agentnet|awdl|llw|gif|stf|bridge|vnic|vmnet|veth|docker)/i;
|
|
4447
|
+
/** True for RFC 6598 carrier-grade-NAT space 100.64.0.0/10 (Tailscale's range). */
|
|
4448
|
+
function isCgnatAddress(host) {
|
|
4449
|
+
const o = host.split(".");
|
|
4450
|
+
if (o.length !== 4)
|
|
4451
|
+
return false;
|
|
4452
|
+
const a = Number(o[0]);
|
|
4453
|
+
const b = Number(o[1]);
|
|
4454
|
+
return a === 100 && b >= 64 && b <= 127;
|
|
4455
|
+
}
|
|
4456
|
+
/** Local IPv4 addresses on PHYSICAL interfaces only (excludes overlay/virtual
|
|
4457
|
+
* interfaces by name and the CGNAT range). Used for LAN-direct host candidates. */
|
|
4458
|
+
function getPhysicalLanAddresses() {
|
|
4459
|
+
const out = [];
|
|
4460
|
+
try {
|
|
4461
|
+
for (const [name, list] of Object.entries(networkInterfaces())) {
|
|
4462
|
+
if (!list || VIRTUAL_IFACE_RE.test(name))
|
|
4463
|
+
continue;
|
|
4464
|
+
for (const info of list) {
|
|
4465
|
+
if (info.family !== "IPv4" || info.internal)
|
|
4466
|
+
continue;
|
|
4467
|
+
if (isCgnatAddress(info.address))
|
|
4468
|
+
continue;
|
|
4469
|
+
out.push(info.address);
|
|
4470
|
+
}
|
|
4471
|
+
}
|
|
4472
|
+
}
|
|
4473
|
+
catch {
|
|
4474
|
+
// best-effort
|
|
4475
|
+
}
|
|
4476
|
+
return out;
|
|
4477
|
+
}
|
|
4478
|
+
/** Subnets of PHYSICAL interfaces only — for matching a peer's host candidate
|
|
4479
|
+
* to OUR real LAN (so we never match a peer's Tailscale/TUN address against our
|
|
4480
|
+
* own overlay subnet). */
|
|
4481
|
+
function getPhysicalLanSubnets() {
|
|
4482
|
+
const out = [];
|
|
4483
|
+
try {
|
|
4484
|
+
for (const [name, list] of Object.entries(networkInterfaces())) {
|
|
4485
|
+
if (!list || VIRTUAL_IFACE_RE.test(name))
|
|
4486
|
+
continue;
|
|
4487
|
+
for (const info of list) {
|
|
4488
|
+
if (info.family !== "IPv4" || info.internal || isCgnatAddress(info.address))
|
|
4489
|
+
continue;
|
|
4490
|
+
const addr = ipv4ToInt(info.address);
|
|
4491
|
+
const mask = netmaskToInt(info.netmask);
|
|
4492
|
+
if (addr === undefined || mask === undefined || mask === 0)
|
|
4493
|
+
continue;
|
|
4494
|
+
out.push({ networkBits: (addr & mask) >>> 0, maskBits: mask });
|
|
4495
|
+
}
|
|
4496
|
+
}
|
|
4497
|
+
}
|
|
4498
|
+
catch {
|
|
4499
|
+
// best-effort
|
|
4500
|
+
}
|
|
4501
|
+
return out;
|
|
4502
|
+
}
|
|
4436
4503
|
function isPrivateAddress(host) {
|
|
4437
4504
|
// RFC1918 IPv4: 10/8, 172.16/12, 192.168/16; loopback 127/8;
|
|
4438
4505
|
// link-local 169.254/16; carrier-grade NAT 100.64/10. IPv6 link-local fe80::/10
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decentnetwork/peer",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.39",
|
|
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",
|