diff --git a/.vscode/launch.json b/.vscode/launch.json index f03bfb9..d8957a8 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 4257943..89aab2b 160000 --- a/dependencies/tlibc +++ b/dependencies/tlibc @@ -1 +1 @@ -Subproject commit 425794361bc52240bb50001becbb5fb6e9ebcf20 +Subproject commit 89aab2b5bffd46ec0538a5e7c2f1674d59d5677a diff --git a/src/ClientCLI/ClientCLI.c b/src/ClientCLI/ClientCLI.c new file mode 100644 index 0000000..28c2608 --- /dev/null +++ b/src/ClientCLI/ClientCLI.c @@ -0,0 +1,243 @@ +#include "ClientCLI/ClientCLI.h" +#include "ClientCLI/db_tables.h" +#include "term.h" +#include "common_constants.h" +#include "tlibc/time.h" +#include "tlibc/filesystem.h" + +static const str greeting_art = STR( + " ^,,^ ╱|\n" + " ( •·•) Meum! (o.o`7\n" + " / ` | Meum... |`˜ \\\n" + "\\(_,J J L l`,)/\n" +); + +static const str farewell_art = STR( + " ^,,^ ╱|\n" + " ( -.-) (>,<`7\n" + " / ` | Goodbye! |`˜ \\\n" + "\\(_,J J L l`,)/\n" +); + + +#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_openUserDB(ClientCLI* self); +static Result(void) ClientCLI_saveServerInfo(ClientCLI* self, cstr server_addr_cstr, cstr server_pk_base64); + + +void ClientCLI_destroy(ClientCLI* self){ + if(!self) + return; + Client_free(self->client); + idb_close(self->user_db); +} +void ClientCLI_construct(ClientCLI* self){ + self->client = NULL; + self->user_db = NULL; +} + +Result(void) ClientCLI_run(ClientCLI* self) { + Deferral(16); + + try_void(term_init()); + term_clear(); + printf("%s\n", greeting_art.data); + + // create Client + str username = str_null, password = str_null; + try_void(ClientCLI_askUserNameAndPassword(&username, &password)); + Defer( + str_free(username); + str_free(password); + ); + Client_free(self->client); + try(self->client, p, Client_create(username, password)); + memset(password.data, 0, password.size); + + // init db + try_void(ClientCLI_openUserDB(self)); + + char input_buf[1024]; + str command_input = str_null; + bool stop = false; + while(!stop){ + sleepMsec(50); + printf("> "); + try_void(term_readLine(input_buf, sizeof(input_buf))); + + command_input = str_from_cstr(input_buf); + str_trim(&command_input, true); + if(command_input.size == 0) + continue; + + ResultVar(void) com_result = ClientCLI_commandExec(self, command_input, &stop); + if(com_result.error){ + str e_str = Error_toStr(com_result.error); + printf("%s\n", e_str.data); + str_free(e_str); + Error_free(com_result.error); + } + } + + Return RESULT_VOID; +} + +static Result(void) ClientCLI_askUserNameAndPassword(str* username_out, str* password_out){ + Deferral(8); + bool success = false; + + // ask username + Array(char) username_buf = Array_alloc_size(128); + Defer(if(!success) Array_free(username_buf)); + str username = str_null; + while(true) { + printf("username: "); + try_void(term_readLine(username_buf.data, username_buf.size)); + username = str_from_cstr(username_buf.data); + str_trim(&username, true); + 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; + } + + // ask password + Array(char) password_buf = Array_alloc_size(128); + Defer(if(!success) Array_free(password_buf)); + str password = str_null; + while(true) { + printf("password: "); + // TODO: hide password + try_void(term_readLineHidden(password_buf.data, password_buf.size)); + password = str_from_cstr(password_buf.data); + str_trim(&password, true); + 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; + } + + *username_out = username; + *password_out = password; + success = true; + Return RESULT_VOID; +} + +static Result(void) ClientCLI_commandExec(ClientCLI* self, str command, bool* stop){ + Deferral(64); + + if(is_alias("q") || is_alias("quit") || is_alias("exit")){ + printf("%s\n", farewell_art.data); + *stop = true; + } + else if(is_alias("clear")){ + term_clear(); + } + 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" + ); + } + 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 + // printf("server name: %s\n", client->server_connection->server_name.data); + // printf("server description: %s\n", client->server_connection->server_description.data); + + + try_void(ClientCLI_saveServerInfo(self, server_addr_cstr, server_pk_cstr)); + // TODO: ask in loop: log in / register + + //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"); + } + + Return RESULT_VOID; +} + +static Result(void) ClientCLI_openUserDB(ClientCLI* self){ + Deferral(8); + + str username = Client_getUserName(self->client); + 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)); + + Return RESULT_VOID; +} + +static Result(void) ClientCLI_saveServerInfo(ClientCLI* self, cstr server_addr_cstr, cstr server_pk_base64){ + Deferral(8); + + ServerInfo si; + memset(&si, 0, sizeof(ServerInfo)); + + // address + si.address_len = strlen(server_addr_cstr); + memcpy(si.address, server_addr_cstr, si.address_len); + si.address[si.address_len] = 0; + + // 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; + + // name + str server_name = str_null; + try_void(Client_getServerName(self->client, &server_name)); + si.name_len = server_name.size; + memcpy(si.name, server_name.data, si.name_len); + si.name[si.name_len] = 0; + + // description + str server_description = str_null; + try_void(Client_getServerName(self->client, &server_description)); + si.desc_len = server_name.size; + memcpy(si.desc, server_description.data, si.desc_len); + si.desc[si.desc_len] = 0; + + // TODO: check cred->server_address_id_cache_map + + // TODO: save server info to user's db + + Return RESULT_VOID; +} diff --git a/src/ClientCLI/ClientCLI.h b/src/ClientCLI/ClientCLI.h new file mode 100644 index 0000000..72f8cc3 --- /dev/null +++ b/src/ClientCLI/ClientCLI.h @@ -0,0 +1,12 @@ +#pragma once +#include "client/client.h" +#include "db/idb.h" + +typedef struct ClientCLI { + Client* client; + IncrementalDB* user_db; +} ClientCLI; + +void ClientCLI_construct(ClientCLI* self); +void ClientCLI_destroy(ClientCLI* self); +Result(void) ClientCLI_run(ClientCLI* self); diff --git a/src/ClientCLI/db_tables.h b/src/ClientCLI/db_tables.h new file mode 100644 index 0000000..7861143 --- /dev/null +++ b/src/ClientCLI/db_tables.h @@ -0,0 +1,14 @@ +#pragma once +#include "common_constants.h" +#include "tlibc/time.h" + +typedef struct ServerInfo { + char address[HOSTADDR_SIZE_MAX + 1]; + u16 address_len; + char pk_base64[PUBLIC_KEY_BASE64_SIZE_MAX + 1]; + u32 pk_base64_len; + char name[CHANNEL_NAME_SIZE_MAX + 1]; + u16 name_len; + char desc[CHANNEL_DESC_SIZE_MAX + 1]; + u16 desc_len; +} ATTRIBUTE_ALIGNED(16*1024) ServerInfo; diff --git a/src/client/ClientCredentials.c b/src/client/ClientCredentials.c deleted file mode 100644 index 4f7c5fa..0000000 --- a/src/client/ClientCredentials.c +++ /dev/null @@ -1,44 +0,0 @@ -#include "client.h" -#include "tlibc/collections/List.h" - -void ClientCredentials_destroy(ClientCredentials* cred){ - if(!cred) - return; - free(cred->username.data); - free(cred->user_data_key.data); - free(cred->token.data); -} - - -Result(void) ClientCredentials_tryConstruct(ClientCredentials* cred, - str username, str password) -{ - Deferral(8); - - memset(cred, 0, sizeof(ClientCredentials)); - bool success = false; - Defer(if(!success) ClientCredentials_destroy(cred)); - - cred->username = str_copy(username); - - // concat password and username - List(u8) data_to_hash = List_alloc_size(password.size + username.size + PASSWORD_HASH_SIZE); - Defer(free(data_to_hash.data)); - List_push_size(&data_to_hash, password.data, password.size); - List_push_size(&data_to_hash, username.data, username.size); - - // lvl 1 hash - is used as AES key for user data - cred->user_data_key = Array_alloc(u8, PASSWORD_HASH_SIZE); - hash_password(List_castTo_Array(data_to_hash), cred->user_data_key.data, PASSWORD_HASH_LVL_ROUNDS); - // concat lvl 1 hash to data_to_hash - List_push_size(&data_to_hash, cred->user_data_key.data, cred->user_data_key.size); - // lvl 2 hash - is used for authentification - cred->token = Array_alloc(u8, PASSWORD_HASH_SIZE); - hash_password(List_castTo_Array(data_to_hash), cred->token.data, PASSWORD_HASH_LVL_ROUNDS); - - AESBlockEncryptor_construct(&cred->user_data_aes_enc, cred->user_data_key, AESBlockEncryptor_DEFAULT_CLASS); - AESBlockDecryptor_construct(&cred->user_data_aes_dec, cred->user_data_key, AESBlockDecryptor_DEFAULT_CLASS); - - success = true; - Return RESULT_VOID; -} diff --git a/src/client/ServerConnection.c b/src/client/ServerConnection.c index 51c036d..896a2ef 100644 --- a/src/client/ServerConnection.c +++ b/src/client/ServerConnection.c @@ -1,4 +1,4 @@ -#include "client.h" +#include "client_internal.h" #include "requests/requests.h" void ServerConnection_close(ServerConnection* conn){ @@ -6,44 +6,14 @@ void ServerConnection_close(ServerConnection* conn){ return; RSA_destroyPublicKey(&conn->server_pk); EncryptedSocketTCP_destroy(&conn->sock); - free(conn->session_key.data); - free(conn->name.data); - free(conn->description.data); + Array_free(conn->session_key); + str_free(conn->server_name); + str_free(conn->server_description); free(conn); } -/// @brief -/// @param server_link_cstr address:port:public_key -/// @return -Result(void) ServerLink_parse(cstr server_link_cstr, EndpointIPv4* server_end_out, br_rsa_public_key* server_key_out){ - Deferral(8); - str server_link_str = str_from_cstr(server_link_cstr); - - // parse address and port - i32 sep_pos = str_seekChar(server_link_str, ':', 0); - if(sep_pos == -1){ - Return RESULT_ERROR_FMT("server link is invalid: %s", server_link_cstr); - } - *server_end_out = EndpointIPv4_INVALID; - try_void(EndpointIPv4_parse(server_link_cstr, server_end_out)); - if(EndpointIPv4_is_invalid(*server_end_out)){ - Return RESULT_ERROR_FMT("server address or port is invalid: %s", server_link_cstr); - } - - // parse public key - sep_pos = str_seekChar(server_link_str, ':', sep_pos + 1); - if(sep_pos == -1){ - Return RESULT_ERROR_FMT("server link is invalid: %s", server_link_cstr); - } - str server_key_str = str_sliceAfter(server_link_str, sep_pos + 1); - char* server_key_cstr = str_copy(server_key_str).data; - Defer(free(server_key_cstr)); - try_void(RSA_parsePublicKey_base64(server_key_cstr, server_key_out)); - - Return RESULT_VOID; -} - -Result(ServerConnection*) ServerConnection_open(cstr server_link_cstr){ +Result(ServerConnection*) ServerConnection_open(cstr server_addr_cstr, cstr server_pk_base64) +{ Deferral(16); ServerConnection* conn = (ServerConnection*)malloc(sizeof(ServerConnection)); @@ -51,7 +21,14 @@ Result(ServerConnection*) ServerConnection_open(cstr server_link_cstr){ bool success = false; Defer(if(!success) ServerConnection_close(conn)); - try_void(ServerLink_parse(server_link_cstr, &conn->server_end, &conn->server_pk)); + // TODO: parse domain name and get ip from it + conn->server_end = EndpointIPv4_INVALID; + try_void(EndpointIPv4_parse(server_addr_cstr, &conn->server_end)); + if(EndpointIPv4_is_invalid(conn->server_end)){ + Return RESULT_ERROR_FMT("server address or port is invalid: %s", server_addr_cstr); + } + + try_void(RSA_parsePublicKey_base64(server_pk_base64, &conn->server_pk)); RSAEncryptor_construct(&conn->rsa_enc, &conn->server_pk); conn->session_key = Array_alloc_size(AES_SESSION_KEY_SIZE); @@ -82,6 +59,21 @@ Result(ServerConnection*) ServerConnection_open(cstr server_link_cstr){ conn->session_id = server_handshake.session_id; // get server name + try_void(ServerConnection_requestServerName(conn)); + // get server description + try_void(ServerConnection_requestServerDescription(conn)); + + success = true; + Return RESULT_VALUE(p, conn); +} + +Result(void) ServerConnection_requestServerName(ServerConnection* conn){ + if(conn == NULL){ + return RESULT_ERROR("Client is not connected to a server", false); + } + Deferral(4); + + PacketHeader req_header, res_header; ServerPublicInfoRequest public_info_req; ServerPublicInfoResponse public_info_res; ServerPublicInfoRequest_construct(&public_info_req, &req_header, @@ -89,16 +81,26 @@ Result(ServerConnection*) ServerConnection_open(cstr server_link_cstr){ try_void(sendRequest(&conn->sock, &req_header, &public_info_req)); try_void(recvResponse(&conn->sock, &res_header, &public_info_res, PacketType_ServerPublicInfoResponse)); - try_void(recvStr(&conn->sock, public_info_res.data_size, &conn->name)); + try_void(recvStr(&conn->sock, public_info_res.data_size, &conn->server_name)); - // get server description + Return RESULT_VOID; +} + +Result(void) ServerConnection_requestServerDescription(ServerConnection* conn){ + if(conn == NULL){ + return RESULT_ERROR("Client is not connected to a server", false); + } + Deferral(4); + + PacketHeader req_header, res_header; + ServerPublicInfoRequest public_info_req; + ServerPublicInfoResponse public_info_res; ServerPublicInfoRequest_construct(&public_info_req, &req_header, ServerPublicInfo_Description); try_void(sendRequest(&conn->sock, &req_header, &public_info_req)); try_void(recvResponse(&conn->sock, &res_header, &public_info_res, PacketType_ServerPublicInfoResponse)); - try_void(recvStr(&conn->sock, public_info_res.data_size, &conn->description)); + try_void(recvStr(&conn->sock, public_info_res.data_size, &conn->server_description)); - success = true; - Return RESULT_VALUE(p, conn); -} + Return RESULT_VOID; +} \ No newline at end of file diff --git a/src/client/client.c b/src/client/client.c index 8352c8b..124d5dd 100644 --- a/src/client/client.c +++ b/src/client/client.c @@ -1,169 +1,79 @@ -#include "client.h" -#include "term.h" -#include "tlibc/time.h" -#include "network/tcp-chat-protocol/v1.h" +#include "client/client_internal.h" -static const str greeting_art = STR( - " ^,,^ ╱|\n" - " ( •·•) Meum! (o.o`7\n" - " / ` | Meum... |`˜ \\\n" - "\\(_,J J L l`,)/\n" -); - -static const str farewell_art = STR( - " ^,,^ ╱|\n" - " ( -.-) (>,<`7\n" - " / ` | Goodbye! |`˜ \\\n" - "\\(_,J J L l`,)/\n" -); - -Result(void) Client_createFromConfig(cstr config_path){ - Deferral(16); - - Client* client = (Client*)malloc(sizeof(Client)); - memset(client, 0, sizeof(Client)); - bool success = false; - Defer(if(!success) Client_free(client)); - - (void)config_path; - - success = true; - Return RESULT_VALUE(p, client); -} - -void Client_free(Client* client){ - if(!client) +void Client_free(Client* self){ + if(!self) return; - ClientCredentials_destroy(&client->cred); - ServerConnection_close(client->server_connection); - free(client); + str_free(self->username); + Array_free(self->token); + Array_free(self->user_data_key); + ServerConnection_close(self->server_connection); + free(self); } -static Result(void) commandExec(Client* client, str command, bool* stop); - -static Result(void) askUserNameAndPassword(ClientCredentials* cred){ - Deferral(8); - - char username_buf[128]; - str username = str_null; - while(true) { - printf("username: "); - try_void(term_readLine(username_buf, sizeof(username_buf))); - username = str_from_cstr(username_buf); - str_trim(&username, true); - 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[128]; - str password = str_null; - while(true) { - printf("password: "); - // TODO: hide password - try_void(term_readLineHidden(password_buf, sizeof(password_buf))); - password = str_from_cstr(password_buf); - str_trim(&password, true); - 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_void(ClientCredentials_tryConstruct(cred, username, password)); - Return RESULT_VOID; -} - -Result(void) Client_run(Client* client) { +Result(Client*) Client_create(str username, str password){ Deferral(16); - try_void(term_init()); - fputs(greeting_art.data, stdout); - try_void(askUserNameAndPassword(&client->cred)); + Client* self = (Client*)malloc(sizeof(Client)); + memset(self, 0, sizeof(Client)); + bool success = false; + Defer(if(!success) Client_free(self)); - Array(char) input_buf = Array_alloc(char, 10000); - Defer(free(input_buf.data)); - str command_input = str_null; - bool stop = false; - while(!stop){ - sleepMsec(50); - fputs("> ", stdout); - try_void(term_readLine(input_buf.data, input_buf.size)); - - command_input = str_from_cstr(input_buf.data); - str_trim(&command_input, true); - if(command_input.size == 0) - continue; - - ResultVar(void) com_result = commandExec(client, command_input, &stop); - if(com_result.error){ - str e_str = Error_toStr(com_result.error); - printf("%s\n", e_str.data); - free(e_str.data); - Error_free(com_result.error); - } - } + self->username = str_copy(username); + // concat password and username + List(u8) data_to_hash = List_alloc_size(password.size + username.size + PASSWORD_HASH_SIZE); + Defer(free(data_to_hash.data)); + List_push_size(&data_to_hash, password.data, password.size); + List_push_size(&data_to_hash, username.data, username.size); + + // lvl 1 hash - is used as AES key for user data + self->user_data_key = Array_alloc(u8, PASSWORD_HASH_SIZE); + hash_password(List_castTo_Array(data_to_hash), self->user_data_key.data, PASSWORD_HASH_LVL_ROUNDS); + // concat lvl 1 hash to data_to_hash + List_push_size(&data_to_hash, self->user_data_key.data, self->user_data_key.size); + // lvl 2 hash - is used for authentification + self->token = Array_alloc(u8, PASSWORD_HASH_SIZE); + // TODO: generate different token for each server + hash_password(List_castTo_Array(data_to_hash), self->token.data, PASSWORD_HASH_LVL_ROUNDS); + + success = true; + Return RESULT_VALUE(p, self); +} + +Result(void) Client_connect(Client* self, cstr server_addr_cstr, cstr server_pk_base64){ + Deferral(8); + Client_disconnect(self); + try(self->server_connection, p, + ServerConnection_open(server_addr_cstr, server_pk_base64)); Return RESULT_VOID; } -#define is_alias(LITERAL) str_equals(command, STR(LITERAL)) - -static Result(void) commandExec(Client* client, str command, bool* stop){ - Deferral(64); - char answer_buf[10000]; - if(is_alias("q") || is_alias("quit") || is_alias("exit")){ - fputs(farewell_art.data, stdout); - *stop = true; - } - else if(is_alias("clear")){ - term_clear(); - } - else if(is_alias("h") || is_alias("help")){ - puts( - "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" - ); - } - else if (is_alias("j") || is_alias("join")){ - ServerConnection_close(client->server_connection); - - puts("Enter server address (ip:port:public_key): "); - try_void(term_readLine(answer_buf, sizeof(answer_buf))); - str new_server_link = str_from_cstr(answer_buf); - str_trim(&new_server_link, true); - - printf("connecting to server...\n"); - try(client->server_connection, p, - ServerConnection_open(new_server_link.data)); - printf("connection established\n"); - - // TODO: show server info - // TODO: save server info to user's db - // TODO: ask in loop: log in / register - - //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"); - } - - Return RESULT_VOID; +void Client_disconnect(Client* self){ + ServerConnection_close(self->server_connection); + self->server_connection = NULL; } +str Client_getUserName(Client* client){ + return client->username; +} + +Array(u8) Client_getUserDataKey(Client* client){ + return client->user_data_key; +} + +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); + } + *out_name = self->server_connection->server_name; + 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); + } + *out_desc = self->server_connection->server_description; + return RESULT_VOID; +} diff --git a/src/client/client.h b/src/client/client.h index 2d934b9..346268c 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -1,45 +1,28 @@ #pragma once -#include "cryptography/AES.h" -#include "cryptography/RSA.h" -#include "network/encrypted_sockets.h" +#include "tlibc/errors.h" #include "tlibc/string/str.h" typedef struct Client Client; -typedef struct ClientCredentials { - str username; - Array(u8) user_data_key; - Array(u8) token; - AESBlockEncryptor user_data_aes_enc; - AESBlockDecryptor user_data_aes_dec; -} ClientCredentials; - -Result(void) ClientCredentials_tryConstruct(ClientCredentials* cred, - str username, str password); - -void ClientCredentials_destroy(ClientCredentials* cred); - - -typedef struct ServerConnection { - EndpointIPv4 server_end; - br_rsa_public_key server_pk; - RSAEncryptor rsa_enc; - u64 session_id; - Array(u8) session_key; - EncryptedSocketTCP sock; - str name; - str description; -} ServerConnection; - -Result(ServerConnection*) ServerConnection_open(cstr server_link_cstr); -void ServerConnection_close(ServerConnection* conn); - - -typedef struct Client { - ClientCredentials cred; - ServerConnection* server_connection; -} Client; - -Result(void) Client_createFromConfig(cstr config_path); +Result(Client*) Client_create(str username, str password); void Client_free(Client* client); -Result(void) Client_run(Client* client); + +/// @return username saved during client initialization +str Client_getUserName(Client* client); + +/// @return AES key calculated from password that can be used to encrypt user data +Array(u8) Client_getUserDataKey(Client* client); + +/// @param server_addr_cstr ip:port +/// @param server_pk_base64 public key encoded by `RSA_serializePublicKey_base64()` +Result(void) Client_connect(Client* client, cstr server_addr_cstr, cstr server_pk_base64); +/// disconnect from current server +void Client_disconnect(Client* client); + +/// @param self connected client +/// @param out_name owned by Client, fetched from server during Client_connect +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); diff --git a/src/client/client_internal.h b/src/client/client_internal.h new file mode 100644 index 0000000..258bd72 --- /dev/null +++ b/src/client/client_internal.h @@ -0,0 +1,36 @@ +#pragma once +#include "client.h" +#include "cryptography/AES.h" +#include "cryptography/RSA.h" +#include "network/encrypted_sockets.h" + +typedef struct ServerConnection ServerConnection; + +typedef struct Client { + str username; + Array(u8) user_data_key; + Array(u8) token; + ServerConnection* server_connection; +} Client; + + +typedef struct ServerConnection { + EndpointIPv4 server_end; + br_rsa_public_key server_pk; + RSAEncryptor rsa_enc; + u64 session_id; + Array(u8) session_key; + EncryptedSocketTCP sock; + str server_name; + str server_description; +} ServerConnection; + +/// @param server_addr_cstr +/// @param server_pk_base64 public key encoded by `RSA_serializePublicKey_base64()` +Result(ServerConnection*) ServerConnection_open(cstr server_addr_cstr, cstr server_pk_base64); +void ServerConnection_close(ServerConnection* conn); + +/// updates conn->server_name +Result(void) ServerConnection_requestServerName(ServerConnection* conn); +/// updates conn->server_description +Result(void) ServerConnection_requestServerDescription(ServerConnection* conn); diff --git a/src/client/requests/requests.h b/src/client/requests/requests.h index abd5bf4..37857c4 100644 --- a/src/client/requests/requests.h +++ b/src/client/requests/requests.h @@ -1,6 +1,6 @@ #pragma once #include "network/tcp-chat-protocol/v1.h" -#include "client/client.h" +#include "client/client_internal.h" Result(void) recvErrorMessage(EncryptedSocketTCP* sock, PacketHeader* res_header, diff --git a/src/common_constants.h b/src/common_constants.h index a2b8ec6..c86f78e 100644 --- a/src/common_constants.h +++ b/src/common_constants.h @@ -6,6 +6,10 @@ #define PASSWORD_SIZE_MIN 8 #define PASSWORD_SIZE_MAX 31 #define PASSWORD_HASH_SIZE 32 -#define CHANNEL_NAME_MIN 1 -#define CHANNEL_NAME_MAX 127 -#define CHANNEL_DESC_MAX 4095 +#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/db/idb.c b/src/db/idb.c index f06bf8c..82e8af6 100644 --- a/src/db/idb.c +++ b/src/db/idb.c @@ -43,11 +43,11 @@ void Table_close(Table* t){ return; fclose(t->table_file); fclose(t->changes_file); - free(t->name.data); - free(t->table_file_path.data); - free(t->changes_file_path.data); + str_free(t->name); + str_free(t->table_file_path); + str_free(t->changes_file_path); pthread_mutex_destroy(&t->mutex); - free(t->enc_buf.data); + Array_free(t->enc_buf); free(t); } @@ -201,8 +201,8 @@ Result(IncrementalDB*) idb_open(str db_dir, NULLABLE(Array(u8) aes_key)){ void idb_close(IncrementalDB* db){ if(db == NULL) return; - free(db->db_dir.data); - free(db->aes_key.data); + str_free(db->db_dir); + Array_free(db->aes_key); HashMap_destroy(&db->tables_map); pthread_mutex_destroy(&db->mutex); free(db); diff --git a/src/main.c b/src/main.c index e5e83f3..1d0744f 100755 --- a/src/main.c +++ b/src/main.c @@ -1,5 +1,5 @@ #include "network/network.h" -#include "client/client.h" +#include "ClientCLI/ClientCLI.h" #include "server/server.h" #include "tlibc/tlibc.h" #include "tlibc/base64.h" @@ -141,9 +141,10 @@ int main(const int argc, cstr const* argv){ if(!config_path) config_path = _DEFAULT_CONFIG_PATH_CLIENT; - try_fatal(Client* client, p, Client_createFromConfig(config_path)); - Defer(Client_free(client)); - try_fatal_void(Client_run(client)); + ClientCLI client; + ClientCLI_construct(&client); + Defer(ClientCLI_destroy(&client)); + try_fatal_void(ClientCLI_run(&client)); break; } @@ -160,7 +161,7 @@ int main(const int argc, cstr const* argv){ case RsaGenStdin: { printfe("reading stdin...\n"); Array(u8) input_buf = Array_alloc_size(64*1024); - Defer(free(input_buf.data)); + Defer(Array_free(input_buf)); br_hmac_drbg_context rng = { .vtable = &br_hmac_drbg_vtable }; br_hmac_drbg_init(&rng, &br_sha256_vtable, NULL, 0); i64 read_n = 0; @@ -184,11 +185,11 @@ int main(const int argc, cstr const* argv){ str sk_str = RSA_serializePrivateKey_base64(&sk); printf("rsa_private_key = %s\n", sk_str.data); - free(sk_str.data); + str_free(sk_str); str pk_str = RSA_serializePublicKey_base64(&pk); printf("\nrsa_public_key = %s\n", pk_str.data); - free(pk_str.data); + str_free(pk_str); break; } @@ -204,11 +205,11 @@ int main(const int argc, cstr const* argv){ str sk_str = RSA_serializePrivateKey_base64(&sk); printf("rsa_private_key = %s\n", sk_str.data); - free(sk_str.data); + str_free(sk_str); str pk_str = RSA_serializePublicKey_base64(&pk); printf("\nrsa_public_key = %s\n", pk_str.data); - free(pk_str.data); + str_free(pk_str); break; } diff --git a/src/network/encrypted_sockets.c b/src/network/encrypted_sockets.c index cb82553..ac32b38 100644 --- a/src/network/encrypted_sockets.c +++ b/src/network/encrypted_sockets.c @@ -18,8 +18,8 @@ void EncryptedSocketTCP_destroy(EncryptedSocketTCP* ptr){ if(!ptr) return; socket_close(ptr->sock); - free(ptr->recv_buf.data); - free(ptr->send_buf.data); + Array_free(ptr->recv_buf); + Array_free(ptr->send_buf); } void EncryptedSocketTCP_changeKey(EncryptedSocketTCP* ptr, Array(u8) aes_key){ @@ -171,8 +171,8 @@ void EncryptedSocketUDP_destroy(EncryptedSocketUDP* ptr){ if(!ptr) return; socket_close(ptr->sock); - free(ptr->recv_buf.data); - free(ptr->send_buf.data); + Array_free(ptr->recv_buf); + Array_free(ptr->send_buf); } void EncryptedSocketUDP_changeKey(EncryptedSocketUDP* ptr, Array(u8) aes_key){ diff --git a/src/server/ClientConnection.c b/src/server/ClientConnection.c index 4dc07a4..01a7a7c 100644 --- a/src/server/ClientConnection.c +++ b/src/server/ClientConnection.c @@ -5,7 +5,7 @@ void ClientConnection_close(ClientConnection* conn){ if(!conn) return; EncryptedSocketTCP_destroy(&conn->sock); - free(conn->session_key.data); + Array_free(conn->session_key); free(conn); } @@ -29,7 +29,7 @@ Result(ClientConnection*) ClientConnection_accept(ConnectionHandlerArgs* args) // decrypt the rsa messages using server private key RSADecryptor rsa_dec; - RSADecryptor_construct(&rsa_dec, &args->server->cred.rsa_sk); + RSADecryptor_construct(&rsa_dec, &args->server->rsa_sk); // receive PacketHeader PacketHeader packet_header; diff --git a/src/server/ServerCredentials.c b/src/server/ServerCredentials.c deleted file mode 100644 index 29c2e81..0000000 --- a/src/server/ServerCredentials.c +++ /dev/null @@ -1,26 +0,0 @@ -#include "server.h" - - - -Result(void) ServerCredentials_tryConstruct(ServerCredentials* cred, - cstr rsa_sk_base64, cstr rsa_pk_base64) -{ - Deferral(4); - - memset(cred, 0, sizeof(*cred)); - bool success = false; - Defer(if(!success) ServerCredentials_destroy(cred)); - - try_void(RSA_parsePrivateKey_base64(rsa_sk_base64, &cred->rsa_sk)); - try_void(RSA_parsePublicKey_base64(rsa_pk_base64, &cred->rsa_pk)); - - success = true; - Return RESULT_VOID; -} - -void ServerCredentials_destroy(ServerCredentials* cred){ - if(!cred) - return; - RSA_destroyPrivateKey(&cred->rsa_sk); - RSA_destroyPublicKey(&cred->rsa_pk); -} diff --git a/src/server/db_tables.h b/src/server/db_tables.h index 6833893..77a7baa 100644 --- a/src/server/db_tables.h +++ b/src/server/db_tables.h @@ -3,15 +3,15 @@ #include "tlibc/time.h" typedef struct User { - u16 name_len; char name[USERNAME_SIZE_MAX + 1]; // null-terminated + u16 name_len; 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]; u16 desc_len; - char name[CHANNEL_NAME_MAX + 1]; - char desc[CHANNEL_DESC_MAX + 1]; -} ATTRIBUTE_ALIGNED(16*1024) Channel; +} ATTRIBUTE_ALIGNED(4*1024) Channel; diff --git a/src/server/request_handlers/Register.c b/src/server/request_handlers/Register.c index b6d6eac..640383f 100644 --- a/src/server/request_handlers/Register.c +++ b/src/server/request_handlers/Register.c @@ -34,13 +34,17 @@ declare_RequestHandler(Register) // initialize new user User user; memset(&user, 0, sizeof(User)); - memcpy(user.name, username_str.data, username_str.size + 1); + + memcpy(user.name, username_str.data, username_str.size); user.name_len = username_str.size; + user.name[user.name_len] = 0; + hash_password( Array_construct_size(req.token, sizeof(req.token)), user.token_hash, PASSWORD_HASH_LVL_ROUNDS ); + DateTime_getUTC(&user.registration_time); // save new user to db and cache diff --git a/src/server/server.c b/src/server/server.c index d13208c..695d7f7 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -11,16 +11,19 @@ static void* handleConnection(void* _args); static Result(void) try_handleConnection(ConnectionHandlerArgs* args, cstr log_ctx); -void Server_free(Server* server){ - if(!server) +void Server_free(Server* self){ + if(!self) return; - free(server->name.data); - free(server->description.data); - ServerCredentials_destroy(&server->cred); - idb_close(server->db); - pthread_mutex_destroy(&server->users_cache_mutex); - free(server->users_cache_list.data); - HashMap_destroy(&server->users_name_id_map); + + str_free(self->name); + str_free(self->description); + RSA_destroyPrivateKey(&self->rsa_sk); + RSA_destroyPublicKey(&self->rsa_pk); + + idb_close(self->db); + pthread_mutex_destroy(&self->users_cache_mutex); + List_destroy(self->users_cache_list); + HashMap_destroy(&self->users_name_id_map); } Result(Server*) Server_createFromConfig(cstr config_path){ @@ -76,11 +79,13 @@ Result(Server*) Server_createFromConfig(cstr config_path){ char* pk_base64_cstr = str_copy(tmp_str).data; Defer(free(pk_base64_cstr)); - try_void(ServerCredentials_tryConstruct(&server->cred, sk_base64_cstr, 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)); // parse db_aes_key try_void(config_findValue(config_str, STR("db_aes_key"), &tmp_str, true)); Array(u8) db_aes_key = Array_alloc_size(base64_decodedSize(tmp_str.data, tmp_str.size)); + Defer(free(db_aes_key.data)); base64_decode(tmp_str.data, tmp_str.size, db_aes_key.data); // parse db_dir and open db @@ -147,7 +152,7 @@ static void* handleConnection(void* _args){ if(r.error){ str e_str = Error_toStr(r.error); logError(log_ctx, "%s", e_str.data); - free(e_str.data); + str_free(e_str); Error_free(r.error); } diff --git a/src/server/server.h b/src/server/server.h index c6ab4e4..9f38cd7 100644 --- a/src/server/server.h +++ b/src/server/server.h @@ -10,17 +10,6 @@ typedef struct Server Server; -typedef struct ServerCredentials { - br_rsa_private_key rsa_sk; - br_rsa_public_key rsa_pk; -} ServerCredentials; - -Result(void) ServerCredentials_tryConstruct(ServerCredentials* cred, - cstr rsa_sk_base64, cstr rsa_pk_base64); - -void ServerCredentials_destroy(ServerCredentials* cred); - - typedef struct ClientConnection { u64 session_id; EndpointIPv4 client_end; @@ -29,7 +18,6 @@ typedef struct ClientConnection { bool authorized; } ClientConnection; - typedef struct ConnectionHandlerArgs { Server* server; Socket accepted_socket_tcp; @@ -47,7 +35,9 @@ typedef struct Server { str description; u64 landing_channel_id; EndpointIPv4 local_end; - ServerCredentials cred; + br_rsa_private_key rsa_sk; + br_rsa_public_key rsa_pk; + IncrementalDB* db; Table* db_users_table; pthread_mutex_t users_cache_mutex;