204 lines
7.2 KiB
C
204 lines
7.2 KiB
C
#include <pthread.h>
|
|
#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;
|
|
}
|