@apocaliss92/nodelink-js 0.5.1-beta.10 → 0.5.1-beta.11

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.
@@ -656,6 +656,10 @@ function sleep(ms) {
656
656
  return new Promise((r) => setTimeout(r, ms));
657
657
  }
658
658
  var P2P_RELAY_HOSTNAMES = [
659
+ // Master anycast routers — try first.
660
+ "p2pm-abr.reolink.com",
661
+ "p2pm-ali.reolink.com",
662
+ // Numbered regional relays.
659
663
  "p2p.reolink.com",
660
664
  "p2p1.reolink.com",
661
665
  "p2p2.reolink.com",
@@ -667,8 +671,29 @@ var P2P_RELAY_HOSTNAMES = [
667
671
  "p2p8.reolink.com",
668
672
  "p2p9.reolink.com",
669
673
  "p2p10.reolink.com",
670
- "p2p11.reolink.com"
674
+ "p2p11.reolink.com",
675
+ // China-region fallbacks (intentionally last).
676
+ "p2p.reolink.com.cn",
677
+ "p2p1.reolink.com.cn",
678
+ "p2p2.reolink.com.cn",
679
+ "p2p3.reolink.com.cn",
680
+ "p2p4.reolink.com.cn",
681
+ "p2p5.reolink.com.cn",
682
+ "p2p6.reolink.com.cn",
683
+ "p2p7.reolink.com.cn",
684
+ "p2p8.reolink.com.cn",
685
+ "p2p9.reolink.com.cn"
671
686
  ];
687
+ function isUnroutableForP2P(ip) {
688
+ if (!ip) return true;
689
+ if (ip === "0.0.0.0") return true;
690
+ if (ip.startsWith("127.")) return true;
691
+ if (ip.startsWith("10.")) return true;
692
+ if (ip.startsWith("192.168.")) return true;
693
+ if (/^172\.(1[6-9]|2[0-9]|3[0-1])\./.test(ip)) return true;
694
+ if (/^100\.(6[4-9]|[7-9][0-9]|1[01][0-9]|12[0-7])\./.test(ip)) return true;
695
+ return false;
696
+ }
672
697
  var P2P_LOOKUP_PORT = 9999;
673
698
  var P2P_MAX_WAIT_MS = 15e3;
674
699
  var P2P_RESEND_WAIT_MS = 500;
@@ -835,19 +860,30 @@ var BcUdpStream = class extends EventEmitter {
835
860
  }
836
861
  async p2pUidLookup(sock, uid) {
837
862
  const resolved = [];
863
+ const sinkholed = [];
838
864
  for (const host of P2P_RELAY_HOSTNAMES) {
839
865
  try {
840
866
  const answers = await dns.lookup(host, { family: 4, all: true });
841
867
  for (const a of answers) {
842
- if (a.address && !resolved.includes(a.address))
843
- resolved.push(a.address);
868
+ if (!a.address) continue;
869
+ if (isUnroutableForP2P(a.address)) {
870
+ sinkholed.push({ host, ip: a.address });
871
+ continue;
872
+ }
873
+ if (!resolved.includes(a.address)) resolved.push(a.address);
844
874
  }
845
875
  } catch {
846
876
  }
847
877
  }
848
878
  if (resolved.length === 0) {
879
+ if (sinkholed.length > 0) {
880
+ const samples = sinkholed.slice(0, 3).map((s) => `${s.host} \u2192 ${s.ip}`).join(", ");
881
+ throw new Error(
882
+ `P2P UID lookup failed: DNS resolves Reolink P2P hostnames to non-routable IPs (${samples}). This is almost certainly an /etc/hosts rewrite or a DNS filter (Pi-hole, AdGuard, NextDNS) blocking *.reolink.com. Battery cameras cannot connect without P2P \u2014 whitelist *.reolink.com (at least p2p*.reolink.com on UDP/9999) or remove the override. Verify with \`getent hosts p2p.reolink.com\` inside your container \u2014 if it differs from \`nslookup p2p.reolink.com\`, the offender is /etc/hosts.`
883
+ );
884
+ }
849
885
  throw new Error(
850
- "P2P UID lookup failed: no p2p.reolink.com addresses resolved"
886
+ "P2P UID lookup failed: no p2p.reolink.com addresses resolved (DNS failure)"
851
887
  );
852
888
  }
853
889
  const start = Date.now();
@@ -24262,19 +24298,95 @@ function isTcpFailureThatShouldFallbackToUdp(e) {
24262
24298
  return message.includes("ECONNREFUSED") || message.includes("ETIMEDOUT") || message.includes("EHOSTUNREACH") || message.includes("ENETUNREACH") || message.includes("socket hang up") || message.includes("TCP connection timeout") || message.includes("Baichuan socket closed") || message.includes("timeout waiting for nonce") || message.includes("expected encryption info") || message.includes("ECONNRESET") || message.includes("EPIPE");
24263
24299
  }
24264
24300
  async function pingHost(host, timeoutMs = 3e3) {
24265
- const { exec } = await import("child_process");
24301
+ if (!host || typeof host !== "string") return false;
24266
24302
  const platform2 = process.platform;
24267
- const pingCmd = platform2 === "win32" ? `ping -n 1 -w ${timeoutMs} ${host}` : platform2 === "darwin" ? (
24268
- // macOS: -W is in milliseconds (Linux: seconds)
24269
- `ping -c 1 -W ${timeoutMs} ${host}`
24270
- ) : (
24271
- // Linux/BSD-ish: -W is in seconds on most distros
24272
- `ping -c 1 -W ${Math.max(1, Math.floor(timeoutMs / 1e3))} ${host}`
24273
- );
24303
+ const pingCandidates = platform2 === "win32" ? ["ping"] : platform2 === "darwin" ? ["/sbin/ping", "/usr/sbin/ping", "ping"] : ["/bin/ping", "/usr/bin/ping", "ping"];
24304
+ const pingArgs = (bin) => {
24305
+ void bin;
24306
+ if (platform2 === "win32") {
24307
+ return ["-n", "1", "-w", String(timeoutMs), host];
24308
+ }
24309
+ if (platform2 === "darwin") {
24310
+ return ["-c", "1", "-W", String(timeoutMs), host];
24311
+ }
24312
+ return ["-c", "1", "-W", String(Math.max(1, Math.floor(timeoutMs / 1e3))), host];
24313
+ };
24314
+ const { spawn: spawn3 } = await import("child_process");
24315
+ for (const bin of pingCandidates) {
24316
+ const ranOk = await new Promise((resolve) => {
24317
+ let settled = false;
24318
+ let child;
24319
+ try {
24320
+ child = spawn3(bin, pingArgs(bin), { stdio: "ignore" });
24321
+ } catch {
24322
+ resolve("spawn-failed");
24323
+ return;
24324
+ }
24325
+ const timer = setTimeout(() => {
24326
+ if (settled) return;
24327
+ settled = true;
24328
+ try {
24329
+ child?.kill("SIGKILL");
24330
+ } catch {
24331
+ }
24332
+ resolve(false);
24333
+ }, timeoutMs + 500);
24334
+ child.on("error", () => {
24335
+ if (settled) return;
24336
+ settled = true;
24337
+ clearTimeout(timer);
24338
+ resolve("spawn-failed");
24339
+ });
24340
+ child.on("exit", (code) => {
24341
+ if (settled) return;
24342
+ settled = true;
24343
+ clearTimeout(timer);
24344
+ resolve(code === 0);
24345
+ });
24346
+ });
24347
+ if (ranOk === true) return true;
24348
+ if (ranOk === "spawn-failed") continue;
24349
+ break;
24350
+ }
24351
+ for (const port of [9e3, 443, 80]) {
24352
+ if (await tcpReachabilityProbe(host, port, 800)) return true;
24353
+ }
24354
+ return false;
24355
+ }
24356
+ async function tcpReachabilityProbe(host, port, timeoutMs) {
24357
+ const net4 = await import("net");
24274
24358
  return new Promise((resolve) => {
24275
- exec(pingCmd, (error) => {
24276
- resolve(!error);
24359
+ let settled = false;
24360
+ const socket = new net4.Socket();
24361
+ const timer = setTimeout(() => {
24362
+ if (settled) return;
24363
+ settled = true;
24364
+ try {
24365
+ socket.destroy();
24366
+ } catch {
24367
+ }
24368
+ resolve(false);
24369
+ }, timeoutMs);
24370
+ const finish = (reachable) => {
24371
+ if (settled) return;
24372
+ settled = true;
24373
+ clearTimeout(timer);
24374
+ try {
24375
+ socket.destroy();
24376
+ } catch {
24377
+ }
24378
+ resolve(reachable);
24379
+ };
24380
+ socket.once("connect", () => finish(true));
24381
+ socket.once("error", (err) => {
24382
+ if (err?.code === "ECONNREFUSED") finish(true);
24383
+ else finish(false);
24277
24384
  });
24385
+ try {
24386
+ socket.connect(port, host);
24387
+ } catch {
24388
+ finish(false);
24389
+ }
24278
24390
  });
24279
24391
  }
24280
24392
  function createBaichuanApi(inputs, transport) {
@@ -24790,6 +24902,7 @@ export {
24790
24902
  encodeHeader,
24791
24903
  decodeHeader,
24792
24904
  BaichuanFrameParser,
24905
+ isUnroutableForP2P,
24793
24906
  BcUdpStream,
24794
24907
  asLogger,
24795
24908
  createNullLogger,
@@ -24854,6 +24967,7 @@ export {
24854
24967
  normalizeUid,
24855
24968
  maskUid,
24856
24969
  isTcpFailureThatShouldFallbackToUdp,
24970
+ tcpReachabilityProbe,
24857
24971
  autoDetectDeviceType
24858
24972
  };
24859
- //# sourceMappingURL=chunk-27IU7NXS.js.map
24973
+ //# sourceMappingURL=chunk-COUH6ZJT.js.map