diff --git a/src/client.c b/src/client.c index 1a9e841..1f13742 100644 --- a/src/client.c +++ b/src/client.c @@ -12,7 +12,8 @@ #include #include -#define PORT 7777 +#define DEFAULT_PORT 7777 +#define DEFAULT_IP "127.0.0.1" #define BUFFER_SIZE 4096 #define USERNAME_MAX 50 #define INPUT_HEIGHT 3 @@ -34,6 +35,10 @@ int scroll_offset = 0; unsigned char aes_key[AES_KEY_SIZE]; +// Параметры подключения +char server_ip[256] = DEFAULT_IP; +int server_port = DEFAULT_PORT; + volatile int need_redraw = 0; volatile int running = 1; @@ -299,7 +304,6 @@ void scroll_down() { } } -// Формат пакета: [total_len:4][iv:12][tag:16][ciphertext:N] void send_encrypted_message(int socket, const char *plaintext) { unsigned char iv[AES_IV_SIZE]; RAND_bytes(iv, AES_IV_SIZE); @@ -324,7 +328,6 @@ void send_encrypted_message(int socket, const char *plaintext) { send(socket, ciphertext, ciphertext_len, 0); } -// Получить и расшифровать сообщение int receive_encrypted_message(int socket, char *plaintext, int max_len) { int net_total_len; int valread = read(socket, &net_total_len, sizeof(int)); @@ -424,7 +427,14 @@ void handle_input() { return; } - // Форматируем и отправляем на сервер, НЕ добавляем локально! + // Проверяем длину чтобы избежать truncation + int max_msg_len = BUFFER_SIZE - strlen(username) - 5; + if (max_msg_len < 0) max_msg_len = 0; + + if ((int) strlen(input_buffer) > max_msg_len) { + input_buffer[max_msg_len] = '\0'; + } + char full_msg[BUFFER_SIZE]; int written = snprintf(full_msg, sizeof(full_msg), "[%s]: %s", username, input_buffer); @@ -435,7 +445,6 @@ void handle_input() { } send_encrypted_message(sock, full_msg); - // Сообщение появится когда придёт от сервера в receive_messages() pos = 0; input_buffer[0] = '\0'; @@ -481,7 +490,6 @@ void receive_messages() { char plaintext[BUFFER_SIZE]; int len = receive_encrypted_message(sock, plaintext, BUFFER_SIZE); if (len > 0) { - // ВСЕ сообщения приходят через сервер — и свои, и чужие add_chat_message(plaintext); } else if (len < 0) { add_chat_message("*** Ошибка расшифровки сообщения ***"); @@ -498,18 +506,35 @@ void sigwinch_handler(int sig) { void parse_args(int argc, char *argv[]) { static struct option long_options[] = { - {"messagebuffer", required_argument, 0, 'm'}, {0, 0, 0, 0}}; + {"messagebuffer", required_argument, 0, 'm'}, + {"ip", required_argument, 0, 'i'}, + {"port", required_argument, 0, 'p'}, + {0, 0, 0, 0}}; int opt; - while ((opt = getopt_long(argc, argv, "m:", long_options, NULL)) != -1) { + while ((opt = getopt_long(argc, argv, "m:i:p:", long_options, NULL)) != -1) { switch (opt) { case 'm': msg_buffer_size = atoi(optarg); if (msg_buffer_size < 10) msg_buffer_size = 10; if (msg_buffer_size > 10000) msg_buffer_size = 10000; break; + case 'i': + strncpy(server_ip, optarg, sizeof(server_ip) - 1); + server_ip[sizeof(server_ip) - 1] = '\0'; + break; + case 'p': + server_port = atoi(optarg); + if (server_port < 1 || server_port > 65535) { + fprintf(stderr, "Invalid port: %s\n", optarg); + exit(1); + } + break; default: - fprintf(stderr, "Usage: %s [-m N|--messagebuffer=N]\n", argv[0]); + fprintf(stderr, + "Usage: %s [-m N|--messagebuffer=N] [-i IP|--ip=IP] [-p " + "PORT|--port=PORT]\n", + argv[0]); exit(1); } } @@ -540,8 +565,13 @@ int main(int argc, char *argv[]) { } server_addr.sin_family = AF_INET; - server_addr.sin_port = htons(PORT); - inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr); + server_addr.sin_port = htons(server_port); + if (inet_pton(AF_INET, server_ip, &server_addr.sin_addr) <= 0) { + fprintf(stderr, "Invalid address: %s\n", server_ip); + return 1; + } + + printf("[INFO] Connecting to %s:%d\n", server_ip, server_port); if (connect(sock, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) { diff --git a/src/main.c b/src/main.c index 756d918..25c84cd 100644 --- a/src/main.c +++ b/src/main.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -6,7 +7,8 @@ #include #include -#define PORT 7777 +#define DEFAULT_PORT 7777 +#define DEFAULT_IP "0.0.0.0" #define BUFFER_SIZE 8192 #define MAX_CLIENTS 100 #define HISTORY_SIZE 1000 @@ -15,16 +17,16 @@ #define AES_TAG_SIZE 16 typedef struct { - int socket; - char username[USERNAME_MAX]; + int socket; + char username[USERNAME_MAX]; } client_t; client_t *clients[MAX_CLIENTS]; pthread_mutex_t clients_mutex = PTHREAD_MUTEX_INITIALIZER; typedef struct { - int data_len; - unsigned char *data; + int data_len; + unsigned char *data; } encrypted_message_t; encrypted_message_t message_history[HISTORY_SIZE]; @@ -32,180 +34,214 @@ int history_count = 0; int history_start = 0; pthread_mutex_t history_mutex = PTHREAD_MUTEX_INITIALIZER; +// Параметры сервера +char bind_ip[256] = DEFAULT_IP; +int bind_port = DEFAULT_PORT; + void add_to_history(const unsigned char *data, int len) { - pthread_mutex_lock(&history_mutex); - int idx = (history_start + history_count) % HISTORY_SIZE; + pthread_mutex_lock(&history_mutex); + int idx = (history_start + history_count) % HISTORY_SIZE; - free(message_history[idx].data); - message_history[idx].data = malloc(len); - memcpy(message_history[idx].data, data, len); - message_history[idx].data_len = len; + free(message_history[idx].data); + message_history[idx].data = malloc(len); + memcpy(message_history[idx].data, data, len); + message_history[idx].data_len = len; - if (history_count < HISTORY_SIZE) { - history_count++; - } else { - history_start = (history_start + 1) % HISTORY_SIZE; - } - pthread_mutex_unlock(&history_mutex); + if (history_count < HISTORY_SIZE) { + history_count++; + } else { + history_start = (history_start + 1) % HISTORY_SIZE; + } + pthread_mutex_unlock(&history_mutex); } void send_history(int socket) { - pthread_mutex_lock(&history_mutex); + pthread_mutex_lock(&history_mutex); - int net_count = htonl(history_count); - write(socket, &net_count, sizeof(int)); + int net_count = htonl(history_count); + write(socket, &net_count, sizeof(int)); - for (int i = 0; i < history_count; i++) { - int idx = (history_start + i) % HISTORY_SIZE; - int net_len = htonl(message_history[idx].data_len); - write(socket, &net_len, sizeof(int)); - write(socket, message_history[idx].data, message_history[idx].data_len); - } + for (int i = 0; i < history_count; i++) { + int idx = (history_start + i) % HISTORY_SIZE; + int net_len = htonl(message_history[idx].data_len); + write(socket, &net_len, sizeof(int)); + write(socket, message_history[idx].data, message_history[idx].data_len); + } - pthread_mutex_unlock(&history_mutex); + pthread_mutex_unlock(&history_mutex); } -// Рассылка ВСЕМ клиентам включая отправителя (-1 = всем) void broadcast_to_all(const unsigned char *data, int len) { - pthread_mutex_lock(&clients_mutex); - for (int i = 0; i < MAX_CLIENTS; i++) { - if (clients[i]) { - int net_len = htonl(len); - send(clients[i]->socket, &net_len, sizeof(int), 0); - send(clients[i]->socket, data, len, 0); - } + pthread_mutex_lock(&clients_mutex); + for (int i = 0; i < MAX_CLIENTS; i++) { + if (clients[i]) { + int net_len = htonl(len); + send(clients[i]->socket, &net_len, sizeof(int), 0); + send(clients[i]->socket, data, len, 0); } - pthread_mutex_unlock(&clients_mutex); + } + pthread_mutex_unlock(&clients_mutex); } void *handle_client(void *arg) { - client_t *cli = (client_t *)arg; + client_t *cli = (client_t *) arg; - int valread = read(cli->socket, cli->username, USERNAME_MAX); - if (valread <= 0) goto cleanup; - cli->username[valread] = '\0'; - cli->username[strcspn(cli->username, "\n")] = '\0'; + int valread = read(cli->socket, cli->username, USERNAME_MAX); + if (valread <= 0) goto cleanup; + cli->username[valread] = '\0'; + cli->username[strcspn(cli->username, "\n")] = '\0'; - printf("User '%s' connected\n", cli->username); + printf("User '%s' connected from %s\n", cli->username, + inet_ntoa(((struct sockaddr_in *) &cli)->sin_addr)); - send_history(cli->socket); + send_history(cli->socket); - while (1) { - int net_total_len; - valread = read(cli->socket, &net_total_len, sizeof(int)); - if (valread <= 0) break; + while (1) { + int net_total_len; + valread = read(cli->socket, &net_total_len, sizeof(int)); + if (valread <= 0) break; - int total_len = ntohl(net_total_len); - if (total_len <= 0 || total_len > BUFFER_SIZE) { - printf("Invalid length from %s: %d\n", cli->username, total_len); - break; - } - - unsigned char *data = malloc(total_len); - int total_read = 0; - while (total_read < total_len) { - valread = read(cli->socket, data + total_read, total_len - total_read); - if (valread <= 0) { - free(data); - goto cleanup; - } - total_read += valread; - } - - // Сохраняем - add_to_history(data, total_len); - - // Рассылаем ВСЕМ включая отправителя - broadcast_to_all(data, total_len); - - free(data); + int total_len = ntohl(net_total_len); + if (total_len <= 0 || total_len > BUFFER_SIZE) { + printf("Invalid length from %s: %d\n", cli->username, total_len); + break; } + unsigned char *data = malloc(total_len); + int total_read = 0; + while (total_read < total_len) { + valread = read(cli->socket, data + total_read, total_len - total_read); + if (valread <= 0) { + free(data); + goto cleanup; + } + total_read += valread; + } + + add_to_history(data, total_len); + broadcast_to_all(data, total_len); + + free(data); + } + cleanup: - printf("User '%s' disconnected\n", cli->username); - close(cli->socket); + printf("User '%s' disconnected\n", cli->username); + close(cli->socket); + + pthread_mutex_lock(&clients_mutex); + for (int i = 0; i < MAX_CLIENTS; i++) { + if (clients[i] == cli) { + clients[i] = NULL; + break; + } + } + pthread_mutex_unlock(&clients_mutex); + + free(cli); + pthread_exit(NULL); +} + +void parse_args(int argc, char *argv[]) { + static struct option long_options[] = {{"ip", required_argument, 0, 'i'}, + {"port", required_argument, 0, 'p'}, + {0, 0, 0, 0}}; + + int opt; + while ((opt = getopt_long(argc, argv, "i:p:", long_options, NULL)) != -1) { + switch (opt) { + case 'i': + strncpy(bind_ip, optarg, sizeof(bind_ip) - 1); + bind_ip[sizeof(bind_ip) - 1] = '\0'; + break; + case 'p': + bind_port = atoi(optarg); + if (bind_port < 1 || bind_port > 65535) { + fprintf(stderr, "Invalid port: %s\n", optarg); + exit(1); + } + break; + default: + fprintf(stderr, "Usage: %s [-i IP|--ip=IP] [-p PORT|--port=PORT]\n", + argv[0]); + exit(1); + } + } +} + +int main(int argc, char *argv[]) { + int server_fd, new_socket; + struct sockaddr_in address; + int opt = 1; + int addrlen = sizeof(address); + + parse_args(argc, argv); + + for (int i = 0; i < HISTORY_SIZE; i++) { + message_history[i].data = NULL; + message_history[i].data_len = 0; + } + + if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { + perror("socket failed"); + exit(EXIT_FAILURE); + } + + if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) { + perror("setsockopt"); + exit(EXIT_FAILURE); + } + + address.sin_family = AF_INET; + address.sin_port = htons(bind_port); + if (inet_pton(AF_INET, bind_ip, &address.sin_addr) <= 0) { + fprintf(stderr, "Invalid bind address: %s\n", bind_ip); + exit(EXIT_FAILURE); + } + + if (bind(server_fd, (struct sockaddr *) &address, sizeof(address)) < 0) { + perror("bind failed"); + exit(EXIT_FAILURE); + } + + if (listen(server_fd, 3) < 0) { + perror("listen"); + exit(EXIT_FAILURE); + } + + printf("E2E Encrypted Server listening on %s:%d\n", bind_ip, bind_port); + + while (1) { + if ((new_socket = accept(server_fd, (struct sockaddr *) &address, + (socklen_t *) &addrlen)) < 0) { + perror("accept"); + continue; + } + + client_t *cli = malloc(sizeof(client_t)); + cli->socket = new_socket; pthread_mutex_lock(&clients_mutex); + int added = 0; for (int i = 0; i < MAX_CLIENTS; i++) { - if (clients[i] == cli) { - clients[i] = NULL; - break; - } + if (!clients[i]) { + clients[i] = cli; + added = 1; + break; + } } pthread_mutex_unlock(&clients_mutex); - free(cli); - pthread_exit(NULL); -} - -int main() { - int server_fd, new_socket; - struct sockaddr_in address; - int opt = 1; - int addrlen = sizeof(address); - - for (int i = 0; i < HISTORY_SIZE; i++) { - message_history[i].data = NULL; - message_history[i].data_len = 0; - } - - if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { - perror("socket failed"); - exit(EXIT_FAILURE); - } - - if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) { - perror("setsockopt"); - exit(EXIT_FAILURE); - } - - address.sin_family = AF_INET; - address.sin_addr.s_addr = INADDR_ANY; - address.sin_port = htons(PORT); - - if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { - perror("bind failed"); - exit(EXIT_FAILURE); + if (!added) { + printf("Max clients reached\n"); + close(new_socket); + free(cli); + continue; } - if (listen(server_fd, 3) < 0) { - perror("listen"); - exit(EXIT_FAILURE); - } - - printf("E2E Encrypted Server listening on port %d\n", PORT); - - while (1) { - if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) { - perror("accept"); - continue; - } - - client_t *cli = malloc(sizeof(client_t)); - cli->socket = new_socket; - - pthread_mutex_lock(&clients_mutex); - int added = 0; - for (int i = 0; i < MAX_CLIENTS; i++) { - if (!clients[i]) { - clients[i] = cli; - added = 1; - break; - } - } - pthread_mutex_unlock(&clients_mutex); - - if (!added) { - printf("Max clients reached\n"); - close(new_socket); - free(cli); - continue; - } - - pthread_t tid; - pthread_create(&tid, NULL, handle_client, cli); - pthread_detach(tid); - } + pthread_t tid; + pthread_create(&tid, NULL, handle_client, cli); + pthread_detach(tid); + } - return 0; + return 0; }