From ea4a649e00bf8f46dd9834158fc8e0f531d1dd48 Mon Sep 17 00:00:00 2001 From: Timerix Date: Sun, 9 Nov 2025 00:49:29 +0500 Subject: [PATCH] added new packet types --- dependencies/tlibc | 2 +- src/client/ServerConnection.c | 8 +-- src/client/client.c | 35 +++++++--- src/cryptography/AES.h | 2 +- src/db/idb.c | 10 +-- src/network/tcp-chat-protocol/constant.c | 19 +++++ src/network/tcp-chat-protocol/constant.h | 4 +- src/network/tcp-chat-protocol/v1.c | 70 ++++++++++++++++++- src/network/tcp-chat-protocol/v1.h | 89 +++++++++++++++++++++--- src/server/ClientConnection.c | 14 ++-- src/server/server.c | 87 +++++++++++++++++++++-- 11 files changed, 291 insertions(+), 49 deletions(-) diff --git a/dependencies/tlibc b/dependencies/tlibc index 30c141f..d6436d0 160000 --- a/dependencies/tlibc +++ b/dependencies/tlibc @@ -1 +1 @@ -Subproject commit 30c141f587ac8fa635812c6f4713dbc20b18d7c9 +Subproject commit d6436d08338a0a762e727f0c816dd5a09782b180 diff --git a/src/client/ServerConnection.c b/src/client/ServerConnection.c index b540996..a26ecc8 100644 --- a/src/client/ServerConnection.c +++ b/src/client/ServerConnection.c @@ -63,18 +63,16 @@ Result(ServerConnection*) ServerConnection_open(ClientCredentials* client_creden // connect to server address try(Socket _s, i, socket_open_TCP()); - try_void(socket_setTimeout(_s, SOCKET_TIMEOUT_MS_DEFAULT)); + try_void(socket_TCP_enableAliveChecks_default(_s)); try_void(socket_connect(_s, conn->server_end)); EncryptedSocketTCP_construct(&conn->sock, _s, NETWORK_BUFFER_SIZE, conn->session_key); // send PacketHeader and ClientHandshake // encryption by server public key PacketHeader packet_header = {0}; - PacketHeader_construct(&packet_header, - PROTOCOL_VERSION, PacketType_ClientHandshake, sizeof(ClientHandshake)); ClientHandshake client_handshake = {0}; - ClientHandshake_construct(&client_handshake, - conn->session_key); + try_void(ClientHandshake_tryConstruct(&client_handshake, &packet_header, + conn->session_key)); try_void(EncryptedSocketTCP_sendStructRSA(&conn->sock, &conn->rsa_enc, &packet_header)); try_void(EncryptedSocketTCP_sendStructRSA(&conn->sock, &conn->rsa_enc, &client_handshake)); diff --git a/src/client/client.c b/src/client/client.c index 9f16814..81d8920 100644 --- a/src/client/client.c +++ b/src/client/client.c @@ -1,6 +1,7 @@ #include "client.h" #include "term.h" #include "tlibc/time.h" +#include "network/tcp-chat-protocol/v1.h" static const str greeting_art = STR( " ^,,^ ╱|\n" @@ -24,21 +25,22 @@ static Result(void) commandExec(str command, bool* stop); static Result(void) askUserNameAndPassword(ClientCredentials** cred){ Deferral(8); - char username_buf[1024]; - str usrername = str_null; + char username_buf[128]; + str username = str_null; while(true) { printf("username: "); if(fgets(username_buf, sizeof(username_buf), stdin) == NULL){ Return RESULT_ERROR("STDIN is closed", false); } - usrername = str_from_cstr(username_buf); - if(usrername.size < 4){ - printf("ERROR: username length must be at least 4\n"); + username = str_from_cstr(username_buf); + if(username.size < USERNAME_SIZE_MIN || username.size > USERNAME_SIZE_MAX){ + printf("ERROR: username length (in bytes) must be >= %i and <= %i\n", + USERNAME_SIZE_MIN, USERNAME_SIZE_MAX); } else break; } - char password_buf[1024]; + char password_buf[128]; str password = str_null; while(true) { printf("password: "); @@ -47,13 +49,14 @@ static Result(void) askUserNameAndPassword(ClientCredentials** cred){ Return RESULT_ERROR("STDIN is closed", false); } password = str_from_cstr(password_buf); - if(password.size < 8){ - printf("ERROR: password length must be at least 8\n"); + if(password.size < PASSWORD_SIZE_MIN || password.size > PASSWORD_SIZE_MAX){ + printf("ERROR: password length (in bytes) must be >= %i and <= %i\n", + PASSWORD_SIZE_MIN, PASSWORD_SIZE_MAX); } else break; } - try(*cred, p, ClientCredentials_create(usrername, password)); + try(*cred, p, ClientCredentials_create(username, password)); Return RESULT_VOID; } @@ -87,7 +90,7 @@ Result(void) client_run() { if(command_input.size == 0) continue; - Result(void) com_result = commandExec(command_input, &stop); + ResultVar(void) com_result = commandExec(command_input, &stop); if(com_result.error){ str e_str = Error_toStr(com_result.error); printf("%s\n", e_str.data); @@ -139,11 +142,21 @@ static Result(void) commandExec(str command, bool* stop){ // TODO: request server info // show server info // save server info to user's db - // request log in + // try log in // if not registered, request registration and then log in + + // call serverConnection_run(): + // function with infinite loop which sends and receives messages + // with navigation across server channels + // } else if(is_alias("c") || is_alias("connect")){ // TODO: read saved servers from database + // show scrollable list of them + // select one + // try log in + // if not registered, ask user if they want to register + // regiser and then log in } else { Return RESULT_ERROR_FMT("unknown kommand: '%s'\n" diff --git a/src/cryptography/AES.h b/src/cryptography/AES.h index 09932bf..5cbfc84 100644 --- a/src/cryptography/AES.h +++ b/src/cryptography/AES.h @@ -18,7 +18,7 @@ //TODO: use PKS#7 instead of this garbage typedef struct EncryptedBlockHeader { u8 padding_size; -} __attribute__((aligned(16))) EncryptedBlockHeader; +} ATTRIBUTE_ALIGNED(16) EncryptedBlockHeader; ////////////////////////////////////////////////////////////////////////////// // AESBlockEncryptor // diff --git a/src/db/idb.c b/src/db/idb.c index 56eed8b..8d68443 100644 --- a/src/db/idb.c +++ b/src/db/idb.c @@ -9,7 +9,7 @@ typedef struct TableFileHeader { u16 version; bool _dirty_bit; u32 row_size; -} __attribute__((aligned(256))) TableFileHeader; +} ATTRIBUTE_ALIGNED(256) TableFileHeader; typedef struct Table { TableFileHeader header; @@ -133,7 +133,7 @@ Result(void) Table_validateHeader(Table* t){ Result(void) Table_validateRowSize(Table* t, u32 row_size){ if(row_size != t->header.row_size){ - Result(void) error_result = RESULT_ERROR_FMT( + ResultVar(void) error_result = RESULT_ERROR_FMT( "Requested row size (%u) doesn't match saved row size (%u)", row_size, t->header.row_size); return error_result; @@ -226,7 +226,7 @@ Result(Table*) idb_getOrCreateTable(IncrementalDB* db, str _table_name, u32 row_ } if(!HashMap_tryPush(&db->tables_map, t->name, &t)){ - Result(void) error_result = RESULT_ERROR_FMT( + ResultVar(void) error_result = RESULT_ERROR_FMT( "Table '%s' is already open", t->name.data); Return error_result; @@ -273,7 +273,7 @@ Result(void) idb_updateRows(Table* t, u64 id, const void* src, u64 count){ } try_void(Table_setDirtyBit(t, true)); - Defer(Table_setDirtyBit(t, false)); + Defer(IGNORE_RESULT Table_setDirtyBit(t, false)); i64 file_pos = sizeof(t->header) + id * t->header.row_size; @@ -295,7 +295,7 @@ Result(u64) idb_pushRows(Table* t, const void* src, u64 count){ Defer(pthread_mutex_unlock(&t->mutex)); try_void(Table_setDirtyBit(t, true)); - Defer(Table_setDirtyBit(t, false)); + Defer(IGNORE_RESULT Table_setDirtyBit(t, false)); const u64 new_row_index = t->row_count; diff --git a/src/network/tcp-chat-protocol/constant.c b/src/network/tcp-chat-protocol/constant.c index 4ee9966..7c6ef44 100644 --- a/src/network/tcp-chat-protocol/constant.c +++ b/src/network/tcp-chat-protocol/constant.c @@ -9,6 +9,25 @@ Result(void) PacketHeader_validateMagic(PacketHeader* ptr){ return RESULT_VOID; } +Result(void) PacketHeader_validateType(PacketHeader* ptr, u16 expected_type){ + if(ptr->type != expected_type){ + return RESULT_ERROR_FMT( + "expected message of type %u, but received of type %u", + expected_type, ptr->type); + } + return RESULT_VOID; +} + +Result(void) PacketHeader_validateContentSize(PacketHeader* ptr, u64 expected_size){ + if(ptr->content_size != expected_size){ + return RESULT_ERROR_FMT( + "expected message with content_size " IFWIN("%llu", "%lu") + ", but received with content_size " IFWIN("%llu", "%lu"), + expected_size, ptr->content_size); + } + return RESULT_VOID; +} + void PacketHeader_construct(PacketHeader* ptr, u8 protocol_version, u16 type, u64 content_size){ ptr->magic.n = PacketHeader_MAGIC.n; ptr->protocol_version = protocol_version; diff --git a/src/network/tcp-chat-protocol/constant.h b/src/network/tcp-chat-protocol/constant.h index 81c437e..45edcc3 100644 --- a/src/network/tcp-chat-protocol/constant.h +++ b/src/network/tcp-chat-protocol/constant.h @@ -14,7 +14,9 @@ typedef struct PacketHeader { u16 type; u32 _reserved4; u64 content_size; -} __attribute__((aligned(64))) PacketHeader; +} ATTRIBUTE_ALIGNED(64) PacketHeader; void PacketHeader_construct(PacketHeader* ptr, u8 protocol_version, u16 type, u64 content_size); Result(void) PacketHeader_validateMagic(PacketHeader* ptr); +Result(void) PacketHeader_validateType(PacketHeader* ptr, u16 expected_type); +Result(void) PacketHeader_validateContentSize(PacketHeader* ptr, u64 expected_size); diff --git a/src/network/tcp-chat-protocol/v1.c b/src/network/tcp-chat-protocol/v1.c index 460cf47..a956538 100644 --- a/src/network/tcp-chat-protocol/v1.c +++ b/src/network/tcp-chat-protocol/v1.c @@ -1,9 +1,73 @@ #include "v1.h" -void ClientHandshake_construct(ClientHandshake* ptr, Array(u8) session_key){ - memcpy(ptr->session_key, session_key.data, sizeof(ptr->session_key)); +#define _PacketHeader_construct(T) \ + PacketHeader_construct(header, PROTOCOL_VERSION, PacketType_##T, sizeof(T)) + +Result(void) ClientHandshake_tryConstruct(ClientHandshake* ptr, PacketHeader* header, + Array(u8) session_key) +{ + Deferral(1); + _PacketHeader_construct(ClientHandshake); + + try_assert(session_key.size == sizeof(ptr->session_key)); + memcpy(ptr->session_key, session_key.data, session_key.size); + + Return RESULT_VOID; } -void ServerHandshake_construct(ServerHandshake* ptr, u64 session_id){ +void ServerHandshake_construct(ServerHandshake* ptr, PacketHeader* header, + u64 session_id) +{ + _PacketHeader_construct(ServerHandshake); ptr->session_id = session_id; } + +void ServerPublicInfoRequest_construct(ServerPublicInfoRequest *ptr, PacketHeader* header, + ServerPublicInfo property) +{ + _PacketHeader_construct(ServerPublicInfoRequest); + ptr->property = property; +} + +Result(void) LoginRequest_tryConstruct(LoginRequest *ptr, PacketHeader* header, + Array(u8) token) +{ + Deferral(1); + _PacketHeader_construct(LoginRequest); + + try_assert(token.size == sizeof(ptr->token)); + memcpy(ptr->token, token.data, token.size); + + Return RESULT_VOID; +} + +void LoginResponse_construct(LoginResponse* ptr, PacketHeader* header, + u64 user_id, u64 landing_channel_id) +{ + _PacketHeader_construct(LoginResponse); + ptr->user_id = user_id; + ptr->landing_channel_id = landing_channel_id; +} + +Result(void) RegisterRequest_tryConstruct(RegisterRequest *ptr, PacketHeader* header, + str username, Array(u8) token) +{ + Deferral(1); + _PacketHeader_construct(RegisterRequest); + + try_assert(username.size >= USERNAME_SIZE_MIN && username.size <= USERNAME_SIZE_MAX); + ptr->username_size = username.size; + memcpy(ptr->username, username.data, username.size); + + try_assert(token.size == sizeof(ptr->token)); + memcpy(ptr->token, token.data, token.size); + + Return RESULT_VOID; +} + +void RegisterResponse_construct(RegisterResponse *ptr, PacketHeader* header, + u64 user_id) +{ + _PacketHeader_construct(RegisterResponse); + ptr->user_id = user_id; +} diff --git a/src/network/tcp-chat-protocol/v1.h b/src/network/tcp-chat-protocol/v1.h index 7136fe3..17f508b 100644 --- a/src/network/tcp-chat-protocol/v1.h +++ b/src/network/tcp-chat-protocol/v1.h @@ -1,32 +1,105 @@ #pragma once #include "tlibc/errors.h" +#include "tlibc/string/str.h" #include "network/tcp-chat-protocol/constant.h" +#include "cryptography/cryptography.h" #define PROTOCOL_VERSION 1 /* 1.0.0 */ #define NETWORK_BUFFER_SIZE 65536 +#define ALIGN_PACKET_STRUCT ATTRIBUTE_ALIGNED(8) + + typedef enum PacketType { PacketType_Invalid, PacketType_ErrorMessage, PacketType_ClientHandshake, PacketType_ServerHandshake, -} __attribute__((__packed__)) PacketType; + PacketType_ServerPublicInfoRequest, + PacketType_ServerPublicInfoResponse, + PacketType_LoginRequest, + PacketType_LoginResponse, + PacketType_RegisterRequest, + PacketType_RegisterResponse, +} ATTRIBUTE_PACKED PacketType; -typedef struct ErrorMessage { - /* content stream of size `header.content_size` */ -} ErrorMessage; +// typedef struct ErrorMessage { +// /* stream of size header.content_size */ +// } ErrorMessage; typedef struct ClientHandshake { u8 session_key[AES_SESSION_KEY_SIZE]; -} ClientHandshake; +} ALIGN_PACKET_STRUCT ClientHandshake; -void ClientHandshake_construct(ClientHandshake* ptr, Array(u8) session_key); +Result(void) ClientHandshake_tryConstruct(ClientHandshake* ptr, PacketHeader* header, + Array(u8) session_key); typedef struct ServerHandshake { u64 session_id; -} ServerHandshake; +} ALIGN_PACKET_STRUCT ServerHandshake; + +void ServerHandshake_construct(ServerHandshake* ptr, PacketHeader* header, + u64 session_id); + + +typedef enum ServerPublicInfo { + ServerPublicInfo_Name, + ServerPublicInfo_Description, +} ATTRIBUTE_PACKED ServerPublicInfo; + +typedef struct ServerPublicInfoRequest { + u32 property; +} ALIGN_PACKET_STRUCT ServerPublicInfoRequest; + +void ServerPublicInfoRequest_construct(ServerPublicInfoRequest* ptr, PacketHeader* header, + ServerPublicInfo property); + + +// typedef struct ServerPublicInfoResponse { +// /* stream of size header.content_size */ +// } ServerPublicInfoResponse; + + +typedef struct LoginRequest { + u8 token[PASSWORD_HASH_SIZE]; +} ALIGN_PACKET_STRUCT LoginRequest; + +Result(void) LoginRequest_tryConstruct(LoginRequest* ptr, PacketHeader* header, + Array(u8) token); + + +typedef struct LoginResponse { + u64 user_id; + u64 landing_channel_id; +} ALIGN_PACKET_STRUCT LoginResponse; + +void LoginResponse_construct(LoginResponse* ptr, PacketHeader* header, + u64 user_id, u64 landing_channel_id); + + +#define USERNAME_SIZE_MIN 4 +#define USERNAME_SIZE_MAX 64 +#define PASSWORD_SIZE_MIN 8 +#define PASSWORD_SIZE_MAX 32 + +typedef struct RegisterRequest { + u32 username_size; + char username[USERNAME_SIZE_MAX]; + u8 token[PASSWORD_HASH_SIZE]; +} ALIGN_PACKET_STRUCT RegisterRequest; + +Result(void) RegisterRequest_tryConstruct(RegisterRequest* ptr, PacketHeader* header, + str username, Array(u8) token); + + +typedef struct RegisterResponse { + u64 user_id; +} ALIGN_PACKET_STRUCT RegisterResponse; + +void RegisterResponse_construct(RegisterResponse* ptr, PacketHeader* header, + u64 user_id); + -void ServerHandshake_construct(ServerHandshake* ptr, u64 session_id); diff --git a/src/server/ClientConnection.c b/src/server/ClientConnection.c index f7949e8..c4425b9 100644 --- a/src/server/ClientConnection.c +++ b/src/server/ClientConnection.c @@ -28,7 +28,7 @@ Result(ClientConnection*) ClientConnection_accept(ServerCredentials* server_cred // correct session key will be received from client later Array_memset(conn->session_key, 0); EncryptedSocketTCP_construct(&conn->sock, sock_tcp, NETWORK_BUFFER_SIZE, conn->session_key); - try_void(socket_setTimeout(sock_tcp, SOCKET_TIMEOUT_MS_DEFAULT)); + try_void(socket_TCP_enableAliveChecks_default(sock_tcp)); // decrypt the rsa messages using server private key RSADecryptor rsa_dec; @@ -38,12 +38,8 @@ Result(ClientConnection*) ClientConnection_accept(ServerCredentials* server_cred PacketHeader packet_header = {0}; try_void(EncryptedSocketTCP_recvStructRSA(&conn->sock, &rsa_dec, &packet_header)); try_void(PacketHeader_validateMagic(&packet_header)); - if(packet_header.type != PacketType_ClientHandshake){ - Return RESULT_ERROR_FMT( - "received message of unexpected type: %u", - packet_header.type - ); - } + try_void(PacketHeader_validateType(&packet_header, PacketType_ClientHandshake)); + try_void(PacketHeader_validateContentSize(&packet_header, sizeof(ClientHandshake))); // receive ClientHandshake ClientHandshake client_handshake = {0}; @@ -54,10 +50,8 @@ Result(ClientConnection*) ClientConnection_accept(ServerCredentials* server_cred EncryptedSocketTCP_changeKey(&conn->sock, conn->session_key); // send PacketHeader and ServerHandshake over encrypted TCP socket - PacketHeader_construct(&packet_header, - PROTOCOL_VERSION, PacketType_ServerHandshake, sizeof(ServerHandshake)); ServerHandshake server_handshake = {0}; - ServerHandshake_construct(&server_handshake, + ServerHandshake_construct(&server_handshake, &packet_header, session_id); try_void(EncryptedSocketTCP_sendStruct(&conn->sock, &packet_header)); try_void(EncryptedSocketTCP_sendStruct(&conn->sock, &server_handshake)); diff --git a/src/server/server.c b/src/server/server.c index 5922d51..bd57f09 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -58,7 +58,7 @@ Result(void) server_run(cstr server_endpoint_cstr, cstr config_path){ logDebug(log_ctx, "initializing main socket"); EndpointIPv4 server_end; - EndpointIPv4_parse(server_endpoint_cstr, &server_end); + try_void(EndpointIPv4_parse(server_endpoint_cstr, &server_end)); try(Socket main_socket, i, socket_open_TCP()); try_void(socket_bind(main_socket, server_end)); try_void(socket_listen(main_socket, 512)); @@ -84,7 +84,7 @@ static void* handle_connection(void* _args){ char log_ctx[64]; sprintf(log_ctx, "Session-" IFWIN("%llx", "%lx"), args->session_id); - Result(void) r = try_handle_connection(args, log_ctx); + ResultVar(void) r = try_handle_connection(args, log_ctx); if(r.error){ str error_s = Error_toStr(r.error); logError(log_ctx, "%s", error_s.data); @@ -115,9 +115,88 @@ static Result(void) try_handle_connection(ConnectionHandlerArgs* args, cstr log_ logInfo(log_ctx, "session accepted"); // handle unauthorized requests + bool ahtorized = false; + PacketHeader req_header = {0}; + PacketHeader res_header = {0}; + while(!ahtorized){ + sleepMsec(50); + //TODO: implement some additional check if socket is dead or not + + try_void(EncryptedSocketTCP_recvStruct(&conn->sock, &req_header)); + try_void(PacketHeader_validateMagic(&req_header)); + //TODO: move request handlers to separate functions + switch(req_header.type){ + default:{ + Array(u8) err_buf = Array_alloc(u8, 128); + bool err_complete = false; + Defer(if(!err_complete) free(err_buf.data)); + sprintf(err_buf.data, "Received unexpected packet of type %u", + req_header.type); + err_buf.size = strlen(err_buf.data); + + PacketHeader_construct(&res_header, + PROTOCOL_VERSION, PacketType_ErrorMessage, err_buf.size); + try_void(EncryptedSocketTCP_sendStruct(&conn->sock, &res_header)); + //TODO: limit ErrorMessage size to fit into EncryptedSocketTCP.internal_buffer_size + try_void(EncryptedSocketTCP_send(&conn->sock, err_buf)); + + err_complete = true; + Return RESULT_ERROR(err_buf.data, true); + } + + case PacketType_ServerPublicInfoRequest:{ + ServerPublicInfoRequest req = {0}; + try_void(PacketHeader_validateContentSize(&req_header, sizeof(req))); + try_void(EncryptedSocketTCP_recvStruct(&conn->sock, &req)); + + //TODO: try find requested info + Array(u8) content; + + PacketHeader_construct(&res_header, + PROTOCOL_VERSION, PacketType_ServerPublicInfoResponse, content.size); + try_void(EncryptedSocketTCP_sendStruct(&conn->sock, &res_header)); + try_void(EncryptedSocketTCP_send(&conn->sock, content)); + break; + } + + case PacketType_LoginRequest:{ + LoginRequest req = {0}; + try_void(PacketHeader_validateContentSize(&req_header, sizeof(req))); + try_void(EncryptedSocketTCP_recvStruct(&conn->sock, &req)); + + //TODO: try authorize client + u64 user_id; + u64 landing_channel_id; + + LoginResponse res = {0}; + LoginResponse_construct(&res, &res_header, user_id, landing_channel_id); + try_void(EncryptedSocketTCP_sendStruct(&conn->sock, &res_header)); + try_void(EncryptedSocketTCP_sendStruct(&conn->sock, &res)); + ahtorized = true; + logInfo(log_ctx, "client authorized"); + break; + } + + case PacketType_RegisterRequest:{ + RegisterRequest req = {0}; + try_void(PacketHeader_validateContentSize(&req_header, sizeof(req))); + try_void(EncryptedSocketTCP_recvStruct(&conn->sock, &req)); + + //TODO: try register client + u64 user_id; + + RegisterResponse res = {0}; + RegisterResponse_construct(&res, &res_header, user_id); + try_void(EncryptedSocketTCP_sendStruct(&conn->sock, &res_header)); + try_void(EncryptedSocketTCP_sendStruct(&conn->sock, &res)); + break; + } + } + } + + // handle authorized requests while(true){ - - sleepMsec(10); + sleepMsec(50); } Return RESULT_VOID;