initial commit
commit
12f4496883
@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct client_conf {
|
||||||
|
uint16_t port;
|
||||||
|
};
|
||||||
|
|
||||||
|
int client(const struct client_conf *conf);
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define DEFAULT_PORT 11333
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct server_conf {
|
||||||
|
uint16_t port;
|
||||||
|
};
|
||||||
|
|
||||||
|
int server(const struct server_conf *conf);
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#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__)
|
||||||
@ -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
|
||||||
|
)
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
#include "client.h"
|
||||||
|
|
||||||
|
int client(const struct client_conf *conf) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
@ -0,0 +1,179 @@
|
|||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue