From 0ef318f533ef86f6033b758f1850d24a065a7cc0 Mon Sep 17 00:00:00 2001 From: Minteck Date: Fri, 30 Apr 2021 14:40:11 +0200 Subject: Initial commit --- index.js | 353 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 353 insertions(+) create mode 100644 index.js (limited to 'index.js') diff --git a/index.js b/index.js new file mode 100644 index 0000000..d4db372 --- /dev/null +++ b/index.js @@ -0,0 +1,353 @@ +const ServerPort = 8888; +const AllowMods = false; + +const _version = "0.1"; +const Net = require('net'); +const clients = {}; + +class MessageBuffer { + constructor(delimiter) { + this.delimiter = delimiter + this.buffer = "" + } + + isFinished() { + if ( + this.buffer.length === 0 || + this.buffer.indexOf(this.delimiter) === -1 + ) { + return true + } + return false + } + + push(data) { + this.buffer += data + } + + getMessage() { + const delimiterIndex = this.buffer.indexOf(this.delimiter) + if (delimiterIndex !== -1) { + const message = this.buffer.slice(0, delimiterIndex) + this.buffer = this.buffer.replace(message + this.delimiter, "") + return message + } + return null + } + + handleData() { + /** + * Try to accumulate the buffer with messages + * + * If the server isnt sending delimiters for some reason + * then nothing will ever come back for these requests + */ + const message = this.getMessage() + return message + } +} + +class KartikError extends Error { + constructor(message, type) { + super(message); + this.name = "KartikError"; + this.ktype = type; + } +} + +const server = new Net.Server(); + +server.on('connection', (socket) => { + socket.connectionId = (Math.random().toString(16).split(".")[1] + Math.random().toString(16).split(".")[1]).substr(0, 8); + socket.linkedTo = null; + clients[socket.connectionId] = socket; + console.log("New connection " + socket.connectionId) + + socket.write(JSON.stringify( + { + _type: "init", + name: "Kartik Server", + version: _version, + id: socket.connectionId, + modded: null + } + ) + "\n") + + setTimeout(() => { + try { + if (socket.linkedTo === null) { + throw new KartikError("Not linked within 3 minutes", "net.minteckprojects.kartik.KartikServer.ClientConnectTimeoutException"); + } + } catch (e) { + console.error(e); + if (e.name !== "KartikError") { + e.ktype = "nodejs.lang." + e.name.replaceAll("Error", "Exception"); + } + socket.write(JSON.stringify({ + _type: "error", + message: e.message, + type: e.ktype + }) + "\n") + socket.end(); + } + }, 180000) + + /*let received = ""; + socket.on("data", (data) => { + data = data.toString(); + + received = received + data.substr(1); + console.log("{{" + data + "}}"); + if (data.startsWith(":")) { + return; + } + try { + raw = chunk.toString().replaceAll("}{", "}|{"); + + datas = raw.split("|").filter(i => i.trim() !== ""); + datas.forEach((data) => { + try { + info = JSON.parse(data); + } catch(e) { + console.dir(data); + throw e; + } + + if (data.length > 1200) { + console.dir(data); + throw new KartikError("Received data is too long", "net.minteckprojects.kartik.KartikServer.DataLengthException"); + } + + if (typeof info['_type'] != "string") { + throw new KartikError("Invalid JSON data", "net.minteckprojects.kartik.KartikServer.JsonDataException"); + } + if (!socket.initialized) { + switch (info['_type']) { + case "init": + if (info['name'] !== "Kartik Core") { + throw new KartikError("Invalid client", "net.minteckprojects.kartik.KartikServer.AuthenticationException"); + } + if (!info.modded) { + console.log("Connection initialized. Client running " + info.name + " version " + info.version + ", official client"); + } else { + console.log("Connection initialized. Client running " + info.name + " version " + info.version + ", MODDED client"); + if (!AllowMods) { + console.log("Modded clients are not accepted"); + socket.end(); + } + } + socket.initialized = true; + break; + default: + throw new KartikError("Trying to receive data but client not initialized", "net.minteckprojects.kartik.KartikServer.AuthenticationException"); + } + } else { + switch (info['_type']) { + case "init": + throw new KartikError("Trying to initialize client but client is already initialized", "net.minteckprojects.kartik.KartikServer.AuthenticationException"); + case "link": + if (typeof info['client'] !== "string" || isNaN(parseInt(info['client'], 16))) { + throw new KartikError("Invalid client link ID", "net.minteckprojects.kartik.KartikServer.GuestIdentifierException"); + } + if (typeof clients[info['client']] === "undefined") { + throw new KartikError("Guest client not found", "net.minteckprojects.kartik.KartikServer.GuestConnectException"); + } + if (clients[info['client']].linkedTo === null) { + socket.linkedTo = clients[info['client']]; + clients[info['client']].linkedTo = socket; + socket.linkedTo.role = "host"; + socket.linkedTo.write(JSON.stringify( + { + _type: "linked", + role: "host", + ids: { + host: socket.linkedTo.connectionId, + guest: socket.connectionId + } + } + )) + socket.role = "guest"; + socket.write(JSON.stringify( + { + _type: "linked", + role: "guest", + ids: { + host: socket.linkedTo.connectionId, + guest: socket.connectionId + } + } + )) + console.log("Link created: (H) " + socket.connectionId + " <-> " + socket.linkedTo.connectionId + " (G)") + } else { + throw new KartikError("Client already linked to another client", "net.minteckprojects.kartik.KartikServer.GuestAllocationException") + } + break; + default: + if (socket.linkedTo === null) { + throw new KartikError("Client not linked to another client", "net.minteckprojects.kartik.KartikServer.DataRoutingException"); + } else { + socket.linkedTo.write(JSON.stringify(info)); + } + } + } + }) + } catch (e) { + console.error(e); + if (e.name !== "KartikError") { + e.ktype = "nodejs.lang." + e.name.replaceAll("Error", "Exception"); + } + socket.write(JSON.stringify({ + _type: "error", + message: e.message, + type: e.ktype + })) + socket.end(); + } + })*/ + + let received = new MessageBuffer("\n") + socket.on("data", data => { + received.push(data) + while (!received.isFinished()) { + const chunk = received.handleData() + try { + raw = chunk.toString().replaceAll("}{", "}|{"); + + datas = raw.split("|").filter(i => i.trim() !== ""); + datas.forEach((data) => { + try { + info = JSON.parse(data); + } catch(e) { + console.dir(data); + throw e; + } + + if (data.length > 1200) { + console.dir(data); + throw new KartikError("Received data is too long", "net.minteckprojects.kartik.KartikServer.DataLengthException"); + } + + if (typeof info['_type'] != "string") { + throw new KartikError("Invalid JSON data", "net.minteckprojects.kartik.KartikServer.JsonDataException"); + } + if (!socket.initialized) { + switch (info['_type']) { + case "init": + if (info['name'] !== "Kartik Core") { + throw new KartikError("Invalid client", "net.minteckprojects.kartik.KartikServer.AuthenticationException"); + } + if (!info.modded) { + console.log("Connection initialized. Client running " + info.name + " version " + info.version + ", official client"); + } else { + console.log("Connection initialized. Client running " + info.name + " version " + info.version + ", MODDED client"); + if (!AllowMods) { + console.log("Modded clients are not accepted"); + socket.end(); + } + } + socket.initialized = true; + break; + default: + throw new KartikError("Trying to receive data but client not initialized", "net.minteckprojects.kartik.KartikServer.AuthenticationException"); + } + } else { + switch (info['_type']) { + case "init": + throw new KartikError("Trying to initialize client but client is already initialized", "net.minteckprojects.kartik.KartikServer.AuthenticationException"); + case "link": + if (typeof info['client'] !== "string" || isNaN(parseInt(info['client'], 16))) { + throw new KartikError("Invalid client link ID", "net.minteckprojects.kartik.KartikServer.GuestIdentifierException"); + } + if (typeof clients[info['client']] === "undefined") { + throw new KartikError("Guest client not found", "net.minteckprojects.kartik.KartikServer.GuestConnectException"); + } + if (clients[info['client']].linkedTo === null) { + socket.linkedTo = clients[info['client']]; + clients[info['client']].linkedTo = socket; + socket.linkedTo.role = "host"; + socket.linkedTo.write(JSON.stringify( + { + _type: "linked", + role: "host", + ids: { + host: socket.linkedTo.connectionId, + guest: socket.connectionId + } + } + ) + "\n") + socket.role = "guest"; + socket.write(JSON.stringify( + { + _type: "linked", + role: "guest", + ids: { + host: socket.linkedTo.connectionId, + guest: socket.connectionId + } + } + ) + "\n") + console.log("Link created: (H) " + socket.connectionId + " <-> " + socket.linkedTo.connectionId + " (G)") + } else { + throw new KartikError("Client already linked to another client", "net.minteckprojects.kartik.KartikServer.GuestAllocationException") + } + break; + default: + if (socket.linkedTo === null) { + throw new KartikError("Client not linked to another client", "net.minteckprojects.kartik.KartikServer.DataRoutingException"); + } else { + socket.linkedTo.write(JSON.stringify(info) + "\n"); + } + } + } + }) + } catch (e) { + console.error(e); + if (e.name !== "KartikError") { + e.ktype = "nodejs.lang." + e.name.replaceAll("Error", "Exception"); + } + socket.write(JSON.stringify({ + _type: "error", + message: e.message, + type: e.ktype + }) + "\n") + socket.end(); + } + } + }) + + socket.on('error', (err) => { + console.error(err); + try { + if (err.code === "ECONNRESET") { + try { + socket.linkedTo.end(); + } catch (e) { + console.log("Cannot end other client's session"); + } + } + } catch (e) { + console.log("Cannot check if connection reset") + } + }) + + socket.on('end', (chunk) => { + console.log("Connection from " + socket.connectionId + " closed"); + if (socket.linkedTo !== null) { + if (socket.role === "guest") { + console.log("Link broken: (H) " + socket.linkedTo.connectionId + " <-> " + socket.connectionId + " (G)"); + } else { + console.log("Link broken: (H) " + socket.connectionId + " <-> " + socket.linkedTo.connectionId + " (G)"); + } + try { + socket.linkedTo.end(); + } catch (e) { + console.log("Cannot end other client's session"); + } + } + delete clients[socket.connectionId]; + }) +}) + +server.listen(ServerPort, () => { + console.log("Kartik Server " + _version + " listening for connections on 0.0.0.0:" + ServerPort) +}) -- cgit