aboutsummaryrefslogtreecommitdiff
path: root/src/client/server_connection.c
diff options
context:
space:
mode:
authorfschildt <florian.schildt@protonmail.com>2025-08-22 15:23:11 +0200
committerfschildt <florian.schildt@protonmail.com>2025-08-22 15:23:11 +0200
commit2050c0e0576f05156f192aa4caf48834d2f28b14 (patch)
treeee58bd35b0df0a1bacfbc9700ed99ce80c99294e /src/client/server_connection.c
first commitHEADmaster
Diffstat (limited to 'src/client/server_connection.c')
-rw-r--r--src/client/server_connection.c293
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;
+}
+