diff --git a/dependencies/tlibc b/dependencies/tlibc index 1775b27..adaf5cc 160000 --- a/dependencies/tlibc +++ b/dependencies/tlibc @@ -1 +1 @@ -Subproject commit 1775b27980d550dd9a50a81b11d797c51253ab22 +Subproject commit adaf5cc31199b8b70404f30b51a0e4ef822d6fab diff --git a/src/common_constants.h b/src/common_constants.h new file mode 100644 index 0000000..7095110 --- /dev/null +++ b/src/common_constants.h @@ -0,0 +1,11 @@ +#pragma once +#include "tlibc/std.h" + +#define USERNAME_SIZE_MIN 2 +#define USERNAME_SIZE_MAX 63 +#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 diff --git a/src/cryptography/cryptography.h b/src/cryptography/cryptography.h index 468febc..2623377 100755 --- a/src/cryptography/cryptography.h +++ b/src/cryptography/cryptography.h @@ -2,6 +2,7 @@ #include "tlibc/collections/Array.h" #include "tlibc/errors.h" #include "bearssl_rand.h" +#include "common_constants.h" ////////////////////////////////////////////////////////////////////////////// // // @@ -14,7 +15,6 @@ /// @param out_buffer u8[PASSWORD_HASH_SIZE] /// @param rounds number of rounds void hash_password(Array(u8) password, u8* out_buffer, i32 rounds); -#define PASSWORD_HASH_SIZE 32 #define PASSWORD_HASH_LVL_ROUNDS 1e5 ////////////////////////////////////////////////////////////////////////////// diff --git a/src/db/idb.h b/src/db/idb.h index 9da7b23..017a9bc 100644 --- a/src/db/idb.h +++ b/src/db/idb.h @@ -14,13 +14,13 @@ void idb_close(IncrementalDB* db); Result(Table*) idb_getOrCreateTable(IncrementalDB* db, str table_name, u32 row_size); -Result(void) idb_getRows(Table* t, u64 id, void* dst, u64 count); +Result(void) idb_getRows(Table* t, u64 start_from_id, void* dst, u64 count); #define idb_getRow(T, ID, DST) idb_getRows(T, ID, DST, 1) Result(u64) idb_pushRows(Table* t, const void* src, u64 count); #define idb_pushRow(T, SRC) idb_pushRows(T, SRC, 1) -Result(void) idb_updateRows(Table* t, u64 id, const void* src, u64 count); +Result(void) idb_updateRows(Table* t, u64 start_from_id, const void* src, u64 count); #define idb_updateRow(T, ID, SRC) idb_updateRows(T, ID, SRC, 1) Result(u64) idb_getRowCount(Table* t); diff --git a/src/network/tcp-chat-protocol/constant.h b/src/network/tcp-chat-protocol/constant.h index 45edcc3..a67448a 100644 --- a/src/network/tcp-chat-protocol/constant.h +++ b/src/network/tcp-chat-protocol/constant.h @@ -1,6 +1,7 @@ #pragma once #include "tlibc/errors.h" #include "magic.h" +#include "common_constants.h" #define AES_SESSION_KEY_SIZE 32 diff --git a/src/network/tcp-chat-protocol/v1.c b/src/network/tcp-chat-protocol/v1.c index a956538..3776970 100644 --- a/src/network/tcp-chat-protocol/v1.c +++ b/src/network/tcp-chat-protocol/v1.c @@ -56,8 +56,8 @@ Result(void) RegisterRequest_tryConstruct(RegisterRequest *ptr, PacketHeader* he _PacketHeader_construct(RegisterRequest); try_assert(username.size >= USERNAME_SIZE_MIN && username.size <= USERNAME_SIZE_MAX); - ptr->username_size = username.size; memcpy(ptr->username, username.data, username.size); + ptr->username[username.size] = 0; try_assert(token.size == sizeof(ptr->token)); memcpy(ptr->token, token.data, token.size); diff --git a/src/network/tcp-chat-protocol/v1.h b/src/network/tcp-chat-protocol/v1.h index 17f508b..0b223bf 100644 --- a/src/network/tcp-chat-protocol/v1.h +++ b/src/network/tcp-chat-protocol/v1.h @@ -2,7 +2,6 @@ #include "tlibc/errors.h" #include "tlibc/string/str.h" #include "network/tcp-chat-protocol/constant.h" -#include "cryptography/cryptography.h" #define PROTOCOL_VERSION 1 /* 1.0.0 */ #define NETWORK_BUFFER_SIZE 65536 @@ -80,14 +79,8 @@ void LoginResponse_construct(LoginResponse* ptr, PacketHeader* header, u64 user_id, u64 landing_channel_id); -#define USERNAME_SIZE_MIN 4 -#define USERNAME_SIZE_MAX 64 -#define PASSWORD_SIZE_MIN 8 -#define PASSWORD_SIZE_MAX 32 - typedef struct RegisterRequest { - u32 username_size; - char username[USERNAME_SIZE_MAX]; + char username[USERNAME_SIZE_MAX + 1]; // null-terminated u8 token[PASSWORD_HASH_SIZE]; } ALIGN_PACKET_STRUCT RegisterRequest; diff --git a/src/server/db_tables.h b/src/server/db_tables.h new file mode 100644 index 0000000..6833893 --- /dev/null +++ b/src/server/db_tables.h @@ -0,0 +1,17 @@ +#pragma once +#include "common_constants.h" +#include "tlibc/time.h" + +typedef struct User { + 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 { + u16 name_len; + u16 desc_len; + char name[CHANNEL_NAME_MAX + 1]; + char desc[CHANNEL_DESC_MAX + 1]; +} ATTRIBUTE_ALIGNED(16*1024) Channel; diff --git a/src/server/request_handlers/Register.c b/src/server/request_handlers/Register.c index a18c9eb..bc955c5 100644 --- a/src/server/request_handlers/Register.c +++ b/src/server/request_handlers/Register.c @@ -9,8 +9,62 @@ declare_RequestHandler(Register) try_void(PacketHeader_validateContentSize(req_head, sizeof(req))); try_void(EncryptedSocketTCP_recvStruct(&conn->sock, &req)); - //TODO: try register client - u64 user_id; + //TODO: reject usernames with restricted characters + // must end with 0 + if(req.username[sizeof(req.username - 1)] != 0){ + try(char* err_msg, p, sendErrorMessage(conn, res_head, + "Username is incorrect\n")); + logWarn(log_ctx, "%s", err_msg); + free(err_msg); + Return RESULT_VOID; + } + // check username size + str username_str = str_from_cstr(req.username); + if(username_str.size < USERNAME_SIZE_MIN){ + try(char* err_msg, p, sendErrorMessage(conn, res_head, + "Username length (in bytes) must be >= %i and <= %i\n", + USERNAME_SIZE_MIN, USERNAME_SIZE_MAX)); + logWarn(log_ctx, "%s", err_msg); + free(err_msg); + Return RESULT_VOID; + } + + // 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)); + + // check if name is taken + if(HashMap_tryGetPtr(&server->users_name_id_map, username_str) != NULL){ + try(char* err_msg, p, sendErrorMessage(conn, res_head, + "User with name '%s' already exists\n", + username_str.data)); + logWarn(log_ctx, "%s", err_msg); + free(err_msg); + Return RESULT_VOID; + } + + // initialize new user + User user; + memset(&user, 0, sizeof(User)); + memcpy(user.name, username_str.data, username_str.size + 1); + user.name_len = username_str.size; + 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 + 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(HashMap_tryPush(&server->users_name_id_map, username_str, &user_id)); + + // manually unlock mutex + pthread_mutex_unlock(&server->users_cache_mutex); + unlocked_users_cache_mutex = true; RegisterResponse res = {0}; RegisterResponse_construct(&res, res_head, user_id); diff --git a/src/server/request_handlers/ServerPublicInfo.c b/src/server/request_handlers/ServerPublicInfo.c index d36061f..e1485d2 100644 --- a/src/server/request_handlers/ServerPublicInfo.c +++ b/src/server/request_handlers/ServerPublicInfo.c @@ -12,12 +12,14 @@ declare_RequestHandler(ServerPublicInfo) //TODO: try find requested info Array(u8) content; switch(req.property){ - default: + default:{ try(char* err_msg, p, sendErrorMessage(conn, res_head, - "unknown ServerPublicInfo property %u", req.property)); + "Unknown ServerPublicInfo property %u", + req.property)); logWarn(log_ctx, "%s", err_msg); + free(err_msg); Return RESULT_VOID; - break; + } case ServerPublicInfo_Name: content = str_castTo_Array(server->name); break; diff --git a/src/server/server.c b/src/server/server.c index 55973f1..e78c358 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -18,6 +18,9 @@ void Server_free(Server* server){ 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); } Result(Server*) Server_createFromConfig(cstr config_path){ @@ -76,6 +79,23 @@ Result(Server*) Server_createFromConfig(cstr config_path){ try_void(config_findValue(config_str, STR("db_dir"), &tmp_str, true)); try(server->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); + // load whole table to list + try_void(idb_getRows(server->db_users_table, 0, server->users_cache_list.data, users_count)); + // build name-id map + for(u64 id; 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); + } + } + success = true; Return RESULT_VALUE(p, server); } @@ -154,10 +174,9 @@ static Result(void) try_handleConnection(ConnectionHandlerArgs* args, cstr log_c try(char* err_msg, p, sendErrorMessage(conn, &res_head, "Received unexpected packet of type %u", - req_head.type - ) - ); + req_head.type)); logWarn(log_ctx, "%s", err_msg); + free(err_msg); Return RESULT_VOID; // unauthorized requests diff --git a/src/server/server.h b/src/server/server.h index 2de6a9c..a03d54c 100644 --- a/src/server/server.h +++ b/src/server/server.h @@ -3,6 +3,10 @@ #include "cryptography/RSA.h" #include "network/encrypted_sockets.h" #include "db/idb.h" +#include "tlibc/collections/HashMap.h" +#include "tlibc/collections/List.h" +#include "db_tables.h" +#include typedef struct Server Server; @@ -44,6 +48,10 @@ typedef struct Server { EndpointIPv4 local_end; ServerCredentials cred; 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 } Server; Result(Server*) Server_createFromConfig(cstr config_path);