cfpilot-scanner 0.4.0 → 0.4.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/cli.js +129 -72
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -406,69 +406,6 @@ function readIpFile(text) {
|
|
|
406
406
|
return out;
|
|
407
407
|
}
|
|
408
408
|
|
|
409
|
-
// src/candidates.ts
|
|
410
|
-
var DEFAULT_CANDIDATE_SAMPLE = 2e4;
|
|
411
|
-
var DEFAULT_CANDIDATE_SEED = 24301;
|
|
412
|
-
var DEFAULT_BRIDGE_SAMPLE = 6556;
|
|
413
|
-
function normalizeCidrInput(cidr) {
|
|
414
|
-
if (cidr === void 0) return [];
|
|
415
|
-
const list = Array.isArray(cidr) ? cidr : [cidr];
|
|
416
|
-
const out = [];
|
|
417
|
-
for (const item of list) {
|
|
418
|
-
if (typeof item !== "string") continue;
|
|
419
|
-
for (const part of item.split(/[\n,]+/)) {
|
|
420
|
-
const value = part.trim();
|
|
421
|
-
if (value) out.push(value);
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
return out;
|
|
425
|
-
}
|
|
426
|
-
function isIpv6Cidr(cidr) {
|
|
427
|
-
return cidr.includes(":");
|
|
428
|
-
}
|
|
429
|
-
function filterByFamily(cidrs, family) {
|
|
430
|
-
if (family === "both") return cidrs;
|
|
431
|
-
const wantIpv6 = family === "v6";
|
|
432
|
-
return cidrs.filter((cidr) => isIpv6Cidr(cidr) === wantIpv6);
|
|
433
|
-
}
|
|
434
|
-
async function loadCloudflareCidrs(family) {
|
|
435
|
-
let ipv4 = BUILTIN_IPV4_CIDRS;
|
|
436
|
-
let ipv6 = BUILTIN_IPV6_CIDRS;
|
|
437
|
-
try {
|
|
438
|
-
const ranges = await fetchCloudflareCidrs();
|
|
439
|
-
if (ranges.ipv4.length > 0) ipv4 = ranges.ipv4;
|
|
440
|
-
if (ranges.ipv6.length > 0) ipv6 = ranges.ipv6;
|
|
441
|
-
} catch {
|
|
442
|
-
}
|
|
443
|
-
if (family === "v4") return [...ipv4];
|
|
444
|
-
if (family === "v6") return [...ipv6];
|
|
445
|
-
return [...ipv4, ...ipv6];
|
|
446
|
-
}
|
|
447
|
-
async function resolveCandidates(opts = {}) {
|
|
448
|
-
const sample = opts.sample ?? DEFAULT_CANDIDATE_SAMPLE;
|
|
449
|
-
const seed = opts.seed ?? DEFAULT_CANDIDATE_SEED;
|
|
450
|
-
const family = opts.family ?? "both";
|
|
451
|
-
const customCidrs = normalizeCidrInput(opts.cidr);
|
|
452
|
-
if (customCidrs.length > 0) {
|
|
453
|
-
return expandCidrs(filterByFamily(customCidrs, family), { sample, seed });
|
|
454
|
-
}
|
|
455
|
-
const cidrs = await loadCloudflareCidrs(family);
|
|
456
|
-
return expandCidrs(cidrs, { sample, seed });
|
|
457
|
-
}
|
|
458
|
-
function normalizeCandidateList(input) {
|
|
459
|
-
if (!Array.isArray(input) || input.length === 0) return [];
|
|
460
|
-
const lines = input.filter((item) => typeof item === "string");
|
|
461
|
-
if (lines.length === 0) return [];
|
|
462
|
-
const seen = /* @__PURE__ */ new Set();
|
|
463
|
-
const out = [];
|
|
464
|
-
for (const ip of readIpFile(lines.join("\n"))) {
|
|
465
|
-
if (seen.has(ip)) continue;
|
|
466
|
-
seen.add(ip);
|
|
467
|
-
out.push(ip);
|
|
468
|
-
}
|
|
469
|
-
return out;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
409
|
// src/net-env.ts
|
|
473
410
|
import * as net from "node:net";
|
|
474
411
|
import { execFile } from "node:child_process";
|
|
@@ -532,6 +469,44 @@ function isFakeIp(ip) {
|
|
|
532
469
|
const [a, b] = parts;
|
|
533
470
|
return a === 198 && (b === 18 || b === 19);
|
|
534
471
|
}
|
|
472
|
+
function isPublicAddress(ip) {
|
|
473
|
+
const value = ip.trim();
|
|
474
|
+
if (!value) return false;
|
|
475
|
+
if (!value.includes(":")) {
|
|
476
|
+
const parts = value.split(".");
|
|
477
|
+
if (parts.length !== 4) return false;
|
|
478
|
+
const nums = [];
|
|
479
|
+
for (const p of parts) {
|
|
480
|
+
if (!/^\d{1,3}$/.test(p)) return false;
|
|
481
|
+
const n = Number(p);
|
|
482
|
+
if (n > 255) return false;
|
|
483
|
+
nums.push(n);
|
|
484
|
+
}
|
|
485
|
+
const [a, b] = nums;
|
|
486
|
+
if (a === 0) return false;
|
|
487
|
+
if (a === 10) return false;
|
|
488
|
+
if (a === 127) return false;
|
|
489
|
+
if (a >= 224) return false;
|
|
490
|
+
if (a === 169 && b === 254) return false;
|
|
491
|
+
if (a === 172 && b >= 16 && b <= 31) return false;
|
|
492
|
+
if (a === 192 && b === 168) return false;
|
|
493
|
+
if (a === 100 && b >= 64 && b <= 127) return false;
|
|
494
|
+
if (a === 198 && (b === 18 || b === 19)) return false;
|
|
495
|
+
return true;
|
|
496
|
+
}
|
|
497
|
+
const lower = value.toLowerCase();
|
|
498
|
+
if (lower.startsWith("::ffff:") && lower.includes(".")) {
|
|
499
|
+
return isPublicAddress(lower.slice(7));
|
|
500
|
+
}
|
|
501
|
+
const groups = lower.split(":");
|
|
502
|
+
if (groups.length < 3) return false;
|
|
503
|
+
const first = groups[0];
|
|
504
|
+
if (!/^[0-9a-f]{1,4}$/.test(first)) return false;
|
|
505
|
+
const firstByte = Number.parseInt(first.slice(0, 2).padEnd(2, "0"), 16);
|
|
506
|
+
if (firstByte < 32 || firstByte > 63) return false;
|
|
507
|
+
if (lower.startsWith("2001:db8:")) return false;
|
|
508
|
+
return true;
|
|
509
|
+
}
|
|
535
510
|
async function detectProxyInterference(host) {
|
|
536
511
|
const findings = [];
|
|
537
512
|
const listening = [];
|
|
@@ -574,6 +549,74 @@ function isMsysPollutedPath(value) {
|
|
|
574
549
|
return looksWindows || hasMsysHint;
|
|
575
550
|
}
|
|
576
551
|
|
|
552
|
+
// src/candidates.ts
|
|
553
|
+
var DEFAULT_CANDIDATE_SAMPLE = 2e4;
|
|
554
|
+
var DEFAULT_CANDIDATE_SEED = 24301;
|
|
555
|
+
var DEFAULT_BRIDGE_SAMPLE = 6556;
|
|
556
|
+
function normalizeCidrInput(cidr) {
|
|
557
|
+
if (cidr === void 0) return [];
|
|
558
|
+
const list = Array.isArray(cidr) ? cidr : [cidr];
|
|
559
|
+
const out = [];
|
|
560
|
+
for (const item of list) {
|
|
561
|
+
if (typeof item !== "string") continue;
|
|
562
|
+
for (const part of item.split(/[\n,]+/)) {
|
|
563
|
+
const value = part.trim();
|
|
564
|
+
if (value) out.push(value);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
return out;
|
|
568
|
+
}
|
|
569
|
+
function isIpv6Cidr(cidr) {
|
|
570
|
+
return cidr.includes(":");
|
|
571
|
+
}
|
|
572
|
+
function filterByFamily(cidrs, family) {
|
|
573
|
+
if (family === "both") return cidrs;
|
|
574
|
+
const wantIpv6 = family === "v6";
|
|
575
|
+
return cidrs.filter((cidr) => isIpv6Cidr(cidr) === wantIpv6);
|
|
576
|
+
}
|
|
577
|
+
async function loadCloudflareCidrs(family) {
|
|
578
|
+
let ipv4 = BUILTIN_IPV4_CIDRS;
|
|
579
|
+
let ipv6 = BUILTIN_IPV6_CIDRS;
|
|
580
|
+
try {
|
|
581
|
+
const ranges = await fetchCloudflareCidrs();
|
|
582
|
+
if (ranges.ipv4.length > 0) ipv4 = ranges.ipv4;
|
|
583
|
+
if (ranges.ipv6.length > 0) ipv6 = ranges.ipv6;
|
|
584
|
+
} catch {
|
|
585
|
+
}
|
|
586
|
+
if (family === "v4") return [...ipv4];
|
|
587
|
+
if (family === "v6") return [...ipv6];
|
|
588
|
+
return [...ipv4, ...ipv6];
|
|
589
|
+
}
|
|
590
|
+
async function resolveCandidates(opts = {}) {
|
|
591
|
+
const sample = opts.sample ?? DEFAULT_CANDIDATE_SAMPLE;
|
|
592
|
+
const seed = opts.seed ?? DEFAULT_CANDIDATE_SEED;
|
|
593
|
+
const family = opts.family ?? "both";
|
|
594
|
+
const customCidrs = normalizeCidrInput(opts.cidr);
|
|
595
|
+
if (customCidrs.length > 0) {
|
|
596
|
+
return expandCidrs(filterByFamily(customCidrs, family), { sample, seed });
|
|
597
|
+
}
|
|
598
|
+
const cidrs = await loadCloudflareCidrs(family);
|
|
599
|
+
return expandCidrs(cidrs, { sample, seed });
|
|
600
|
+
}
|
|
601
|
+
function normalizeCandidateList(input) {
|
|
602
|
+
if (!Array.isArray(input) || input.length === 0) return [];
|
|
603
|
+
const lines = input.filter((item) => typeof item === "string");
|
|
604
|
+
if (lines.length === 0) return [];
|
|
605
|
+
const seen = /* @__PURE__ */ new Set();
|
|
606
|
+
const out = [];
|
|
607
|
+
for (const ip of readIpFile(lines.join("\n"))) {
|
|
608
|
+
if (seen.has(ip)) continue;
|
|
609
|
+
seen.add(ip);
|
|
610
|
+
const isIpv4Literal = /^\d{1,3}(\.\d{1,3}){3}$/.test(ip);
|
|
611
|
+
const isIpv6Literal = ip.includes(":");
|
|
612
|
+
if (isIpv4Literal || isIpv6Literal) {
|
|
613
|
+
if (!isPublicAddress(ip)) continue;
|
|
614
|
+
}
|
|
615
|
+
out.push(ip);
|
|
616
|
+
}
|
|
617
|
+
return out;
|
|
618
|
+
}
|
|
619
|
+
|
|
577
620
|
// src/speed-url.ts
|
|
578
621
|
import * as tls from "node:tls";
|
|
579
622
|
var SPEED_URL_PRESETS = [
|
|
@@ -1307,10 +1350,20 @@ async function pool(items, concurrency, worker) {
|
|
|
1307
1350
|
await Promise.all(runners);
|
|
1308
1351
|
return out;
|
|
1309
1352
|
}
|
|
1353
|
+
async function withRetry(attempts, fn, isOk = (r) => r.ok === true) {
|
|
1354
|
+
const total = Math.max(1, Math.floor(attempts));
|
|
1355
|
+
let last;
|
|
1356
|
+
for (let i = 0; i < total; i++) {
|
|
1357
|
+
last = await fn();
|
|
1358
|
+
if (isOk(last)) return last;
|
|
1359
|
+
}
|
|
1360
|
+
return last;
|
|
1361
|
+
}
|
|
1310
1362
|
async function runScan(options) {
|
|
1311
1363
|
const started = Date.now();
|
|
1312
1364
|
const concurrency = options.concurrency ?? 300;
|
|
1313
1365
|
const tcpTimeoutMs = options.tcpTimeoutMs ?? 1200;
|
|
1366
|
+
const retries = options.retries ?? 2;
|
|
1314
1367
|
const tlsConcurrency = options.tlsConcurrency ?? 64;
|
|
1315
1368
|
const tlsTimeoutMs = options.tlsTimeoutMs ?? 4e3;
|
|
1316
1369
|
const maxLatencyMs = options.maxLatencyMs ?? 0;
|
|
@@ -1328,7 +1381,7 @@ async function runScan(options) {
|
|
|
1328
1381
|
report({ type: "stage", stage: 1, name: "TCP \u63E1\u624B\u521D\u7B5B", total: candidates.length });
|
|
1329
1382
|
let done = 0;
|
|
1330
1383
|
const tcpResults = await pool(candidates, concurrency, async (ip) => {
|
|
1331
|
-
const result = await tcpPing(ip, options.port, tcpTimeoutMs);
|
|
1384
|
+
const result = await withRetry(retries, () => tcpPing(ip, options.port, tcpTimeoutMs));
|
|
1332
1385
|
done++;
|
|
1333
1386
|
if (done % 500 === 0 || done === candidates.length) {
|
|
1334
1387
|
report({
|
|
@@ -1346,7 +1399,11 @@ async function runScan(options) {
|
|
|
1346
1399
|
report({ type: "stage", stage: 1, name: "TLS \u63E1\u624B\u7B5B\u9009", total: filtered.length });
|
|
1347
1400
|
let tlsDone = 0;
|
|
1348
1401
|
const tlsResults = await pool(filtered, tlsConcurrency, async (item) => {
|
|
1349
|
-
const probe = await
|
|
1402
|
+
const probe = await withRetry(
|
|
1403
|
+
retries,
|
|
1404
|
+
() => probeTlsHandshake(item.ip, options.port, options.host, tlsTimeoutMs),
|
|
1405
|
+
(r) => r.tlsMs > 0
|
|
1406
|
+
);
|
|
1350
1407
|
tlsDone++;
|
|
1351
1408
|
if (tlsDone % 200 === 0 || tlsDone === filtered.length) {
|
|
1352
1409
|
report({
|
|
@@ -1374,12 +1431,9 @@ async function runScan(options) {
|
|
|
1374
1431
|
const probeResults = await pool(stage1, probeConcurrency, async (item) => {
|
|
1375
1432
|
const probeFn = strictProbe ? probeWsPing : probeWs101;
|
|
1376
1433
|
const probePath = strictProbe ? strictProbePath : options.wsPath ?? "/__edge_probe";
|
|
1377
|
-
const result = await
|
|
1378
|
-
|
|
1379
|
-
options.port,
|
|
1380
|
-
options.host,
|
|
1381
|
-
probeTimeoutMs,
|
|
1382
|
-
probePath
|
|
1434
|
+
const result = await withRetry(
|
|
1435
|
+
retries,
|
|
1436
|
+
() => probeFn(item.ip, options.port, options.host, probeTimeoutMs, probePath)
|
|
1383
1437
|
);
|
|
1384
1438
|
probeDone++;
|
|
1385
1439
|
if (result.pongVerified) verifiedCount++;
|
|
@@ -1804,7 +1858,7 @@ async function handleRequest(req, res) {
|
|
|
1804
1858
|
}
|
|
1805
1859
|
|
|
1806
1860
|
// src/version.ts
|
|
1807
|
-
var VERSION = "0.4.
|
|
1861
|
+
var VERSION = "0.4.1";
|
|
1808
1862
|
var NAME = "cfpilot-scanner";
|
|
1809
1863
|
|
|
1810
1864
|
// src/cli.ts
|
|
@@ -1838,6 +1892,8 @@ scan \u9009\u9879:
|
|
|
1838
1892
|
--speed-conns <n> \u6D4B\u901F\u5E76\u53D1\u8FDE\u63A5\u6570\uFF0C\u9ED8\u8BA4 3
|
|
1839
1893
|
\u5355\u6761 TCP \u8FDE\u63A5\u53D7\u62E5\u585E\u7A97\u53E3\u9650\u5236\uFF0C\u591A\u8FDE\u63A5\u66F4\u63A5\u8FD1\u771F\u5B9E\u5E26\u5BBD
|
|
1840
1894
|
--speed-duration <ms> \u6D4B\u901F\u65F6\u957F\uFF0C\u9ED8\u8BA4 6000
|
|
1895
|
+
--retries <n> \u6BCF\u4E2A\u63A2\u6D4B\u7684\u5C1D\u8BD5\u6B21\u6570\uFF08\u542B\u9996\u6B21\uFF09\uFF0C\u9ED8\u8BA4 2
|
|
1896
|
+
\u771F\u673A\u5076\u53D1\u5931\u8D25\u7387 3~5%\uFF0C\u91CD\u8BD5\u53D6\u7B2C\u4E00\u6B21\u6210\u529F\u7684\u7ED3\u679C
|
|
1841
1897
|
--no-verify \u9636\u6BB5\u4E8C\u53EA\u505A 101 \u63E1\u624B\uFF0C\u8DF3\u8FC7\u54CD\u5E94\u4F53\u6821\u9A8C
|
|
1842
1898
|
\uFF08\u9ED8\u8BA4\u6821\u9A8C\uFF1A\u53D1 PING \u5E76\u9A8C\u8BC1\u6570\u636E\u9762\u56DE {"pong":true}\uFF09
|
|
1843
1899
|
--no-speed \u8DF3\u8FC7\u6D4B\u901F\uFF0C\u53EA\u6309\u5EF6\u8FDF\u6392\u5E8F
|
|
@@ -2121,6 +2177,7 @@ ${NAME} v${VERSION}
|
|
|
2121
2177
|
skipSpeed,
|
|
2122
2178
|
verifyPayload,
|
|
2123
2179
|
concurrency: flagNumber(args, "concurrency", 300),
|
|
2180
|
+
retries: flagNumber(args, "retries", 2),
|
|
2124
2181
|
tcpTimeoutMs: flagNumber(args, "timeout", 1200),
|
|
2125
2182
|
tlsConcurrency: flagNumber(args, "tls-concurrency", 64),
|
|
2126
2183
|
tlsTimeoutMs: flagNumber(args, "tls-timeout", 4e3),
|