summaryrefslogtreecommitdiff
path: root/server.c
diff options
context:
space:
mode:
authorOxore <oxore@protonmail.com>2023-12-28 00:04:20 +0300
committerOxore <oxore@protonmail.com>2023-12-28 00:04:20 +0300
commit18893127524d87e47a948b9a92d8d8b2ab869852 (patch)
tree1491660a439221174a776b9c7f38f05234590cdb /server.c
parent4faf778b6ec4b9d8ab0c19e22d27c76f4c67ab28 (diff)
WIP
Diffstat (limited to 'server.c')
-rw-r--r--server.c258
1 files changed, 182 insertions, 76 deletions
diff --git a/server.c b/server.c
index ffb7c42..8d00dc7 100644
--- a/server.c
+++ b/server.c
@@ -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);