@manch1kz/yanac 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.prettierrc ADDED
@@ -0,0 +1,4 @@
1
+ {
2
+ "tabWidth": 4,
3
+ "useTabs": false
4
+ }
package/README.md CHANGED
@@ -14,6 +14,8 @@ Starting the сlient:
14
14
  yanac start client [options]
15
15
  ```
16
16
 
17
+ Type ```/help``` in chat to see all commands
18
+
17
19
  Options for server:
18
20
  * ```--port <limit>``` - port (default: 3000)
19
21
  * ```--limit <limit>``` - chat maximum size (default: 100)
@@ -23,6 +25,12 @@ Options for client:
23
25
  * ```--host <limit>``` - host (default: default)
24
26
  * ```--name <limit>``` - port (default: random_string)
25
27
 
28
+ ## Demonstration
29
+
30
+ https://github.com/user-attachments/assets/2d44a7cc-3b39-4c13-bada-8d6654053ac2
31
+
32
+
33
+
26
34
 
27
35
 
28
36
 
@@ -21,16 +21,32 @@ class Client {
21
21
  this.server = net_1.default.connect({ host: host, port: port });
22
22
  this.key_generated = false;
23
23
  this.manager = new sessionManager_1.SessionManager();
24
- this.rl = promises_1.default.createInterface({ input: process.stdin, output: process.stdout, prompt: `${this.name} >>> ` });
24
+ this.rl = promises_1.default.createInterface({
25
+ input: process.stdin,
26
+ output: process.stdout,
27
+ prompt: `${this.name} >>> `,
28
+ });
25
29
  this.server.on("connect", () => {
26
- this.server.write(JSON.stringify({ type: 'public_key_request', body: this.manager.getClient() }));
30
+ this.server.write(JSON.stringify({
31
+ type: "public_key_request",
32
+ body: this.manager.getClient(),
33
+ }));
27
34
  });
28
35
  this.server.on("data", (data) => {
29
36
  processMessage_1.processMessage.call(this, data);
30
37
  });
31
38
  this.rl.on("line", mainInputHandler_1.onLine.bind(this));
32
- this.server.on("close", () => { this.server.end(); process.exit(); });
33
- this.rl.on("SIGINT", () => { this.server.end(); process.exit(); });
39
+ this.server.on("error", () => {
40
+ console.log("\nServer closed or doesn't exist :(");
41
+ });
42
+ this.server.on("close", () => {
43
+ this.server.end();
44
+ process.exit();
45
+ });
46
+ this.rl.on("SIGINT", () => {
47
+ this.server.end();
48
+ process.exit();
49
+ });
34
50
  }
35
51
  }
36
52
  exports.Client = Client;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.processEnteredCommand = processEnteredCommand;
4
+ const edUtils_1 = require("../../utils/edUtils");
5
+ function processEnteredCommand(command) {
6
+ let formated = command.slice(1, command.length);
7
+ switch (formated) {
8
+ case "exit":
9
+ this.rl.emit("SIGINT");
10
+ break;
11
+ case "help":
12
+ console.log("/help - show this menu\n/ping - check ping\n/online - check count of users online");
13
+ this.rl.prompt();
14
+ break;
15
+ case "online":
16
+ case "ping":
17
+ this.server.write((0, edUtils_1.encrypt)(JSON.stringify({ type: "command", body: formated }), this.manager.session_key));
18
+ break;
19
+ default:
20
+ console.log("No command found");
21
+ this.rl.prompt();
22
+ }
23
+ }
@@ -2,20 +2,22 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.onLine = onLine;
4
4
  const edUtils_1 = require("../../utils/edUtils");
5
+ const clientCommandHandler_1 = require("../commands/clientCommandHandler");
5
6
  function onLine(line) {
6
7
  if (line.length > 0) {
7
8
  process.stdout.moveCursor(0, -1);
8
- process.stdout.write('\r\x1B[K');
9
+ process.stdout.write("\r\x1B[K");
9
10
  console.log(`${this.name}: ${line}`);
10
- if (line == "/exit") {
11
- this.rl.emit("SIGINT");
11
+ if (line.startsWith("/")) {
12
+ clientCommandHandler_1.processEnteredCommand.call(this, line);
13
+ return;
12
14
  }
13
- this.server.write((0, edUtils_1.encrypt)(JSON.stringify({ type: 'msg', body: [this.name, line] }), this.manager.session_key));
15
+ this.server.write((0, edUtils_1.encrypt)(JSON.stringify({ type: "msg", body: [this.name, line] }), this.manager.session_key));
14
16
  this.rl.prompt();
15
17
  }
16
18
  else {
17
19
  process.stdout.moveCursor(0, -1);
18
- process.stdout.write('\r\x1B[K');
20
+ process.stdout.write("\r\x1B[K");
19
21
  this.rl.prompt();
20
22
  }
21
23
  }
@@ -7,35 +7,44 @@ async function processMessage(data) {
7
7
  let processed;
8
8
  if (this.key_generated) {
9
9
  processed = JSON.parse((0, edUtils_1.decrypt)(data.toString(), this.manager.session_key));
10
+ process.stdout.write("\r\x1B[K");
10
11
  }
11
12
  else {
12
- processed = JSON.parse(data.toString('utf-8'));
13
+ processed = JSON.parse(data.toString("utf-8"));
13
14
  }
14
15
  switch (processed.type) {
15
- case 'public_key_response':
16
+ case "public_key_response":
16
17
  this.manager.setServer(processed.body[1]);
17
- this.server.write(JSON.stringify({ type: 'master_key_response', body: [this.manager.getMaster(processed.body[0]), this.name] }));
18
+ this.server.write(JSON.stringify({
19
+ type: "master_key_response",
20
+ body: [
21
+ this.manager.getMaster(processed.body[0]),
22
+ this.name,
23
+ ],
24
+ }));
18
25
  break;
19
- case 'finished':
26
+ case "finished":
20
27
  this.manager.generateSession();
21
28
  this.key_generated = true;
22
29
  this.server.write((0, edUtils_1.encrypt)(JSON.stringify({ type: "finished", body: this.name }), this.manager.session_key));
23
- this.rl.prompt();
24
30
  break;
25
- case 'history':
26
- process.stdout.write('\r\x1B[K');
27
- for (let i = 0; i < processed.body.length; i++) {
28
- console.log(processed.body[i][0] + ':', processed.body[i][1]);
31
+ case "history":
32
+ let historyLength = processed.body.length;
33
+ for (let i = 0; i < historyLength; i++) {
34
+ console.log(processed.body[i][0] + ":", processed.body[i][1]);
29
35
  }
30
- console.log('[SERVER]:', this.name, 'connected to server');
31
- this.rl.prompt(true);
32
- process.stdout.cursorTo(cur.cols);
36
+ console.log("[SERVER]:", this.name, "connected to server");
33
37
  break;
34
- case 'msg':
35
- process.stdout.write('\r\x1B[K');
36
- console.log(processed.data[0] + ':', processed.data[1]);
37
- this.rl.prompt(true);
38
- process.stdout.cursorTo(cur.cols);
38
+ case "msg":
39
+ console.log(processed.body[0] + ":", processed.body[1]);
40
+ break;
41
+ case "online":
42
+ console.log(processed.body);
43
+ break;
44
+ case "ping":
45
+ console.log(`Current ping is: ${Date.now() - Number(processed.body)}`);
39
46
  break;
40
47
  }
48
+ this.rl.prompt(true);
49
+ process.stdout.cursorTo(cur.cols);
41
50
  }
@@ -17,24 +17,30 @@ class SessionManager {
17
17
  this.server_random = null;
18
18
  this.master_random = null;
19
19
  this.session_key = null;
20
- const { publicKey, privateKey } = crypto_1.default.generateKeyPairSync('rsa', {
20
+ const { publicKey, privateKey } = crypto_1.default.generateKeyPairSync("rsa", {
21
21
  modulusLength: 2048,
22
22
  publicKeyEncoding: {
23
- format: 'pem',
24
- type: 'pkcs1'
23
+ format: "pem",
24
+ type: "pkcs1",
25
25
  },
26
26
  privateKeyEncoding: {
27
- format: 'pem',
28
- type: 'pkcs1'
29
- }
27
+ format: "pem",
28
+ type: "pkcs1",
29
+ },
30
30
  });
31
31
  this.privateKey = privateKey;
32
32
  this.publicKey = publicKey;
33
33
  }
34
34
  generateSession() {
35
35
  if (this.client_random && this.server_random && this.master_random) {
36
- const sum = this.client_random + this.server_random + Number(this.master_random);
37
- this.session_key = crypto_1.default.createHash('sha256').update(String(sum)).digest().toString('hex');
36
+ const sum = this.client_random +
37
+ this.server_random +
38
+ Number(this.master_random);
39
+ this.session_key = crypto_1.default
40
+ .createHash("sha256")
41
+ .update(String(sum))
42
+ .digest()
43
+ .toString("hex");
38
44
  }
39
45
  }
40
46
  setClient(client_random) {
@@ -59,16 +65,20 @@ class SessionManager {
59
65
  if (this.master_random == null) {
60
66
  this.master_random = String(Math.floor(Math.random() * 5000));
61
67
  }
62
- return crypto_1.default.publicEncrypt({
68
+ return crypto_1.default
69
+ .publicEncrypt({
63
70
  key: public_key,
64
71
  padding: crypto_1.default.constants.RSA_PKCS1_OAEP_PADDING,
65
- }, this.master_random).toString('base64');
72
+ }, this.master_random)
73
+ .toString("base64");
66
74
  }
67
75
  setMaster(master_random, private_key) {
68
- this.master_random = crypto_1.default.privateDecrypt({
76
+ this.master_random = crypto_1.default
77
+ .privateDecrypt({
69
78
  key: private_key,
70
79
  padding: crypto_1.default.constants.RSA_PKCS1_OAEP_PADDING,
71
- }, Buffer.from(master_random, 'base64')).toString();
80
+ }, Buffer.from(master_random, "base64"))
81
+ .toString();
72
82
  }
73
83
  }
74
84
  exports.SessionManager = SessionManager;
@@ -2,47 +2,52 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.processMessage = processMessage;
4
4
  const edUtils_1 = require("../../utils/edUtils");
5
+ const serverCommandHandler_1 = require("./serverCommandHandler");
6
+ function broadcast(body, exclude = []) {
7
+ let stringifyed = JSON.stringify({ type: "msg", body: body });
8
+ this.localChatStore.push(body);
9
+ for (let [connection, metadata] of this.connections) {
10
+ if (!exclude.includes(connection)) {
11
+ connection.write((0, edUtils_1.encrypt)(stringifyed, metadata.generator.session_key));
12
+ }
13
+ }
14
+ }
5
15
  function processMessage(socket, data) {
6
16
  const socket_item = this.connections.get(socket);
7
17
  const socket_generator = socket_item.generator;
8
- let message;
9
- if (socket_item.key_generated) {
10
- message = JSON.parse((0, edUtils_1.decrypt)(data.toString(), socket_item.generator.session_key));
11
- }
12
- else {
13
- message = JSON.parse(data.toString());
14
- }
18
+ let message = socket_item.key_generated
19
+ ? JSON.parse((0, edUtils_1.decrypt)(data.toString(), socket_generator.session_key))
20
+ : JSON.parse(data.toString());
15
21
  switch (message.type) {
16
- case 'msg':
17
- this.localChatStore.push(message.body);
18
- Array.from(this.connections.keys()).forEach((i) => {
19
- if (i != socket) {
20
- i.write((0, edUtils_1.encrypt)(JSON.stringify({ type: 'msg', data: message.body }), this.connections.get(i).generator.session_key));
21
- }
22
- });
22
+ case "msg":
23
+ broadcast.call(this, message.body, [socket]);
24
+ break;
25
+ case "command":
26
+ serverCommandHandler_1.processClientCommand.call(this, message.body, socket, socket_generator);
23
27
  break;
24
- case 'public_key_request':
28
+ case "finished":
29
+ socket_item.name = message.body;
30
+ socket.write((0, edUtils_1.encrypt)(JSON.stringify({
31
+ type: "history",
32
+ body: this.localChatStore.getChat(),
33
+ }), socket_generator.session_key));
34
+ broadcast.call(this, ["[SERVER]", message.body + " connected to server"], [socket]);
35
+ break;
36
+ case "public_key_request":
25
37
  const server_random = this.manager.getServer();
26
38
  socket_generator.setClient(message.body);
27
39
  socket_generator.setServer(server_random);
28
- socket.write(JSON.stringify({ type: 'public_key_response', body: [this.manager.publicKey, server_random] }));
40
+ socket.write(JSON.stringify({
41
+ type: "public_key_response",
42
+ body: [this.manager.publicKey, server_random],
43
+ }));
29
44
  break;
30
- case 'master_key_response':
45
+ case "master_key_response":
31
46
  socket_generator.setMaster(message.body[0], this.manager.privateKey);
32
47
  socket_generator.generateSession();
33
48
  socket_item.name = message.body[1];
34
49
  socket_item.key_generated = true;
35
- socket.write(JSON.stringify({ type: 'finished' }));
36
- break;
37
- case 'finished':
38
- socket_item.name = message.body;
39
- socket.write((0, edUtils_1.encrypt)(JSON.stringify({ type: 'history', body: this.localChatStore.getChat() }), socket_generator.session_key));
40
- this.localChatStore.push(['[SERVER]', message.body + ' connected to server']);
41
- Array.from(this.connections.keys()).forEach((i) => {
42
- if (i != socket) {
43
- i.write((0, edUtils_1.encrypt)(JSON.stringify({ type: 'msg', data: ['[SERVER]', message.body + ' connected to server'] }), this.connections.get(i).generator.session_key));
44
- }
45
- });
50
+ socket.write(JSON.stringify({ type: "finished" }));
46
51
  break;
47
52
  }
48
53
  }
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.processClientCommand = processClientCommand;
4
+ const edUtils_1 = require("../../utils/edUtils");
5
+ function processClientCommand(command, socket, socket_generator) {
6
+ switch (command) {
7
+ case "online":
8
+ socket.write((0, edUtils_1.encrypt)(JSON.stringify({
9
+ type: "online",
10
+ body: `Current connections: ${this.connections.size}`,
11
+ }), socket_generator.session_key));
12
+ break;
13
+ case "ping":
14
+ socket.write((0, edUtils_1.encrypt)(JSON.stringify({ type: "ping", body: Date.now() }), socket_generator.session_key));
15
+ break;
16
+ }
17
+ }
@@ -4,17 +4,22 @@ exports.setupSocket = setupSocket;
4
4
  const sessionManager_1 = require("../../security/sessionManager");
5
5
  const edUtils_1 = require("../../utils/edUtils");
6
6
  function setupSocket(socket, cb) {
7
- this.connections.set(socket, { name: null, generator: new sessionManager_1.SessionManager(), key_generated: false });
8
- socket.on('close', () => {
9
- let body = ['[SERVER]', this.connections.get(socket).name + ' disconnected from server'];
7
+ this.connections.set(socket, {
8
+ name: null,
9
+ generator: new sessionManager_1.SessionManager(),
10
+ key_generated: false,
11
+ });
12
+ socket.on("close", () => {
13
+ let name = this.connections.get(socket).name;
14
+ let body = ["[SERVER]", name + " disconnected from server"];
10
15
  this.localChatStore.push(body);
11
- Array.from(this.connections.keys()).forEach((i) => {
12
- i.write((0, edUtils_1.encrypt)(JSON.stringify({ type: 'msg', data: body }), this.connections.get(i).generator.session_key));
13
- });
16
+ for (let [connection, metadata] of this.connections) {
17
+ connection.write((0, edUtils_1.encrypt)(JSON.stringify({ type: "msg", body: body }), metadata.generator.session_key));
18
+ }
14
19
  this.connections.delete(socket);
15
20
  });
16
21
  socket.on("error", () => { });
17
- socket.on('data', (data) => {
22
+ socket.on("data", (data) => {
18
23
  cb(socket, data);
19
24
  });
20
25
  }
@@ -14,15 +14,26 @@ class Server {
14
14
  server;
15
15
  manager;
16
16
  connections;
17
+ cur;
17
18
  constructor(limit = 100, port = 3000) {
18
- this.localChatStore = new localChatStore_1.LocalChatStore(100);
19
+ this.localChatStore = new localChatStore_1.LocalChatStore(limit);
19
20
  this.manager = new sessionManager_1.SessionManager();
20
21
  this.connections = new Map();
21
22
  this.server = net_1.default.createServer();
23
+ this.cur = Promise.resolve();
22
24
  this.server.on("connection", (socket) => {
23
- setupSocket_1.setupSocket.call(this, socket, processMessage_1.processMessage.bind(this));
25
+ this.add(() => {
26
+ setupSocket_1.setupSocket.call(this, socket, processMessage_1.processMessage.bind(this));
27
+ });
24
28
  });
25
29
  this.server.listen(port);
30
+ console.log("Server is listening...\nPress CTRL + C to stop server\n");
31
+ }
32
+ add(cb) {
33
+ this.cur = this.cur.then(async () => {
34
+ await cb();
35
+ await new Promise((res) => setTimeout(res, 500));
36
+ });
26
37
  }
27
38
  }
28
39
  exports.Server = Server;
@@ -1,34 +1,52 @@
1
- import net from 'net'
2
- import readline from 'readline/promises'
3
- import { SessionManager } from '../security/sessionManager'
4
- import { processMessage } from './handlers/processMessage'
5
- import { onLine } from "./handlers/mainInputHandler"
6
- import { genName } from '../utils/genNameAlias'
1
+ import net from "net";
2
+ import readline from "readline/promises";
3
+ import { SessionManager } from "../security/sessionManager";
4
+ import { processMessage } from "./handlers/processMessage";
5
+ import { onLine } from "./handlers/mainInputHandler";
6
+ import { genName } from "../utils/genNameAlias";
7
7
 
8
8
  export class Client {
9
- manager: SessionManager
10
- rl: readline.Interface
11
- name: string
12
- server: net.Socket
13
- key_generated: boolean
9
+ manager: SessionManager;
10
+ rl: readline.Interface;
11
+ name: string;
12
+ server: net.Socket;
13
+ key_generated: boolean;
14
14
 
15
- constructor(name=genName(), host="localhost", port=3000) {
16
- this.name = name
17
- this.server = net.connect({ host: host, port: port })
18
- this.key_generated = false
19
- this.manager = new SessionManager()
20
- this.rl = readline.createInterface({ input: process.stdin, output: process.stdout, prompt: `${this.name} >>> ` })
15
+ constructor(name = genName(), host = "localhost", port = 3000) {
16
+ this.name = name;
17
+ this.server = net.connect({ host: host, port: port });
18
+ this.key_generated = false;
19
+ this.manager = new SessionManager();
20
+ this.rl = readline.createInterface({
21
+ input: process.stdin,
22
+ output: process.stdout,
23
+ prompt: `${this.name} >>> `,
24
+ });
21
25
 
22
26
  this.server.on("connect", () => {
23
- this.server.write(JSON.stringify({type: 'public_key_request', body: this.manager.getClient()}))
24
- })
27
+ this.server.write(
28
+ JSON.stringify({
29
+ type: "public_key_request",
30
+ body: this.manager.getClient(),
31
+ }),
32
+ );
33
+ });
25
34
  this.server.on("data", (data) => {
26
- processMessage.call(this, data)
27
- })
35
+ processMessage.call(this, data);
36
+ });
28
37
 
29
- this.rl.on("line", onLine.bind(this))
38
+ this.rl.on("line", onLine.bind(this));
30
39
 
31
- this.server.on("close", () => {this.server.end(); process.exit()})
32
- this.rl.on("SIGINT", () => {this.server.end(); process.exit()})
40
+ this.server.on("error", () => {
41
+ console.log("\nServer closed or doesn't exist :(");
42
+ });
43
+ this.server.on("close", () => {
44
+ this.server.end();
45
+ process.exit();
46
+ });
47
+ this.rl.on("SIGINT", () => {
48
+ this.server.end();
49
+ process.exit();
50
+ });
33
51
  }
34
- }
52
+ }
@@ -0,0 +1,32 @@
1
+ import { Client } from "../clientInstance";
2
+ import { encrypt } from "../../utils/edUtils";
3
+
4
+ function processEnteredCommand(this: Client, command: string) {
5
+ let formated = command.slice(1, command.length);
6
+
7
+ switch (formated) {
8
+ case "exit":
9
+ this.rl.emit("SIGINT");
10
+ break;
11
+ case "help":
12
+ console.log(
13
+ "/help - show this menu\n/ping - check ping\n/online - check count of users online",
14
+ );
15
+ this.rl.prompt();
16
+ break;
17
+ case "online":
18
+ case "ping":
19
+ this.server.write(
20
+ encrypt(
21
+ JSON.stringify({ type: "command", body: formated }),
22
+ this.manager.session_key,
23
+ ),
24
+ );
25
+ break;
26
+ default:
27
+ console.log("No command found");
28
+ this.rl.prompt();
29
+ }
30
+ }
31
+
32
+ export { processEnteredCommand };
@@ -1,21 +1,30 @@
1
1
  import { Client } from "../clientInstance";
2
2
  import { encrypt } from "../../utils/edUtils";
3
+ import { processEnteredCommand } from "../commands/clientCommandHandler";
3
4
 
4
5
  function onLine(this: Client, line: string) {
5
6
  if (line.length > 0) {
6
- process.stdout.moveCursor(0, -1)
7
- process.stdout.write('\r\x1B[K');
8
- console.log(`${this.name}: ${line}`)
9
- if (line == "/exit") {
10
- this.rl.emit("SIGINT")
7
+ process.stdout.moveCursor(0, -1);
8
+ process.stdout.write("\r\x1B[K");
9
+ console.log(`${this.name}: ${line}`);
10
+
11
+ if (line.startsWith("/")) {
12
+ processEnteredCommand.call(this, line);
13
+ return;
11
14
  }
12
- this.server.write(encrypt(JSON.stringify({type: 'msg', body: [this.name, line]}), this.manager.session_key))
13
- this.rl.prompt()
15
+
16
+ this.server.write(
17
+ encrypt(
18
+ JSON.stringify({ type: "msg", body: [this.name, line] }),
19
+ this.manager.session_key,
20
+ ),
21
+ );
22
+ this.rl.prompt();
14
23
  } else {
15
- process.stdout.moveCursor(0, -1)
16
- process.stdout.write('\r\x1B[K');
17
- this.rl.prompt()
24
+ process.stdout.moveCursor(0, -1);
25
+ process.stdout.write("\r\x1B[K");
26
+ this.rl.prompt();
18
27
  }
19
28
  }
20
29
 
21
- export { onLine }
30
+ export { onLine };
@@ -1,47 +1,64 @@
1
- import { decrypt, encrypt } from "../../utils/edUtils"
2
- import { Client } from "../clientInstance"
1
+ import { decrypt, encrypt } from "../../utils/edUtils";
2
+ import { Client } from "../clientInstance";
3
3
 
4
4
  async function processMessage(this: Client, data: any) {
5
- const cur = this.rl.getCursorPos()
6
- let processed
5
+ const cur = this.rl.getCursorPos();
6
+ let processed;
7
7
 
8
8
  if (this.key_generated) {
9
- processed = JSON.parse(decrypt(data.toString(), this.manager.session_key))
9
+ processed = JSON.parse(
10
+ decrypt(data.toString(), this.manager.session_key),
11
+ );
12
+ process.stdout.write("\r\x1B[K");
10
13
  } else {
11
- processed = JSON.parse(data.toString('utf-8'))
14
+ processed = JSON.parse(data.toString("utf-8"));
12
15
  }
13
-
16
+
14
17
  switch (processed.type) {
15
- case 'public_key_response':
16
- this.manager.setServer(processed.body[1])
17
- this.server.write(JSON.stringify({type: 'master_key_response', body: [this.manager.getMaster(processed.body[0]), this.name]}))
18
+ case "public_key_response":
19
+ this.manager.setServer(processed.body[1]);
20
+ this.server.write(
21
+ JSON.stringify({
22
+ type: "master_key_response",
23
+ body: [
24
+ this.manager.getMaster(processed.body[0]),
25
+ this.name,
26
+ ],
27
+ }),
28
+ );
18
29
  break;
19
- case 'finished':
20
- this.manager.generateSession()
21
- this.key_generated = true
22
- this.server.write(encrypt(JSON.stringify({type: "finished", body: this.name}), this.manager.session_key))
23
- this.rl.prompt()
30
+ case "finished":
31
+ this.manager.generateSession();
32
+ this.key_generated = true;
33
+ this.server.write(
34
+ encrypt(
35
+ JSON.stringify({ type: "finished", body: this.name }),
36
+ this.manager.session_key,
37
+ ),
38
+ );
24
39
  break;
25
- case 'history':
26
- process.stdout.write('\r\x1B[K')
27
-
28
- for (let i = 0; i < processed.body.length; i++) {
29
- console.log(processed.body[i][0] + ':', processed.body[i][1])
40
+ case "history":
41
+ let historyLength = processed.body.length;
42
+ for (let i = 0; i < historyLength; i++) {
43
+ console.log(processed.body[i][0] + ":", processed.body[i][1]);
30
44
  }
31
-
32
- console.log('[SERVER]:', this.name, 'connected to server')
33
-
34
- this.rl.prompt(true)
35
- process.stdout.cursorTo(cur.cols)
45
+ console.log("[SERVER]:", this.name, "connected to server");
36
46
  break;
37
- case 'msg':
38
- process.stdout.write('\r\x1B[K');
39
- console.log(processed.data[0] + ':', processed.data[1])
40
-
41
- this.rl.prompt(true)
42
- process.stdout.cursorTo(cur.cols)
47
+ case "msg":
48
+ console.log(processed.body[0] + ":", processed.body[1]);
49
+ break;
50
+ case "online":
51
+ console.log(processed.body);
52
+ break;
53
+ case "ping":
54
+ console.log(
55
+ `Current ping is: ${Date.now() - Number(processed.body)}`,
56
+ );
43
57
  break;
44
58
  }
59
+
60
+ this.rl.prompt(true);
61
+ process.stdout.cursorTo(cur.cols);
45
62
  }
46
63
 
47
- export { processMessage }
64
+ export { processMessage };
package/package.json CHANGED
@@ -1,36 +1,36 @@
1
1
  {
2
- "name": "@manch1kz/yanac",
3
- "version": "1.0.3",
4
- "description": "(YANAC) Yet another not another chat",
5
- "repository": {
6
- "type": "git",
7
- "url": "https://github.com/manch1kz/YANAC"
8
- },
9
- "main": "./build/app.js",
10
- "bin": "./build/app.js",
11
- "scripts": {
12
- "prepack": "npm run build",
13
- "build": "tsc"
14
- },
15
- "keywords": [
16
- "cryptography",
17
- "handshake",
18
- "tcp",
19
- "educational",
20
- "cli",
21
- "security"
22
- ],
23
- "author": "manch1kz",
24
- "license": "ISC",
25
- "type": "commonjs",
26
- "publishConfig": {
27
- "access": "public"
28
- },
29
- "dependencies": {
30
- "commander": "^14.0.2"
31
- },
32
- "devDependencies": {
33
- "@types/node": "^25.0.8",
34
- "typescript": "^5.0.0"
35
- }
2
+ "name": "@manch1kz/yanac",
3
+ "version": "1.0.5",
4
+ "description": "(YANAC) Yet another not another chat",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/manch1kz/YANAC"
8
+ },
9
+ "main": "./build/app.js",
10
+ "bin": "./build/app.js",
11
+ "scripts": {
12
+ "prepack": "npm run build",
13
+ "build": "tsc"
14
+ },
15
+ "keywords": [
16
+ "cryptography",
17
+ "handshake",
18
+ "tcp",
19
+ "educational",
20
+ "cli",
21
+ "security"
22
+ ],
23
+ "author": "manch1kz",
24
+ "license": "ISC",
25
+ "type": "commonjs",
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "dependencies": {
30
+ "commander": "^14.0.2"
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "^25.0.8",
34
+ "typescript": "^5.0.0"
35
+ }
36
36
  }
@@ -1,78 +1,96 @@
1
- import crypto from "crypto"
1
+ import crypto from "crypto";
2
2
 
3
3
  export class SessionManager {
4
- client_random: any
5
- server_random: any
6
- master_random: any
7
- session_key: any
8
- privateKey: any
9
- publicKey: any
4
+ client_random: any;
5
+ server_random: any;
6
+ master_random: any;
7
+ session_key: any;
8
+ privateKey: any;
9
+ publicKey: any;
10
10
 
11
11
  constructor() {
12
- this.client_random = null
13
- this.server_random = null
14
- this.master_random = null
15
- this.session_key = null
12
+ this.client_random = null;
13
+ this.server_random = null;
14
+ this.master_random = null;
15
+ this.session_key = null;
16
16
 
17
- const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
17
+ const { publicKey, privateKey } = crypto.generateKeyPairSync("rsa", {
18
18
  modulusLength: 2048,
19
19
  publicKeyEncoding: {
20
- format: 'pem',
21
- type: 'pkcs1'
20
+ format: "pem",
21
+ type: "pkcs1",
22
22
  },
23
23
  privateKeyEncoding: {
24
- format: 'pem',
25
- type: 'pkcs1'
26
- }})
24
+ format: "pem",
25
+ type: "pkcs1",
26
+ },
27
+ });
27
28
 
28
- this.privateKey = privateKey
29
- this.publicKey = publicKey
29
+ this.privateKey = privateKey;
30
+ this.publicKey = publicKey;
30
31
  }
31
32
 
32
33
  generateSession() {
33
34
  if (this.client_random && this.server_random && this.master_random) {
34
- const sum = this.client_random + this.server_random + Number(this.master_random)
35
- this.session_key = crypto.createHash('sha256').update(String(sum)).digest().toString('hex')
35
+ const sum =
36
+ this.client_random +
37
+ this.server_random +
38
+ Number(this.master_random);
39
+ this.session_key = crypto
40
+ .createHash("sha256")
41
+ .update(String(sum))
42
+ .digest()
43
+ .toString("hex");
36
44
  }
37
45
  }
38
46
 
39
47
  setClient(client_random: any) {
40
- this.client_random = client_random
48
+ this.client_random = client_random;
41
49
  }
42
50
 
43
51
  getClient() {
44
52
  if (this.client_random == null) {
45
- this.client_random = Math.floor(Math.random() * 5000)
53
+ this.client_random = Math.floor(Math.random() * 5000);
46
54
  }
47
- return this.client_random
55
+ return this.client_random;
48
56
  }
49
57
 
50
58
  getServer() {
51
59
  if (this.server_random == null) {
52
- this.server_random = Math.floor(Math.random() * 5000)
60
+ this.server_random = Math.floor(Math.random() * 5000);
53
61
  }
54
- return this.server_random
62
+ return this.server_random;
55
63
  }
56
64
 
57
65
  setServer(server_random: any) {
58
- this.server_random = server_random
66
+ this.server_random = server_random;
59
67
  }
60
68
 
61
69
  getMaster(public_key: any) {
62
70
  if (this.master_random == null) {
63
- this.master_random = String(Math.floor(Math.random() * 5000))
71
+ this.master_random = String(Math.floor(Math.random() * 5000));
64
72
  }
65
73
 
66
- return crypto.publicEncrypt({
67
- key: public_key,
68
- padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
69
- }, this.master_random).toString('base64')
74
+ return crypto
75
+ .publicEncrypt(
76
+ {
77
+ key: public_key,
78
+ padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
79
+ },
80
+ this.master_random,
81
+ )
82
+ .toString("base64");
70
83
  }
71
84
 
72
85
  setMaster(master_random: any, private_key: any) {
73
- this.master_random = crypto.privateDecrypt({
74
- key: private_key,
75
- padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
76
- }, Buffer.from(master_random, 'base64')).toString()
86
+ this.master_random = crypto
87
+ .privateDecrypt(
88
+ {
89
+ key: private_key,
90
+ padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
91
+ },
92
+ Buffer.from(master_random, "base64"),
93
+ )
94
+ .toString();
77
95
  }
78
- }
96
+ }
@@ -1,57 +1,79 @@
1
- import { encrypt, decrypt } from "../../utils/edUtils"
2
- import { Server } from "../serverInstance"
1
+ import { encrypt, decrypt } from "../../utils/edUtils";
2
+ import { processClientCommand } from "./serverCommandHandler";
3
+ import { Server } from "../serverInstance";
3
4
 
4
- function processMessage(this: Server, socket: any, data: any) {
5
- const socket_item = this.connections.get(socket)
6
- const socket_generator = socket_item.generator
7
- let message
5
+ function broadcast(this: Server, body: any, exclude = []) {
6
+ let stringifyed = JSON.stringify({ type: "msg", body: body });
7
+ this.localChatStore.push(body);
8
8
 
9
- if (socket_item.key_generated) {
10
- message = JSON.parse(decrypt(data.toString(), socket_item.generator.session_key))
11
- } else {
12
- message = JSON.parse(data.toString())
9
+ for (let [connection, metadata] of this.connections) {
10
+ if (!exclude.includes(connection)) {
11
+ connection.write(
12
+ encrypt(stringifyed, metadata.generator.session_key),
13
+ );
14
+ }
13
15
  }
16
+ }
14
17
 
15
- switch (message.type) {
16
- case 'msg':
17
- this.localChatStore.push(message.body)
18
- Array.from(this.connections.keys()).forEach((i: any) => {
19
- if (i != socket) {
20
- i.write(encrypt(JSON.stringify({type: 'msg', data: message.body}), this.connections.get(i).generator.session_key))
21
- }
22
- })
23
- break;
24
- case 'public_key_request':
25
- const server_random = this.manager.getServer()
18
+ function processMessage(this: Server, socket: any, data: any) {
19
+ const socket_item = this.connections.get(socket);
20
+ const socket_generator = socket_item.generator;
26
21
 
27
- socket_generator.setClient(message.body)
28
- socket_generator.setServer(server_random)
22
+ let message = socket_item.key_generated
23
+ ? JSON.parse(decrypt(data.toString(), socket_generator.session_key))
24
+ : JSON.parse(data.toString());
29
25
 
30
- socket.write(JSON.stringify({type: 'public_key_response', body: [this.manager.publicKey, server_random]}))
26
+ switch (message.type) {
27
+ case "msg":
28
+ broadcast.call(this, message.body, [socket]);
31
29
  break;
32
- case 'master_key_response':
33
- socket_generator.setMaster(message.body[0], this.manager.privateKey)
34
- socket_generator.generateSession()
35
- socket_item.name = message.body[1]
36
- socket_item.key_generated = true
37
-
38
- socket.write(JSON.stringify({type: 'finished'}))
30
+ case "command":
31
+ processClientCommand.call(
32
+ this,
33
+ message.body,
34
+ socket,
35
+ socket_generator,
36
+ );
39
37
  break;
40
- case 'finished':
41
- socket_item.name = message.body
42
- socket.write(encrypt(JSON.stringify({type: 'history', body: this.localChatStore.getChat()}), socket_generator.session_key))
43
- this.localChatStore.push(['[SERVER]', message.body + ' connected to server'])
44
-
45
- Array.from(this.connections.keys()).forEach((i: any) => {
46
- if (i != socket) {
47
- i.write(
48
- encrypt(JSON.stringify({type: 'msg', data: ['[SERVER]', message.body + ' connected to server']}),
49
- this.connections.get(i).generator.session_key)
50
- )
51
- }
52
- })
38
+ case "finished":
39
+ socket_item.name = message.body;
40
+ socket.write(
41
+ encrypt(
42
+ JSON.stringify({
43
+ type: "history",
44
+ body: this.localChatStore.getChat(),
45
+ }),
46
+ socket_generator.session_key,
47
+ ),
48
+ );
49
+ broadcast.call(
50
+ this,
51
+ ["[SERVER]", message.body + " connected to server"],
52
+ [socket],
53
+ );
54
+ break;
55
+ case "public_key_request":
56
+ const server_random = this.manager.getServer();
57
+ socket_generator.setClient(message.body);
58
+ socket_generator.setServer(server_random);
59
+ socket.write(
60
+ JSON.stringify({
61
+ type: "public_key_response",
62
+ body: [this.manager.publicKey, server_random],
63
+ }),
64
+ );
65
+ break;
66
+ case "master_key_response":
67
+ socket_generator.setMaster(
68
+ message.body[0],
69
+ this.manager.privateKey,
70
+ );
71
+ socket_generator.generateSession();
72
+ socket_item.name = message.body[1];
73
+ socket_item.key_generated = true;
74
+ socket.write(JSON.stringify({ type: "finished" }));
53
75
  break;
54
76
  }
55
77
  }
56
78
 
57
- export { processMessage }
79
+ export { processMessage };
@@ -0,0 +1,33 @@
1
+ import { Server } from "../serverInstance";
2
+ import { encrypt } from "../../utils/edUtils";
3
+
4
+ function processClientCommand(
5
+ this: Server,
6
+ command: string,
7
+ socket: any,
8
+ socket_generator: any,
9
+ ) {
10
+ switch (command) {
11
+ case "online":
12
+ socket.write(
13
+ encrypt(
14
+ JSON.stringify({
15
+ type: "online",
16
+ body: `Current connections: ${this.connections.size}`,
17
+ }),
18
+ socket_generator.session_key,
19
+ ),
20
+ );
21
+ break;
22
+ case "ping":
23
+ socket.write(
24
+ encrypt(
25
+ JSON.stringify({ type: "ping", body: Date.now() }),
26
+ socket_generator.session_key,
27
+ ),
28
+ );
29
+ break;
30
+ }
31
+ }
32
+
33
+ export { processClientCommand };
@@ -1,26 +1,37 @@
1
- import { SessionManager } from "../../security/sessionManager"
2
- import { encrypt, decrypt } from "../../utils/edUtils"
3
- import { Server } from "../serverInstance"
4
- import net from 'net'
1
+ import { SessionManager } from "../../security/sessionManager";
2
+ import { encrypt, decrypt } from "../../utils/edUtils";
3
+ import { Server } from "../serverInstance";
4
+ import net from "net";
5
5
 
6
6
  function setupSocket(this: Server, socket: net.Socket, cb: Function) {
7
- this.connections.set(socket, {name: null, generator: new SessionManager(), key_generated: false})
7
+ this.connections.set(socket, {
8
+ name: null,
9
+ generator: new SessionManager(),
10
+ key_generated: false,
11
+ });
8
12
 
9
- socket.on('close', () => {
10
- let body = ['[SERVER]', this.connections.get(socket).name + ' disconnected from server']
11
- this.localChatStore.push(body)
13
+ socket.on("close", () => {
14
+ let name = this.connections.get(socket).name;
15
+ let body = ["[SERVER]", name + " disconnected from server"];
16
+ this.localChatStore.push(body);
12
17
 
13
- Array.from(this.connections.keys()).forEach((i: any) => {
14
- i.write(encrypt(JSON.stringify({type: 'msg', data: body}), this.connections.get(i).generator.session_key))
15
- })
16
- this.connections.delete(socket)
17
- })
18
+ for (let [connection, metadata] of this.connections) {
19
+ connection.write(
20
+ encrypt(
21
+ JSON.stringify({ type: "msg", body: body }),
22
+ metadata.generator.session_key,
23
+ ),
24
+ );
25
+ }
18
26
 
19
- socket.on("error", () => {})
27
+ this.connections.delete(socket);
28
+ });
20
29
 
21
- socket.on('data', (data: any) => {
22
- cb(socket, data)
23
- })
30
+ socket.on("error", () => {});
31
+
32
+ socket.on("data", (data: any) => {
33
+ cb(socket, data);
34
+ });
24
35
  }
25
36
 
26
- export { setupSocket }
37
+ export { setupSocket };
@@ -1,25 +1,37 @@
1
- import net from 'net'
2
- import { SessionManager } from '../security/sessionManager'
3
- import { LocalChatStore } from '../utils/localChatStore'
4
- import { processMessage } from "./handlers/processMessage"
5
- import { setupSocket } from "./handlers/setupSocket"
1
+ import net from "net";
2
+ import { SessionManager } from "../security/sessionManager";
3
+ import { LocalChatStore } from "../utils/localChatStore";
4
+ import { processMessage } from "./handlers/processMessage";
5
+ import { setupSocket } from "./handlers/setupSocket";
6
6
 
7
7
  export class Server {
8
- localChatStore: LocalChatStore
9
- server: net.Server
10
- manager: SessionManager
11
- connections: Map<any, any>
8
+ localChatStore: LocalChatStore;
9
+ server: net.Server;
10
+ manager: SessionManager;
11
+ connections: Map<any, any>;
12
+ cur: Promise<void>;
12
13
 
13
- constructor(limit=100, port=3000) {
14
- this.localChatStore = new LocalChatStore(100)
15
- this.manager = new SessionManager()
16
- this.connections = new Map()
17
- this.server = net.createServer()
14
+ constructor(limit = 100, port = 3000) {
15
+ this.localChatStore = new LocalChatStore(limit);
16
+ this.manager = new SessionManager();
17
+ this.connections = new Map();
18
+ this.server = net.createServer();
19
+ this.cur = Promise.resolve();
18
20
 
19
21
  this.server.on("connection", (socket) => {
20
- setupSocket.call(this, socket, processMessage.bind(this))
21
- })
22
+ this.add(() => {
23
+ setupSocket.call(this, socket, processMessage.bind(this));
24
+ });
25
+ });
22
26
 
23
- this.server.listen(port)
27
+ this.server.listen(port);
28
+ console.log("Server is listening...\nPress CTRL + C to stop server\n");
24
29
  }
25
- }
30
+
31
+ add(cb: Function) {
32
+ this.cur = this.cur.then(async () => {
33
+ await cb();
34
+ await new Promise((res) => setTimeout(res, 500));
35
+ });
36
+ }
37
+ }
package/tsconfig.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
- "compilerOptions": {
3
- "target": "es2022",
4
- "module": "commonjs",
5
- "rootDir": "./",
6
- "outDir": "./build",
7
- "sourceMap": false,
8
- "declaration": false,
9
- "declarationMap": false,
10
- "moduleResolution": "node",
11
- "esModuleInterop": true,
12
- },
13
- "exclude": ["node_modules", "build"]
2
+ "compilerOptions": {
3
+ "target": "es2022",
4
+ "module": "commonjs",
5
+ "rootDir": "./",
6
+ "outDir": "./build",
7
+ "sourceMap": false,
8
+ "declaration": false,
9
+ "declarationMap": false,
10
+ "moduleResolution": "node",
11
+ "esModuleInterop": true
12
+ },
13
+ "exclude": ["node_modules", "build"]
14
14
  }