From 12f449688331efc5145fe03ee11f8d70c20ef305 Mon Sep 17 00:00:00 2001 From: pantonshire Date: Thu, 18 Dec 2025 16:34:03 +0000 Subject: [PATCH] initial commit --- include/client.h | 9 +++ include/defaults.h | 3 + include/server.h | 9 +++ include/str.h | 26 +++++++ include/util.h | 14 ++++ meson.build | 24 ++++++ src/client.c | 5 ++ src/main.c | 31 ++++++++ src/server.c | 179 +++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 300 insertions(+) create mode 100644 include/client.h create mode 100644 include/defaults.h create mode 100644 include/server.h create mode 100644 include/str.h create mode 100644 include/util.h create mode 100644 meson.build create mode 100644 src/client.c create mode 100644 src/main.c create mode 100644 src/server.c diff --git a/include/client.h b/include/client.h new file mode 100644 index 0000000..afa6f49 --- /dev/null +++ b/include/client.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +struct client_conf { + uint16_t port; +}; + +int client(const struct client_conf *conf); diff --git a/include/defaults.h b/include/defaults.h new file mode 100644 index 0000000..18e43c7 --- /dev/null +++ b/include/defaults.h @@ -0,0 +1,3 @@ +#pragma once + +#define DEFAULT_PORT 11333 diff --git a/include/server.h b/include/server.h new file mode 100644 index 0000000..b78ed1d --- /dev/null +++ b/include/server.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +struct server_conf { + uint16_t port; +}; + +int server(const struct server_conf *conf); diff --git a/include/str.h b/include/str.h new file mode 100644 index 0000000..2306caf --- /dev/null +++ b/include/str.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +#define SV_LIT(S) (S "LITERAL_CHECK", (str_view_t) { S, sizeof(S) - 1 }) +#define SV_NULL ((str_view_t) { NULL, 0 }) + +typedef struct { + const char *ptr; + size_t len; +} str_view_t; + +static inline str_view_t sv_from_parts(const char *ptr, size_t len) { + return (str_view_t) { ptr, len }; +} + +static inline str_view_t sv_from_cstr(const char *ptr, size_t maxlen) { + size_t len = strnlen(ptr, maxlen); + return sv_from_parts(ptr, len); +} + +static inline str_view_t sv_from_cstr_unbounded(const char *ptr) { + size_t len = strlen(ptr); + return sv_from_parts(ptr, len); +} diff --git a/include/util.h b/include/util.h new file mode 100644 index 0000000..af7dfda --- /dev/null +++ b/include/util.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +#define LOG_ERR(FMT_STR, ...) do { \ + fprintf(stderr, FMT_STR "\n" __VA_OPT__(,) __VA_ARGS__); \ +} while (0) + +#define FAIL_WITH(RET, FMT_STR, ...) do { \ + LOG_ERR(FMT_STR, __VA_ARGS__); \ + return (RET); \ +} while (0) + +#define FAIL(FMT_STR, ...) FAIL_WITH(-1, FMT_STR, __VA_ARGS__) diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..84d4678 --- /dev/null +++ b/meson.build @@ -0,0 +1,24 @@ +project( + 'owdtest', + 'c', + default_options : ['c_std=c17'] +) + +sources = [ + 'src/main.c', + 'src/server.c', + 'src/client.c', +] + +includes = include_directories('include') + +args = [ + '-Wall' +] + +executable( + meson.project_name(), + sources, + include_directories: includes, + c_args: args +) diff --git a/src/client.c b/src/client.c new file mode 100644 index 0000000..2492172 --- /dev/null +++ b/src/client.c @@ -0,0 +1,5 @@ +#include "client.h" + +int client(const struct client_conf *conf) { + return 0; +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..cae9dc1 --- /dev/null +++ b/src/main.c @@ -0,0 +1,31 @@ +#include +#include +#include +#include + +#include "server.h" +#include "client.h" +#include "defaults.h" + +// #define DEFAULT_PORT 34802 + +int main(int argc, char **argv) { + union { + struct server_conf server; + struct client_conf client; + } conf; + + if (argc < 2) + return 1; + + if (!strcmp(argv[1], "server")) { + conf.server.port = DEFAULT_PORT; + return !!server(&conf.server); + } + if (!strcmp(argv[1], "client")) { + conf.client.port = DEFAULT_PORT; + return !!client(&conf.client); + } + + return 1; +} diff --git a/src/server.c b/src/server.c new file mode 100644 index 0000000..745c700 --- /dev/null +++ b/src/server.c @@ -0,0 +1,179 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "server.h" +#include "util.h" +#include "protocol.h" + +struct server_ctx { + int fd; +}; + +struct rx_info { + const void *name; + socklen_t namelen; + struct timeval tv; + uint8_t tv_set : 1; +}; + +static int server_init(const struct server_conf *conf, struct server_ctx *ctx) { + struct sockaddr_in server_addr = { 0 }; + int sock_fd; + int sockopt_int_yes = 1; + + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = htonl(INADDR_ANY); + server_addr.sin_port = htons(conf->port); + + if ((sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) + FAIL("failed to create socket: %s", strerror(errno)); + + if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &sockopt_int_yes, sizeof(sockopt_int_yes))) + FAIL("failed to set SO_REUSEADDR: %s", strerror(errno)); + + if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMP, &sockopt_int_yes, sizeof(sockopt_int_yes))) + FAIL("failed to set SO_TIMESTAMP: %s", strerror(errno)); + + if (bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr))) + FAIL("failed to bind: %s", strerror(errno)); + + ctx->fd = sock_fd; + return 0; +} + +static int send_to_client( + struct server_ctx *ctx, + const struct rx_info *rx, + void *msg, + size_t len) +{ + ssize_t send_res; + + do { + send_res = sendto( + ctx->fd, msg, len, 0, (const struct sockaddr *)rx->name, rx->namelen); + } while ((send_res < 0) && (errno == EINTR)); + + if (send_res < 0) + FAIL("failed to send message: %s", strerror(errno)); + + return 0; +} + +static void send_error_msg( + struct server_ctx *ctx, + const struct rx_info *rx, + enum owd_error_code err) +{ + struct owd_msg_err msg; + + owd_hdr_populate(&msg.hdr, OWD_MSG_ERR, OWD_DATA_LEN(msg)); + msg.err = (uint8_t)err; + if (send_to_client(ctx, rx, &msg, OWD_DATA_LEN(msg))) + LOG_ERR("failed to send error message"); +} + +static int handle_sync_request(struct server_ctx *ctx, const struct rx_info *rx) { + if (!rx->tv_set) + FAIL("no rx timestamp available"); + + return 0; +} + +static int handle_msg(struct server_ctx *ctx, char *buf, ssize_t buf_len, struct msghdr *msg_hdr) { + struct cmsghdr *cmsg; + struct owdhdr *hdr; + struct timeval tv; + struct rx_info rx; + + rx.tv_set = false; + rx.name = msg_hdr->msg_name; + rx.namelen = msg_hdr->msg_namelen; + + for (cmsg = CMSG_FIRSTHDR(msg_hdr); cmsg; cmsg = CMSG_NXTHDR(msg_hdr, cmsg)) { + switch (cmsg->cmsg_type) { + case SCM_TIMESTAMP: + if (cmsg->cmsg_len >= (sizeof(struct cmsghdr) + sizeof(struct timeval))) { + memcpy(&rx.tv, CMSG_DATA(cmsg), sizeof(tv)); + rx.tv_set = true; + } + break; + + default: + break; + } + } + + if (rx.tv_set) { + printf(" tv_sec=%lld tv_usec=%lld\n", (long long)rx.tv.tv_sec, (long long)rx.tv.tv_usec); + } + + if (buf_len < sizeof(struct owdhdr)) { + send_error_msg(ctx, &rx, OWD_ERR_BAD_HEADER); + FAIL("invalid packet: too short"); + } + + hdr = (struct owdhdr *)buf; + + switch (hdr->msg_type) { + case OWD_MSG_SYNC_REQUEST: + return handle_sync_request(ctx, &rx); + + default: + send_error_msg(ctx, &rx, OWD_ERR_BAD_TYPE); + FAIL("invalid packet: bad type"); + } +} + +static int server_loop(struct server_ctx *ctx) { + struct msghdr msg_hdr; + ssize_t recv_res; + struct iovec iov[1]; + char client_addr_buf[64] __attribute__((aligned)); + char msg_buf[256] __attribute__((aligned)); + char ctrl_buf[256] __attribute__((aligned)); + + for (;;) { + iov[0].iov_base = msg_buf; + iov[0].iov_len = sizeof(msg_buf); + + memset(&msg_hdr, 0, sizeof(msg_hdr)); + msg_hdr.msg_name = &client_addr_buf; + msg_hdr.msg_namelen = sizeof(client_addr_buf); + msg_hdr.msg_iov = iov; + msg_hdr.msg_iovlen = 1; + msg_hdr.msg_control = ctrl_buf; + msg_hdr.msg_controllen = sizeof(ctrl_buf); + + printf("namelen_before=%zu\n", (size_t)msg_hdr.msg_namelen); + + if ((recv_res = recvmsg(ctx->fd, &msg_hdr, 0)) < 0) { + if ((errno == EINTR) || (errno == EAGAIN) || (errno == EWOULDBLOCK)) + continue; + + LOG_ERR("recvmsg: %s", strerror(errno)); + continue; + } + + printf("namelen_after=%zu\n", (size_t)msg_hdr.msg_namelen); + + handle_msg(ctx, msg_buf, recv_res, &msg_hdr); + } +} + +int server(const struct server_conf *conf) { + struct server_ctx ctx; + + if (server_init(conf, &ctx)) + FAIL("failed to create server socket"); + + server_loop(&ctx); + + return 0; +}