diff options
author | Oxore <oxore@protonmail.com> | 2023-12-28 00:04:20 +0300 |
---|---|---|
committer | Oxore <oxore@protonmail.com> | 2023-12-28 00:04:20 +0300 |
commit | 18893127524d87e47a948b9a92d8d8b2ab869852 (patch) | |
tree | 1491660a439221174a776b9c7f38f05234590cdb /server.c | |
parent | 4faf778b6ec4b9d8ab0c19e22d27c76f4c67ab28 (diff) |
WIP
Diffstat (limited to 'server.c')
-rw-r--r-- | server.c | 258 |
1 files changed, 182 insertions, 76 deletions
@@ -1,88 +1,148 @@ -// Initially based on https://stackoverflow.com/a/35570418 +/* SPDX-License-Identifier: Unlicense */ +#include "proto_io.h" +#include "proto.h" + +#include "timespec/timespec.h" + +#include <assert.h> #include <errno.h> #include <fcntl.h> +#include <inttypes.h> #include <netinet/in.h> #include <poll.h> +#include <signal.h> #include <stdbool.h> #include <stdint.h> +#include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/socket.h> +#include <time.h> #include <unistd.h> -enum frame_type { - FT_NONE = 0, - FT_HELLO = 42, -}; +#define TICK_PERIOD_MIN_MS 1u +#define TICK_PERIOD_MAX_MS 10000u +#define CONNECTIONS_MAX 256u -struct frame { - uint32_t addr; +struct connection { + bool exist; + bool prev_is_lost; uint16_t port; - enum frame_type type; + uint32_t connection_id; + uint32_t addr; + struct timespec last_syn; + struct timespec tick_period; + uint32_t tick; + uint32_t server_roundtrip_prev; + uint32_t client_roundtrip_prev; + uint32_t repeat; }; -static unsigned char udp_buffer[UINT16_MAX]; +static struct connection g_connections[CONNECTIONS_MAX] = {{0}, }; +static volatile bool g_should_exit = false; -static int ReceiveFrame(int const fd, struct frame *const frame) +static void sigintHandler(int a) { - frame->type = FT_NONE; - struct sockaddr_in source_address; - socklen_t source_address_len = sizeof(source_address); - int const length = recvfrom( - fd, - udp_buffer, - sizeof(udp_buffer) - 1, - 0, - (struct sockaddr*)&source_address, - &source_address_len); - if (length == 0) { - return -EWOULDBLOCK; - } - if (length < 0) { - int const err = errno; - if (err != EWOULDBLOCK) { - perror("recvfrom failed"); - } - return -err; - } - uint32_t const addr = ntohl(source_address.sin_addr.s_addr); - uint32_t const port = ntohs(source_address.sin_port); - if (length) { - unsigned char frame_type = udp_buffer[0]; - if (frame_type == FT_HELLO) { - *frame = (struct frame){ - .addr = addr, - .port = port, - .type = frame_type, - }; + (void) a; + g_should_exit = true; +} + +static void ConnectionHandleFrame( + struct connection *self, + struct frame const *frame, + struct timespec now, + int fd) +{ + if (frame->type == FT_SYNACK) { + if (LOG_TRACE) { + fprintf(stderr, + "FT_SYNACK(tick=%u, lost=%d, rt_prev=%u, repeat=%u)\n", + frame->syn.tick, frame->syn.prev_is_lost, frame->syn.roundtrip_prev, frame->syn.repeat); } + self->client_roundtrip_prev = frame->syn.roundtrip_prev; + struct frame outgoing = *frame; + outgoing.type = FT_ACK, + outgoing.ack = (struct frame_data_ack){ + .tick = self->tick, + }; + SendFrame(fd, &outgoing); + long const srt_ms = timespec_to_ms(timespec_sub(now, self->last_syn)); + assert(srt_ms >= 0); + self->server_roundtrip_prev = srt_ms; + fprintf(stderr, "%5" PRIu32 " server_rt %5lu ms, client_rt %5" PRIu32 " ms\n", + self->tick, (unsigned long)srt_ms, frame->syn.roundtrip_prev); + self->tick++; } - uint8_t const a0 = addr >> 24; - uint8_t const a1 = addr >> 16; - uint8_t const a2 = addr >> 8; - uint8_t const a3 = addr; - printf("Received %u from %u.%u.%u.%u:%u\n", udp_buffer[0], a0, a1, a2, a3, port); - return 0; } -static int SendFrame(int const fd, struct frame const *const frame) +static void ConnectionHandleSynExpiration(struct connection *self, int fd) { - udp_buffer[0] = frame->type; - struct sockaddr_in const addr = { - .sin_family = AF_INET, - .sin_port = htons(frame->port), - .sin_addr.s_addr = htonl(frame->addr), + struct frame const outgoing = { + .addr = self->addr, + .port = self->port, + .connection_id = self->connection_id, + .type = FT_SYN, + .syn = { + .tick = self->tick, + .prev_is_lost = self->prev_is_lost, + .roundtrip_prev = self->server_roundtrip_prev, + }, }; - ssize_t const ret = sendto( - fd, udp_buffer, 1, 0, (struct sockaddr const *)&addr, sizeof(addr)); - if (ret < 0) { - int const err = errno; - perror("sendto failed"); - return -err; + SendFrame(fd, &outgoing); +} + +static void HandleFrame(struct frame const *frame, struct timespec now, int fd) +{ + uint32_t const connection_id = frame->connection_id; + if (connection_id >= CONNECTIONS_MAX) { + return; + } + if (frame->type == FT_HANDSHAKE) { + if (LOG_DEBUG) { + fprintf(stderr, "FT_HANDSHAKE(tick_period=%" PRIu32 ")\n", frame->handshake.tick_period); + } + struct frame outgoing = *frame; + bool const is_tick_period_valid = + frame->handshake.tick_period >= TICK_PERIOD_MIN_MS && + frame->handshake.tick_period <= TICK_PERIOD_MAX_MS; + if (!is_tick_period_valid) { + outgoing.type = FT_RST; + SendFrame(fd, &outgoing); + return; + } + for (size_t i = 0; i < CONNECTIONS_MAX; i++) { + if (!g_connections[i].exist) { + g_connections[i] = (struct connection) { + .exist = true, + .tick_period = timespec_from_ms(frame->handshake.tick_period), + .addr = frame->addr, + .port = frame->port, + .connection_id = i, + .last_syn = now, + }; + outgoing.type = FT_HANDSHAKE_RESP; + outgoing.connection_id = i; + SendFrame(fd, &outgoing); + if (LOG_DEBUG) { + fprintf(stderr, "New connection id=%" PRIu32 "\n", connection_id); + } + return; + } + } + outgoing.type = FT_RST; + SendFrame(fd, &outgoing); + return; + } + if (g_connections[connection_id].exist) { + ConnectionHandleFrame(&g_connections[connection_id], frame, now, fd); + } + if (frame->type == FT_RST) { + g_connections[connection_id].exist = false; + if (LOG_DEBUG) { + fprintf(stderr, "Connection closed id=%" PRIu32 "\n", connection_id); + } } - printf("message sent\n"); - return 0; } int main(void) @@ -93,54 +153,100 @@ int main(void) return 1; } fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); + uint32_t const addr = INADDR_ANY; + uint16_t const port = 50037u; struct sockaddr_in const serveraddr = { .sin_family = AF_INET, - .sin_port = htons(50037u), - .sin_addr.s_addr = htonl(INADDR_ANY), + .sin_port = htons(port), + .sin_addr.s_addr = htonl(addr), }; + struct sigaction sa = { + .sa_handler = sigintHandler, + }; + sigaction(SIGINT, &sa, NULL); if (bind(fd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) { perror("bind failed"); return 1; } + { + uint8_t const a0 = addr >> 24; + uint8_t const a1 = addr >> 16; + uint8_t const a2 = addr >> 8; + uint8_t const a3 = (uint8_t)addr; + fprintf(stderr, "Server is listening on %u.%u.%u.%u:%u\n", a0, a1, a2, a3, port); + } struct pollfd fds[1] = { { .fd = fd, .events = POLLIN, }, }; - for (;;) { - int const timeout_msecs = 500; - int const pollret = poll(fds, sizeof(fds)/sizeof(*fds), timeout_msecs); + int timeout_ms = 10000u; + while (!g_should_exit) { + int const pollret = poll(fds, sizeof(fds)/sizeof(*fds), timeout_ms); + int const poll_err = errno; + struct timespec now; + if (-1 == clock_gettime(CLOCK_MONOTONIC, &now)) { + int const err = errno; + fprintf(stderr, + "clock_gettime(CLOCK_MONOTONIC, &now) failed (%d): \"%s\"", + err, strerror(err)); + } if (pollret > 0) { if (fds[0].revents & POLLIN) { while (1) { struct frame frame = { .type = FT_NONE, }; - int const ret = ReceiveFrame(fd, &frame); + int const ret = ReceiveFrame(fds[0].fd, &frame); if (ret) { break; } - if (frame.type == FT_HELLO) { - SendFrame(fd, &frame); - } + HandleFrame(&frame, now, fds[0].fd); } } if (fds[0].revents & POLLRDNORM) { - printf("POLLRDNORM, ignoring...\n"); + fprintf(stderr, "POLLRDNORM, ignoring...\n"); } if (fds[0].revents & POLLRDBAND) { - printf("POLLRDBAND, ignoring...\n"); + fprintf(stderr, "POLLRDBAND, ignoring...\n"); } if (fds[0].revents & POLLPRI) { - printf("POLLPRI, ignoring...\n"); + fprintf(stderr, "POLLPRI, ignoring...\n"); } if (fds[0].revents & POLLPRI) { - printf("POLLPRI, ignoring...\n"); + fprintf(stderr, "POLLPRI, ignoring...\n"); } if (fds[0].revents & POLLHUP) { - printf("POLLHUP, ignoring...\n"); + fprintf(stderr, "POLLHUP, ignoring...\n"); } if (fds[0].revents & POLLERR) { - printf("POLLERR, ignoring...\n"); + fprintf(stderr, "POLLERR, ignoring...\n"); } if (fds[0].revents & POLLNVAL) { - printf("POLLNVAL, exiting...\n"); + fprintf(stderr, "POLLNVAL, exiting...\n"); return 1; } + } else if (pollret < 0) { + if (poll_err == EINTR) { + fprintf(stderr, "SIGINT received\n"); + break; + } + fprintf(stderr, + "poll() failed (%d): \"%s\"", + poll_err, strerror(poll_err)); + } + timeout_ms = 10000u; + for (size_t i = 0; i < CONNECTIONS_MAX; i++) { + struct connection *const conn = g_connections + i; + if (!conn->exist) { + continue; + } + struct timespec const expiration = timespec_add( + conn->last_syn, conn->tick_period); + if (timespec_ge(now, expiration)) { + ConnectionHandleSynExpiration(conn, fd); + // Using `expiration` instead of `now` time here to keep SYN + // rate consistent, although it might have a little jitter. + conn->last_syn = expiration; + } + timeout_ms = timespec_to_ms( + timespec_min( + timespec_from_ms(timeout_ms), + timespec_sub(expiration, now))); } } close(fd); |