Playit.gg tunnel #491
4
package-lock.json
generated
4
package-lock.json
generated
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@the-bds-maneger/core",
|
"name": "@the-bds-maneger/core",
|
||||||
"version": "5.2.0",
|
"version": "5.3.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@the-bds-maneger/core",
|
"name": "@the-bds-maneger/core",
|
||||||
"version": "5.2.0",
|
"version": "5.3.0",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@the-bds-maneger/core-utils": "^1.0.0",
|
"@the-bds-maneger/core-utils": "^1.0.0",
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
"docs": "typedoc --readme none --out docs src/index.ts",
|
"docs": "typedoc --readme none --out docs src/index.ts",
|
||||||
"dev": "nodemon",
|
"dev": "nodemon",
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"test:partial": "mocha -r ts-node/register --exit",
|
"test:partial": "mocha -r ts-node/register -r tsconfig-paths/register --exit",
|
||||||
"test": "npm run test:partial -- 'tests/**/*.ts'"
|
"test": "npm run test:partial -- 'tests/**/*.ts'"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { platformManeger } from "@the-bds-maneger/server_versions";
|
import { platformManeger } from "@the-bds-maneger/server_versions";
|
||||||
import { manegerConfigProprieties } from "./configManipulate";
|
import { manegerConfigProprieties } from "./configManipulate";
|
||||||
import { randomPort } from "./lib/randomPort";
|
import { randomPort } from "./utils/randomPort";
|
||||||
import { pathControl, bdsPlatformOptions } from "./platformPathManeger";
|
import { pathControl, bdsPlatformOptions } from "./platformPathManeger";
|
||||||
import * as globalPlatfroms from "./globalPlatfroms";
|
import * as globalPlatfroms from "./globalPlatfroms";
|
||||||
import * as coreUtils from "@the-bds-maneger/core-utils";
|
import * as coreUtils from "@the-bds-maneger/core-utils";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import fs from "node:fs/promises";
|
import fs from "node:fs/promises";
|
||||||
import utils from "node:util";
|
import utils from "node:util";
|
||||||
import Proprieties, { properitiesBase } from "./lib/Proprieties";
|
import Proprieties, { properitiesBase } from "./utils/Proprieties";
|
||||||
|
|
||||||
export type configEdit = {name: string, data?: any};
|
export type configEdit = {name: string, data?: any};
|
||||||
export async function manegerConfigProprieties<updateConfig extends configEdit, configJson extends properitiesBase = any>(config: {configPath: string, configManipulate: {[Properties in updateConfig["name"]]: ((config: string, value: updateConfig["data"]) => string)|{validate?: (value: updateConfig["data"]) => boolean, regexReplace: RegExp, valueFormat: string, addIfNotExist?: string}}}) {
|
export async function manegerConfigProprieties<updateConfig extends configEdit, configJson extends properitiesBase = any>(config: {configPath: string, configManipulate: {[Properties in updateConfig["name"]]: ((config: string, value: updateConfig["data"]) => string)|{validate?: (value: updateConfig["data"]) => boolean, regexReplace: RegExp, valueFormat: string, addIfNotExist?: string}}}) {
|
||||||
|
@ -4,7 +4,7 @@ import * as globalPlatfroms from "./globalPlatfroms";
|
|||||||
import * as pluginManeger from "./plugin/plugin";
|
import * as pluginManeger from "./plugin/plugin";
|
||||||
import * as export_import from "./export_import";
|
import * as export_import from "./export_import";
|
||||||
import * as pluginHooks from "./plugin/hook";
|
import * as pluginHooks from "./plugin/hook";
|
||||||
import * as proxy from "./lib/proxy";
|
import * as proxy from "./utils/proxy";
|
||||||
|
|
||||||
// Platforms
|
// Platforms
|
||||||
import * as Bedrock from "./bedrock";
|
import * as Bedrock from "./bedrock";
|
||||||
|
@ -3,7 +3,7 @@ import { platformManeger } from "@the-bds-maneger/server_versions";
|
|||||||
import { actionV2, actionsV2 } from "./globalPlatfroms";
|
import { actionV2, actionsV2 } from "./globalPlatfroms";
|
||||||
import { pathControl, bdsPlatformOptions } from "./platformPathManeger";
|
import { pathControl, bdsPlatformOptions } from "./platformPathManeger";
|
||||||
import { manegerConfigProprieties } from "./configManipulate";
|
import { manegerConfigProprieties } from "./configManipulate";
|
||||||
import { randomPort } from "./lib/randomPort";
|
import { randomPort } from "./utils/randomPort";
|
||||||
import fsOld from "node:fs";
|
import fsOld from "node:fs";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import fs from "node:fs/promises";
|
import fs from "node:fs/promises";
|
||||||
|
@ -1,89 +0,0 @@
|
|||||||
import dgram from "node:dgram";
|
|
||||||
import net from "node:net";
|
|
||||||
|
|
||||||
export type playitTunnelOptions = {};
|
|
||||||
export async function playitTunnel(options: playitTunnelOptions) {}
|
|
||||||
|
|
||||||
export type proxyUdpToTcpOptions = {
|
|
||||||
udpType?: dgram.SocketType,
|
|
||||||
listen?: number,
|
|
||||||
portListen?: (port: number) => void
|
|
||||||
};
|
|
||||||
|
|
||||||
export type proxyTcpToUdpClient = {
|
|
||||||
udpType?: dgram.SocketType,
|
|
||||||
listen?: number,
|
|
||||||
remote: {
|
|
||||||
host: string,
|
|
||||||
port: number
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transfer packets from UDP to TCP to send through some tunnel that only accepts TCP
|
|
||||||
*
|
|
||||||
* This also means that it will also have error transporting the data, so it is not guaranteed to work properly even more when dealing with UDP packets.
|
|
||||||
*/
|
|
||||||
export function proxyUdpToTcp(udpPort: number, options?: proxyUdpToTcpOptions) {
|
|
||||||
const tcpServer = net.createServer();
|
|
||||||
tcpServer.on("error", err => console.error(err));
|
|
||||||
tcpServer.on("connection", socket => {
|
|
||||||
const udpClient = dgram.createSocket(options?.udpType||"udp4");
|
|
||||||
|
|
||||||
// Close Sockets
|
|
||||||
udpClient.once("close", () => socket.end());
|
|
||||||
socket.once("close", () => udpClient.close());
|
|
||||||
|
|
||||||
// Print error
|
|
||||||
udpClient.on("error", console.error);
|
|
||||||
socket.on("error", console.error);
|
|
||||||
|
|
||||||
// Pipe Datas
|
|
||||||
udpClient.on("message", data => socket.write(data));
|
|
||||||
socket.on("data", data => udpClient.send(data));
|
|
||||||
|
|
||||||
// Connect
|
|
||||||
udpClient.connect(udpPort);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Listen
|
|
||||||
tcpServer.listen(options?.listen||0, function() {
|
|
||||||
const addr = this.address();
|
|
||||||
if (options?.portListen) options.portListen(addr.port);
|
|
||||||
console.debug("bds proxy port listen, %s, (udp -> tcp)", addr.port);
|
|
||||||
tcpServer.once("close", () => console.debug("bds proxy close, %s", addr.port));
|
|
||||||
});
|
|
||||||
|
|
||||||
return tcpServer;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function proxyTcpToUdp(options: proxyTcpToUdpClient) {
|
|
||||||
const sessions: {[keyIP: string]: net.Socket} = {};
|
|
||||||
const udp = dgram.createSocket(options?.udpType||"udp4");
|
|
||||||
|
|
||||||
udp.on("error", console.error);
|
|
||||||
udp.on("message", (msg, ipInfo) => {
|
|
||||||
const keyInfo = `${ipInfo.address}:${ipInfo.port}`;
|
|
||||||
|
|
||||||
// Client TCP
|
|
||||||
if (!sessions[keyInfo]) {
|
|
||||||
sessions[keyInfo] = net.createConnection(options.remote);
|
|
||||||
sessions[keyInfo].on("data", data => udp.send(data, ipInfo.port, ipInfo.address));
|
|
||||||
sessions[keyInfo].on("error", console.error);
|
|
||||||
sessions[keyInfo].once("close", () => {
|
|
||||||
delete sessions[keyInfo];
|
|
||||||
console.log("Client %s:%f close", ipInfo.address, ipInfo.port);
|
|
||||||
});
|
|
||||||
console.log("Client %s:%f connected", ipInfo.address, ipInfo.port);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send message
|
|
||||||
sessions[keyInfo].write(msg);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Listen port
|
|
||||||
udp.bind(options.listen||0, function(){
|
|
||||||
const addr = this.address();
|
|
||||||
console.log("Port listen, %s (tcp -> udp)", addr.port);
|
|
||||||
});
|
|
||||||
}
|
|
@ -3,7 +3,7 @@ import { pathControl, bdsPlatformOptions } from "./platformPathManeger";
|
|||||||
import { spigotProprieties } from "./spigot";
|
import { spigotProprieties } from "./spigot";
|
||||||
import * as globalPlatfroms from "./globalPlatfroms";
|
import * as globalPlatfroms from "./globalPlatfroms";
|
||||||
import * as coreUtils from "@the-bds-maneger/core-utils";
|
import * as coreUtils from "@the-bds-maneger/core-utils";
|
||||||
import Proprieties from "./lib/Proprieties";
|
import Proprieties from "./utils/Proprieties";
|
||||||
import fsOld from "node:fs";
|
import fsOld from "node:fs";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import fs from "node:fs/promises";
|
import fs from "node:fs/promises";
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { httpRequest, httpRequestLarge, httpRequestGithub } from "@the-bds-maneger/core-utils";
|
import { httpRequest, httpRequestLarge, httpRequestGithub } from "@the-bds-maneger/core-utils";
|
||||||
import { pathControl, bdsPlatformOptions } from "./platformPathManeger";
|
import { pathControl, bdsPlatformOptions } from "./platformPathManeger";
|
||||||
import * as globalPlatfroms from "./globalPlatfroms";
|
import * as globalPlatfroms from "./globalPlatfroms";
|
||||||
import Proprieties from "./lib/Proprieties"
|
import Proprieties from "./utils/Proprieties"
|
||||||
import fsOld from "node:fs";
|
import fsOld from "node:fs";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import fs from "node:fs/promises";
|
import fs from "node:fs/promises";
|
||||||
|
163
src/utils/proxy.ts
Normal file
163
src/utils/proxy.ts
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
import type { HTTPError } from "got";
|
||||||
|
import { httpRequest } from "@the-bds-maneger/core-utils";
|
||||||
|
import { format } from "node:util";
|
||||||
|
import crypto from "node:crypto";
|
||||||
|
import dgram from "node:dgram";
|
||||||
|
import net from "node:net";
|
||||||
|
|
||||||
|
export type playitAgentAccountStatus = {type: "agent-account-status", status: "verified-account", account_id: number};
|
||||||
|
export type agentConfig = {
|
||||||
|
type: "agent-config",
|
||||||
|
last_update: number,
|
||||||
|
ping_targets: string[],
|
||||||
|
ping_target_addresses: string[],
|
||||||
|
control_address: string,
|
||||||
|
refresh_from_api: true,
|
||||||
|
secret_key: string,
|
||||||
|
mappings: []
|
||||||
|
};
|
||||||
|
|
||||||
|
export type playitTunnelAuth = {
|
||||||
|
type: "signed-tunnel-request",
|
||||||
|
auth: {
|
||||||
|
details: {
|
||||||
|
account_id: number,
|
||||||
|
request_timestamp: number,
|
||||||
|
session_id: number
|
||||||
|
},
|
||||||
|
signature: {
|
||||||
|
System: {
|
||||||
|
signature: number[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
content: number[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type playitTunnelOptions = {secretKey: string, apiUrl?: string, controlAddress?: string, clientVersion?: string};
|
||||||
|
export async function playitTunnel(options: playitTunnelOptions) {
|
||||||
|
options = {apiUrl: "api.playit.cloud", controlAddress: "control.playit.gg", clientVersion: "0.2.3", ...options};
|
||||||
|
if (!options.secretKey) throw new Error("Required secret key to auth in playit.gg");
|
||||||
|
const Authorization = format("agent-key %s", options.secretKey);
|
||||||
|
const accountInfo = await httpRequest.getJSON<playitAgentAccountStatus>({url: format("https://%s/agent", options.apiUrl), method: "POST", headers: {Authorization}, body: {type: "get-agent-account-status", client_version: options.clientVersion}}).catch((err: HTTPError) => {
|
||||||
|
if (err.response.statusCode === 400) throw new Error("Secret key is invalid or not registred");
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
if (accountInfo.status !== "verified-account") throw new Error("Verify account fist");
|
||||||
|
const agentInfo = await httpRequest.getJSON<agentConfig>({url: format("https://%s/agent", options.apiUrl), method: "POST", headers: {Authorization}, body: {type: "get-agent-config", client_version: options.clientVersion}});
|
||||||
|
const signTunnel = await httpRequest.getJSON<playitTunnelAuth>({url: format("https://%s/agent", options.apiUrl), method: "POST", headers: {Authorization}, body: { type: "sign-tunnel-request", RegisterAgent: null }}).catch(err => err.response?.body?.toString()||err);
|
||||||
|
|
||||||
|
return {agentInfo, signTunnel};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type agentSecret = {type: "agent-secret", secret_key: string};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a key to asynchronously authenticate playit.gg clients
|
||||||
|
*/
|
||||||
|
export async function playitClaimUrl(clainUrlCallback?: (url: string) => void) {
|
||||||
|
const claimCode = crypto.pseudoRandomBytes(5).toString("hex");
|
||||||
|
const url = format("https://playit.gg/claim/%s?type=%s&name=%s", claimCode, "bdscore_agent", `bdscore_agent`);
|
||||||
|
if (clainUrlCallback) clainUrlCallback(url); else console.log("Playit claim url: %s", url);
|
||||||
|
|
||||||
|
// Register to API
|
||||||
|
let waitAuth = 0;
|
||||||
|
let authAttempts = 0;
|
||||||
|
async function getSecret(): Promise<agentSecret> {
|
||||||
|
return httpRequest.getJSON({url: "https://api.playit.cloud/agent", method: "POST", headers: {}, body: {type: "exchange-claim-for-secret", claim_key: claimCode}}).catch(async (err: HTTPError) => {
|
||||||
|
if (err?.response?.statusCode === 404||err?.response?.statusCode === 401) {
|
||||||
|
if (err.response.statusCode === 404) if (authAttempts++ > 225) throw new Error("client not open auth url");
|
||||||
|
if (err.response.statusCode === 401) if (waitAuth++ > 16) throw new Error("Claim code not authorized per client");
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 500));
|
||||||
|
return getSecret();
|
||||||
|
}
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (await getSecret()).secret_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type proxyUdpToTcpOptions = {
|
||||||
|
udpType?: dgram.SocketType,
|
||||||
|
listen?: number,
|
||||||
|
portListen?: (port: number) => void
|
||||||
|
};
|
||||||
|
|
||||||
|
export type proxyTcpToUdpClient = {
|
||||||
|
udpType?: dgram.SocketType,
|
||||||
|
listen?: number,
|
||||||
|
remote: {
|
||||||
|
host: string,
|
||||||
|
port: number
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transfer packets from UDP to TCP to send through some tunnel that only accepts TCP
|
||||||
|
*
|
||||||
|
* This also means that it will also have error transporting the data, so it is not guaranteed to work properly even more when dealing with UDP packets.
|
||||||
|
*/
|
||||||
|
export function proxyUdpToTcp(udpPort: number, options?: proxyUdpToTcpOptions) {
|
||||||
|
const tcpServer = net.createServer();
|
||||||
|
tcpServer.on("error", err => console.error(err));
|
||||||
|
tcpServer.on("connection", socket => {
|
||||||
|
const udpClient = dgram.createSocket(options?.udpType||"udp4");
|
||||||
|
|
||||||
|
// Close Sockets
|
||||||
|
udpClient.once("close", () => socket.end());
|
||||||
|
socket.once("close", () => udpClient.close());
|
||||||
|
|
||||||
|
// Print error
|
||||||
|
udpClient.on("error", console.error);
|
||||||
|
socket.on("error", console.error);
|
||||||
|
|
||||||
|
// Pipe Datas
|
||||||
|
udpClient.on("message", data => socket.write(data));
|
||||||
|
socket.on("data", data => udpClient.send(data));
|
||||||
|
|
||||||
|
// Connect
|
||||||
|
udpClient.connect(udpPort);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Listen
|
||||||
|
tcpServer.listen(options?.listen||0, function() {
|
||||||
|
const addr = this.address();
|
||||||
|
if (options?.portListen) options.portListen(addr.port);
|
||||||
|
console.debug("bds proxy port listen, %s, (udp -> tcp)", addr.port);
|
||||||
|
tcpServer.once("close", () => console.debug("bds proxy close, %s", addr.port));
|
||||||
|
});
|
||||||
|
|
||||||
|
return tcpServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function proxyTcpToUdp(options: proxyTcpToUdpClient) {
|
||||||
|
const sessions: {[keyIP: string]: net.Socket} = {};
|
||||||
|
const udp = dgram.createSocket(options?.udpType||"udp4");
|
||||||
|
|
||||||
|
udp.on("error", console.error);
|
||||||
|
udp.on("message", (msg, ipInfo) => {
|
||||||
|
const keyInfo = `${ipInfo.address}:${ipInfo.port}`;
|
||||||
|
|
||||||
|
// Client TCP
|
||||||
|
if (!sessions[keyInfo]) {
|
||||||
|
sessions[keyInfo] = net.createConnection(options.remote);
|
||||||
|
sessions[keyInfo].on("data", data => udp.send(data, ipInfo.port, ipInfo.address));
|
||||||
|
sessions[keyInfo].on("error", console.error);
|
||||||
|
sessions[keyInfo].once("close", () => {
|
||||||
|
delete sessions[keyInfo];
|
||||||
|
console.log("Client %s:%f close", ipInfo.address, ipInfo.port);
|
||||||
|
});
|
||||||
|
console.log("Client %s:%f connected", ipInfo.address, ipInfo.port);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send message
|
||||||
|
sessions[keyInfo].write(msg);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Listen port
|
||||||
|
udp.bind(options.listen||0, function(){
|
||||||
|
const addr = this.address();
|
||||||
|
console.log("Port listen, %s (tcp -> udp)", addr.port);
|
||||||
|
});
|
||||||
|
}
|
@ -11,7 +11,11 @@
|
|||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"lib": ["ES6"]
|
"lib": ["ES6"],
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@core_utils/*": ["./src/utils/*"]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"src/**/*"
|
"src/**/*"
|
||||||
|
Reference in New Issue
Block a user