@decentnetwork/peer 0.1.56 → 0.1.57
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.d.ts +3 -0
- package/dist/peer.js +36 -4
- package/package.json +1 -1
package/dist/peer.d.ts
CHANGED
|
@@ -194,6 +194,9 @@ export declare class Peer {
|
|
|
194
194
|
* tcp-relay, discovery itself is broken — we don't even know
|
|
195
195
|
* where to send a cookie request. */
|
|
196
196
|
endpointCandidatesCount: number;
|
|
197
|
+
/** The actual candidate endpoints (host:port) — lets diag see whether a
|
|
198
|
+
* same-LAN host candidate (e.g. 10.0.0.x) was received but not adopted. */
|
|
199
|
+
endpointCandidates: string[];
|
|
197
200
|
/** Last time we sent an outbound cookie request via UDP for this
|
|
198
201
|
* peer (null = never). Used to confirm Phase 1.2 retries are
|
|
199
202
|
* actually firing. */
|
package/dist/peer.js
CHANGED
|
@@ -1062,6 +1062,7 @@ export class Peer {
|
|
|
1062
1062
|
ourLocalUdpPort: this.#udp?.localPort() ?? null,
|
|
1063
1063
|
friendUdpEndpoint: friendUdp,
|
|
1064
1064
|
endpointCandidatesCount: s.endpointCandidates?.length ?? 0,
|
|
1065
|
+
endpointCandidates: (s.endpointCandidates ?? []).map((c) => `${c.host}:${c.port}`),
|
|
1065
1066
|
cookieRequestSentMs: s.cookieRequestSentMs ?? null,
|
|
1066
1067
|
};
|
|
1067
1068
|
}
|
|
@@ -1628,7 +1629,19 @@ export class Peer {
|
|
|
1628
1629
|
state.lastRelayRecvMs = Date.now();
|
|
1629
1630
|
}
|
|
1630
1631
|
else {
|
|
1631
|
-
|
|
1632
|
+
// Adopt the source as our send endpoint — but NEVER downgrade from a
|
|
1633
|
+
// same-LAN (physical) path to a non-LAN source. A packet that loops in
|
|
1634
|
+
// via the peer's public / Tailscale-exit endpoint must not knock us off
|
|
1635
|
+
// the direct LAN path, which is strictly better. (This is the other
|
|
1636
|
+
// half of the "use the physical LAN, not Tailscale" rule.)
|
|
1637
|
+
const cur = state.remote?.host;
|
|
1638
|
+
const onSameLanAlready = !!cur && !cur.startsWith("tcp:") && isPrivateAddress(cur) && !isCgnatAddress(cur) &&
|
|
1639
|
+
getPhysicalLanSubnets().some((s) => isInIpv4Subnet(cur, s));
|
|
1640
|
+
const incomingSameLan = isPrivateAddress(remote.address) && !isCgnatAddress(remote.address) &&
|
|
1641
|
+
getPhysicalLanSubnets().some((s) => isInIpv4Subnet(remote.address, s));
|
|
1642
|
+
if (incomingSameLan || !onSameLanAlready) {
|
|
1643
|
+
state.remote = { host: remote.address, port: remote.port };
|
|
1644
|
+
}
|
|
1632
1645
|
if (!state.lastUdpRecvMs) {
|
|
1633
1646
|
this.#debugLog(`udp_confirmed friend=${friendId} via=${remote.address}:${remote.port} (direct UDP path live)`);
|
|
1634
1647
|
}
|
|
@@ -3353,8 +3366,19 @@ export class Peer {
|
|
|
3353
3366
|
if (++ln >= 6)
|
|
3354
3367
|
clearInterval(lanTimer);
|
|
3355
3368
|
}, 120);
|
|
3356
|
-
|
|
3357
|
-
|
|
3369
|
+
// The physical LAN is strictly better than ANY public path — direct, no
|
|
3370
|
+
// NAT, no relay, and crucially no Tailscale-exit hairpin (a peer whose
|
|
3371
|
+
// internet egresses via Tailscale advertises its exit's public IP as
|
|
3372
|
+
// its srflx, so the "direct" public path actually loops out through
|
|
3373
|
+
// Tailscale and back). So adopt the LAN candidate as session.remote even
|
|
3374
|
+
// when we already have a public "real UDP" remote — UNLESS we're already
|
|
3375
|
+
// on a same-LAN address (don't flap between two LAN candidates). Without
|
|
3376
|
+
// this the session stuck to the public/Tailscale endpoint and never sent
|
|
3377
|
+
// over the LAN (observed: session.remote stayed public, data on relay).
|
|
3378
|
+
const cur = session.remote?.host;
|
|
3379
|
+
const onSameLanAlready = !!cur && !cur.startsWith("tcp:") && isPrivateAddress(cur) && !isCgnatAddress(cur) &&
|
|
3380
|
+
getPhysicalLanSubnets().some((s) => isInIpv4Subnet(cur, s));
|
|
3381
|
+
if (!onSameLanAlready) {
|
|
3358
3382
|
session.remote = { host: lanHost, port: lanPort };
|
|
3359
3383
|
}
|
|
3360
3384
|
if (session.established) {
|
|
@@ -4920,7 +4944,15 @@ function isInIpv4Subnet(host, subnet) {
|
|
|
4920
4944
|
// path onto the overlay or loops into our own mesh, then silently falls back to
|
|
4921
4945
|
// the relay — the snoopy/lili "lastUdpRecv=never -> bufferbloat" path. (lili
|
|
4922
4946
|
// runs Tailscale; that's exactly this.)
|
|
4923
|
-
|
|
4947
|
+
// Names of interfaces that are NOT a real physical LAN — overlays (tailscale,
|
|
4948
|
+
// zerotier, wireguard, the agentnet TUN), and especially container/VM bridges.
|
|
4949
|
+
// Docker creates `docker0` AND per-network `br-<hash>` bridges (172.x); libvirt
|
|
4950
|
+
// `virbr0`; k8s `cni0`/`flannel`/`kube`. These were leaking their 172.x address
|
|
4951
|
+
// into the LAN host-candidate offer, so a peer on a real 10.x LAN advertised a
|
|
4952
|
+
// Docker-bridge IP its LAN partner could never match → LAN-direct never engaged
|
|
4953
|
+
// (observed: two boxes on one 10.0.0.0/24 stuck hairpinning through the public
|
|
4954
|
+
// internet). `^bridge` did NOT cover `br-…`.
|
|
4955
|
+
const VIRTUAL_IFACE_RE = /^(utun|tun|tap|wg|tailscale|zt|ham|agentnet|awdl|llw|gif|stf|bridge|br-|virbr|cni|flannel|weave|kube|vnet|vnic|vmnet|veth|docker)/i;
|
|
4924
4956
|
/** True for RFC 6598 carrier-grade-NAT space 100.64.0.0/10 (Tailscale's range). */
|
|
4925
4957
|
function isCgnatAddress(host) {
|
|
4926
4958
|
const o = host.split(".");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decentnetwork/peer",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.57",
|
|
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",
|