summaryrefslogtreecommitdiff
path: root/proto.c
diff options
context:
space:
mode:
Diffstat (limited to 'proto.c')
-rw-r--r--proto.c196
1 files changed, 196 insertions, 0 deletions
diff --git a/proto.c b/proto.c
new file mode 100644
index 0000000..b1f4e02
--- /dev/null
+++ b/proto.c
@@ -0,0 +1,196 @@
+/* SPDX-License-Identifier: Unlicense */
+
+#include "proto.h"
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+
+static inline uint8_t GetU8NE(void const *data)
+{
+ return ((uint8_t const *)data)[0];
+}
+
+static inline uint16_t GetU16NE(void const *data)
+{
+ return (uint16_t)(((uint8_t const *)data)[1]) |
+ ((uint16_t)(((uint8_t const *)data)[0]) << 8);
+}
+
+static inline uint32_t GetU32NE(void const *data)
+{
+ return (uint32_t)(((uint8_t const *)data)[3]) |
+ ((uint32_t)(((uint8_t const *)data)[2]) << 8) |
+ ((uint32_t)(((uint8_t const *)data)[1]) << 16) |
+ ((uint32_t)(((uint8_t const *)data)[0]) << 24);
+}
+
+static inline void SetU8NE(void *buffer, uint8_t v)
+{
+ ((uint8_t *)buffer)[0] = v;
+}
+
+static inline void SetU16NE(void *buffer, uint16_t v)
+{
+ ((uint8_t *)buffer)[0] = (v >> 8) & 0xff;
+ ((uint8_t *)buffer)[1] = v & 0xff;
+}
+
+static inline void SetU32NE(void *buffer, uint32_t v)
+{
+ ((uint8_t *)buffer)[3] = v & 0xff;
+ ((uint8_t *)buffer)[2] = (v >> 8) & 0xff;
+ ((uint8_t *)buffer)[1] = (v >> 16) & 0xff;
+ ((uint8_t *)buffer)[0] = (v >> 24) & 0xff;
+}
+
+static char ByteToPrintableChar(uint32_t byte)
+{
+ return (byte >= 0x20 && byte <= 0x7f) ? byte : '.';
+}
+
+static void FPrintRaw(FILE *s, void const *data_arg, size_t size)
+{
+ const size_t cols = 16;
+ const size_t section_width = 8;
+ if (size == 0) {
+ return;
+ }
+ uint8_t const *data = data_arg;
+ for (size_t line = 0; line < (size - 1) / cols + 1; line++) {
+ fprintf(s, "%08zx: ", line * cols);
+ for (size_t c = 0; c < cols; c++) {
+ fprintf(s, "%02x ", data[line * cols + c]);
+ if (c % section_width == section_width - 1 && c != cols - 1) {
+ fprintf(s, " ");
+ }
+ }
+ fprintf(s, " |");
+ for (size_t c = 0; c < cols; c++) {
+ fprintf(s, "%c", ByteToPrintableChar(data[line * cols + c]));
+ if (c % section_width == section_width - 1 && c != cols - 1) {
+ fprintf(s, " ");
+ }
+ }
+ fprintf(s, "|\n");
+ }
+}
+
+struct frame ParseFrame(void const *buffer_arg, size_t buffer_size)
+{
+ assert(buffer_size >= 8);
+ uint8_t const *buffer = buffer_arg;
+ if (LOG_TRACE) {
+ FPrintRaw(stderr, buffer, buffer_size);
+ }
+ if (GetU16NE(buffer) != 0) {
+ return (struct frame){0};
+ }
+ uint16_t frame_type = GetU16NE(buffer + 2);
+ if (frame_type == FT_NONE || frame_type >= FT_MAX) {
+ return (struct frame){0};
+ }
+ uint32_t connection_id = GetU32NE(buffer + 4);
+ buffer += 8;
+ buffer_size -= 8;
+ (void) buffer_size;
+ switch ((enum frame_type)frame_type) {
+ case FT_NONE:
+ assert(0);
+ break;
+ case FT_MAX:
+ assert(0);
+ break;
+ case FT_HANDSHAKE:
+ case FT_HANDSHAKE_RESP:
+ assert(buffer_size >= sizeof(uint32_t));
+ return (struct frame){
+ .connection_id = connection_id,
+ .type = frame_type,
+ .handshake.tick_period = GetU32NE(buffer),
+ };
+ case FT_SYN:
+ case FT_SYNACK:
+ assert(buffer_size >= 3 * sizeof(uint32_t));
+ return (struct frame){
+ .connection_id = connection_id,
+ .type = frame_type,
+ .syn = {
+ .tick = GetU32NE(buffer),
+ .roundtrip_prev = GetU32NE(buffer + 8),
+ .repeat = GetU32NE(buffer + 12),
+ .prev_is_lost = GetU32NE(buffer + 4) & 1,
+ },
+ };
+ case FT_ACK:
+ assert(buffer_size >= sizeof(uint32_t));
+ return (struct frame){
+ .connection_id = connection_id,
+ .type = frame_type,
+ .ack.tick = GetU32NE(buffer),
+ };
+ case FT_RST:
+ return (struct frame){
+ .connection_id = connection_id,
+ .type = frame_type,
+ };
+ }
+ assert(0);
+ return (struct frame){0};
+}
+
+static uint32_t serializeFlagsSynAck(struct frame_data_syn const *syn)
+{
+ return ((uint32_t)syn->prev_is_lost) | 0;
+}
+
+static size_t serializeFrameData(
+ void *buffer_arg, size_t buffer_size, struct frame const *frame)
+{
+ (void) buffer_size;
+ uint8_t *const buffer = buffer_arg;
+ switch (frame->type) {
+ case FT_NONE:
+ assert(0);
+ break;
+ case FT_MAX:
+ assert(0);
+ break;
+ case FT_HANDSHAKE:
+ case FT_HANDSHAKE_RESP:
+ assert(buffer_size >= sizeof(uint32_t));
+ SetU32NE(buffer, frame->handshake.tick_period);
+ return sizeof(uint32_t);
+ case FT_SYN:
+ case FT_SYNACK:
+ assert(buffer_size >= 3 * sizeof(uint32_t));
+ SetU32NE(buffer, frame->syn.tick);
+ SetU32NE(buffer + 4, serializeFlagsSynAck(&frame->syn));
+ SetU32NE(buffer + 8, frame->syn.roundtrip_prev);
+ SetU32NE(buffer + 16, frame->syn.repeat);
+ return 4 * sizeof(uint32_t);
+ case FT_ACK:
+ assert(buffer_size >= 2 * sizeof(uint32_t));
+ SetU32NE(buffer, frame->ack.tick);
+ return sizeof(uint32_t);
+ case FT_RST:
+ return 0;
+ }
+ assert(0);
+ return 0;
+}
+
+size_t SerializeFrame(void *buffer_arg, size_t buffer_size, struct frame const *frame)
+{
+ (void) buffer_size;
+ uint8_t *const buffer = buffer_arg;
+ assert(buffer_size >= 8);
+ SetU16NE(buffer, 0);
+ SetU16NE(buffer + 2, frame->type);
+ SetU32NE(buffer + 4, frame->connection_id);
+ size_t const size = 8 + serializeFrameData(buffer + 8, buffer_size - 8, frame);
+ if (LOG_TRACE) {
+ FPrintRaw(stderr, buffer, size);
+ }
+ return size;
+}