separated Client from ClientCLI

This commit is contained in:
Timerix 2025-11-18 13:53:05 +05:00
parent 5266872c2b
commit eec45cac71
21 changed files with 497 additions and 363 deletions

2
.vscode/launch.json vendored
View File

@ -7,7 +7,7 @@
"request": "launch", "request": "launch",
"program": "${workspaceFolder}/bin/tcp-chat", "program": "${workspaceFolder}/bin/tcp-chat",
"windows": { "program": "${workspaceFolder}/bin/tcp-chat.exe" }, "windows": { "program": "${workspaceFolder}/bin/tcp-chat.exe" },
"args": [ "-l" ], // "args": [ "-l" ],
"preLaunchTask": "build_exec_dbg", "preLaunchTask": "build_exec_dbg",
"stopAtEntry": false, "stopAtEntry": false,
"cwd": "${workspaceFolder}/bin", "cwd": "${workspaceFolder}/bin",

2
dependencies/tlibc vendored

@ -1 +1 @@
Subproject commit 425794361bc52240bb50001becbb5fb6e9ebcf20 Subproject commit 89aab2b5bffd46ec0538a5e7c2f1674d59d5677a

243
src/ClientCLI/ClientCLI.c Normal file
View File

@ -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-<SIZE>:<DATA_BASE64>):\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;
}

12
src/ClientCLI/ClientCLI.h Normal file
View File

@ -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);

14
src/ClientCLI/db_tables.h Normal file
View File

@ -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;

View File

@ -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;
}

View File

@ -1,4 +1,4 @@
#include "client.h" #include "client_internal.h"
#include "requests/requests.h" #include "requests/requests.h"
void ServerConnection_close(ServerConnection* conn){ void ServerConnection_close(ServerConnection* conn){
@ -6,44 +6,14 @@ void ServerConnection_close(ServerConnection* conn){
return; return;
RSA_destroyPublicKey(&conn->server_pk); RSA_destroyPublicKey(&conn->server_pk);
EncryptedSocketTCP_destroy(&conn->sock); EncryptedSocketTCP_destroy(&conn->sock);
free(conn->session_key.data); Array_free(conn->session_key);
free(conn->name.data); str_free(conn->server_name);
free(conn->description.data); str_free(conn->server_description);
free(conn); free(conn);
} }
/// @brief Result(ServerConnection*) ServerConnection_open(cstr server_addr_cstr, cstr server_pk_base64)
/// @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){
Deferral(16); Deferral(16);
ServerConnection* conn = (ServerConnection*)malloc(sizeof(ServerConnection)); ServerConnection* conn = (ServerConnection*)malloc(sizeof(ServerConnection));
@ -51,7 +21,14 @@ Result(ServerConnection*) ServerConnection_open(cstr server_link_cstr){
bool success = false; bool success = false;
Defer(if(!success) ServerConnection_close(conn)); 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); RSAEncryptor_construct(&conn->rsa_enc, &conn->server_pk);
conn->session_key = Array_alloc_size(AES_SESSION_KEY_SIZE); 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; conn->session_id = server_handshake.session_id;
// get server name // 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; ServerPublicInfoRequest public_info_req;
ServerPublicInfoResponse public_info_res; ServerPublicInfoResponse public_info_res;
ServerPublicInfoRequest_construct(&public_info_req, &req_header, 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(sendRequest(&conn->sock, &req_header, &public_info_req));
try_void(recvResponse(&conn->sock, &res_header, &public_info_res, try_void(recvResponse(&conn->sock, &res_header, &public_info_res,
PacketType_ServerPublicInfoResponse)); 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, ServerPublicInfoRequest_construct(&public_info_req, &req_header,
ServerPublicInfo_Description); ServerPublicInfo_Description);
try_void(sendRequest(&conn->sock, &req_header, &public_info_req)); try_void(sendRequest(&conn->sock, &req_header, &public_info_req));
try_void(recvResponse(&conn->sock, &res_header, &public_info_res, try_void(recvResponse(&conn->sock, &res_header, &public_info_res,
PacketType_ServerPublicInfoResponse)); 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_VOID;
Return RESULT_VALUE(p, conn); }
}

View File

@ -1,169 +1,79 @@
#include "client.h" #include "client/client_internal.h"
#include "term.h"
#include "tlibc/time.h"
#include "network/tcp-chat-protocol/v1.h"
static const str greeting_art = STR( void Client_free(Client* self){
" ^,,^ |\n" if(!self)
" ( •·•) 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)
return; return;
ClientCredentials_destroy(&client->cred); str_free(self->username);
ServerConnection_close(client->server_connection); Array_free(self->token);
free(client); Array_free(self->user_data_key);
ServerConnection_close(self->server_connection);
free(self);
} }
static Result(void) commandExec(Client* client, str command, bool* stop); Result(Client*) Client_create(str username, str password){
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) {
Deferral(16); Deferral(16);
try_void(term_init());
fputs(greeting_art.data, stdout); Client* self = (Client*)malloc(sizeof(Client));
try_void(askUserNameAndPassword(&client->cred)); memset(self, 0, sizeof(Client));
bool success = false;
Defer(if(!success) Client_free(self));
Array(char) input_buf = Array_alloc(char, 10000); self->username = str_copy(username);
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);
}
}
// 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; Return RESULT_VOID;
} }
#define is_alias(LITERAL) str_equals(command, STR(LITERAL)) void Client_disconnect(Client* self){
ServerConnection_close(self->server_connection);
static Result(void) commandExec(Client* client, str command, bool* stop){ self->server_connection = NULL;
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;
} }
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;
}

View File

@ -1,45 +1,28 @@
#pragma once #pragma once
#include "cryptography/AES.h" #include "tlibc/errors.h"
#include "cryptography/RSA.h"
#include "network/encrypted_sockets.h"
#include "tlibc/string/str.h" #include "tlibc/string/str.h"
typedef struct Client Client; typedef struct Client Client;
typedef struct ClientCredentials { Result(Client*) Client_create(str username, str password);
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);
void Client_free(Client* client); 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);

View File

@ -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);

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
#include "network/tcp-chat-protocol/v1.h" #include "network/tcp-chat-protocol/v1.h"
#include "client/client.h" #include "client/client_internal.h"
Result(void) recvErrorMessage(EncryptedSocketTCP* sock, PacketHeader* res_header, Result(void) recvErrorMessage(EncryptedSocketTCP* sock, PacketHeader* res_header,

View File

@ -6,6 +6,10 @@
#define PASSWORD_SIZE_MIN 8 #define PASSWORD_SIZE_MIN 8
#define PASSWORD_SIZE_MAX 31 #define PASSWORD_SIZE_MAX 31
#define PASSWORD_HASH_SIZE 32 #define PASSWORD_HASH_SIZE 32
#define CHANNEL_NAME_MIN 1 #define CHANNEL_NAME_SIZE_MIN 1
#define CHANNEL_NAME_MAX 127 #define CHANNEL_NAME_SIZE_MAX 127
#define CHANNEL_DESC_MAX 4095 #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

View File

@ -43,11 +43,11 @@ void Table_close(Table* t){
return; return;
fclose(t->table_file); fclose(t->table_file);
fclose(t->changes_file); fclose(t->changes_file);
free(t->name.data); str_free(t->name);
free(t->table_file_path.data); str_free(t->table_file_path);
free(t->changes_file_path.data); str_free(t->changes_file_path);
pthread_mutex_destroy(&t->mutex); pthread_mutex_destroy(&t->mutex);
free(t->enc_buf.data); Array_free(t->enc_buf);
free(t); free(t);
} }
@ -201,8 +201,8 @@ Result(IncrementalDB*) idb_open(str db_dir, NULLABLE(Array(u8) aes_key)){
void idb_close(IncrementalDB* db){ void idb_close(IncrementalDB* db){
if(db == NULL) if(db == NULL)
return; return;
free(db->db_dir.data); str_free(db->db_dir);
free(db->aes_key.data); Array_free(db->aes_key);
HashMap_destroy(&db->tables_map); HashMap_destroy(&db->tables_map);
pthread_mutex_destroy(&db->mutex); pthread_mutex_destroy(&db->mutex);
free(db); free(db);

View File

@ -1,5 +1,5 @@
#include "network/network.h" #include "network/network.h"
#include "client/client.h" #include "ClientCLI/ClientCLI.h"
#include "server/server.h" #include "server/server.h"
#include "tlibc/tlibc.h" #include "tlibc/tlibc.h"
#include "tlibc/base64.h" #include "tlibc/base64.h"
@ -141,9 +141,10 @@ int main(const int argc, cstr const* argv){
if(!config_path) if(!config_path)
config_path = _DEFAULT_CONFIG_PATH_CLIENT; config_path = _DEFAULT_CONFIG_PATH_CLIENT;
try_fatal(Client* client, p, Client_createFromConfig(config_path)); ClientCLI client;
Defer(Client_free(client)); ClientCLI_construct(&client);
try_fatal_void(Client_run(client)); Defer(ClientCLI_destroy(&client));
try_fatal_void(ClientCLI_run(&client));
break; break;
} }
@ -160,7 +161,7 @@ int main(const int argc, cstr const* argv){
case RsaGenStdin: { case RsaGenStdin: {
printfe("reading stdin...\n"); printfe("reading stdin...\n");
Array(u8) input_buf = Array_alloc_size(64*1024); 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_context rng = { .vtable = &br_hmac_drbg_vtable };
br_hmac_drbg_init(&rng, &br_sha256_vtable, NULL, 0); br_hmac_drbg_init(&rng, &br_sha256_vtable, NULL, 0);
i64 read_n = 0; i64 read_n = 0;
@ -184,11 +185,11 @@ int main(const int argc, cstr const* argv){
str sk_str = RSA_serializePrivateKey_base64(&sk); str sk_str = RSA_serializePrivateKey_base64(&sk);
printf("rsa_private_key = %s\n", sk_str.data); printf("rsa_private_key = %s\n", sk_str.data);
free(sk_str.data); str_free(sk_str);
str pk_str = RSA_serializePublicKey_base64(&pk); str pk_str = RSA_serializePublicKey_base64(&pk);
printf("\nrsa_public_key = %s\n", pk_str.data); printf("\nrsa_public_key = %s\n", pk_str.data);
free(pk_str.data); str_free(pk_str);
break; break;
} }
@ -204,11 +205,11 @@ int main(const int argc, cstr const* argv){
str sk_str = RSA_serializePrivateKey_base64(&sk); str sk_str = RSA_serializePrivateKey_base64(&sk);
printf("rsa_private_key = %s\n", sk_str.data); printf("rsa_private_key = %s\n", sk_str.data);
free(sk_str.data); str_free(sk_str);
str pk_str = RSA_serializePublicKey_base64(&pk); str pk_str = RSA_serializePublicKey_base64(&pk);
printf("\nrsa_public_key = %s\n", pk_str.data); printf("\nrsa_public_key = %s\n", pk_str.data);
free(pk_str.data); str_free(pk_str);
break; break;
} }

View File

@ -18,8 +18,8 @@ void EncryptedSocketTCP_destroy(EncryptedSocketTCP* ptr){
if(!ptr) if(!ptr)
return; return;
socket_close(ptr->sock); socket_close(ptr->sock);
free(ptr->recv_buf.data); Array_free(ptr->recv_buf);
free(ptr->send_buf.data); Array_free(ptr->send_buf);
} }
void EncryptedSocketTCP_changeKey(EncryptedSocketTCP* ptr, Array(u8) aes_key){ void EncryptedSocketTCP_changeKey(EncryptedSocketTCP* ptr, Array(u8) aes_key){
@ -171,8 +171,8 @@ void EncryptedSocketUDP_destroy(EncryptedSocketUDP* ptr){
if(!ptr) if(!ptr)
return; return;
socket_close(ptr->sock); socket_close(ptr->sock);
free(ptr->recv_buf.data); Array_free(ptr->recv_buf);
free(ptr->send_buf.data); Array_free(ptr->send_buf);
} }
void EncryptedSocketUDP_changeKey(EncryptedSocketUDP* ptr, Array(u8) aes_key){ void EncryptedSocketUDP_changeKey(EncryptedSocketUDP* ptr, Array(u8) aes_key){

View File

@ -5,7 +5,7 @@ void ClientConnection_close(ClientConnection* conn){
if(!conn) if(!conn)
return; return;
EncryptedSocketTCP_destroy(&conn->sock); EncryptedSocketTCP_destroy(&conn->sock);
free(conn->session_key.data); Array_free(conn->session_key);
free(conn); free(conn);
} }
@ -29,7 +29,7 @@ Result(ClientConnection*) ClientConnection_accept(ConnectionHandlerArgs* args)
// decrypt the rsa messages using server private key // decrypt the rsa messages using server private key
RSADecryptor rsa_dec; RSADecryptor rsa_dec;
RSADecryptor_construct(&rsa_dec, &args->server->cred.rsa_sk); RSADecryptor_construct(&rsa_dec, &args->server->rsa_sk);
// receive PacketHeader // receive PacketHeader
PacketHeader packet_header; PacketHeader packet_header;

View File

@ -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);
}

View File

@ -3,15 +3,15 @@
#include "tlibc/time.h" #include "tlibc/time.h"
typedef struct User { typedef struct User {
u16 name_len;
char name[USERNAME_SIZE_MAX + 1]; // null-terminated char name[USERNAME_SIZE_MAX + 1]; // null-terminated
u16 name_len;
u8 token_hash[PASSWORD_HASH_SIZE]; // token is hashed again on server side u8 token_hash[PASSWORD_HASH_SIZE]; // token is hashed again on server side
DateTime registration_time; DateTime registration_time;
} ATTRIBUTE_ALIGNED(256) User; } ATTRIBUTE_ALIGNED(256) User;
typedef struct Channel { typedef struct Channel {
char name[CHANNEL_NAME_SIZE_MAX + 1];
u16 name_len; u16 name_len;
char desc[CHANNEL_DESC_SIZE_MAX + 1];
u16 desc_len; u16 desc_len;
char name[CHANNEL_NAME_MAX + 1]; } ATTRIBUTE_ALIGNED(4*1024) Channel;
char desc[CHANNEL_DESC_MAX + 1];
} ATTRIBUTE_ALIGNED(16*1024) Channel;

View File

@ -34,13 +34,17 @@ declare_RequestHandler(Register)
// initialize new user // initialize new user
User user; User user;
memset(&user, 0, sizeof(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_len = username_str.size;
user.name[user.name_len] = 0;
hash_password( hash_password(
Array_construct_size(req.token, sizeof(req.token)), Array_construct_size(req.token, sizeof(req.token)),
user.token_hash, user.token_hash,
PASSWORD_HASH_LVL_ROUNDS PASSWORD_HASH_LVL_ROUNDS
); );
DateTime_getUTC(&user.registration_time); DateTime_getUTC(&user.registration_time);
// save new user to db and cache // save new user to db and cache

View File

@ -11,16 +11,19 @@
static void* handleConnection(void* _args); static void* handleConnection(void* _args);
static Result(void) try_handleConnection(ConnectionHandlerArgs* args, cstr log_ctx); static Result(void) try_handleConnection(ConnectionHandlerArgs* args, cstr log_ctx);
void Server_free(Server* server){ void Server_free(Server* self){
if(!server) if(!self)
return; return;
free(server->name.data);
free(server->description.data); str_free(self->name);
ServerCredentials_destroy(&server->cred); str_free(self->description);
idb_close(server->db); RSA_destroyPrivateKey(&self->rsa_sk);
pthread_mutex_destroy(&server->users_cache_mutex); RSA_destroyPublicKey(&self->rsa_pk);
free(server->users_cache_list.data);
HashMap_destroy(&server->users_name_id_map); 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){ 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; char* pk_base64_cstr = str_copy(tmp_str).data;
Defer(free(pk_base64_cstr)); 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 // parse db_aes_key
try_void(config_findValue(config_str, STR("db_aes_key"), &tmp_str, true)); 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)); 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); base64_decode(tmp_str.data, tmp_str.size, db_aes_key.data);
// parse db_dir and open db // parse db_dir and open db
@ -147,7 +152,7 @@ static void* handleConnection(void* _args){
if(r.error){ if(r.error){
str e_str = Error_toStr(r.error); str e_str = Error_toStr(r.error);
logError(log_ctx, "%s", e_str.data); logError(log_ctx, "%s", e_str.data);
free(e_str.data); str_free(e_str);
Error_free(r.error); Error_free(r.error);
} }

View File

@ -10,17 +10,6 @@
typedef struct Server Server; 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 { typedef struct ClientConnection {
u64 session_id; u64 session_id;
EndpointIPv4 client_end; EndpointIPv4 client_end;
@ -29,7 +18,6 @@ typedef struct ClientConnection {
bool authorized; bool authorized;
} ClientConnection; } ClientConnection;
typedef struct ConnectionHandlerArgs { typedef struct ConnectionHandlerArgs {
Server* server; Server* server;
Socket accepted_socket_tcp; Socket accepted_socket_tcp;
@ -47,7 +35,9 @@ typedef struct Server {
str description; str description;
u64 landing_channel_id; u64 landing_channel_id;
EndpointIPv4 local_end; EndpointIPv4 local_end;
ServerCredentials cred; br_rsa_private_key rsa_sk;
br_rsa_public_key rsa_pk;
IncrementalDB* db; IncrementalDB* db;
Table* db_users_table; Table* db_users_table;
pthread_mutex_t users_cache_mutex; pthread_mutex_t users_cache_mutex;