From 806f0359d0fc1cbbd7e10c2137656d0b4c42e482 Mon Sep 17 00:00:00 2001 From: Timerix Date: Tue, 18 Nov 2025 23:07:31 +0500 Subject: [PATCH] implemented Login and Register requests --- .vscode/launch.json | 2 +- dependencies/tlibc | 2 +- include/tcp-chat/client.h | 6 + include/tcp-chat/common_constants.h | 11 +- src/cli/ClientCLI/ClientCLI.c | 334 +++++++++++++++++++------ src/cli/ClientCLI/ClientCLI.h | 10 +- src/cli/ClientCLI/db_tables.h | 12 +- src/client/ServerConnection.c | 1 + src/client/client.c | 58 ++++- src/client/client_internal.h | 1 + src/config.c | 4 +- src/cryptography/AES.c | 5 +- src/db/idb.c | 4 + src/network/tcp-chat-protocol/v1.c | 2 - src/server/db_tables.h | 6 +- src/server/request_handlers/Login.c | 6 +- src/server/request_handlers/Register.c | 23 +- src/server/server.c | 51 ++-- src/server/server_internal.h | 4 +- 19 files changed, 395 insertions(+), 147 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index d8957a8..f03bfb9 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,7 +7,7 @@ "request": "launch", "program": "${workspaceFolder}/bin/tcp-chat", "windows": { "program": "${workspaceFolder}/bin/tcp-chat.exe" }, - // "args": [ "-l" ], + "args": [ "-l" ], "preLaunchTask": "build_exec_dbg", "stopAtEntry": false, "cwd": "${workspaceFolder}/bin", diff --git a/dependencies/tlibc b/dependencies/tlibc index ec164dc..225a48a 160000 --- a/dependencies/tlibc +++ b/dependencies/tlibc @@ -1 +1 @@ -Subproject commit ec164dc4e92dd20401b97e979507ec8f04f04af7 +Subproject commit 225a48a8d98fb5ba7edf80e882a8ae2c40852407 diff --git a/include/tcp-chat/client.h b/include/tcp-chat/client.h index 346268c..14a04a5 100644 --- a/include/tcp-chat/client.h +++ b/include/tcp-chat/client.h @@ -26,3 +26,9 @@ Result(void) Client_getServerName(Client* self, str* out_name); /// @param self connected client /// @param out_name owned by Client, fetched from server during Client_connect Result(void) Client_getServerDescription(Client* self, str* out_desc); + +/// Create new account on connected server +Result(void) Client_register(Client* self, u64* out_user_id); + +/// Authorize on connected server +Result(void) Client_login(Client* self, u64* out_user_id, u64* out_landing_channel_id); diff --git a/include/tcp-chat/common_constants.h b/include/tcp-chat/common_constants.h index c86f78e..5759c90 100644 --- a/include/tcp-chat/common_constants.h +++ b/include/tcp-chat/common_constants.h @@ -6,10 +6,13 @@ #define PASSWORD_SIZE_MIN 8 #define PASSWORD_SIZE_MAX 31 #define PASSWORD_HASH_SIZE 32 +#define HOSTADDR_SIZE_MIN 4 +#define HOSTADDR_SIZE_MAX 255 +#define PRIVATE_KEY_BASE64_SIZE_MAX 1724 +#define PUBLIC_KEY_BASE64_SIZE_MAX 699 +#define SERVER_NAME_SIZE_MIN 1 +#define SERVER_NAME_SIZE_MAX 127 +#define SERVER_DESC_SIZE_MAX 1023 #define CHANNEL_NAME_SIZE_MIN 1 #define CHANNEL_NAME_SIZE_MAX 127 #define CHANNEL_DESC_SIZE_MAX 1023 -#define PRIVATE_KEY_BASE64_SIZE_MAX 1724 -#define PUBLIC_KEY_BASE64_SIZE_MAX 699 -#define HOSTADDR_SIZE_MIN 4 -#define HOSTADDR_SIZE_MAX 255 diff --git a/src/cli/ClientCLI/ClientCLI.c b/src/cli/ClientCLI/ClientCLI.c index 0c22491..f93e634 100644 --- a/src/cli/ClientCLI/ClientCLI.c +++ b/src/cli/ClientCLI/ClientCLI.c @@ -1,5 +1,4 @@ #include "cli/ClientCLI/ClientCLI.h" -#include "cli/ClientCLI/db_tables.h" #include "cli/term.h" #include "tcp-chat/common_constants.h" #include "tlibc/time.h" @@ -23,22 +22,28 @@ static const str farewell_art = STR( #define is_alias(LITERAL) str_equals(command, STR(LITERAL)) static Result(void) ClientCLI_askUserNameAndPassword(str* username_out, str* password_out); -static Result(void) ClientCLI_commandExec(ClientCLI* self, str command, bool* stop); +static Result(void) ClientCLI_execCommand(ClientCLI* self, str command, bool* stop); static Result(void) ClientCLI_openUserDB(ClientCLI* self); -static Result(void) ClientCLI_saveServerInfo(ClientCLI* self, - cstr server_addr_cstr, cstr server_pk_base64, - str server_name, str server_description); - +static Result(Server*) ClientCLI_saveServerInfo(ClientCLI* self, + str addr, str pk_base64, str name, str desc); +static Result(Server*) ClientCLI_joinNewServer(ClientCLI* self); +static Result(Server*) ClientCLI_selectServerFromCache(ClientCLI* self); +static Result(void) ClientCLI_showServerInfo(ClientCLI* self, Server* server); +static Result(void) ClientCLI_register(ClientCLI* self); +static Result(void) ClientCLI_login(ClientCLI* self); void ClientCLI_destroy(ClientCLI* self){ if(!self) return; Client_free(self->client); - idb_close(self->user_db); + idb_close(self->db); + pthread_mutex_destroy(&self->servers_cache_mutex); + List_destroy(self->servers_cache_list); + HashMap_destroy(&self->servers_addr_id_map); } void ClientCLI_construct(ClientCLI* self){ self->client = NULL; - self->user_db = NULL; + self->db = NULL; } Result(void) ClientCLI_run(ClientCLI* self) { @@ -46,7 +51,7 @@ Result(void) ClientCLI_run(ClientCLI* self) { try_void(term_init()); term_clear(); - printf("%s\n", greeting_art.data); + printf(FMT_str"\n", greeting_art.size, greeting_art.data); // create Client str username = str_null, password = str_null; @@ -75,11 +80,11 @@ Result(void) ClientCLI_run(ClientCLI* self) { if(command_input.size == 0) continue; - ResultVar(void) com_result = ClientCLI_commandExec(self, command_input, &stop); + ResultVar(void) com_result = ClientCLI_execCommand(self, command_input, &stop); if(com_result.error){ Error_addCallPos(com_result.error, ErrorCallPos_here()); str e_str = Error_toStr(com_result.error); - printf("%s\n", e_str.data); + printf(FMT_str"\n", e_str.size, e_str.data); str_free(e_str); Error_free(com_result.error); } @@ -105,6 +110,7 @@ static Result(void) ClientCLI_askUserNameAndPassword(str* username_out, str* pas printf("ERROR: username length (in bytes) must be >= %i and <= %i\n", USERNAME_SIZE_MIN, USERNAME_SIZE_MAX); } + //TODO: validate username characters else break; } @@ -131,11 +137,11 @@ static Result(void) ClientCLI_askUserNameAndPassword(str* username_out, str* pas Return RESULT_VOID; } -static Result(void) ClientCLI_commandExec(ClientCLI* self, str command, bool* stop){ +static Result(void) ClientCLI_execCommand(ClientCLI* self, str command, bool* stop){ Deferral(64); if(is_alias("q") || is_alias("quit") || is_alias("exit")){ - printf("%s\n", farewell_art.data); + printf(FMT_str"\n", farewell_art.size, farewell_art.data); *stop = true; } else if(is_alias("clear")){ @@ -143,55 +149,37 @@ static Result(void) ClientCLI_commandExec(ClientCLI* self, str command, bool* st } else if(is_alias("h") || is_alias("help")){ printf( - "COMMANDS:\n" - "h, help Show this message.\n" - "q, quit, exit Close the program.\n" - "clear Clear the screen.\n" - "j, join Join a server.\n" - "c, connect Connect to a server you joined.\n" + "COMMANDS:\n" + "Without connection:\n" + " h, help Show this message.\n" + " q, quit, exit Close the program.\n" + " clear Clear the screen.\n" + "Connection:\n" + " j, join Join a new server and select it.\n" + " s, select Select a server you joined before.\n" + "After connection:\n" + " r, register Create account on selected server\n" + " l, login Authorize on selected server\n" + "Authorized:\n" ); } else if (is_alias("j") || is_alias("join")){ - // ask server address - printf("Enter server address (ip:port):\n"); - char server_addr_cstr[HOSTADDR_SIZE_MAX + 1]; - try_void(term_readLine(server_addr_cstr, sizeof(server_addr_cstr))); - str server_addr_str = str_from_cstr(server_addr_cstr); - str_trim(&server_addr_str, true); - - // ask server public key - printf("Enter server public key (RSA-Public-:):\n"); - char server_pk_cstr[PUBLIC_KEY_BASE64_SIZE_MAX + 1]; - try_void(term_readLine(server_pk_cstr, sizeof(server_pk_cstr))); - str server_pk_str = str_from_cstr(server_pk_cstr); - str_trim(&server_pk_str, true); - - // connect to server - printf("connecting to server...\n"); - try_void(Client_connect(self->client, server_addr_cstr, server_pk_cstr)); - printf("connection established\n"); - - // show server info - str server_name = str_null; - str server_description = str_null; - try_void(Client_getServerName(self->client, &server_name)); - try_void(Client_getServerName(self->client, &server_description)); - printf("server name: %s\n", server_name.data); - printf("server description: %s\n", server_description.data); - - try_void(ClientCLI_saveServerInfo(self, server_addr_cstr, server_pk_cstr, - server_name, server_description)); - // TODO: ask in loop: log in / register - - //TODO: call Client_runIO(): + // ask address and key, connect to server + try_void(ClientCLI_joinNewServer(self)); + } + else if(is_alias("s") || is_alias("select")){ + // show scrollable list of servers, get selected one + try_void(ClientCLI_selectServerFromCache(self)); + } + else if(is_alias("r") || is_alias("register")){ + try_void(ClientCLI_register(self)); + } + else if(is_alias("l") || is_alias("login")){ + try_void(ClientCLI_login(self)); + // TODO: call Client_runIO(): // 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 - // TODO: show scrollable list of servers, get selected one - // TODO: ask in loop: log in / register - } else { printf("ERROR: unknown command.\n" "Use 'h' to see list of avaliable commands\n"); @@ -200,6 +188,127 @@ static Result(void) ClientCLI_commandExec(ClientCLI* self, str command, bool* st Return RESULT_VOID; } +static Result(void) ClientCLI_joinNewServer(ClientCLI* self){ + Deferral(8); + + // ask server address + printf("Enter server address (ip:port):\n"); + char server_addr_cstr[HOSTADDR_SIZE_MAX + 1]; + try_void(term_readLine(server_addr_cstr, sizeof(server_addr_cstr))); + str server_addr_str = str_from_cstr(server_addr_cstr); + str_trim(&server_addr_str, true); + + // ask server public key + printf("Enter server public key (RSA-Public-:):\n"); + char server_pk_cstr[PUBLIC_KEY_BASE64_SIZE_MAX + 1]; + try_void(term_readLine(server_pk_cstr, sizeof(server_pk_cstr))); + str server_pk_str = str_from_cstr(server_pk_cstr); + str_trim(&server_pk_str, true); + + printf("Connecting to server...\n"); + try_void(Client_connect(self->client, server_addr_cstr, server_pk_cstr)); + printf("Connection established\n"); + + str server_name = str_null; + str server_description = str_null; + try_void(Client_getServerName(self->client, &server_name)); + try_void(Client_getServerDescription(self->client, &server_description)); + try(Server* server, p, ClientCLI_saveServerInfo(self, + server_addr_str, server_pk_str, + server_name, server_description)); + + try_void(ClientCLI_showServerInfo(self, server)); + + Return RESULT_VOID; +} + +static Result(void) ClientCLI_selectServerFromCache(ClientCLI* self){ + Deferral(8); + + // lock servers cache + try_stderrcode(pthread_mutex_lock(&self->servers_cache_mutex)); + Defer(pthread_mutex_unlock(&self->servers_cache_mutex)); + + u32 servers_count = List_len(self->servers_cache_list, Server); + if(servers_count == 0){ + printf("No servers found in cache\n"); + Return RESULT_VOID; + } + + for(u32 id = 0; id < servers_count; id++){ + Server* row = &List_index(self->servers_cache_list, Server, id); + printf("[%02u] "FMT_str"\n", id, row->name_len, row->name); + } + + char buf[32]; + u32 id = -1; + while(true) { + printf("Type 'q' to cancel\n"); + printf("Select server (number): "); + try_void(term_readLine(buf, sizeof(buf))); + str input_line = str_from_cstr(buf); + str_trim(&input_line, true); + if(str_equals(input_line, STR("q"))){ + Return RESULT_VOID; + } + if(sscanf(buf, FMT_u32, &id) != 1){ + printf("ERROR: not a number\n"); + } + else if(id >= servers_count){ + printf("ERROR: not a server number: %u\n", id); + } + else break; + } + Server* server = &List_index(self->servers_cache_list, Server, id); + + printf("Connecting to '"FMT_str"'...\n", server->address_len, server->address); + try_void(Client_connect(self->client, server->address, server->pk_base64)); + printf("Connection established\n"); + + bool server_info_changed = false; + // update cached server name + str name = str_null; + try_void(Client_getServerName(self->client, &name)); + if(!str_equals(name, str_construct(server->name, server->name_len, true))){ + server_info_changed = true; + if(name.size > SERVER_NAME_SIZE_MAX) + name.size = SERVER_NAME_SIZE_MAX; + server->name_len = name.size; + memcpy(server->name, name.data, server->name_len); + } + // update cached server description + str desc = str_null; + try_void(Client_getServerDescription(self->client, &desc)); + if(!str_equals(desc, str_construct(server->desc, server->desc_len, true))){ + server_info_changed = true; + if(desc.size > SERVER_DESC_SIZE_MAX) + desc.size = SERVER_DESC_SIZE_MAX; + server->desc_len = desc.size; + memcpy(server->desc, desc.data, server->desc_len); + } + if(server_info_changed){ + try_void(idb_updateRow(self->db_servers_table, id, server)); + } + + try_void(ClientCLI_showServerInfo(self, server)); + + Return RESULT_VOID; +} + +static Result(void) ClientCLI_showServerInfo(ClientCLI* self, Server* server){ + Deferral(8); + (void)self; + + printf("Server Name: "FMT_str"\n", server->name_len, server->name); + printf("Host Address: "FMT_str"\n", server->address_len, server->address); + printf("Description:\n"FMT_str"\n\n", server->desc_len, server->desc); + printf("Public Key:\n" FMT_str"\n\n", server->pk_base64_len, server->pk_base64); + printf("Type 'register' if you don't have an account on the server.\n"); + printf("Type 'login' to authorize on the server.\n"); + + Return RESULT_VOID; +} + static Result(void) ClientCLI_openUserDB(ClientCLI* self){ Deferral(8); @@ -207,42 +316,105 @@ static Result(void) ClientCLI_openUserDB(ClientCLI* self){ Array(u8) user_data_key = Client_getUserDataKey(self->client); str user_db_dir = str_from_cstr(strcat_malloc("client-db", path_seps, username.data)); Defer(free(user_db_dir.data)); - try(self->user_db, p, idb_open(user_db_dir, user_data_key)); + try(self->db, p, idb_open(user_db_dir, user_data_key)); + + // load servers table + pthread_mutex_init(&self->servers_cache_mutex, NULL); + try(self->db_servers_table, p, idb_getOrCreateTable(self->db, STR("servers"), sizeof(Server))); + // load whole table to list + try(u64 servers_count, u, idb_getRowCount(self->db_servers_table)); + self->servers_cache_list = List_alloc(Server, servers_count); + try_void(idb_getRows(self->db_servers_table, 0, self->servers_cache_list.data, servers_count)); + self->servers_cache_list.size = sizeof(Server) * servers_count; + // build address-id map + HashMap_construct(&self->servers_addr_id_map, u64, NULL); + for(u64 id = 0; id < servers_count; id++){ + Server* row = &List_index(self->servers_cache_list, Server, id); + str key = str_construct(row->address, row->address_len, true); + if(!HashMap_tryPush(&self->servers_addr_id_map, key, &id)){ + Return RESULT_ERROR_FMT("duplicate server address '"FMT_str"'", key.size, key.data); + } + } Return RESULT_VOID; } -static Result(void) ClientCLI_saveServerInfo(ClientCLI* self, - cstr server_addr_cstr, cstr server_pk_base64, - str server_name, str server_description){ +static Result(Server*) ClientCLI_saveServerInfo(ClientCLI* self, + str addr, str pk_base64, str name, str desc){ Deferral(8); - ServerInfo si; - memset(&si, 0, sizeof(ServerInfo)); - + // create new server info + Server server; + memset(&server, 0, sizeof(Server)); // address - si.address_len = strlen(server_addr_cstr); - memcpy(si.address, server_addr_cstr, si.address_len); - si.address[si.address_len] = 0; - + if(addr.size > HOSTADDR_SIZE_MAX) + addr.size = HOSTADDR_SIZE_MAX; + server.address_len = addr.size; + memcpy(server.address, addr.data, server.address_len); // public key - si.pk_base64_len = strlen(server_pk_base64); - memcpy(si.pk_base64, server_addr_cstr, si.pk_base64_len); - si.pk_base64[si.pk_base64_len] = 0; - + if(pk_base64.size > PUBLIC_KEY_BASE64_SIZE_MAX) + pk_base64.size = PUBLIC_KEY_BASE64_SIZE_MAX; + server.pk_base64_len = pk_base64.size; + memcpy(server.pk_base64, pk_base64.data, server.pk_base64_len); // name - si.name_len = server_name.size; - memcpy(si.name, server_name.data, si.name_len); - si.name[si.name_len] = 0; - + if(name.size > SERVER_NAME_SIZE_MAX) + name.size = SERVER_NAME_SIZE_MAX; + server.name_len = name.size; + memcpy(server.name, name.data, server.name_len); // description - si.desc_len = server_name.size; - memcpy(si.desc, server_description.data, si.desc_len); - si.desc[si.desc_len] = 0; + if(desc.size > SERVER_DESC_SIZE_MAX) + desc.size = SERVER_DESC_SIZE_MAX; + server.desc_len = desc.size; + memcpy(server.desc, desc.data, server.desc_len); - // TODO: check server_address_id_cache_map - (void)self; - // TODO: save server info to user's db + // lock servers cache + try_stderrcode(pthread_mutex_lock(&self->servers_cache_mutex)); + Defer(pthread_mutex_unlock(&self->servers_cache_mutex)); + + // try find server id in cache + Server* cached_row_ptr = NULL; + u64* id_ptr = NULL; + id_ptr = HashMap_tryGetPtr(&self->servers_addr_id_map, addr); + if(id_ptr){ + // update existing server + u64 id = *id_ptr; + try_void(idb_updateRow(self->db_servers_table, id, &server)); + try_assert(id < List_len(self->servers_cache_list, Server)); + cached_row_ptr = &List_index(self->servers_cache_list, Server, id); + memcpy(cached_row_ptr, &server, sizeof(Server)); + } + else { + // push new server + try(u64 id, u, idb_pushRow(self->db_servers_table, &server)); + try_assert(id == List_len(self->servers_cache_list, Server)); + List_pushMany(&self->servers_cache_list, Server, &server, 1); + cached_row_ptr = &List_index(self->servers_cache_list, Server, id); + try_assert(HashMap_tryPush(&self->servers_addr_id_map, addr, &id)); + } + + Return RESULT_VALUE(p, cached_row_ptr); +} + +static Result(void) ClientCLI_register(ClientCLI* self){ + Deferral(8); + + u64 user_id = 0; + try_void(Client_register(self->client, &user_id)); + printf("Registered successfully\n"); + printf("user_id: "FMT_u64"\n", user_id); + // TODO: use user_id somewhere + + Return RESULT_VOID; +} + +static Result(void) ClientCLI_login(ClientCLI* self){ + Deferral(8); + + u64 user_id = 0, landing_channel_id = 0; + try_void(Client_login(self->client, &user_id, &landing_channel_id)); + printf("Authorized successfully\n"); + printf("user_id: "FMT_u64", landing_channel_id: "FMT_u64"\n", user_id, landing_channel_id); + // TODO: use user_id, landing_channel_id somewhere Return RESULT_VOID; } diff --git a/src/cli/ClientCLI/ClientCLI.h b/src/cli/ClientCLI/ClientCLI.h index 40d7565..5fc210e 100644 --- a/src/cli/ClientCLI/ClientCLI.h +++ b/src/cli/ClientCLI/ClientCLI.h @@ -1,10 +1,18 @@ #pragma once +#include +#include "tlibc/collections/HashMap.h" +#include "tlibc/collections/List.h" #include "tcp-chat/client.h" #include "db/idb.h" +#include "cli/ClientCLI/db_tables.h" typedef struct ClientCLI { Client* client; - IncrementalDB* user_db; + IncrementalDB* db; + Table* db_servers_table; + pthread_mutex_t servers_cache_mutex; + List(Server) servers_cache_list; // index is id + HashMap(u64) servers_addr_id_map; // key is server address } ClientCLI; void ClientCLI_construct(ClientCLI* self); diff --git a/src/cli/ClientCLI/db_tables.h b/src/cli/ClientCLI/db_tables.h index ca3df97..37409ba 100644 --- a/src/cli/ClientCLI/db_tables.h +++ b/src/cli/ClientCLI/db_tables.h @@ -2,13 +2,13 @@ #include "tcp-chat/common_constants.h" #include "tlibc/time.h" -typedef struct ServerInfo { - char address[HOSTADDR_SIZE_MAX + 1]; +typedef struct Server { u16 address_len; - char pk_base64[PUBLIC_KEY_BASE64_SIZE_MAX + 1]; + char address[HOSTADDR_SIZE_MAX + 1]; u32 pk_base64_len; - char name[CHANNEL_NAME_SIZE_MAX + 1]; + char pk_base64[PUBLIC_KEY_BASE64_SIZE_MAX + 1]; u16 name_len; - char desc[CHANNEL_DESC_SIZE_MAX + 1]; + char name[SERVER_NAME_SIZE_MAX + 1]; u16 desc_len; -} ATTRIBUTE_ALIGNED(16*1024) ServerInfo; + char desc[SERVER_DESC_SIZE_MAX + 1]; +} ATTRIBUTE_ALIGNED(16*1024) Server; diff --git a/src/client/ServerConnection.c b/src/client/ServerConnection.c index 896a2ef..9833bb8 100644 --- a/src/client/ServerConnection.c +++ b/src/client/ServerConnection.c @@ -39,6 +39,7 @@ Result(ServerConnection*) ServerConnection_open(cstr server_addr_cstr, cstr serv // connect to server address try(Socket _s, i, socket_open_TCP()); + // TODO: client socket waits infinitely if server is paused on breakpoint 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); diff --git a/src/client/client.c b/src/client/client.c index 124d5dd..331471f 100644 --- a/src/client/client.c +++ b/src/client/client.c @@ -1,4 +1,5 @@ #include "client/client_internal.h" +#include "client/requests/requests.h" void Client_free(Client* self){ if(!self) @@ -63,17 +64,58 @@ Array(u8) Client_getUserDataKey(Client* client){ } Result(void) Client_getServerName(Client* self, str* out_name){ - if(self->server_connection == NULL){ - return RESULT_ERROR("Client is not connected to a server", false); - } + Deferral(1); + try_assert(self != NULL); + try_assert(self->server_connection != NULL); + *out_name = self->server_connection->server_name; - return RESULT_VOID; + + Return RESULT_VOID; } Result(void) Client_getServerDescription(Client* self, str* out_desc){ - if(self->server_connection == NULL){ - return RESULT_ERROR("Client is not connected to a server", false); - } + Deferral(1); + try_assert(self != NULL); + try_assert(self->server_connection != NULL); + *out_desc = self->server_connection->server_description; - return RESULT_VOID; + + Return RESULT_VOID; +} + +Result(void) Client_register(Client* self, u64* out_user_id){ + Deferral(1); + try_assert(self != NULL); + try_assert(self->server_connection != NULL); + + PacketHeader req_head, res_head; + RegisterRequest req; + RegisterResponse res; + // TODO: hash token with server public key + try_void(RegisterRequest_tryConstruct(&req, &req_head, self->username, self->token)); + try_void(sendRequest(&self->server_connection->sock, &req_head, &req)); + try_void(recvResponse(&self->server_connection->sock, &res_head, &res, PacketType_RegisterResponse)); + self->server_connection->user_id = res.user_id; + *out_user_id = res.user_id; + + Return RESULT_VOID; +} + +Result(void) Client_login(Client* self, u64* out_user_id, u64* out_landing_channel_id){ + Deferral(1); + try_assert(self != NULL); + try_assert(self->server_connection != NULL); + + PacketHeader req_head, res_head; + LoginRequest req; + LoginResponse res; + // TODO: hash token with server public key + try_void(LoginRequest_tryConstruct(&req, &req_head, self->username, self->token)); + try_void(sendRequest(&self->server_connection->sock, &req_head, &req)); + try_void(recvResponse(&self->server_connection->sock, &res_head, &res, PacketType_LoginResponse)); + self->server_connection->user_id = res.user_id; + *out_user_id = res.user_id; + *out_landing_channel_id = res.landing_channel_id; + + Return RESULT_VOID; } diff --git a/src/client/client_internal.h b/src/client/client_internal.h index 8bcec88..21ca694 100644 --- a/src/client/client_internal.h +++ b/src/client/client_internal.h @@ -23,6 +23,7 @@ typedef struct ServerConnection { EncryptedSocketTCP sock; str server_name; str server_description; + u64 user_id; } ServerConnection; /// @param server_addr_cstr diff --git a/src/config.c b/src/config.c index a970e21..5dab1d8 100644 --- a/src/config.c +++ b/src/config.c @@ -27,9 +27,7 @@ Result(void) config_findValue(str config_str, str key, str* value, bool throwNot } if(throwNotFoundError){ - char* key_cstr = str_copy(key).data; - char* err_msg = sprintf_malloc("can't find key '%s'", key_cstr); - free(key_cstr); + char* err_msg = sprintf_malloc("can't find key '"FMT_str"'", key.size, key.data); return RESULT_ERROR(err_msg, true); } return RESULT_VOID; diff --git a/src/cryptography/AES.c b/src/cryptography/AES.c index c92c6b1..976d0c1 100755 --- a/src/cryptography/AES.c +++ b/src/cryptography/AES.c @@ -103,9 +103,10 @@ Result(u32) AESBlockDecryptor_decrypt(AESBlockDecryptor* ptr, Array(u8) src, Array(u8) dst) { Deferral(4); - try_assert(src.size >= AESBlockEncryptor_calcDstSize(0)); + u32 overhead_size = AESBlockEncryptor_calcDstSize(0); + try_assert(src.size >= overhead_size); try_assert(src.size % 16 == 0 && "src must be array of 16-byte blocks"); - try_assert(dst.size >= src.size); + try_assert(dst.size >= src.size - overhead_size); // read IV from the beginning of src __Array_readNext(ptr->iv, &src, __AES_BLOCK_IV_SIZE); diff --git a/src/db/idb.c b/src/db/idb.c index 135c717..abd5b32 100644 --- a/src/db/idb.c +++ b/src/db/idb.c @@ -86,6 +86,9 @@ static Result(void) Table_writeHeader(Table* t){ try_void(file_seek(t->table_file, 0, SeekOrigin_Start)); // write header try_void(file_writeStructs(t->table_file, &t->header, sizeof(t->header), 1)); + // TODO: add more fflush calls + fflush(t->table_file); + fflush(t->changes_file); Return RESULT_VOID; } @@ -187,6 +190,7 @@ Result(IncrementalDB*) idb_open(str db_dir, NULLABLE(Array(u8) aes_key)){ if(aes_key.size != 0){ db->aes_key = Array_copy(aes_key); + //TODO: validate aes encryption key } db->db_dir = str_copy(db_dir); diff --git a/src/network/tcp-chat-protocol/v1.c b/src/network/tcp-chat-protocol/v1.c index b2f54b9..ef7a2a2 100644 --- a/src/network/tcp-chat-protocol/v1.c +++ b/src/network/tcp-chat-protocol/v1.c @@ -93,7 +93,6 @@ Result(void) LoginRequest_tryConstruct(LoginRequest *ptr, PacketHeader* header, Return RESULT_ERROR(username_check_error.data, false); } memcpy(ptr->username, username.data, username.size); - ptr->username[username.size] = 0; try_assert(token.size == sizeof(ptr->token)); memcpy(ptr->token, token.data, token.size); @@ -123,7 +122,6 @@ Result(void) RegisterRequest_tryConstruct(RegisterRequest *ptr, PacketHeader* he Return RESULT_ERROR(username_check_error.data, false); } memcpy(ptr->username, username.data, username.size); - ptr->username[username.size] = 0; try_assert(token.size == sizeof(ptr->token)); memcpy(ptr->token, token.data, token.size); diff --git a/src/server/db_tables.h b/src/server/db_tables.h index 719af13..336f957 100644 --- a/src/server/db_tables.h +++ b/src/server/db_tables.h @@ -3,15 +3,15 @@ #include "tlibc/time.h" typedef struct User { - char name[USERNAME_SIZE_MAX + 1]; // null-terminated u16 name_len; + char name[USERNAME_SIZE_MAX + 1]; // null-terminated u8 token_hash[PASSWORD_HASH_SIZE]; // token is hashed again on server side DateTime registration_time; } ATTRIBUTE_ALIGNED(256) User; typedef struct Channel { - char name[CHANNEL_NAME_SIZE_MAX + 1]; u16 name_len; - char desc[CHANNEL_DESC_SIZE_MAX + 1]; + char name[CHANNEL_NAME_SIZE_MAX + 1]; u16 desc_len; + char desc[CHANNEL_DESC_SIZE_MAX + 1]; } ATTRIBUTE_ALIGNED(4*1024) Channel; diff --git a/src/server/request_handlers/Login.c b/src/server/request_handlers/Login.c index a1609d3..449cc85 100644 --- a/src/server/request_handlers/Login.c +++ b/src/server/request_handlers/Login.c @@ -16,7 +16,7 @@ declare_RequestHandler(Login) if(conn->authorized){ try_void(sendErrorMessage(server, log_ctx, conn, res_head, LogSeverity_Warn, - STR("is logged in already") + STR("is authorized in already") )); Return RESULT_VOID; } @@ -50,7 +50,7 @@ declare_RequestHandler(Login) if(id_ptr == NULL){ try_void(sendErrorMessage_f(server, log_ctx, conn, res_head, LogSeverity_Warn, - "Username '%s' is not registered\n", + "Username '%s' is not registered", username_str.data )); Return RESULT_VOID; @@ -58,7 +58,7 @@ declare_RequestHandler(Login) u64 user_id = *id_ptr; // get user by id - try_assert(List_len(server->users_cache_list, User) < user_id); + try_assert(user_id < List_len(server->users_cache_list, User)); User* u = &List_index(server->users_cache_list, User, user_id); // validate token hash diff --git a/src/server/request_handlers/Register.c b/src/server/request_handlers/Register.c index 1d92769..e47b75b 100644 --- a/src/server/request_handlers/Register.c +++ b/src/server/request_handlers/Register.c @@ -13,6 +13,14 @@ declare_RequestHandler(Register) try_void(PacketHeader_validateContentSize(req_head, sizeof(req))); try_void(EncryptedSocketTCP_recvStruct(&conn->sock, &req)); + if(conn->authorized){ + try_void(sendErrorMessage(server, log_ctx, conn, res_head, + LogSeverity_Warn, + STR("is authorized in already") + )); + Return RESULT_VOID; + } + // validate username str username_str = str_null; str username_check_error = validateUsername_cstr(req.username, &username_str); @@ -27,13 +35,17 @@ declare_RequestHandler(Register) // lock users cache try_stderrcode(pthread_mutex_lock(&server->users_cache_mutex)); bool unlocked_users_cache_mutex = false; - Defer(if(!unlocked_users_cache_mutex) pthread_mutex_unlock(&server->users_cache_mutex)); + // unlock mutex on error catch + Defer( + if(!unlocked_users_cache_mutex) + pthread_mutex_unlock(&server->users_cache_mutex) + ); // check if name is taken if(HashMap_tryGetPtr(&server->users_name_id_map, username_str) != NULL){ try_void(sendErrorMessage_f(server, log_ctx, conn, res_head, LogSeverity_Warn, - "Username'%s' already exists\n", + "Username'%s' already exists", username_str.data)); Return RESULT_VOID; } @@ -42,9 +54,8 @@ declare_RequestHandler(Register) User user; memset(&user, 0, sizeof(User)); - memcpy(user.name, username_str.data, username_str.size); user.name_len = username_str.size; - user.name[user.name_len] = 0; + memcpy(user.name, username_str.data, user.name_len); hash_password( Array_construct_size(req.token, sizeof(req.token)), @@ -56,8 +67,8 @@ declare_RequestHandler(Register) // save new user to db and cache try(u64 user_id, u, idb_pushRow(server->db_users_table, &user)); - try_assert(List_len(server->users_cache_list, User) == user_id); - List_push(&server->users_cache_list, User, user); + try_assert(user_id == List_len(server->users_cache_list, User)); + List_pushMany(&server->users_cache_list, User, &user, 1); try_assert(HashMap_tryPush(&server->users_name_id_map, username_str, &user_id)); // manually unlock mutex diff --git a/src/server/server.c b/src/server/server.c index f08706b..2122f3a 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -33,29 +33,29 @@ Result(Server*) Server_create(str config_str, void* logger, LogFunction_t log_fu Deferral(16); cstr log_ctx = "ServerInit"; - Server* server = (Server*)malloc(sizeof(Server)); - memset(server, 0, sizeof(Server)); + Server* self = (Server*)malloc(sizeof(Server)); + memset(self, 0, sizeof(Server)); bool success = false; - Defer(if(!success) Server_free(server)); + Defer(if(!success) Server_free(self)); - server->logger = logger; - server->log_func = log_func; + self->logger = logger; + self->log_func = log_func; logDebug(log_ctx, "parsing config"); // parse name str tmp_str = str_null; try_void(config_findValue(config_str, STR("name"), &tmp_str, true)); - server->name = str_copy(tmp_str); + self->name = str_copy(tmp_str); // parse description try_void(config_findValue(config_str, STR("description"), &tmp_str, true)); - server->description = str_copy(tmp_str); + self->description = str_copy(tmp_str); // parse landing_channel_id try_void(config_findValue(config_str, STR("landing_channel_id"), &tmp_str, true)); char* lci_cstr = str_copy(tmp_str).data; Defer(free(lci_cstr)); - if(sscanf(lci_cstr, FMT_u64, &server->landing_channel_id) != 1){ + if(sscanf(lci_cstr, FMT_u64, &self->landing_channel_id) != 1){ Return RESULT_ERROR("can't parse 'landing_channel_id' value as number", false); } @@ -63,7 +63,7 @@ Result(Server*) Server_create(str config_str, void* logger, LogFunction_t log_fu try_void(config_findValue(config_str, STR("local_address"), &tmp_str, true)); char* local_end_cstr = str_copy(tmp_str).data; Defer(free(local_end_cstr)); - try_void(EndpointIPv4_parse(local_end_cstr, &server->local_end)); + try_void(EndpointIPv4_parse(local_end_cstr, &self->local_end)); // parse rsa_private_key try_void(config_findValue(config_str, STR("rsa_private_key"), &tmp_str, true)); @@ -75,8 +75,8 @@ Result(Server*) Server_create(str config_str, void* logger, LogFunction_t log_fu char* pk_base64_cstr = str_copy(tmp_str).data; Defer(free(pk_base64_cstr)); - try_void(RSA_parsePrivateKey_base64(sk_base64_cstr, &server->rsa_sk)); - try_void(RSA_parsePublicKey_base64(pk_base64_cstr, &server->rsa_pk)); + try_void(RSA_parsePrivateKey_base64(sk_base64_cstr, &self->rsa_sk)); + try_void(RSA_parsePublicKey_base64(pk_base64_cstr, &self->rsa_pk)); // parse db_aes_key try_void(config_findValue(config_str, STR("db_aes_key"), &tmp_str, true)); @@ -86,27 +86,30 @@ Result(Server*) Server_create(str config_str, void* logger, LogFunction_t log_fu // parse db_dir and open db try_void(config_findValue(config_str, STR("db_dir"), &tmp_str, true)); - try(server->db, p, idb_open(tmp_str, db_aes_key)); + try(self->db, p, idb_open(tmp_str, db_aes_key)); // build users cache - pthread_mutex_init(&server->users_cache_mutex, NULL); - try(server->db_users_table, p, idb_getOrCreateTable(server->db, STR("users"), sizeof(User))); - try(u64 users_count, u, idb_getRowCount(server->db_users_table)); - server->users_cache_list = List_alloc(User, users_count); - HashMap_construct(&server->users_name_id_map, u64, NULL); + logDebug(log_ctx, "loading users..."); + pthread_mutex_init(&self->users_cache_mutex, NULL); + try(self->db_users_table, p, idb_getOrCreateTable(self->db, STR("users"), sizeof(User))); // load whole table to list - try_void(idb_getRows(server->db_users_table, 0, server->users_cache_list.data, users_count)); + try(u64 users_count, u, idb_getRowCount(self->db_users_table)); + self->users_cache_list = List_alloc(User, users_count); + try_void(idb_getRows(self->db_users_table, 0, self->users_cache_list.data, users_count)); + self->users_cache_list.size = sizeof(User) * users_count; // build name-id map + HashMap_construct(&self->users_name_id_map, u64, NULL); for(u64 id = 0; id < users_count; id++){ - User* u = &List_index(server->users_cache_list, User, id); - str key = str_construct(u->name, u->name_len, true); - if(!HashMap_tryPush(&server->users_name_id_map, key, &id)){ - Return RESULT_ERROR_FMT("duplicate user name '%s'", u->name); + User* row = &List_index(self->users_cache_list, User, id); + str key = str_construct(row->name, row->name_len, true); + if(!HashMap_tryPush(&self->users_name_id_map, key, &id)){ + Return RESULT_ERROR_FMT("duplicate user name '"FMT_str"'", key.size, key.data); } } + logDebug(log_ctx, "loaded "FMT_u64" users", users_count); success = true; - Return RESULT_VALUE(p, server); + Return RESULT_VALUE(p, self); } #undef LOGGER @@ -155,7 +158,7 @@ static void* handleConnection(void* _args){ if(r.error){ Error_addCallPos(r.error, ErrorCallPos_here()); str e_str = Error_toStr(r.error); - logError(log_ctx, "%s", e_str.data); + logError(log_ctx, FMT_str, e_str.size, e_str.data); str_free(e_str); Error_free(r.error); } diff --git a/src/server/server_internal.h b/src/server/server_internal.h index abc79d7..55e23c4 100644 --- a/src/server/server_internal.h +++ b/src/server/server_internal.h @@ -28,8 +28,8 @@ typedef struct Server { IncrementalDB* db; Table* db_users_table; pthread_mutex_t users_cache_mutex; - List(User) users_cache_list; // index is id - HashMap(u64) users_name_id_map; //key is user name + List(User) users_cache_list; // index is id + HashMap(u64) users_name_id_map; // key is user name } Server;