diff options
author | fschildt <florian.schildt@protonmail.com> | 2025-08-22 15:23:11 +0200 |
---|---|---|
committer | fschildt <florian.schildt@protonmail.com> | 2025-08-22 15:23:11 +0200 |
commit | 2050c0e0576f05156f192aa4caf48834d2f28b14 (patch) | |
tree | ee58bd35b0df0a1bacfbc9700ed99ce80c99294e /src/server/client_connections.c |
Diffstat (limited to 'src/server/client_connections.c')
-rw-r--r-- | src/server/client_connections.c | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/src/server/client_connections.c b/src/server/client_connections.c new file mode 100644 index 0000000..262d1d9 --- /dev/null +++ b/src/server/client_connections.c @@ -0,0 +1,249 @@ +#include <basic/string32.h> +#include <basic/basic.h> +#include <messages/messages.h> +#include <crypto/rsa.h> +#include <os/os.h> +#include <server/client_connections.h> +#include <server/c2s_handler.h> +#include <server/s2c_sender.h> + +#include <pthread.h> +#include <sys/socket.h> +#include <unistd.h> +#include <sys/epoll.h> + + + +ClientConnection * +client_connection_id_to_ptr(ClientConnections *conns, u32 id) +{ + ClientConnection *conn = &conns->connections[id]; + return conn; +} + + +void +client_connection_rm(ClientConnections *conns, u32 connection_id) +{ + ClientConnection *conn = client_connection_id_to_ptr(conns, connection_id); + + int fd = os_net_secure_stream_get_fd(conn->secure_stream_id); + int deleted = epoll_ctl(conns->epoll_fd, EPOLL_CTL_DEL, fd, 0); + if (deleted == -1) { + perror("epoll_ctl<del>:"); + } + + os_net_secure_stream_close(conn->secure_stream_id); + + + // Todo: use string32_create with a (temporary) arena + String32 username; + u32 username_codepoints[MESSAGES_MAX_MESSAGE_LEN]; + for (size_t i = 0; i < conn->username->len; i++) { + username_codepoints[i] = conn->username->codepoints[i]; + } + username.len = conn->username->len; + + + conn->username->len = 0; + conns->free_ids[conns->free_id_count++] = connection_id; + + + if (username.len) { + // send user updates + // Todo: make proper groups + for (size_t i = 0; i < conns->max_connection_count; i++) { + ClientConnection *connection = conns->connections + i; + if (connection->username->len > 0) { + send_s2c_user_update(connection, &username, S2C_USER_UPDATE_OFFLINE); + } + } + + + // Todo: string32_printf(...) + printf("<"); + string32_print(&username); + printf("> disconnected\n"); + } +} + + +u32 +client_connection_add(ClientConnections *conns, u32 secure_stream_id) +{ + if (conns->free_id_count == 0) { + return CLIENT_CONNECTION_INVALID_ID; + } + u32 conn_id = conns->free_ids[--conns->free_id_count]; + ClientConnection *conn = client_connection_id_to_ptr(conns, conn_id); + conn->id = conn_id; + + + struct epoll_event event; + event.events = EPOLLIN; + event.data.u32 = conn_id; + + int fd = os_net_secure_stream_get_fd(secure_stream_id); + + if (epoll_ctl(conns->epoll_fd, EPOLL_CTL_ADD, fd, &event) == -1) { + conns->free_ids[conns->free_id_count++] = conn_id; + perror("epoll_ctl<add>:"); + return false; + } + + + conn->secure_stream_id = secure_stream_id; + conn->recv_buff_size_used = 0; + + return conn_id; +} + + +internal_fn void +handle_client_event(ClientConnections *conns, struct epoll_event event) +{ + u32 conn_id = event.data.u32; + ClientConnection *conn = client_connection_id_to_ptr(conns, conn_id); + + + u32 secure_stream_id = conn->secure_stream_id; + OSNetSecureStreamStatus status = os_net_secure_stream_get_status(conn_id); + if (status == OS_NET_SECURE_STREAM_ERROR || + status == OS_NET_SECURE_STREAM_DISCONNECTED) { + client_connection_rm(conns, conn_id); + return; + } + + + int fd = os_net_secure_stream_get_fd(conn->secure_stream_id); + + if (event.events & EPOLLERR) { + printf("EPOLLERR occured for client_id = %d, fd = %d\n", conn_id, fd); + client_connection_rm(conns, conn_id); + } + else if (event.events & EPOLLHUP) { + printf("EPOLLHUP occured for client_id = %d, fd = %d\n", conn_id, fd); + client_connection_rm(conns, conn_id); + } + else if (event.events & EPOLLIN) { + if (!handle_c2s(conns, conn)) { + return client_connection_rm(conns, conn_id); + } + } + else { + printf("EPOLL??? (%d) occured for client_id = %d, fd = %d\n", event.events, conn_id, fd); + client_connection_rm(conns, conn_id); + } +} + + +internal_fn void +handle_listener_event(ClientConnections *conns, struct epoll_event event) +{ + if (event.events & (EPOLLHUP | EPOLLERR)) { + printf("listener failed\n"); + exit(0); + } + if (event.events & EPOLLIN) { + u32 secure_stream_id = os_net_secure_stream_accept(conns->listener_id); + if (secure_stream_id == OS_NET_SECURE_STREAM_ID_INVALID) { + return; + } + + client_connection_add(conns, secure_stream_id); + } + else { + printf("unhandled listener event %d\n", event.events); + } +} + + +void +client_connections_manage(ClientConnections *conns) +{ + struct epoll_event listener_events; + listener_events.data.u32 = conns->listener_id; + listener_events.events = EPOLLIN; + int listener_fd = os_net_secure_stream_get_fd(conns->listener_id); + if (epoll_ctl(conns->epoll_fd, EPOLL_CTL_ADD, listener_fd, &listener_events) < 0) { + perror("epoll_ctl add for listener fd:"); + return; + } + + for (;;) { + struct epoll_event events[conns->max_connection_count]; + i32 event_count = epoll_wait(conns->epoll_fd, events, ARRAY_COUNT(events), -1); + if (event_count < -1) { + perror("epoll_wait:"); + continue; + } + + if (event_count > 0) { + printf("event.events = %d\n", events[0].events); + } + + for (size_t i = 0; i < event_count; i++) { + if (events[i].data.u32 == conns->listener_id) { + handle_listener_event(conns, events[i]); + } else { + handle_client_event(conns, events[i]); + } + } + } +} + + +ClientConnections * +client_connections_create(Arena *arena, u16 port) +{ + u32 max_user_count = MESSAGES_MAX_USER_COUNT; + size_t max_username_len = MESSAGES_MAX_USERNAME_LEN; + + + os_net_secure_streams_init(arena, max_user_count + 1); + + + ClientConnections *conns = arena_push(arena, sizeof(ClientConnections)); + + conns->server_rsa_pri = rsa_create_via_file(arena, "./server_rsa_pri.pem", false); + + conns->listener_id = os_net_secure_stream_listen(port, conns->server_rsa_pri); + if (conns->listener_id == OS_NET_SECURE_STREAM_ID_INVALID) { + return 0; + } + + + size_t push_size = sizeof(ClientConnection) * max_user_count; + conns->max_connection_count = max_user_count; + conns->connections = arena_push(arena, push_size); + + ClientConnection *conn = conns->connections; + for (size_t i = 0; i < conns->max_connection_count; i++) { + conn->secure_stream_id = CLIENT_CONNECTION_INVALID_ID; + conn->recv_buff_size_used = 0; + + conn->username = (String32*)conn->username_buff; + conn->username->len = 0; + + conn++; + } + + + conns->free_id_count = max_user_count; + conns->free_ids = arena_push(arena, max_user_count * sizeof(*conns->free_ids)); + for (size_t i = 0; i < max_user_count; i++) { + conns->free_ids[i] = i; + } + + + conns->epoll_fd = epoll_create(max_user_count); + if (conns->epoll_fd < 0) { + perror("epoll_create:"); + return 0; + } + + s2c_sender_init(arena); + + return conns; +} + |