diff --git a/config.example.toml b/config.example.toml
index ab7d5a6..0ba3885 100644
--- a/config.example.toml
+++ b/config.example.toml
@@ -10,8 +10,6 @@ proxyAllowedIps = ["127.0.0.1"]
origin = false
# Origins to accept connections from.
originAllowedDomains = ["computernewb.com"]
-# Maximum amount of active connections allowed from the same IP.
-maxConnections = 3
[auth]
enabled = false
@@ -39,6 +37,8 @@ qmpSockDir = "/tmp/"
node = "acoolvm"
displayname = "A Really Cool CollabVM Instance"
motd = "welcome!"
+# Maximum amount of active connections allowed from the same IP.
+maxConnections = 3
# Command used to ban an IP.
# Use $IP to specify an ip and (optionally) use $NAME to specify a username
bancmd = "iptables -A INPUT -s $IP -j REJECT"
diff --git a/cvmts/src/CollabVMServer.ts b/cvmts/src/CollabVMServer.ts
index d3dab83..b75287d 100644
--- a/cvmts/src/CollabVMServer.ts
+++ b/cvmts/src/CollabVMServer.ts
@@ -119,6 +119,12 @@ export default class CollabVMServer {
}
public addUser(user: User) {
+ let sameip = this.clients.filter(c => c.IP.address === user.IP.address);
+ if (sameip.length >= this.Config.collabvm.maxConnections) {
+ // Kick the oldest client
+ // I think this is a better solution than just rejecting the connection
+ sameip[0].kick();
+ }
this.clients.push(user);
user.socket.on('msg', (msg: string) => this.onMessage(user, msg));
user.socket.on('disconnect', () => this.connectionClosed(user));
diff --git a/cvmts/src/IConfig.ts b/cvmts/src/IConfig.ts
index 5d0f98a..d438e56 100644
--- a/cvmts/src/IConfig.ts
+++ b/cvmts/src/IConfig.ts
@@ -6,8 +6,12 @@ export default interface IConfig {
proxyAllowedIps: string[];
origin: boolean;
originAllowedDomains: string[];
- maxConnections: number;
};
+ tcp: {
+ enabled: boolean;
+ host: string;
+ port: number;
+ }
auth: {
enabled: boolean;
apiEndpoint: string;
@@ -31,6 +35,7 @@ export default interface IConfig {
node: string;
displayname: string;
motd: string;
+ maxConnections: number;
bancmd: string | string[];
moderatorEnabled: boolean;
usernameblacklist: string[];
diff --git a/cvmts/src/TCP/TCPClient.ts b/cvmts/src/TCP/TCPClient.ts
new file mode 100644
index 0000000..806e4e1
--- /dev/null
+++ b/cvmts/src/TCP/TCPClient.ts
@@ -0,0 +1,55 @@
+import EventEmitter from "events";
+import NetworkClient from "../NetworkClient.js";
+import { Socket } from "net";
+
+export default class TCPClient extends EventEmitter implements NetworkClient {
+ private socket: Socket;
+ private cache: string;
+
+ constructor(socket: Socket) {
+ super();
+ this.socket = socket;
+ this.cache = '';
+ this.socket.on('end', () => {
+ this.emit('disconnect');
+ })
+ this.socket.on('data', (data) => {
+ var msg = data.toString('utf-8');
+ if (msg[msg.length - 1] === '\n') msg = msg.slice(0, -1);
+ this.cache += msg;
+ this.readCache();
+ });
+ }
+
+ private readCache() {
+ for (var index = this.cache.indexOf(';'); index !== -1; index = this.cache.indexOf(';')) {
+ this.emit('msg', this.cache.slice(0, index + 1));
+ this.cache = this.cache.slice(index + 1);
+ }
+ }
+
+ getIP(): string {
+ return this.socket.remoteAddress!;
+ }
+
+ send(msg: string): Promise {
+ return new Promise((res, rej) => {
+ this.socket.write(msg, (err) => {
+ if (err) {
+ rej(err);
+ return;
+ }
+ res();
+ });
+ });
+ }
+
+ close(): void {
+ this.emit('disconnect');
+ this.socket.end();
+ }
+
+ isOpen(): boolean {
+ return this.socket.writable;
+ }
+}
\ No newline at end of file
diff --git a/cvmts/src/TCP/TCPServer.ts b/cvmts/src/TCP/TCPServer.ts
new file mode 100644
index 0000000..61d4250
--- /dev/null
+++ b/cvmts/src/TCP/TCPServer.ts
@@ -0,0 +1,39 @@
+import EventEmitter from "events";
+import NetworkServer from "../NetworkServer.js";
+import { Server, Socket } from "net";
+import IConfig from "../IConfig.js";
+import { Logger } from "@cvmts/shared";
+import TCPClient from "./TCPClient.js";
+import { IPDataManager } from "../IPData.js";
+import { User } from "../User.js";
+
+export default class TCPServer extends EventEmitter implements NetworkServer {
+ listener: Server;
+ Config: IConfig;
+ logger: Logger;
+ clients: TCPClient[];
+
+ constructor(config: IConfig) {
+ super();
+ this.logger = new Logger("CVMTS.TCPServer");
+ this.Config = config;
+ this.listener = new Server();
+ this.clients = [];
+ this.listener.on('connection', socket => this.onConnection(socket));
+ }
+
+ private onConnection(socket: Socket) {
+ var client = new TCPClient(socket);
+ this.clients.push(client);
+ this.emit('connect', new User(client, IPDataManager.GetIPData(client.getIP()), this.Config));
+ }
+
+ start(): void {
+ this.listener.listen(this.Config.tcp.port, this.Config.tcp.host, () => {
+ this.logger.Info(`TCP server listening on ${this.Config.tcp.host}:${this.Config.tcp.port}`);
+ })
+ }
+ stop(): void {
+ this.listener.close();
+ }
+}
\ No newline at end of file
diff --git a/cvmts/src/WebSocket/WSClient.ts b/cvmts/src/WebSocket/WSClient.ts
index 96b9a36..69b6468 100644
--- a/cvmts/src/WebSocket/WSClient.ts
+++ b/cvmts/src/WebSocket/WSClient.ts
@@ -6,13 +6,11 @@ import { Logger } from "@cvmts/shared";
export default class WSClient extends EventEmitter implements NetworkClient {
socket: WebSocket;
ip: string;
- logger: Logger;
constructor(ws: WebSocket, ip: string) {
super();
this.socket = ws;
this.ip = ip;
- this.logger = new Logger("CVMTS.WSClient");
this.socket.on('message', (buf: Buffer, isBinary: boolean) => {
// Close the user's connection if they send a non-string message
if (isBinary) {
@@ -25,7 +23,6 @@ export default class WSClient extends EventEmitter implements NetworkClient {
this.socket.on('close', () => {
this.emit('disconnect');
-
});
}
diff --git a/cvmts/src/index.ts b/cvmts/src/index.ts
index 980c984..37f7ee6 100644
--- a/cvmts/src/index.ts
+++ b/cvmts/src/index.ts
@@ -9,6 +9,7 @@ import * as Shared from '@cvmts/shared';
import AuthManager from './AuthManager.js';
import WSServer from './WebSocket/WSServer.js';
import { User } from './User.js';
+import TCPServer from './TCP/TCPServer.js';
let logger = new Shared.Logger('CVMTS.Init');
@@ -58,5 +59,11 @@ async function start() {
var WS = new WSServer(Config);
WS.on('connect', (client: User) => CVM.addUser(client));
WS.start();
+
+ if (Config.tcp.enabled) {
+ var TCP = new TCPServer(Config);
+ TCP.on('connect', (client: User) => CVM.addUser(client));
+ TCP.start();
+ }
}
start();