CH-J Server Managerspráva serverů přes SSH
Menu
Vydané zdroje

CH-J Server Manager

Procházejte adresáře a soubory konkrétního vydání aplikace.

Stáhnout zdroje ZIP
CH-J Proprietary Software License 1.14

Zdroje jsou zveřejněny pod CH-J Proprietary Software License 1.14. Jejich dostupnost nemění licenční podmínky ani neposkytuje další oprávnění.

9,3 KB · 90 řádkůStáhnout soubor
1"use strict";
2const { spawn } = require("node:child_process");
3const net = require("node:net");
4const { setTimeout: delay } = require("node:timers/promises");
5const { DiagnosticError, checkAbort, stats, now } = require("./common");
6const { parsePing } = require("../sessions/latencyMonitor");
7function runProcess(executable, args, { timeoutMs = 5000, signal, onLine, maxBytes = 256 * 1024, spawnImpl = spawn } = {}) {
8 checkAbort(signal);
9 return new Promise((resolve, reject) => {
10 const child = spawnImpl(executable, args, { shell: false, windowsHide: true, env: { ...process.env, LC_ALL: "C", LANG: "C" }, stdio: ["ignore", "pipe", "pipe"] });
11 let stdout = [], stderr = [], bytes = 0, pending = "", failure, settled = false, killTimer;
12 const terminate = (error) => { failure ||= error; child.kill(); killTimer ||= setTimeout(() => child.kill("SIGKILL"), 1000); killTimer.unref?.(); };
13 const abort = () => terminate(new DiagnosticError("CANCELLED"));
14 const timer = setTimeout(() => terminate(new DiagnosticError("TIMEOUT")), timeoutMs);
15 signal?.addEventListener("abort", abort, { once: true });
16 const finish = (error, code) => { if (settled) return; settled = true; clearTimeout(timer); clearTimeout(killTimer); signal?.removeEventListener("abort", abort); if (pending && onLine) onLine(pending); error || failure ? reject(error || failure) : resolve({ code, stdout: Buffer.concat(stdout), stderr: Buffer.concat(stderr).toString("utf8") }); };
17 child.once("error", (error) => finish(error));
18 child.stdout.on("data", (chunk) => { bytes += chunk.length; if (bytes > maxBytes) return terminate(new DiagnosticError("OUTPUT_LIMIT")); stdout.push(chunk); if (onLine) { pending += chunk.toString("utf8"); const lines = pending.split(/\r?\n/); pending = lines.pop(); lines.forEach(onLine); } });
19 child.stderr.on("data", (chunk) => { bytes += chunk.length; if (bytes > maxBytes) return terminate(new DiagnosticError("OUTPUT_LIMIT")); stderr.push(chunk); });
20 child.once("close", (code) => finish(null, code));
21 });
23function pingCommand(address, timeoutMs, platform = process.platform) {
24 if (!net.isIP(address)) throw new DiagnosticError("INVALID_IP");
25 const v6 = net.isIP(address) === 6;
26 if (platform === "win32") return { executable: "ping.exe", args: [v6 ? "-6" : "-4", "-n", "1", "-w", String(timeoutMs), address] };
27 if (platform === "darwin") return { executable: v6 ? "/sbin/ping6" : "/sbin/ping", args: ["-n", "-c", "1", ...(v6 ? [] : ["-W", String(timeoutMs)]), address] };
28 if (platform === "linux") return { executable: "ping", args: [v6 ? "-6" : "-4", "-n", "-c", "1", "-W", String(Math.ceil(timeoutMs / 1000)), address] };
29 throw new DiagnosticError("PLATFORM_UNSUPPORTED");
31function parsePingReply(output) {
32 const rttMs = parsePing(output), upper = String(output).match(/<\s*(\d+(?:[.,]\d+)?)\s*ms/i), ttl = String(output).match(/(?:ttl|hlim|hop limit)[=:\s]+(\d+)/i);
33 return { received: rttMs !== null || Boolean(upper), rttMs, upperBoundMs: upper ? Number(upper[1].replace(",", ".")) : null, ttl: ttl ? Number(ttl[1]) : null };
35async function ping(address, options, signal, progress, runner = runProcess) {
36 const packets = []; let sent = 0, received = 0, cycle = 0;
37 do {
38 for (let index = 0; index < options.count; index++) {
39 checkAbort(signal); sent++; let packet;
40 try {
41 const command = pingCommand(address, options.timeoutMs);
42 const result = await runner(command.executable, command.args, { timeoutMs: options.timeoutMs + 500, signal });
43 const raw = result.stdout.toString("utf8"); const reply = parsePingReply(raw);
44 const explicitTimeout = /Request timed out|Request timeout|Zeitüberschreitung|Vypršel.*časov[ýy].*limit/i.test(raw);
45 packet = { sequence: sent, address, ...reply, status: reply.received ? "success" : explicitTimeout ? "timeout" : "no-reply", raw: (raw + result.stderr).slice(0, 4096) };
46 if (!reply.received && (result.code > (process.platform === "darwin" ? 2 : 1) || /not permitted|permission denied|invalid option|unknown option|network is unreachable|no route to host|general failure|transmit failed/i.test(result.stderr))) packet.status = "error";
47 } catch (error) {
48 if (signal.aborted) throw error;
49 packet = { sequence: sent, address, received: false, rttMs: null, status: error.code === "ENOENT" ? "unavailable" : error.code === "TIMEOUT" ? "timeout" : "error", code: error.code, raw: String(error.message).slice(0, 400) };
50 }
51 packet.measuredAt = new Date().toISOString();
52 if (["unavailable", "error"].includes(packet.status)) sent--;
53 if (packet.received) received++;
54 packets.push(packet); if (packets.length > 500) packets.shift();
55 progress({ kind: "ping-packet", ...packet, sent, received, lossPercent: sent ? 100 * (sent - received) / sent : null, statistics: stats(packets.map((p) => p.rttMs)) });
56 if (["unavailable", "error"].includes(packet.status)) return { status: packet.status, code: packet.code || "PING_TOOL_ERROR", reason: packet.raw, address, packets, sent, received, lossPercent: sent ? 100 * (sent - received) / sent : null, statistics: stats(packets.map((p) => p.rttMs)), note: "ICMP failure does not establish server unavailability or prove a firewall." };
57 if (index + 1 < options.count) await delay(1000, undefined, { signal });
58 }
59 cycle++;
60 if (options.continuous || cycle < options.repetitions) await delay(1000, undefined, { signal });
61 } while (options.continuous || cycle < options.repetitions);
62 return { status: received ? "success" : "warning", address, sent, received, lossPercent: sent ? 100 * (sent - received) / sent : null, packets, statistics: stats(packets.map((p) => p.rttMs)), note: "No ICMP reply may mean filtering or loss; it does not establish server unavailability." };
64function tracerouteCommand(address, options, platform = process.platform) {
65 const v6 = net.isIP(address) === 6;
66 if (!net.isIP(address)) throw new DiagnosticError("INVALID_IP");
67 if (platform === "win32") return { executable: "tracert.exe", args: [v6 ? "-6" : "-4", "-d", "-h", String(options.maxHops), "-w", String(options.timeoutMs), address] };
68 if (platform === "darwin") return { executable: v6 ? "/usr/sbin/traceroute6" : "/usr/sbin/traceroute", args: ["-n", "-m", String(options.maxHops), "-w", String(Math.ceil(options.timeoutMs / 1000)), address] };
69 if (platform === "linux") return { executable: "traceroute", args: [v6 ? "-6" : "-4", "-n", "-m", String(options.maxHops), "-w", String(options.timeoutMs / 1000), address] };
70 throw new DiagnosticError("PLATFORM_UNSUPPORTED");
72function parseHop(line) {
73 const match = String(line).match(/^\s*(\d{1,2})\s+(.+)/); if (!match) return null;
74 const tokens = match[2].split(/\s+/), addresses = tokens.map((token) => token.replace(/[()[\],]/g, "")).filter((value) => net.isIP(value));
75 const probes = [...match[2].matchAll(/(<\s*)?(\d+(?:[.,]\d+)?)\s*ms|\*/gi)].map((m) => m[0] === "*" ? { status: "timeout", rttMs: null } : { status: "success", rttMs: m[1] ? null : Number(m[2].replace(",", ".")), upperBoundMs: m[1] ? Number(m[2]) : null });
76 if (!probes.length && !addresses.length) return null;
77 return { hop: Number(match[1]), addresses: [...new Set(addresses)], probes, averageMs: stats(probes.map((p) => p.rttMs)).average, lossPercent: probes.length ? 100 * probes.filter((p) => p.status === "timeout").length / probes.length : null, raw: line.slice(0, 1024) };
79async function traceroute(address, options, signal, progress, runner = runProcess) {
80 const routes = [];
81 for (let iteration = 0; iteration < options.repetitions; iteration++) {
82 checkAbort(signal); const hops = [], command = tracerouteCommand(address, options);
83 try {
84 const result = await runner(command.executable, command.args, { signal, timeoutMs: Math.min(180000, options.maxHops * Math.ceil(options.timeoutMs / 1000) * 1000 * 3 + 2000), onLine(line) { const hop = parseHop(line); if (hop) { hops.push(hop); progress({ kind: "trace-hop", iteration, address, ...hop }); } } });
85 routes.push({ iteration, hops, exitCode: result.code, raw: result.stdout.toString("utf8").slice(0, 32000), reason: result.stderr.slice(0, 1000) });
86 } catch (error) { if (signal.aborted) throw error; if (hops.length) routes.push({ iteration, hops, incomplete: true, reason: error.message }); return { status: error.code === "ENOENT" ? "unavailable" : "error", code: error.code, reason: error.message, address, routes }; }
87 }
88 return { status: routes.some((route) => route.hops.some((hop) => hop.addresses.some((ip) => ip === address || net.isIP(ip) === 6 && net.isIP(address) === 6 && new URL(`http://[${ip}]/`).hostname === new URL(`http://[${address}]/`).hostname))) ? "success" : routes.every((route) => route.exitCode !== 0 && !route.hops.length) ? "error" : "warning", address, routes, effectiveProbeTimeoutMs: process.platform === "darwin" ? Math.ceil(options.timeoutMs / 1000) * 1000 : options.timeoutMs, routeChanged: routes.length > 1 ? routes.some((route) => JSON.stringify(route.hops.map((h) => h.addresses)) !== JSON.stringify(routes[0].hops.map((h) => h.addresses))) : null, method: process.platform === "win32" ? "ICMP tracert" : "System UDP traceroute", note: "Intermediate timeouts do not prove a broken route. Loss is the fraction of unanswered displayed probes, not end-to-end packet loss." };
90module.exports = { runProcess, pingCommand, parsePingReply, ping, tracerouteCommand, parseHop, traceroute };

SHA-256: 73c6240be94fb54609d1d0cbc2a2a257b5e7bd6edeb878eba2eea8cfd240d6af

SHA-256 archivu: 5ac91caf4fa32a6fdb114f2430deed486fbe7489d5eea343d1f034169fafb5e0