diff options
Diffstat (limited to 'src/client/server_connection.c')
-rw-r--r-- | src/client/server_connection.c | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/src/client/server_connection.c b/src/client/server_connection.c new file mode 100644 index 0000000..3172340 --- /dev/null +++ b/src/client/server_connection.c @@ -0,0 +1,293 @@ +#include <os/os.h> +#include <client/server_connection.h> +#include <basic/basic.h> +#include <client/fscord.h> +#include <crypto/rsa.h> +#include <messages/messages.h> +#include <string.h> + +#include <poll.h> // Todo: use os/os.h + + +typedef struct { + volatile ServerConnectionStatus status; + volatile u32 secure_stream_id; + + char address[128]; + u16 port; + EVP_PKEY *server_rsa_pub; + + Arena send_arena; + + u64 recv_buff_size_used; + u8 recv_buff[MESSAGES_MAX_PACKAGE_SIZE]; +} ServerConnection; + + +internal_var ServerConnection s_conn; +internal_var struct Fscord *s_fscord; + + +void +send_c2s_chat_message(String32 *content) +{ + Arena *send_arena = &s_conn.send_arena; + + + C2S_ChatMessage *chat_message = arena_push(send_arena, sizeof(C2S_ChatMessage)); + + String32 *content_copy = string32_create_from_string32(send_arena, content); + chat_message->content = (String32*)((u8*)content_copy - (u8*)chat_message); + + chat_message->header.type = C2S_CHAT_MESSAGE; + chat_message->header.size = send_arena->size_used; + + + i64 size_sent = os_net_secure_stream_send(s_conn.secure_stream_id, send_arena->memory, send_arena->size_used); + if (size_sent < 0) { + server_connection_terminate(); + } + + + arena_clear(send_arena); +} + + +void +send_c2s_login(String32 *username, String32 *password) +{ + Arena *send_arena = &s_conn.send_arena; + + + C2S_Login *login = arena_push(send_arena, sizeof(C2S_Login)); + + String32 *username_copy = string32_create_from_string32(send_arena, username); + login->username = (String32*)((u8*)username_copy - (u8*)login); + + String32 *password_copy = string32_create_from_string32(send_arena, password); + login->password = (String32*)((u8*)password_copy - (u8*)login); + + login->header.type = C2S_LOGIN; + login->header.size = send_arena->size_used; + + + i64 size_sent = os_net_secure_stream_send(s_conn.secure_stream_id, send_arena->memory, send_arena->size_used); + if (size_sent < 0) { + server_connection_terminate(); + } + + + arena_clear(send_arena); +} + + +internal_fn void +handle_s2c_user_update(void) +{ + S2C_UserUpdate *user_update = (S2C_UserUpdate*)s_conn.recv_buff; + user_update->username = (String32*)((u8*)user_update + (size_t)user_update->username); + + if (user_update->status == S2C_USER_UPDATE_ONLINE) { + session_add_user(s_fscord->session, user_update->username); + } else { + session_rm_user(s_fscord->session, user_update->username); + } +} + + +internal_fn void +handle_s2c_chat_message(void) +{ + S2C_ChatMessage *chat_message = (S2C_ChatMessage*)s_conn.recv_buff; + chat_message->username = (String32*)((u8*)chat_message + (size_t)chat_message->username); + chat_message->content = (String32*)((u8*)chat_message + (size_t)chat_message->content); + + Time time = {chat_message->epoch_time_seconds, chat_message->epoch_time_nanoseconds}; + + session_add_chat_message(s_fscord->session, time, chat_message->username, chat_message->content); +} + + +internal_fn void +handle_s2c_login(void) +{ + S2C_Login *login_response = (S2C_Login*)s_conn.recv_buff; + + Login *login = s_fscord->login; + login_process_login_result(login, login_response->login_result); +} + + +// Todo: security checks on the message size. +internal_fn b32 +handle_s2c(void) +{ + i64 size_recvd; + i64 size_to_recv; + + // recv message header + if (s_conn.recv_buff_size_used < sizeof(MessageHeader)) { + size_to_recv = sizeof(MessageHeader) - s_conn.recv_buff_size_used; + size_recvd = os_net_secure_stream_recv(s_conn.secure_stream_id, s_conn.recv_buff + s_conn.recv_buff_size_used, size_to_recv); + if (size_recvd < 0) { + printf("handle_s2c error: size_recvd < 0 when recv'ing message header\n"); + return false; + } + + s_conn.recv_buff_size_used += size_recvd; + if (s_conn.recv_buff_size_used < sizeof(MessageHeader)) { + return true; + } + } + + + // recv message body + MessageHeader *header = (MessageHeader*)s_conn.recv_buff; + if (s_conn.recv_buff_size_used < header->size) { + size_to_recv = header->size - s_conn.recv_buff_size_used; + size_recvd = os_net_secure_stream_recv(s_conn.secure_stream_id, s_conn.recv_buff + s_conn.recv_buff_size_used, size_to_recv); + if (size_recvd < 0) { + printf("handle_s2c error: size_recvd < 0 when recv'ing message body\n"); + return false; + } + + s_conn.recv_buff_size_used += size_recvd; + if (s_conn.recv_buff_size_used < header->size) { + return true; + } + } + + + // dispatch + switch (header->type) { + case S2C_LOGIN: handle_s2c_login(); break; + case S2C_CHAT_MESSAGE: handle_s2c_chat_message(); break; + case S2C_USER_UPDATE: handle_s2c_user_update(); break; + InvalidDefaultCase; + } + + + // cleanup + s_conn.recv_buff_size_used = 0; + return true; +} + + +b32 +server_connection_handle_events(void) +{ + // Todo: use <os/os.h> functions! + struct pollfd pollfd; + pollfd.fd = os_net_secure_stream_get_fd(s_conn.secure_stream_id); + pollfd.events = POLLIN; + + for (;;) { + OSNetSecureStreamStatus status = os_net_secure_stream_get_status(s_conn.secure_stream_id); + if (status == OS_NET_SECURE_STREAM_DISCONNECTED || + status == OS_NET_SECURE_STREAM_ERROR) { + return false; + } + + int ret = poll(&pollfd, 1, 0); + if (ret == -1) { + printf("poll error\n"); + return false; // Todo: handle error + } + else if (ret == 0) { + return true; // do nothing + } + + if (pollfd.revents & POLLERR) { + printf("poll POLLERR\n"); + return false; // Todo: handle err + } + if (pollfd.revents & POLLHUP) { + printf("poll POLLHUP\n"); + return false; // Todo: handle hup + } + if (pollfd.revents & POLLIN) { + printf("poll POLLIN\n"); + b32 handled = handle_s2c(); + if (!handled) { + return false; + } + continue; + } + } + + return true; +} + + +void +server_connection_terminate(void) +{ + os_net_secure_stream_close(s_conn.secure_stream_id); + s_conn.secure_stream_id = OS_NET_SECURE_STREAM_ID_INVALID; + s_conn.status = SERVER_CONNECTION_NOT_ESTABLISHED; +} + + + +internal_fn void * +server_connection_establish_runner(void *data) +{ + // connect + + char *address = s_conn.address; + u16 port = s_conn.port; + EVP_PKEY *rsa = s_conn.server_rsa_pub; + + u32 secure_stream_id = os_net_secure_stream_connect(address, port, rsa); + if (secure_stream_id == OS_NET_SECURE_STREAM_ID_INVALID) { + s_conn.status = SERVER_CONNECTION_NOT_ESTABLISHED; + pthread_exit(0); + } + + s_conn.secure_stream_id = secure_stream_id; + s_conn.status = SERVER_CONNECTION_ESTABLISHED; + + + pthread_exit(0); +} + + +void +server_connection_establish(char *address, u16 port, EVP_PKEY *server_rsa_pub) +{ + s_conn.status = SERVER_CONNECTION_ESTABLISHING; + + strncpy(s_conn.address, address, sizeof(s_conn.address)-1); + s_conn.port = port; + s_conn.server_rsa_pub = server_rsa_pub; + + pthread_t tid; + int err = pthread_create(&tid, 0, server_connection_establish_runner, s_fscord->login); + if (err != 0) { + printf("pthread_create error in server_connection_establish\n"); + s_conn.status = SERVER_CONNECTION_NOT_ESTABLISHED; + } + + return; +} + + +ServerConnectionStatus +server_connection_get_status(void) +{ + return s_conn.status; +} + + +void +server_connection_create(Arena *arena, struct Fscord *fscord) +{ + s_fscord = fscord; + s_conn.status = SERVER_CONNECTION_NOT_ESTABLISHED; + s_conn.secure_stream_id = OS_NET_SECURE_STREAM_ID_INVALID; + + arena_init(&s_conn.send_arena, MESSAGES_MAX_PACKAGE_SIZE); + + s_conn.recv_buff_size_used = 0; +} + |