@apocaliss92/nodelink-js 0.5.1-beta.8 → 0.5.1
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/{chunk-27IU7NXS.js → chunk-OJQLZETO.js} +157 -46
- package/dist/chunk-OJQLZETO.js.map +1 -0
- package/dist/cli/rtsp-server.cjs +154 -45
- package/dist/cli/rtsp-server.cjs.map +1 -1
- package/dist/cli/rtsp-server.js +1 -1
- package/dist/index.cjs +235 -48
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -1
- package/dist/index.d.ts +25 -0
- package/dist/index.js +82 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-27IU7NXS.js.map +0 -1
|
@@ -292,6 +292,7 @@ var BCUDP_DEFAULT_MTU = 1350;
|
|
|
292
292
|
var BCUDP_DATA_HEADER_SIZE = 20;
|
|
293
293
|
var BCUDP_DISCOVERY_PORT_LOCAL_ANY = 2015;
|
|
294
294
|
var BCUDP_DISCOVERY_PORT_LOCAL_UID = 2018;
|
|
295
|
+
var BCUDP_DISCOVERY_PORT_P2P_SCAN = 9999;
|
|
295
296
|
|
|
296
297
|
// src/bcudp/crc.ts
|
|
297
298
|
var table;
|
|
@@ -656,6 +657,10 @@ function sleep(ms) {
|
|
|
656
657
|
return new Promise((r) => setTimeout(r, ms));
|
|
657
658
|
}
|
|
658
659
|
var P2P_RELAY_HOSTNAMES = [
|
|
660
|
+
// Master anycast routers — try first.
|
|
661
|
+
"p2pm-abr.reolink.com",
|
|
662
|
+
"p2pm-ali.reolink.com",
|
|
663
|
+
// Numbered regional relays.
|
|
659
664
|
"p2p.reolink.com",
|
|
660
665
|
"p2p1.reolink.com",
|
|
661
666
|
"p2p2.reolink.com",
|
|
@@ -667,8 +672,29 @@ var P2P_RELAY_HOSTNAMES = [
|
|
|
667
672
|
"p2p8.reolink.com",
|
|
668
673
|
"p2p9.reolink.com",
|
|
669
674
|
"p2p10.reolink.com",
|
|
670
|
-
"p2p11.reolink.com"
|
|
675
|
+
"p2p11.reolink.com",
|
|
676
|
+
// China-region fallbacks (intentionally last).
|
|
677
|
+
"p2p.reolink.com.cn",
|
|
678
|
+
"p2p1.reolink.com.cn",
|
|
679
|
+
"p2p2.reolink.com.cn",
|
|
680
|
+
"p2p3.reolink.com.cn",
|
|
681
|
+
"p2p4.reolink.com.cn",
|
|
682
|
+
"p2p5.reolink.com.cn",
|
|
683
|
+
"p2p6.reolink.com.cn",
|
|
684
|
+
"p2p7.reolink.com.cn",
|
|
685
|
+
"p2p8.reolink.com.cn",
|
|
686
|
+
"p2p9.reolink.com.cn"
|
|
671
687
|
];
|
|
688
|
+
function isUnroutableForP2P(ip) {
|
|
689
|
+
if (!ip) return true;
|
|
690
|
+
if (ip === "0.0.0.0") return true;
|
|
691
|
+
if (ip.startsWith("127.")) return true;
|
|
692
|
+
if (ip.startsWith("10.")) return true;
|
|
693
|
+
if (ip.startsWith("192.168.")) return true;
|
|
694
|
+
if (/^172\.(1[6-9]|2[0-9]|3[0-1])\./.test(ip)) return true;
|
|
695
|
+
if (/^100\.(6[4-9]|[7-9][0-9]|1[01][0-9]|12[0-7])\./.test(ip)) return true;
|
|
696
|
+
return false;
|
|
697
|
+
}
|
|
672
698
|
var P2P_LOOKUP_PORT = 9999;
|
|
673
699
|
var P2P_MAX_WAIT_MS = 15e3;
|
|
674
700
|
var P2P_RESEND_WAIT_MS = 500;
|
|
@@ -835,19 +861,30 @@ var BcUdpStream = class extends EventEmitter {
|
|
|
835
861
|
}
|
|
836
862
|
async p2pUidLookup(sock, uid) {
|
|
837
863
|
const resolved = [];
|
|
864
|
+
const sinkholed = [];
|
|
838
865
|
for (const host of P2P_RELAY_HOSTNAMES) {
|
|
839
866
|
try {
|
|
840
867
|
const answers = await dns.lookup(host, { family: 4, all: true });
|
|
841
868
|
for (const a of answers) {
|
|
842
|
-
if (
|
|
843
|
-
|
|
869
|
+
if (!a.address) continue;
|
|
870
|
+
if (isUnroutableForP2P(a.address)) {
|
|
871
|
+
sinkholed.push({ host, ip: a.address });
|
|
872
|
+
continue;
|
|
873
|
+
}
|
|
874
|
+
if (!resolved.includes(a.address)) resolved.push(a.address);
|
|
844
875
|
}
|
|
845
876
|
} catch {
|
|
846
877
|
}
|
|
847
878
|
}
|
|
848
879
|
if (resolved.length === 0) {
|
|
880
|
+
if (sinkholed.length > 0) {
|
|
881
|
+
const samples = sinkholed.slice(0, 3).map((s) => `${s.host} \u2192 ${s.ip}`).join(", ");
|
|
882
|
+
throw new Error(
|
|
883
|
+
`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.`
|
|
884
|
+
);
|
|
885
|
+
}
|
|
849
886
|
throw new Error(
|
|
850
|
-
"P2P UID lookup failed: no p2p.reolink.com addresses resolved"
|
|
887
|
+
"P2P UID lookup failed: no p2p.reolink.com addresses resolved (DNS failure)"
|
|
851
888
|
);
|
|
852
889
|
}
|
|
853
890
|
const start = Date.now();
|
|
@@ -1181,7 +1218,8 @@ var BcUdpStream = class extends EventEmitter {
|
|
|
1181
1218
|
throw new Error("Internal: discoveryUidLocal called for non-uid mode");
|
|
1182
1219
|
const ports = [
|
|
1183
1220
|
BCUDP_DISCOVERY_PORT_LOCAL_ANY,
|
|
1184
|
-
BCUDP_DISCOVERY_PORT_LOCAL_UID
|
|
1221
|
+
BCUDP_DISCOVERY_PORT_LOCAL_UID,
|
|
1222
|
+
BCUDP_DISCOVERY_PORT_P2P_SCAN
|
|
1185
1223
|
];
|
|
1186
1224
|
const broadcastHosts = ["255.255.255.255"];
|
|
1187
1225
|
const ifaces = networkInterfaces();
|
|
@@ -23478,7 +23516,11 @@ async function discoverViaUdpDirect(host, options) {
|
|
|
23478
23516
|
});
|
|
23479
23517
|
socket.bind(() => {
|
|
23480
23518
|
const localPort = socket.address().port;
|
|
23481
|
-
const discoveryPorts = [
|
|
23519
|
+
const discoveryPorts = [
|
|
23520
|
+
BCUDP_DISCOVERY_PORT_LOCAL_ANY,
|
|
23521
|
+
BCUDP_DISCOVERY_PORT_LOCAL_UID,
|
|
23522
|
+
BCUDP_DISCOVERY_PORT_P2P_SCAN
|
|
23523
|
+
];
|
|
23482
23524
|
for (const port of discoveryPorts) {
|
|
23483
23525
|
try {
|
|
23484
23526
|
const tid = Math.floor(Math.random() * 255) || 1;
|
|
@@ -23767,7 +23809,11 @@ async function discoverViaUdpBroadcast(options) {
|
|
|
23767
23809
|
socket.bind(() => {
|
|
23768
23810
|
socket.setBroadcast(true);
|
|
23769
23811
|
const localPort = socket.address().port;
|
|
23770
|
-
const discoveryPorts = [
|
|
23812
|
+
const discoveryPorts = [
|
|
23813
|
+
BCUDP_DISCOVERY_PORT_LOCAL_ANY,
|
|
23814
|
+
BCUDP_DISCOVERY_PORT_LOCAL_UID,
|
|
23815
|
+
BCUDP_DISCOVERY_PORT_P2P_SCAN
|
|
23816
|
+
];
|
|
23771
23817
|
for (const port of discoveryPorts) {
|
|
23772
23818
|
try {
|
|
23773
23819
|
const tid = Math.floor(Math.random() * 255) || 1;
|
|
@@ -24262,19 +24308,95 @@ function isTcpFailureThatShouldFallbackToUdp(e) {
|
|
|
24262
24308
|
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
24309
|
}
|
|
24264
24310
|
async function pingHost(host, timeoutMs = 3e3) {
|
|
24265
|
-
|
|
24311
|
+
if (!host || typeof host !== "string") return false;
|
|
24266
24312
|
const platform2 = process.platform;
|
|
24267
|
-
const
|
|
24268
|
-
|
|
24269
|
-
|
|
24270
|
-
|
|
24271
|
-
|
|
24272
|
-
|
|
24273
|
-
|
|
24313
|
+
const pingCandidates = platform2 === "win32" ? ["ping"] : platform2 === "darwin" ? ["/sbin/ping", "/usr/sbin/ping", "ping"] : ["/bin/ping", "/usr/bin/ping", "ping"];
|
|
24314
|
+
const pingArgs = (bin) => {
|
|
24315
|
+
void bin;
|
|
24316
|
+
if (platform2 === "win32") {
|
|
24317
|
+
return ["-n", "1", "-w", String(timeoutMs), host];
|
|
24318
|
+
}
|
|
24319
|
+
if (platform2 === "darwin") {
|
|
24320
|
+
return ["-c", "1", "-W", String(timeoutMs), host];
|
|
24321
|
+
}
|
|
24322
|
+
return ["-c", "1", "-W", String(Math.max(1, Math.floor(timeoutMs / 1e3))), host];
|
|
24323
|
+
};
|
|
24324
|
+
const { spawn: spawn3 } = await import("child_process");
|
|
24325
|
+
for (const bin of pingCandidates) {
|
|
24326
|
+
const ranOk = await new Promise((resolve) => {
|
|
24327
|
+
let settled = false;
|
|
24328
|
+
let child;
|
|
24329
|
+
try {
|
|
24330
|
+
child = spawn3(bin, pingArgs(bin), { stdio: "ignore" });
|
|
24331
|
+
} catch {
|
|
24332
|
+
resolve("spawn-failed");
|
|
24333
|
+
return;
|
|
24334
|
+
}
|
|
24335
|
+
const timer = setTimeout(() => {
|
|
24336
|
+
if (settled) return;
|
|
24337
|
+
settled = true;
|
|
24338
|
+
try {
|
|
24339
|
+
child?.kill("SIGKILL");
|
|
24340
|
+
} catch {
|
|
24341
|
+
}
|
|
24342
|
+
resolve(false);
|
|
24343
|
+
}, timeoutMs + 500);
|
|
24344
|
+
child.on("error", () => {
|
|
24345
|
+
if (settled) return;
|
|
24346
|
+
settled = true;
|
|
24347
|
+
clearTimeout(timer);
|
|
24348
|
+
resolve("spawn-failed");
|
|
24349
|
+
});
|
|
24350
|
+
child.on("exit", (code) => {
|
|
24351
|
+
if (settled) return;
|
|
24352
|
+
settled = true;
|
|
24353
|
+
clearTimeout(timer);
|
|
24354
|
+
resolve(code === 0);
|
|
24355
|
+
});
|
|
24356
|
+
});
|
|
24357
|
+
if (ranOk === true) return true;
|
|
24358
|
+
if (ranOk === "spawn-failed") continue;
|
|
24359
|
+
break;
|
|
24360
|
+
}
|
|
24361
|
+
for (const port of [9e3, 443, 80]) {
|
|
24362
|
+
if (await tcpReachabilityProbe(host, port, 800)) return true;
|
|
24363
|
+
}
|
|
24364
|
+
return false;
|
|
24365
|
+
}
|
|
24366
|
+
async function tcpReachabilityProbe(host, port, timeoutMs) {
|
|
24367
|
+
const net4 = await import("net");
|
|
24274
24368
|
return new Promise((resolve) => {
|
|
24275
|
-
|
|
24276
|
-
|
|
24369
|
+
let settled = false;
|
|
24370
|
+
const socket = new net4.Socket();
|
|
24371
|
+
const timer = setTimeout(() => {
|
|
24372
|
+
if (settled) return;
|
|
24373
|
+
settled = true;
|
|
24374
|
+
try {
|
|
24375
|
+
socket.destroy();
|
|
24376
|
+
} catch {
|
|
24377
|
+
}
|
|
24378
|
+
resolve(false);
|
|
24379
|
+
}, timeoutMs);
|
|
24380
|
+
const finish = (reachable) => {
|
|
24381
|
+
if (settled) return;
|
|
24382
|
+
settled = true;
|
|
24383
|
+
clearTimeout(timer);
|
|
24384
|
+
try {
|
|
24385
|
+
socket.destroy();
|
|
24386
|
+
} catch {
|
|
24387
|
+
}
|
|
24388
|
+
resolve(reachable);
|
|
24389
|
+
};
|
|
24390
|
+
socket.once("connect", () => finish(true));
|
|
24391
|
+
socket.once("error", (err) => {
|
|
24392
|
+
if (err?.code === "ECONNREFUSED") finish(true);
|
|
24393
|
+
else finish(false);
|
|
24277
24394
|
});
|
|
24395
|
+
try {
|
|
24396
|
+
socket.connect(port, host);
|
|
24397
|
+
} catch {
|
|
24398
|
+
finish(false);
|
|
24399
|
+
}
|
|
24278
24400
|
});
|
|
24279
24401
|
}
|
|
24280
24402
|
function createBaichuanApi(inputs, transport) {
|
|
@@ -24347,6 +24469,7 @@ async function autoDetectDeviceType(inputs) {
|
|
|
24347
24469
|
const sleepMs2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
24348
24470
|
const shouldRetryTcp = (e) => {
|
|
24349
24471
|
const msg = fmtErr(e);
|
|
24472
|
+
if (msg.includes("ECONNREFUSED")) return false;
|
|
24350
24473
|
return isTcpFailureThatShouldFallbackToUdp(e) || msg.includes("timeout waiting for nonce") || msg.includes("expected encryption info") || msg.includes("Baichuan socket closed") || msg.includes("ECONNRESET") || msg.includes("EPIPE");
|
|
24351
24474
|
};
|
|
24352
24475
|
const shouldRetryUdp = (e) => {
|
|
@@ -24401,6 +24524,11 @@ async function autoDetectDeviceType(inputs) {
|
|
|
24401
24524
|
}
|
|
24402
24525
|
};
|
|
24403
24526
|
const effectiveUid = normalizeUid(uid);
|
|
24527
|
+
const speculativeUidPromise = effectiveUid !== void 0 ? Promise.resolve(effectiveUid) : mode === "tcp" ? Promise.resolve(void 0) : discoverUidForHost(host, logger).then((d) => normalizeUid(d) ?? void 0).catch(() => void 0);
|
|
24528
|
+
speculativeUidPromise.then(
|
|
24529
|
+
() => void 0,
|
|
24530
|
+
() => void 0
|
|
24531
|
+
);
|
|
24404
24532
|
logger?.log?.(`[AutoDetect] Pinging ${host}...`);
|
|
24405
24533
|
const isReachable = await pingHost(host);
|
|
24406
24534
|
if (!isReachable) {
|
|
@@ -24414,19 +24542,11 @@ async function autoDetectDeviceType(inputs) {
|
|
|
24414
24542
|
logger?.log?.(
|
|
24415
24543
|
`[AutoDetect] Forced mode=udp, skipping TCP and starting UDP discovery/login...`
|
|
24416
24544
|
);
|
|
24417
|
-
let normalizedUid =
|
|
24545
|
+
let normalizedUid = await speculativeUidPromise;
|
|
24418
24546
|
if (!normalizedUid) {
|
|
24419
|
-
|
|
24420
|
-
`
|
|
24547
|
+
throw new Error(
|
|
24548
|
+
`Forced UDP autodetect requires UID (or successful UDP UID discovery), but none was provided/discovered (ip=${host}).`
|
|
24421
24549
|
);
|
|
24422
|
-
const discovered = await discoverUidForHost(host, logger);
|
|
24423
|
-
const normalizedDiscovered = normalizeUid(discovered);
|
|
24424
|
-
if (!normalizedDiscovered) {
|
|
24425
|
-
throw new Error(
|
|
24426
|
-
`Forced UDP autodetect requires UID (or successful UDP UID discovery), but none was provided/discovered (ip=${host}).`
|
|
24427
|
-
);
|
|
24428
|
-
}
|
|
24429
|
-
normalizedUid = normalizedDiscovered;
|
|
24430
24550
|
}
|
|
24431
24551
|
const methodsToTry = inputs.udpDiscoveryMethod ? [inputs.udpDiscoveryMethod] : ["local-direct", "local-broadcast", "remote", "relay", "map"];
|
|
24432
24552
|
return await runUdpMethodsParallel(
|
|
@@ -24673,26 +24793,15 @@ async function autoDetectDeviceType(inputs) {
|
|
|
24673
24793
|
throw tcpError;
|
|
24674
24794
|
}
|
|
24675
24795
|
logger?.log?.(`[AutoDetect] TCP failed, trying UDP...`);
|
|
24676
|
-
let normalizedUid =
|
|
24796
|
+
let normalizedUid = await speculativeUidPromise;
|
|
24677
24797
|
if (!normalizedUid) {
|
|
24678
24798
|
logger?.log?.(
|
|
24679
|
-
`[AutoDetect] UID
|
|
24799
|
+
`[AutoDetect] UID discovery failed; only local-direct can run without a UID. If the camera is sleeping or on a different subnet, supply its UID to enable BCUDP P2P fallback (remote/relay/map) which can wake it via Reolink's servers.`
|
|
24800
|
+
);
|
|
24801
|
+
} else if (effectiveUid === void 0) {
|
|
24802
|
+
logger?.log?.(
|
|
24803
|
+
`[AutoDetect] UID resolved via concurrent broadcast discovery: ${normalizedUid}`
|
|
24680
24804
|
);
|
|
24681
|
-
const discovered = await discoverUidForHost(host, logger);
|
|
24682
|
-
if (discovered) {
|
|
24683
|
-
const normalizedDiscovered = normalizeUid(discovered);
|
|
24684
|
-
if (normalizedDiscovered) {
|
|
24685
|
-
normalizedUid = normalizedDiscovered;
|
|
24686
|
-
logger?.log?.(
|
|
24687
|
-
`[AutoDetect] UID discovered via broadcast: ${normalizedUid}`
|
|
24688
|
-
);
|
|
24689
|
-
}
|
|
24690
|
-
}
|
|
24691
|
-
if (!normalizedUid) {
|
|
24692
|
-
logger?.log?.(
|
|
24693
|
-
`[AutoDetect] UID discovery failed; only local-direct can run without a UID. If the camera is sleeping or on a different subnet, supply its UID to enable BCUDP P2P fallback (remote/relay/map) which can wake it via Reolink's servers.`
|
|
24694
|
-
);
|
|
24695
|
-
}
|
|
24696
24805
|
}
|
|
24697
24806
|
try {
|
|
24698
24807
|
const detectOverUdpApi = async (udpApi, udpDiscoveryMethod) => {
|
|
@@ -24790,6 +24899,7 @@ export {
|
|
|
24790
24899
|
encodeHeader,
|
|
24791
24900
|
decodeHeader,
|
|
24792
24901
|
BaichuanFrameParser,
|
|
24902
|
+
isUnroutableForP2P,
|
|
24793
24903
|
BcUdpStream,
|
|
24794
24904
|
asLogger,
|
|
24795
24905
|
createNullLogger,
|
|
@@ -24854,6 +24964,7 @@ export {
|
|
|
24854
24964
|
normalizeUid,
|
|
24855
24965
|
maskUid,
|
|
24856
24966
|
isTcpFailureThatShouldFallbackToUdp,
|
|
24967
|
+
tcpReachabilityProbe,
|
|
24857
24968
|
autoDetectDeviceType
|
|
24858
24969
|
};
|
|
24859
|
-
//# sourceMappingURL=chunk-
|
|
24970
|
+
//# sourceMappingURL=chunk-OJQLZETO.js.map
|