@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 +4 -0
- package/README.md +8 -0
- package/build/client/clientInstance.js +20 -4
- package/build/client/commands/ClientCommandHandler.js +23 -0
- package/build/client/handlers/mainInputHandler.js +7 -5
- package/build/client/handlers/processMessage.js +26 -17
- package/build/security/sessionManager.js +22 -12
- package/build/server/handlers/processMessage.js +33 -28
- package/build/server/handlers/serverCommandHandler.js +17 -0
- package/build/server/handlers/setupSocket.js +12 -7
- package/build/server/serverInstance.js +13 -2
- package/client/clientInstance.ts +43 -25
- package/client/commands/clientCommandHandler.ts +32 -0
- package/client/handlers/mainInputHandler.ts +20 -11
- package/client/handlers/processMessage.ts +49 -32
- package/package.json +34 -34
- package/security/sessionManager.ts +55 -37
- package/server/handlers/processMessage.ts +67 -45
- package/server/handlers/serverCommandHandler.ts +33 -0
- package/server/handlers/setupSocket.ts +29 -18
- package/server/serverInstance.ts +30 -18
- package/tsconfig.json +12 -12
package/.prettierrc
ADDED
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({
|
|
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({
|
|
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("
|
|
33
|
-
|
|
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(
|
|
9
|
+
process.stdout.write("\r\x1B[K");
|
|
9
10
|
console.log(`${this.name}: ${line}`);
|
|
10
|
-
if (line
|
|
11
|
-
|
|
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:
|
|
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(
|
|
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(
|
|
13
|
+
processed = JSON.parse(data.toString("utf-8"));
|
|
13
14
|
}
|
|
14
15
|
switch (processed.type) {
|
|
15
|
-
case
|
|
16
|
+
case "public_key_response":
|
|
16
17
|
this.manager.setServer(processed.body[1]);
|
|
17
|
-
this.server.write(JSON.stringify({
|
|
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
|
|
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
|
|
26
|
-
|
|
27
|
-
for (let i = 0; i <
|
|
28
|
-
console.log(processed.body[i][0] +
|
|
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(
|
|
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
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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(
|
|
20
|
+
const { publicKey, privateKey } = crypto_1.default.generateKeyPairSync("rsa", {
|
|
21
21
|
modulusLength: 2048,
|
|
22
22
|
publicKeyEncoding: {
|
|
23
|
-
format:
|
|
24
|
-
type:
|
|
23
|
+
format: "pem",
|
|
24
|
+
type: "pkcs1",
|
|
25
25
|
},
|
|
26
26
|
privateKeyEncoding: {
|
|
27
|
-
format:
|
|
28
|
-
type:
|
|
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 +
|
|
37
|
-
|
|
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
|
|
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)
|
|
72
|
+
}, this.master_random)
|
|
73
|
+
.toString("base64");
|
|
66
74
|
}
|
|
67
75
|
setMaster(master_random, private_key) {
|
|
68
|
-
this.master_random = crypto_1.default
|
|
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,
|
|
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
|
-
|
|
10
|
-
|
|
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
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
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({
|
|
40
|
+
socket.write(JSON.stringify({
|
|
41
|
+
type: "public_key_response",
|
|
42
|
+
body: [this.manager.publicKey, server_random],
|
|
43
|
+
}));
|
|
29
44
|
break;
|
|
30
|
-
case
|
|
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:
|
|
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, {
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
12
|
-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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;
|
package/client/clientInstance.ts
CHANGED
|
@@ -1,34 +1,52 @@
|
|
|
1
|
-
import net from
|
|
2
|
-
import readline from
|
|
3
|
-
import { SessionManager } from
|
|
4
|
-
import { processMessage } from
|
|
5
|
-
import { onLine } from "./handlers/mainInputHandler"
|
|
6
|
-
import { genName } from
|
|
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({
|
|
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(
|
|
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("
|
|
32
|
-
|
|
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(
|
|
8
|
-
console.log(`${this.name}: ${line}`)
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
13
|
-
this.
|
|
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(
|
|
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(
|
|
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(
|
|
14
|
+
processed = JSON.parse(data.toString("utf-8"));
|
|
12
15
|
}
|
|
13
|
-
|
|
16
|
+
|
|
14
17
|
switch (processed.type) {
|
|
15
|
-
case
|
|
16
|
-
this.manager.setServer(processed.body[1])
|
|
17
|
-
this.server.write(
|
|
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
|
|
20
|
-
this.manager.generateSession()
|
|
21
|
-
this.key_generated = true
|
|
22
|
-
this.server.write(
|
|
23
|
-
|
|
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
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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(
|
|
17
|
+
const { publicKey, privateKey } = crypto.generateKeyPairSync("rsa", {
|
|
18
18
|
modulusLength: 2048,
|
|
19
19
|
publicKeyEncoding: {
|
|
20
|
-
format:
|
|
21
|
-
type:
|
|
20
|
+
format: "pem",
|
|
21
|
+
type: "pkcs1",
|
|
22
22
|
},
|
|
23
23
|
privateKeyEncoding: {
|
|
24
|
-
format:
|
|
25
|
-
type:
|
|
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 =
|
|
35
|
-
|
|
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
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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 {
|
|
1
|
+
import { encrypt, decrypt } from "../../utils/edUtils";
|
|
2
|
+
import { processClientCommand } from "./serverCommandHandler";
|
|
3
|
+
import { Server } from "../serverInstance";
|
|
3
4
|
|
|
4
|
-
function
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
28
|
-
|
|
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
|
-
|
|
26
|
+
switch (message.type) {
|
|
27
|
+
case "msg":
|
|
28
|
+
broadcast.call(this, message.body, [socket]);
|
|
31
29
|
break;
|
|
32
|
-
case
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
30
|
+
case "command":
|
|
31
|
+
processClientCommand.call(
|
|
32
|
+
this,
|
|
33
|
+
message.body,
|
|
34
|
+
socket,
|
|
35
|
+
socket_generator,
|
|
36
|
+
);
|
|
39
37
|
break;
|
|
40
|
-
case
|
|
41
|
-
socket_item.name = message.body
|
|
42
|
-
socket.write(
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
|
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, {
|
|
7
|
+
this.connections.set(socket, {
|
|
8
|
+
name: null,
|
|
9
|
+
generator: new SessionManager(),
|
|
10
|
+
key_generated: false,
|
|
11
|
+
});
|
|
8
12
|
|
|
9
|
-
socket.on(
|
|
10
|
-
let
|
|
11
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
27
|
+
this.connections.delete(socket);
|
|
28
|
+
});
|
|
20
29
|
|
|
21
|
-
socket.on(
|
|
22
|
-
|
|
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 };
|
package/server/serverInstance.ts
CHANGED
|
@@ -1,25 +1,37 @@
|
|
|
1
|
-
import net from
|
|
2
|
-
import { SessionManager } from
|
|
3
|
-
import { LocalChatStore } from
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
}
|