From 8c88d736651efe77d1af8517265e933245288130 Mon Sep 17 00:00:00 2001 From: andrei-mihnea-cerbu Date: Tue, 12 Nov 2024 12:32:29 +0200 Subject: [PATCH] chunks v1 --- UC/db/database.db | Bin 36864 -> 36864 bytes .../tcp_server_communicator.ts | 88 +++++++++----- .../user_to_user_operations.ts | 49 +++++++- .../tcp_client_communicator.ts | 112 +++++++++++------- .../tcp_server_communicator.ts | 88 +++++++++----- User/src/network/tcp/tcp_client.ts | 10 +- User/src/network/tcp/tcp_server.ts | 15 ++- User/src/network/udp/udp_server.ts | 4 +- 8 files changed, 240 insertions(+), 126 deletions(-) diff --git a/UC/db/database.db b/UC/db/database.db index 4296ffb3e3009840b658255934498df32ba8493e..c53a3e36d3f86b65cf4cc5acba2580bce3793425 100644 GIT binary patch delta 261 zcmZozz|^pSX@WGP;6xc`M!}5~OL1CDIV*QIWl=pnvLLe$le2#%1SbPk36tD^C9?rQ{|_&XFeU&1 diff --git a/UC/src/network/socket_communicator/tcp_server_communicator.ts b/UC/src/network/socket_communicator/tcp_server_communicator.ts index 8f57dba..5bf5220 100644 --- a/UC/src/network/socket_communicator/tcp_server_communicator.ts +++ b/UC/src/network/socket_communicator/tcp_server_communicator.ts @@ -1,11 +1,11 @@ import { Socket } from 'net'; -import { privateEncrypt, privateDecrypt, generateKeyPairSync, createCipheriv, createDecipheriv, randomBytes } from 'crypto'; -import { MessageHandler, ParsedMessage } from '../message_handler'; +import { privateEncrypt, generateKeyPairSync, createCipheriv, createDecipheriv, randomBytes, constants } from 'crypto'; +import { MessageHandler } from '../message_handler'; import { SocketCommunicatorBase } from './socket_communicator_base'; import { OperationHandler } from '../operations_base/operation_handler'; -import {constants} from "node:crypto"; const END_OF_MESSAGE = ''; // Define a unique marker for end of message +const CHUNK_SIZE = 1024; // Define chunk size export class TcpServerCommunicator extends SocketCommunicatorBase { private readonly socket: Socket; @@ -14,6 +14,7 @@ export class TcpServerCommunicator extends SocketCommunicatorBase { private aesKey: Buffer | null; private aesIv: Buffer | null; private messageBuffer: string; + private chunkBuffers: { [messageId: string]: string[] }; // Buffer for reassembling chunks constructor(socket: Socket, ip: string, port: number, operationHandler: OperationHandler) { super(ip, port, operationHandler); @@ -23,6 +24,7 @@ export class TcpServerCommunicator extends SocketCommunicatorBase { this.aesKey = null; this.aesIv = null; this.messageBuffer = ''; // Initialize the message buffer + this.chunkBuffers = {}; // Buffer for reassembling incoming messages this.generateKeyPair(); // Generate RSA key pair for encryption } @@ -35,7 +37,6 @@ export class TcpServerCommunicator extends SocketCommunicatorBase { }); this.privateKey = privateKey; this.publicKey = publicKey; - console.log('RSA key pair generated.'); } // Send the server's public key to the client @@ -44,9 +45,7 @@ export class TcpServerCommunicator extends SocketCommunicatorBase { throw new Error('Public key is not available. Please generate RSA key pair.'); } - const message = MessageHandler.formatMessage('SET_PUBLIC_KEY', { publicKey: this.publicKey }); - await this.writeToSocket(message + END_OF_MESSAGE); - console.log('Public key sent to client.'); + await this.sendChunkedMessage('SET_PUBLIC_KEY', { publicKey: this.publicKey }); } // Generate AES key and IV, then send them to the client @@ -57,16 +56,11 @@ export class TcpServerCommunicator extends SocketCommunicatorBase { const aesKeyBase64 = this.aesKey.toString('base64'); const aesIvBase64 = this.aesIv.toString('base64'); - const formattedMessage = MessageHandler.formatMessage('SET_AES_KEY', { aesKey: aesKeyBase64, aesIv: aesIvBase64 }); - const encryptedMessage = this.encryptWithRsa(Buffer.from(formattedMessage)).toString('base64'); - - await this.writeToSocket(encryptedMessage + END_OF_MESSAGE); - console.log('AES key and IV sent to client.'); + await this.sendChunkedMessage('SET_AES_KEY', { aesKey: aesKeyBase64, aesIv: aesIvBase64 }); } // Encrypt a message with the server's private key (RSA encryption) - private encryptWithRsa(message: Buffer): Buffer { - const bufferMessage = Buffer.isBuffer(message) ? message : Buffer.from(message); + private encryptWithRsa(message: string): string { if (!this.privateKey) throw new Error('Server private key not set.'); return privateEncrypt( @@ -74,8 +68,8 @@ export class TcpServerCommunicator extends SocketCommunicatorBase { key: this.privateKey, padding: constants.RSA_PKCS1_PADDING, // PKCS1 padding }, - bufferMessage - ); + Buffer.from(message) + ).toString('base64'); } // Decrypt AES-encrypted messages @@ -106,17 +100,34 @@ export class TcpServerCommunicator extends SocketCommunicatorBase { const incomingMessage = data.toString(); this.messageBuffer += incomingMessage; - // Check if the message ends with END_OF_MESSAGE - if (this.messageBuffer.endsWith(END_OF_MESSAGE)) { - // Remove the END_OF_MESSAGE marker and process the message - const completeMessage = this.messageBuffer.slice(0, -END_OF_MESSAGE.length); + if (this.messageBuffer.includes(END_OF_MESSAGE)) { + const messages = this.messageBuffer.split(END_OF_MESSAGE); - console.log(`\n\nComplete Message:\n${completeMessage}\n\n`); + for (let i = 0; i < messages.length - 1; i++) { + const completeMessage = messages[i]; + if (completeMessage) { + this.processCompleteMessage(completeMessage); + } + } - this.handleIncomingMessage(completeMessage); + this.messageBuffer = messages[messages.length - 1]; + } + } - // Clear the message buffer after processing - this.messageBuffer = ''; + private processCompleteMessage(completeMessage: string): void { + const [headerJson, chunkContent] = completeMessage.split('|'); + const header = JSON.parse(headerJson); + + if (!this.chunkBuffers[header.messageId]) { + this.chunkBuffers[header.messageId] = new Array(header.totalChunks); + } + + this.chunkBuffers[header.messageId][header.sequenceNumber] = chunkContent; + + if (this.chunkBuffers[header.messageId].every((chunk) => chunk !== undefined)) { + const fullMessage = this.chunkBuffers[header.messageId].join(''); + this.handleIncomingMessage(fullMessage); + delete this.chunkBuffers[header.messageId]; } } @@ -125,14 +136,29 @@ export class TcpServerCommunicator extends SocketCommunicatorBase { const message = MessageHandler.formatMessage(operationCode, metaInfo, fileContent); let outgoingMessage: string; - if (this.aesKey && this.aesIv) { - outgoingMessage = this.encryptWithAes(message); - } else { - outgoingMessage = message; - } + if (this.aesKey && this.aesIv) outgoingMessage = this.encryptWithAes(message); + else if(this.privateKey) outgoingMessage = this.encryptWithRsa(message); + else outgoingMessage = message; - outgoingMessage += END_OF_MESSAGE; // Append end-of-message marker - await this.writeToSocket(outgoingMessage); + const totalChunks = Math.ceil(outgoingMessage.length / CHUNK_SIZE); + const messageId = Date.now().toString(); + + for (let i = 0; i < totalChunks; i++) { + const chunk = outgoingMessage.slice(i * CHUNK_SIZE, (i + 1) * CHUNK_SIZE); + const chunkHeader = JSON.stringify({ + messageId, + sequenceNumber: i, + totalChunks, + }); + + const chunkWithHeader = `${chunkHeader}|${chunk}`; + + await this.writeToSocket(chunkWithHeader); + + if (i === totalChunks - 1) { + await this.writeToSocket(END_OF_MESSAGE); + } + } } // Handle incoming message (decrypt with AES if available) diff --git a/User/src/network/operations_custom/user_to_user_operations.ts b/User/src/network/operations_custom/user_to_user_operations.ts index 5aa8c63..3e5c214 100644 --- a/User/src/network/operations_custom/user_to_user_operations.ts +++ b/User/src/network/operations_custom/user_to_user_operations.ts @@ -2,8 +2,8 @@ import { ParsedMessage } from '../message_handler'; import { OperationBase } from '../operations_base/operation_base'; import { OperationHandler } from '../operations_base/operation_handler'; import path from 'path'; +import { execSync } from 'child_process'; import fs from 'fs'; -import { FileEncryptor } from '../../helpers/file_encryptor'; const LOCK_FILE_EXTENSION = '.lock'; @@ -101,6 +101,40 @@ export class UserToUserOperations extends OperationBase { } } + private static hasEnoughDiskSpace(directory: string, requiredPercentage: number): boolean { + try { + let availableSpace = 0; + let totalSpace = 0; + + if (process.platform === 'win32') { + // Windows + const output = execSync(`wmic logicaldisk where "DeviceID='${directory[0]}:'" get FreeSpace,Size`).toString(); + const lines = output.trim().split('\n'); + const [freeSpaceStr, totalSpaceStr] = lines[1].trim().split(/\s+/); + availableSpace = parseInt(freeSpaceStr, 10); // Available space in bytes + totalSpace = parseInt(totalSpaceStr, 10); // Total space in bytes + } else { + // Unix-based (Linux/macOS) + const output = execSync(`df -k "${directory}"`).toString(); + const lines = output.trim().split('\n'); + const parts = lines[lines.length - 1].split(/\s+/); + const availableSpaceInKb = parseInt(parts[3], 10); // Available space in KB + const totalSpaceInKb = parseInt(parts[1], 10); // Total space in KB + availableSpace = availableSpaceInKb * 1024; + totalSpace = totalSpaceInKb * 1024; + } + + // Calculate available space as a percentage of the total space + const availablePercentage = (availableSpace / totalSpace) * 100; + + // Return true if the available percentage is greater than or equal to the required percentage + return availablePercentage >= requiredPercentage; + } catch (error) { + console.error(`Error checking disk space: ${error}`); + return false; // Return false if there's an error + } + } + public static handleResetDatabase(parsedMessage: ParsedMessage): ParsedMessage { // Path to the application.json file const pathToApplicationJson = path.join(__dirname, '..', '..', 'json_files', 'application.json'); @@ -217,6 +251,15 @@ export class UserToUserOperations extends OperationBase { const fullFilePath = path.join(baseBackupDir, userName, relativeFilePath); try { + // Check if there is enough disk space + const requiredPercentage = 25; + if (!UserToUserOperations.hasEnoughDiskSpace(baseBackupDir, requiredPercentage)) { + return { + operationCode: UserToUserOperations.operationCodes.ERR, + metaInfo: { message: 'Insufficient disk space for backup.' }, + }; + } + // Ensure the directory structure exists (create directories if they don't exist) const dirPath = path.dirname(fullFilePath); if (!fs.existsSync(dirPath)) { @@ -370,7 +413,7 @@ export class UserToUserOperations extends OperationBase { // Get the share directory path const baseDepartmentDir = appInfo.departmentDirectory.path; - const userDepartmentDir = path.join(baseDepartmentDir, userName); + const userDepartmentDir = path.join(baseDepartmentDir, '..', 'DEPARTMENT_FILES', userName); try { // Check if the user's backup directory exists @@ -433,7 +476,7 @@ export class UserToUserOperations extends OperationBase { const departmentDirectory = appInfo.departmentDirectory.path; // Full path where the file will be stored (under the user's directory in the shared folder) - const fullFilePath = path.join(departmentDirectory, userName, relativeFilePath); + const fullFilePath = path.join(departmentDirectory, '..', 'DEPARTMENT_FILES', userName, relativeFilePath); try { // Ensure the directory structure exists (create directories if they don't exist) diff --git a/User/src/network/socket_communicator/tcp_client_communicator.ts b/User/src/network/socket_communicator/tcp_client_communicator.ts index 6d78dd2..91920c0 100644 --- a/User/src/network/socket_communicator/tcp_client_communicator.ts +++ b/User/src/network/socket_communicator/tcp_client_communicator.ts @@ -1,12 +1,12 @@ import { Socket } from 'net'; -import { createCipheriv, createDecipheriv } from 'crypto'; -import { MessageHandler, ParsedMessage } from '../message_handler'; +import { createCipheriv, createDecipheriv, constants, publicDecrypt } from 'crypto'; +import {MessageHandler, ParsedMessage} from '../message_handler'; import { SocketCommunicatorBase } from './socket_communicator_base'; import { OperationHandler } from '../operations_base/operation_handler'; -import {constants, publicDecrypt} from "node:crypto"; -import {operationCodes} from "../operation_codes"; +import { operationCodes } from '../operation_codes'; -const END_OF_MESSAGE = ''; // Define a unique marker for end of message +const END_OF_MESSAGE = ''; // Unique marker for the end of message +const CHUNK_SIZE = 1024; // Define chunk size export class TcpClientCommunicator extends SocketCommunicatorBase { private readonly socket: Socket; @@ -15,30 +15,28 @@ export class TcpClientCommunicator extends SocketCommunicatorBase { private messageBuffer: string; private serverPublicKey: string | null; private isAesKeySetFlag: boolean; + private chunkBuffers: { [messageId: string]: string[] }; // Buffer for reassembling chunks constructor(socket: Socket, ip: string, port: number, operationHandler: OperationHandler) { - super(ip, port, operationHandler); + super(ip, port, operationHandler); // Call parent constructor this.socket = socket; this.aesKey = null; this.aesIv = null; this.serverPublicKey = null; this.messageBuffer = ''; // Buffer for message reassembly this.isAesKeySetFlag = false; + this.chunkBuffers = {}; // Initialize chunk buffer for reassembling messages } setServerPublicKey(publicKey: string): void { this.serverPublicKey = publicKey; - console.log('Server public key set.'); } - // Set the AES key when received setAesKey(aesKey: string, aesIv: string): void { this.aesKey = Buffer.from(aesKey, 'base64'); this.aesIv = Buffer.from(aesIv, 'base64'); - console.log('AES key set.'); } - // Encrypt a message with AES private encryptWithAes(message: string): string { if (!this.aesKey || !this.aesIv) { throw new Error('AES key or IV is not set.'); @@ -65,62 +63,54 @@ export class TcpClientCommunicator extends SocketCommunicatorBase { } try { const encryptedMessage = Buffer.from(message.toString(), 'base64'); - // Decrypt the message using the server's public key const decrypted = publicDecrypt( { key: this.serverPublicKey, - padding: constants.RSA_PKCS1_PADDING, // Matching padding for decryption + padding: constants.RSA_PKCS1_PADDING, }, encryptedMessage ); return decrypted.toString('utf-8'); } catch (error) { - console.error('RSA decryption failed:', error); throw new Error('Failed to decrypt RSA message.'); } } - // Handle incoming chunks of data - async handleIncomingChunk(data: Buffer): Promise { - const incomingMessage = data.toString(); - this.messageBuffer += incomingMessage; - - // Check if the message ends with END_OF_MESSAGE - if (this.messageBuffer.endsWith(END_OF_MESSAGE)) { - // Remove the END_OF_MESSAGE marker and process the message - const completeMessage = this.messageBuffer.slice(0, -END_OF_MESSAGE.length); - - this.handleIncomingMessage(completeMessage); - - // Clear the message buffer after processing - this.messageBuffer = ''; - } - } - - // Send a chunked message over the socket async sendChunkedMessage(operationCode: string, metaInfo?: { [key: string]: any }, fileContent?: Buffer): Promise { const message = MessageHandler.formatMessage(operationCode, metaInfo, fileContent); let outgoingMessage: string; - // Encrypt the message with AES if available if (this.aesKey && this.aesIv) { outgoingMessage = this.encryptWithAes(message); } else { - outgoingMessage = message // Send plain text if AES is not set + outgoingMessage = message; } - // Append the end marker to the message - outgoingMessage += END_OF_MESSAGE; + const totalChunks = Math.ceil(outgoingMessage.length / CHUNK_SIZE); + const messageId = Date.now().toString(); - await this.writeToSocket(outgoingMessage); + for (let i = 0; i < totalChunks; i++) { + const chunk = outgoingMessage.slice(i * CHUNK_SIZE, (i + 1) * CHUNK_SIZE); + const chunkHeader = JSON.stringify({ + messageId, + sequenceNumber: i, + totalChunks, + }); + + const chunkWithHeader = `${chunkHeader}|${chunk}`; + + await this.writeToSocket(chunkWithHeader); + + if (i === totalChunks - 1) { + await this.writeToSocket(END_OF_MESSAGE); + } + } } - // Write message to socket private writeToSocket(message: string): Promise { return new Promise((resolve, reject) => { this.socket.write(message, (err: any) => { if (err) { - console.error('Error sending message over TCP:', err); return reject(err); } resolve(); @@ -128,37 +118,69 @@ export class TcpClientCommunicator extends SocketCommunicatorBase { }); } - // Handle incoming message (decrypted if AES is set) + async handleIncomingChunk(data: Buffer): Promise { + const incomingMessage = data.toString(); + this.messageBuffer += incomingMessage; + + if (this.messageBuffer.includes(END_OF_MESSAGE)) { + const messages = this.messageBuffer.split(END_OF_MESSAGE); + + for (let i = 0; i < messages.length - 1; i++) { + const completeMessage = messages[i]; + if (completeMessage) { + this.processCompleteMessage(completeMessage); + } + } + + this.messageBuffer = messages[messages.length - 1]; + } + } + + private processCompleteMessage(completeMessage: string): void { + const [headerJson, chunkContent] = completeMessage.split('|'); + const header = JSON.parse(headerJson); + + if (!this.chunkBuffers[header.messageId]) { + this.chunkBuffers[header.messageId] = new Array(header.totalChunks); + } + + this.chunkBuffers[header.messageId][header.sequenceNumber] = chunkContent; + + if (this.chunkBuffers[header.messageId].every((chunk) => chunk !== undefined)) { + const fullMessage = this.chunkBuffers[header.messageId].join(''); + this.handleIncomingMessage(fullMessage); + delete this.chunkBuffers[header.messageId]; + } + } + handleIncomingMessage(incomingMessage: string): void { let messageToProcess = incomingMessage; if (this.aesKey && this.aesIv) { messageToProcess = this.decryptWithAes(incomingMessage); - }else if(this.serverPublicKey){ + } else if (this.serverPublicKey) { messageToProcess = this.decryptWithRsa(incomingMessage); } const result = this.operationHandler.handleOperation(messageToProcess); - if(result.operationCode === operationCodes.SET_AES_KEY){ + if (result.operationCode === operationCodes.SET_AES_KEY) { this.isAesKeySetFlag = true; this.setAesKey(result.metaInfo?.aesKey, result.metaInfo?.aesIv); return; } - if(result.operationCode === operationCodes.SET_PUBLIC_KEY) { + if (result.operationCode === operationCodes.SET_PUBLIC_KEY) { this.setServerPublicKey(result.metaInfo?.publicKey); return; } - this.handlerResult = result + this.handlerResult = result; } - // Check if AES key is set isAesKeySet(): boolean { return this.isAesKeySetFlag; } - // Get handler result for operation handling getHandlerResult(): ParsedMessage | null { return this.handlerResult; } diff --git a/User/src/network/socket_communicator/tcp_server_communicator.ts b/User/src/network/socket_communicator/tcp_server_communicator.ts index 8f57dba..5bf5220 100644 --- a/User/src/network/socket_communicator/tcp_server_communicator.ts +++ b/User/src/network/socket_communicator/tcp_server_communicator.ts @@ -1,11 +1,11 @@ import { Socket } from 'net'; -import { privateEncrypt, privateDecrypt, generateKeyPairSync, createCipheriv, createDecipheriv, randomBytes } from 'crypto'; -import { MessageHandler, ParsedMessage } from '../message_handler'; +import { privateEncrypt, generateKeyPairSync, createCipheriv, createDecipheriv, randomBytes, constants } from 'crypto'; +import { MessageHandler } from '../message_handler'; import { SocketCommunicatorBase } from './socket_communicator_base'; import { OperationHandler } from '../operations_base/operation_handler'; -import {constants} from "node:crypto"; const END_OF_MESSAGE = ''; // Define a unique marker for end of message +const CHUNK_SIZE = 1024; // Define chunk size export class TcpServerCommunicator extends SocketCommunicatorBase { private readonly socket: Socket; @@ -14,6 +14,7 @@ export class TcpServerCommunicator extends SocketCommunicatorBase { private aesKey: Buffer | null; private aesIv: Buffer | null; private messageBuffer: string; + private chunkBuffers: { [messageId: string]: string[] }; // Buffer for reassembling chunks constructor(socket: Socket, ip: string, port: number, operationHandler: OperationHandler) { super(ip, port, operationHandler); @@ -23,6 +24,7 @@ export class TcpServerCommunicator extends SocketCommunicatorBase { this.aesKey = null; this.aesIv = null; this.messageBuffer = ''; // Initialize the message buffer + this.chunkBuffers = {}; // Buffer for reassembling incoming messages this.generateKeyPair(); // Generate RSA key pair for encryption } @@ -35,7 +37,6 @@ export class TcpServerCommunicator extends SocketCommunicatorBase { }); this.privateKey = privateKey; this.publicKey = publicKey; - console.log('RSA key pair generated.'); } // Send the server's public key to the client @@ -44,9 +45,7 @@ export class TcpServerCommunicator extends SocketCommunicatorBase { throw new Error('Public key is not available. Please generate RSA key pair.'); } - const message = MessageHandler.formatMessage('SET_PUBLIC_KEY', { publicKey: this.publicKey }); - await this.writeToSocket(message + END_OF_MESSAGE); - console.log('Public key sent to client.'); + await this.sendChunkedMessage('SET_PUBLIC_KEY', { publicKey: this.publicKey }); } // Generate AES key and IV, then send them to the client @@ -57,16 +56,11 @@ export class TcpServerCommunicator extends SocketCommunicatorBase { const aesKeyBase64 = this.aesKey.toString('base64'); const aesIvBase64 = this.aesIv.toString('base64'); - const formattedMessage = MessageHandler.formatMessage('SET_AES_KEY', { aesKey: aesKeyBase64, aesIv: aesIvBase64 }); - const encryptedMessage = this.encryptWithRsa(Buffer.from(formattedMessage)).toString('base64'); - - await this.writeToSocket(encryptedMessage + END_OF_MESSAGE); - console.log('AES key and IV sent to client.'); + await this.sendChunkedMessage('SET_AES_KEY', { aesKey: aesKeyBase64, aesIv: aesIvBase64 }); } // Encrypt a message with the server's private key (RSA encryption) - private encryptWithRsa(message: Buffer): Buffer { - const bufferMessage = Buffer.isBuffer(message) ? message : Buffer.from(message); + private encryptWithRsa(message: string): string { if (!this.privateKey) throw new Error('Server private key not set.'); return privateEncrypt( @@ -74,8 +68,8 @@ export class TcpServerCommunicator extends SocketCommunicatorBase { key: this.privateKey, padding: constants.RSA_PKCS1_PADDING, // PKCS1 padding }, - bufferMessage - ); + Buffer.from(message) + ).toString('base64'); } // Decrypt AES-encrypted messages @@ -106,17 +100,34 @@ export class TcpServerCommunicator extends SocketCommunicatorBase { const incomingMessage = data.toString(); this.messageBuffer += incomingMessage; - // Check if the message ends with END_OF_MESSAGE - if (this.messageBuffer.endsWith(END_OF_MESSAGE)) { - // Remove the END_OF_MESSAGE marker and process the message - const completeMessage = this.messageBuffer.slice(0, -END_OF_MESSAGE.length); + if (this.messageBuffer.includes(END_OF_MESSAGE)) { + const messages = this.messageBuffer.split(END_OF_MESSAGE); - console.log(`\n\nComplete Message:\n${completeMessage}\n\n`); + for (let i = 0; i < messages.length - 1; i++) { + const completeMessage = messages[i]; + if (completeMessage) { + this.processCompleteMessage(completeMessage); + } + } - this.handleIncomingMessage(completeMessage); + this.messageBuffer = messages[messages.length - 1]; + } + } - // Clear the message buffer after processing - this.messageBuffer = ''; + private processCompleteMessage(completeMessage: string): void { + const [headerJson, chunkContent] = completeMessage.split('|'); + const header = JSON.parse(headerJson); + + if (!this.chunkBuffers[header.messageId]) { + this.chunkBuffers[header.messageId] = new Array(header.totalChunks); + } + + this.chunkBuffers[header.messageId][header.sequenceNumber] = chunkContent; + + if (this.chunkBuffers[header.messageId].every((chunk) => chunk !== undefined)) { + const fullMessage = this.chunkBuffers[header.messageId].join(''); + this.handleIncomingMessage(fullMessage); + delete this.chunkBuffers[header.messageId]; } } @@ -125,14 +136,29 @@ export class TcpServerCommunicator extends SocketCommunicatorBase { const message = MessageHandler.formatMessage(operationCode, metaInfo, fileContent); let outgoingMessage: string; - if (this.aesKey && this.aesIv) { - outgoingMessage = this.encryptWithAes(message); - } else { - outgoingMessage = message; - } + if (this.aesKey && this.aesIv) outgoingMessage = this.encryptWithAes(message); + else if(this.privateKey) outgoingMessage = this.encryptWithRsa(message); + else outgoingMessage = message; - outgoingMessage += END_OF_MESSAGE; // Append end-of-message marker - await this.writeToSocket(outgoingMessage); + const totalChunks = Math.ceil(outgoingMessage.length / CHUNK_SIZE); + const messageId = Date.now().toString(); + + for (let i = 0; i < totalChunks; i++) { + const chunk = outgoingMessage.slice(i * CHUNK_SIZE, (i + 1) * CHUNK_SIZE); + const chunkHeader = JSON.stringify({ + messageId, + sequenceNumber: i, + totalChunks, + }); + + const chunkWithHeader = `${chunkHeader}|${chunk}`; + + await this.writeToSocket(chunkWithHeader); + + if (i === totalChunks - 1) { + await this.writeToSocket(END_OF_MESSAGE); + } + } } // Handle incoming message (decrypt with AES if available) diff --git a/User/src/network/tcp/tcp_client.ts b/User/src/network/tcp/tcp_client.ts index e6d5d62..7ad64f3 100644 --- a/User/src/network/tcp/tcp_client.ts +++ b/User/src/network/tcp/tcp_client.ts @@ -29,12 +29,12 @@ export class TcpClient { this.socket = new net.Socket(); this.socket.connect(this.tcp_port, ip, () => { - console.log(`Client connected to server at ${ip}:${this.tcp_port}`); + //console.log(`Client connected to server at ${ip}:${this.tcp_port}`); this.communicator = new TcpClientCommunicator(this.socket as Socket, ip, this.tcp_port, this.operationHandler); }); this.socket.on('error', (err) => { - console.error(`Connection error to server at ${ip}:${this.tcp_port}: ${err.message}`); + //console.error(`Connection error to server at ${ip}:${this.tcp_port}: ${err.message}`); }); this.socket.on('data', async (data: Buffer) => { @@ -45,7 +45,7 @@ export class TcpClient { }); this.socket.on('close', () => { - console.log(`Connection closed: ${ip}:${this.tcp_port}`); + //console.log(`Connection closed: ${ip}:${this.tcp_port}`); this.lastResult = null; // Clear the last result on socket close }); } @@ -57,14 +57,14 @@ export class TcpClient { this.socket = null; this.communicator = null; this.lastResult = null; // Clear the last result on close - console.log('Client socket connection closed.'); + //console.log('Client socket connection closed.'); } } // Send a message with operationCode, metaInfo, and fileContent in chunks async sendMessage(operationCode: string, metaInfo?: { [key: string]: any }, fileContent?: Buffer): Promise { if (!this.communicator || !this.isAesKeySet()) { - console.error('Communicator not initialized or AES key not set.'); + //console.error('Communicator not initialized or AES key not set.'); return false; } diff --git a/User/src/network/tcp/tcp_server.ts b/User/src/network/tcp/tcp_server.ts index 98d1504..e0e66ab 100644 --- a/User/src/network/tcp/tcp_server.ts +++ b/User/src/network/tcp/tcp_server.ts @@ -35,7 +35,7 @@ export class TcpServer { const port = socket.remotePort || 0; const clientId = `${ip}:${port}`; // Use IP and port to identify the client - console.log(`Client connected: ${clientId}`); + //console.log(`Client connected: ${clientId}`); const tcpCommunicator = new TcpServerCommunicator(socket, ip, port, this.operationHandler); this.connectionManager.addConnection(ip, port, tcpCommunicator); @@ -44,9 +44,8 @@ export class TcpServer { tcpCommunicator.generateKeyPair(); tcpCommunicator.sendPublicKey() .then(() => tcpCommunicator.sendAesKey()) - .then(() => console.log('Public key and AES key sent successfully.')) .catch(err => { - console.error(`Error during key exchange with client ${clientId}:`, err); + //console.error(`Error during key exchange with client ${clientId}:`, err); socket.end(); // Close the connection in case of any error }); @@ -57,13 +56,13 @@ export class TcpServer { // Handle client disconnect socket.on('end', () => { - console.log(`Client disconnected: ${clientId}`); + //console.log(`Client disconnected: ${clientId}`); this.connectionManager.removeCommunicator(ip, port); }); // Handle socket errors socket.on('error', (err: Error) => { - console.error(`Error from client ${clientId}: ${err.message}`); + //console.error(`Error from client ${clientId}: ${err.message}`); this.connectionManager.removeCommunicator(ip, port); }); }); @@ -86,7 +85,7 @@ export class TcpServer { // Retrieve the communicator associated with this connection const communicator = this.connectionManager.getCommunicator(ip, port) as TcpServerCommunicator; if (!communicator) { - console.error(`No communicator found for ${clientId}`); + //console.error(`No communicator found for ${clientId}`); return; } @@ -102,9 +101,9 @@ export class TcpServer { handlerResult.metaInfo, handlerResult.fileContent ); - console.log(`Response sent to ${clientId}`); + //console.log(`Response sent to ${clientId}`); } catch (err) { - console.error(`Failed to send response to ${clientId}:`, err); + //console.error(`Failed to send response to ${clientId}:`, err); } } } diff --git a/User/src/network/udp/udp_server.ts b/User/src/network/udp/udp_server.ts index b87b82f..3c66550 100644 --- a/User/src/network/udp/udp_server.ts +++ b/User/src/network/udp/udp_server.ts @@ -34,7 +34,7 @@ export class UdpServer { const ip = rinfo.address; const port = rinfo.port; - console.log(`Received message from ${ip}:${port}`); + //console.log(`Received message from ${ip}:${port}`); // Create a temporary communicator for the incoming message const communicator = new UdpSocketCommunicator(this.udpServer, ip, port, this.operationHandler); @@ -46,8 +46,6 @@ export class UdpServer { if (communicatorResult) { // Send response back to the client using the temporary communicator await communicator.sendMessage(communicatorResult.operationCode, communicatorResult.metaInfo); - } else { - console.error(`No handler result for ${ip}:${port}`); } }