const cfgHandle = require("./configHandler"); const logger = require("./logger"); const parser = require("./packet-parser"); const frontend = require("./frontend"); const websocket = require("./ws-handler"); const crypt = require("./crypt"); const EventEmitter = require("events"); const net = require("net"); const fs = require("fs"); const path = require("path"); const { API } = require("./api"); class Server extends EventEmitter { constructor() { super(); this.on("config.update", ({ oldConfig, newConfig }) => {}); } run(address, port) { this.address = address; this.port = port; const wsPort = global.config.server?.staticInfo?.wsPort ?? 57891; const httpPort = global.config.server?.staticInfo?.httpPort ?? 49901; logger.log({ wsPort, httpPort, }); this.api = new API(); if (global.config.server.useFrontend) this.frontend = frontend("127.0.0.1", httpPort); if (global.config.server.useWebSocketAPI) this.websocket = websocket("127.0.0.1", wsPort, this.api); this.server = net.createServer((srvSocket) => { srvSocket.on("error", (err) => { logger.log("Caught server socket error: "); logger.log(err.stack); }); }); function socketErrHanler(err) { if (err) { logger.log("Caught server socket error: "); logger.log(err.stack); } } function socketDataHandler(data, socket) { socket.write(data, function (err) { if (err) { logger.log( `error: ${socket.remoteAddress} (remote) throwed: ${err.name}\n${err.stack}`, ); } }); } this.server.on("connection", (socket) => { logger.log(`${socket.remoteAddress}${socket.remotePort} is connected`); socket.mainconnect = null; if (global.config.server.useWebSocketAPI) { socket.wsReverse = net.createConnection(wsPort, "127.0.0.1"); socket.wsReverse.on("error", socketErrHanler); socket.wsReverse.on("data", (data) => socketDataHandler(data, socket)); } if (global.config.server.useFrontend) { socket.httpReverse = net.createConnection(httpPort, "127.0.0.1"); socket.httpReverse.on("error", socketErrHanler); socket.httpReverse.on("data", (data) => socketDataHandler(data, socket), ); } socket.on("error", (err) => { logger.log(`socket.on(${socket.remoteAddress}) error:`); logger.log(err.stack); try { socket.end(); } catch (_) {} // socket.end(); }); socket.on("data", async (data) => { const parserResult = await parser(data); // if (global.config.server.secureMode && data.toString().includes("http/1.1")) { // logger.log("Encrypted WS!"); // parserResult.result.isWebSocket = true; // } // const parserDecryptedResult = await parser(global.openSSH.decrypt(data)); const peerAddress = `${socket.remoteAddress}:${socket.remotePort}`; //logger.log("Parser result is", parserResult); if (!socket.mainconnect) { // if (parserResult.result.isWebSocket || parserDecryptedResult.result.isWebSocket) { if (parserResult.result.isWebSocket) { socket.mainconnect = "ws"; if (global.config.server.useFrontend) socket.httpReverse.end(); if (global.config.server.useWebSocketAPI) { return socket.wsReverse.write(data, socketErrHanler); } return; } else if (parserResult.result.isHTTP) { socket.mainconnect = "http"; socket.wsReverse.end(); if (global.config.server.useFrontend) { return socket.httpReverse.write(data, socketErrHanler); } return; } else { if (global.config.server.useFrontend) socket.httpReverse.end(); if (global.config.server.useWebSocketAPI) socket.wsReverse.end(); socket.mainconnect = "tcp"; return await this.api.exec(socket, data, () => {}); } } else { switch (socket.mainconnect) { case "ws": if (global.config.server.useWebSocketAPI) { if (!socket.wsReverse.destroyed) { return socket.wsReverse.write(data, socketErrHanler); } else { socket.wsReverse = net.createConnection(wsPort, "127.0.0.1"); socket.wsReverse.on("error", socketErrHanler); socket.wsReverse.on("data", (data) => socketDataHandler(data, socket), ); return socket.wsReverse.write(data, socketErrHanler); } } case "http": if (global.config.server.useFrontend) { if (!socket.httpReverse.destroyed) { return socket.httpReverse.write(data, socketErrHanler); } else { socket.httpReverse = net.createConnection( httpPort, "127.0.0.1", ); socket.httpReverse.on("error", socketErrHanler); socket.httpReverse.on("data", (data) => socketDataHandler(data, socket), ); return socket.httpReverse.write(data, socketErrHanler); } } case "tcp": return await this.api.exec(socket, data, () => {}); default: return; } } }); }); this.server.on("error", (err) => { logger.log("Error of server"); logger.log(err.stack); }); this.server.listen(this.port, this.address, (err) => { if (err) throw err; logger.log( `TCP server for ai-adventure labs connected on ${this.address}:${this.port}`, ); }); } } const defaultConfigData = { server: { address: "0.0.0.0", port: 8841, useWebSocketAPI: true, useFrontend: true, staticInfo: { wsPort: 57891, httpPort: 49901, }, }, database: { dialect: "sqlite", path: "./data", }, ai: { apiType: "kobold", }, rootuser: { login: "admin", password: "admin", activated: true, }, }; global.server = new Server(); cfgHandle(global.server, defaultConfigData, logger); logger.log("Current config:", global.config); global.server.run(global.config.server.address, global.config.server.port); /* if (global.config.server.secureMode) { async function initEncryption () { const datadir = global.config.server["ssl-path"] ?? path.join(__dirname, "/encryption"); function generate () { logger.log("Generate new keys..."); global.openSSH = crypt.generateOpenSSH({ commonName: global.config.server.info.name }, { days: 365, pkcs7: true, clientCertificate: true, clientCertificateCN: "AI Adventure Labs", algorithm: 'sha256', keySize: 2048, }); fs.writeFileSync(path.join(datadir, "public.pem"), global.openSSH.public_key.bytes); fs.writeFileSync(path.join(datadir, "private.pem"), global.openSSH.private_key.bytes); fs.writeFileSync(path.join(datadir, "cert.crt"), global.openSSH.cert); } const readFilePromise = new Promise((resolve, reject) => { fs.readFile(path.join(datadir, "public.pem"), (err, public_key) => { if (err) { return resolve(generate()); } try { public_key = new crypt.CryptKey(public_key); const private_key = new crypt.CryptKey(fs.readFileSync(path.join(datadir, "private.pem"))); const cert = fs.readFileSync(path.join(datadir, "cert.crt")); global.openSSH = new crypt.OpenSSH({ public_key, private_key, cert }); resolve(); } catch (_) { resolve(generate()); } }); }); await readFilePromise; logger.log(`loaded from ${datadir}`); }; initEncryption().then(() => { server.run(global.config.server.address, global.config.server.port); }).catch(e => { throw e; }); } else server.run(global.config.server.address, global.config.server.port); */