#include #include "tlibc/filesystem.h" #include "tlibc/time.h" #include "db/idb.h" #include "server.h" #include "config.h" #include "log.h" #include "network/tcp-chat-protocol/v1.h" typedef struct ConnectionHandlerArgs { Socket accepted_socket; EndpointIPv4 client_end; u64 session_id; } ConnectionHandlerArgs; static void* handle_connection(void* _args); static Result(void) try_handle_connection(ConnectionHandlerArgs* args, cstr log_ctx); static ServerCredentials* _server_credentials = NULL; static Result(void) parseConfig(cstr config_path){ Deferral(8); // open file try(FILE* config_file, p, file_open(config_path, FO_ReadExisting)); Defer(file_close(config_file)); // read whole file into Array(char) try(i64 config_file_size, i, file_getSize(config_file)); Array(char) config_buf = Array_alloc(char, config_file_size); Defer(free(config_buf.data)); try_void(file_readBytesArray(config_file, config_buf)); str config_str = Array_castTo_str(config_buf, false); str sk_base64; str pk_base64; try_void(config_findValue(config_str, STR("rsa_private_key"), &sk_base64, true)); try_void(config_findValue(config_str, STR("rsa_public_key"), &pk_base64, true)); char* sk_base64_cstr = str_copy(sk_base64).data; char* pk_base64_cstr = str_copy(pk_base64).data; Defer( free(sk_base64_cstr); free(pk_base64_cstr); ); try(_server_credentials, p, ServerCredentials_create(sk_base64_cstr, pk_base64_cstr)); Return RESULT_VOID; } Result(void) server_run(cstr server_endpoint_cstr, cstr config_path){ Deferral(32); cstr log_ctx = "Server/MainThread"; logInfo(log_ctx, "starting server"); logDebug(log_ctx, "parsing config"); try_void(parseConfig(config_path)); Defer(ServerCredentials_free(_server_credentials)); logDebug(log_ctx, "initializing main socket"); EndpointIPv4 server_end; try_void(EndpointIPv4_parse(server_endpoint_cstr, &server_end)); try(Socket main_socket, i, socket_open_TCP()); try_void(socket_bind(main_socket, server_end)); try_void(socket_listen(main_socket, 512)); logInfo(log_ctx, "server is listening at %s", server_endpoint_cstr); u64 session_id = 1; while(true){ ConnectionHandlerArgs* args = (ConnectionHandlerArgs*)malloc(sizeof(ConnectionHandlerArgs)); try(args->accepted_socket, i, socket_accept(main_socket, &args->client_end)); args->session_id = session_id++; pthread_t conn_thread = {0}; //TODO: use async IO instead of threads to not waste system resources // while waiting for incoming data in 100500 threads try_stderrcode(pthread_create(&conn_thread, NULL, handle_connection, args)); try_stderrcode(pthread_detach(conn_thread)); } Return RESULT_VOID; } static void* handle_connection(void* _args){ ConnectionHandlerArgs* args = (ConnectionHandlerArgs*)_args; char log_ctx[64]; sprintf(log_ctx, "Session-" IFWIN("%llx", "%lx"), args->session_id); ResultVar(void) r = try_handle_connection(args, log_ctx); if(r.error){ str error_s = Error_toStr(r.error); logError(log_ctx, "%s", error_s.data); free(error_s.data); } return NULL; } static Result(void) try_handle_connection(ConnectionHandlerArgs* args, cstr log_ctx){ Deferral(64); Defer(free(args)); ClientConnection* conn = NULL; Defer( ClientConnection_close(conn); logInfo(log_ctx, "session closed"); ); // establish encrypted connection try(conn, p, ClientConnection_accept( _server_credentials, args->accepted_socket, args->client_end, args->session_id ) ); logInfo(log_ctx, "session accepted"); // handle unauthorized requests bool ahtorized = false; PacketHeader req_header = {0}; PacketHeader res_header = {0}; while(!ahtorized){ sleepMsec(50); //TODO: implement some additional check if socket is dead or not try_void(EncryptedSocketTCP_recvStruct(&conn->sock, &req_header)); try_void(PacketHeader_validateMagic(&req_header)); //TODO: move request handlers to separate functions switch(req_header.type){ default:{ Array(u8) err_buf = Array_alloc(u8, 128); bool err_complete = false; Defer(if(!err_complete) free(err_buf.data)); sprintf(err_buf.data, "Received unexpected packet of type %u", req_header.type); err_buf.size = strlen(err_buf.data); PacketHeader_construct(&res_header, PROTOCOL_VERSION, PacketType_ErrorMessage, err_buf.size); try_void(EncryptedSocketTCP_sendStruct(&conn->sock, &res_header)); //TODO: limit ErrorMessage size to fit into EncryptedSocketTCP.internal_buffer_size try_void(EncryptedSocketTCP_send(&conn->sock, err_buf)); err_complete = true; Return RESULT_ERROR(err_buf.data, true); } case PacketType_ServerPublicInfoRequest:{ ServerPublicInfoRequest req = {0}; try_void(PacketHeader_validateContentSize(&req_header, sizeof(req))); try_void(EncryptedSocketTCP_recvStruct(&conn->sock, &req)); //TODO: try find requested info Array(u8) content; PacketHeader_construct(&res_header, PROTOCOL_VERSION, PacketType_ServerPublicInfoResponse, content.size); try_void(EncryptedSocketTCP_sendStruct(&conn->sock, &res_header)); try_void(EncryptedSocketTCP_send(&conn->sock, content)); break; } case PacketType_LoginRequest:{ LoginRequest req = {0}; try_void(PacketHeader_validateContentSize(&req_header, sizeof(req))); try_void(EncryptedSocketTCP_recvStruct(&conn->sock, &req)); //TODO: try authorize client u64 user_id; u64 landing_channel_id; LoginResponse res = {0}; LoginResponse_construct(&res, &res_header, user_id, landing_channel_id); try_void(EncryptedSocketTCP_sendStruct(&conn->sock, &res_header)); try_void(EncryptedSocketTCP_sendStruct(&conn->sock, &res)); ahtorized = true; logInfo(log_ctx, "client authorized"); break; } case PacketType_RegisterRequest:{ RegisterRequest req = {0}; try_void(PacketHeader_validateContentSize(&req_header, sizeof(req))); try_void(EncryptedSocketTCP_recvStruct(&conn->sock, &req)); //TODO: try register client u64 user_id; RegisterResponse res = {0}; RegisterResponse_construct(&res, &res_header, user_id); try_void(EncryptedSocketTCP_sendStruct(&conn->sock, &res_header)); try_void(EncryptedSocketTCP_sendStruct(&conn->sock, &res)); break; } } } // handle authorized requests while(true){ sleepMsec(50); } Return RESULT_VOID; }