mirror of
https://github.com/YuzuZensai/Termix.git
synced 2026-09-13 18:58:52 +00:00
86 lines
2.3 KiB
JavaScript
86 lines
2.3 KiB
JavaScript
const fs = require("fs");
|
|||
|
|
const os = require("os");
|
||
|
|
const path = require("path");
|
||
|
|
const { spawn } = require("child_process");
|
||
|
|
|
||
|
|
function singleLine(value, maxLength = 255) {
|
||
|
|
return String(value ?? "")
|
||
|
|
.replace(/[\r\n\0]/g, "")
|
||
|
|
.trim()
|
||
|
|
.slice(0, maxLength);
|
||
|
|
}
|
||
|
|
|
||
|
|
function validateNativeRdpOptions(options) {
|
||
|
|
const host = singleLine(options?.host);
|
||
|
|
const port = Number(options?.port ?? 3389);
|
||
|
|
if (!host || host.length > 253 || /[\\/]/.test(host)) {
|
||
|
|
throw new Error("Invalid RDP host");
|
||
|
|
}
|
||
|
|
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
||
|
|
throw new Error("Invalid RDP port");
|
||
|
|
}
|
||
|
|
return {
|
||
|
|
host,
|
||
|
|
port,
|
||
|
|
username: singleLine(options?.username),
|
||
|
|
domain: singleLine(options?.domain),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
function buildRdpFile(options) {
|
||
|
|
const { host, port, username, domain } = validateNativeRdpOptions(options);
|
||
|
|
const address = host.includes(":") ? `[${host}]:${port}` : `${host}:${port}`;
|
||
|
|
const qualifiedUsername = username
|
||
|
|
? domain
|
||
|
|
? `${domain}\\${username}`
|
||
|
|
: username
|
||
|
|
: "";
|
||
|
|
return [
|
||
|
|
`full address:s:${address}`,
|
||
|
|
"prompt for credentials:i:1",
|
||
|
|
"administrative session:i:0",
|
||
|
|
...(qualifiedUsername ? [`username:s:${qualifiedUsername}`] : []),
|
||
|
|
"",
|
||
|
|
].join("\r\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
async function launchNativeRdp(options, platform = process.platform) {
|
||
|
|
if (platform !== "win32") {
|
||
|
|
return {
|
||
|
|
success: false,
|
||
|
|
error: "Windows Remote Desktop is only available on Windows",
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
const rdpContent = buildRdpFile(options);
|
||
|
|
const tempDir = await fs.promises.mkdtemp(
|
||
|
|
path.join(os.tmpdir(), "termix-rdp-"),
|
||
|
|
);
|
||
|
|
const rdpPath = path.join(tempDir, "connection.rdp");
|
||
|
|
await fs.promises.writeFile(rdpPath, rdpContent, {
|
||
|
|
encoding: "utf8",
|
||
|
|
mode: 0o600,
|
||
|
|
});
|
||
|
|
|
||
|
|
return new Promise((resolve) => {
|
||
|
|
const child = spawn("mstsc.exe", [rdpPath], {
|
||
|
|
detached: true,
|
||
|
|
windowsHide: true,
|
||
|
|
stdio: "ignore",
|
||
|
|
});
|
||
|
|
const cleanup = () =>
|
||
|
|
fs.promises.rm(tempDir, { recursive: true, force: true }).catch(() => {});
|
||
|
|
child.once("error", (error) => {
|
||
|
|
cleanup();
|
||
|
|
resolve({ success: false, error: error.message });
|
||
|
|
});
|
||
|
|
child.once("spawn", () => {
|
||
|
|
child.unref();
|
||
|
|
setTimeout(cleanup, 30_000).unref();
|
||
|
|
resolve({ success: true });
|
||
|
|
});
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
module.exports = { buildRdpFile, launchNativeRdp, validateNativeRdpOptions };
|