From bf4c7f8f5e0ccb9e3eae897df59d602841aa71c4 Mon Sep 17 00:00:00 2001 From: Rokas Puzonas Date: Thu, 21 Mar 2024 21:57:00 +0200 Subject: [PATCH] initial commit --- .gitignore | 2 + README.md | 4 + build.zig | 82 ++++++++++++++++++ compile_flags.txt | 14 ++++ libflex/libflex.c | 206 ++++++++++++++++++++++++++++++++++++++++++++++ libflex/libflex.h | 71 ++++++++++++++++ rolexhound/main.c | 190 ++++++++++++++++++++++++++++++++++++++++++ smartwatch/main.c | 52 ++++++++++++ 8 files changed, 621 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 build.zig create mode 100644 compile_flags.txt create mode 100644 libflex/libflex.c create mode 100644 libflex/libflex.h create mode 100644 rolexhound/main.c create mode 100644 smartwatch/main.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4c82b07 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +zig-cache +zig-out diff --git a/README.md b/README.md new file mode 100644 index 0000000..b03a4a8 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# Over engineered custom network protocol experiment + +Youtube tutorial: https://www.youtube.com/watch?v=AS_nxNS6YKY + diff --git a/build.zig b/build.zig new file mode 100644 index 0000000..d80fd44 --- /dev/null +++ b/build.zig @@ -0,0 +1,82 @@ +const std = @import("std"); + +fn linkPkgConfigOutput(b: *std.build.Builder, obj: *std.build.LibExeObjStep, pkg_config_args: []const []const u8) !void { + var argv = std.ArrayList([]const u8).init(b.allocator); + try argv.append("pkg-config"); + try argv.appendSlice(pkg_config_args); + + const result = try std.ChildProcess.exec(.{ + .allocator = b.allocator, + .argv = argv.items + }); + + const stdout_trimmed = std.mem.trim(u8, result.stdout, " \n"); + var cflags_iter = std.mem.splitScalar(u8, stdout_trimmed, ' '); + while (cflags_iter.next()) |cflag| { + if (std.mem.startsWith(u8, cflag, "-l")) { + obj.linkSystemLibrary(cflag[2..]); + } else if (std.mem.startsWith(u8, cflag, "-I")) { + obj.addIncludePath(.{ .path = cflag[2..] }); + } + } +} + +pub fn build(b: *std.build.Builder) !void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const libflex = b.addStaticLibrary(.{ + .name = "flex", + .target = target, + .optimize = optimize, + }); + libflex.addCSourceFile(.{ + .file = .{ .path = "libflex/libflex.c" }, + .flags = &.{} + }); + libflex.linkLibC(); + libflex.installHeader("libflex/libflex.h", "libflex.h"); + + { + const rolexhound = b.addExecutable(.{ + .name = "rolexhound", + .target = target, + .optimize = optimize, + .root_source_file = .{ .path = "rolexhound/main.c" } + }); + rolexhound.linkLibrary(libflex); + rolexhound.linkLibC(); + try linkPkgConfigOutput(b, rolexhound, &.{ "--cflags", "--libs", "libnotify" }); + + b.installArtifact(rolexhound); + + const run_rolexhound = b.addRunArtifact(rolexhound); + if (b.args) |args| { + run_rolexhound.addArgs(args); + } + + const run_step = b.step("rolexhound", "Run the server"); + run_step.dependOn(&run_rolexhound.step); + } + + { + const smartwatch = b.addExecutable(.{ + .name = "smartwatch", + .target = target, + .optimize = optimize, + .root_source_file = .{ .path = "smartwatch/main.c" } + }); + smartwatch.linkLibrary(libflex); + smartwatch.linkLibC(); + + b.installArtifact(smartwatch); + + const run_smartwatch = b.addRunArtifact(smartwatch); + if (b.args) |args| { + run_smartwatch.addArgs(args); + } + + const run_step = b.step("smartwatch", "Run the client"); + run_step.dependOn(&run_smartwatch.step); + } +} diff --git a/compile_flags.txt b/compile_flags.txt new file mode 100644 index 0000000..b1bd933 --- /dev/null +++ b/compile_flags.txt @@ -0,0 +1,14 @@ +-Ilibflex/ +-I/usr/include/gdk-pixbuf-2.0 +-I/usr/include/glib-2.0 +-I/usr/lib/glib-2.0/include +-I/usr/include/blkid +-I/usr/include/libmount +-I/usr/include/sysprof-6 +-pthread +-I/usr/include/libpng16 +-lnotify +-lgdk_pixbuf-2.0 +-lgio-2.0 +-lgobject-2.0 +-lglib-2.0 diff --git a/libflex/libflex.c b/libflex/libflex.c new file mode 100644 index 0000000..7939747 --- /dev/null +++ b/libflex/libflex.c @@ -0,0 +1,206 @@ +#include +#include +#include + +#include "libflex.h" + +#define ARRAY_LEN(arr) sizeof(arr) / sizeof(arr[0]) + +const flx_opt OPTION_RANGES[5] = { + FLX_WATCH_REM, + FLX_QUIT_ERROR, + FLX_NOTIFY_MOVE, + FLX_REPLY_INVALID_DATA, + FLX_STATUS_ERR_READ_INOTIFY, +}; + +const flx_opt ACTION_SIZES[5] = { + FLX_DLEN_WATCH, + FLX_DLEN_QUIT, + FLX_DLEN_NOTIFY, + FLX_DLEN_REPLY, + FLX_DLEN_STATUS, +}; + +const flx_opt LAST_ACTION_INDEX = ARRAY_LEN(OPTION_RANGES) - 1; + +void flx_serialize_result_init(struct flx_serialize_result *result) { + result->size = -1; + result->reply = FLX_REPLY_UNSET; +} + +void flx_msg_init(struct flx_msg *msg) { + msg->action = FLX_ACT_UNSET; + msg->option = FLX_UNSET_UNSET; + msg->size = 0; + + msg->data = NULL; + msg->data_len = 0; +} + +void flx_msg_free(struct flx_msg *msg) { + if (msg->data == NULL) return; + + for (int i = 0; i < msg->data_len; i++) { + if (msg->data[i] == NULL) continue; + free(msg->data[i]); + } + free(msg->data); + + msg->data = NULL; +} + +void flx_msg_reset(struct flx_msg *msg) { + flx_msg_free(msg); + flx_msg_init(msg); +} + +void flx_serialize( + uint8_t out_buffer[FLX_PKT_MAXIMUM_SIZE], + struct flx_msg *msg, + struct flx_serialize_result *result +) { + int valid_data_length = -1; + uint8_t data_size = 0; + flx_serialize_result_init(result); + + if (msg->action <= LAST_ACTION_INDEX) { + out_buffer[0] = msg->action; + valid_data_length = ACTION_SIZES[msg->action]; + } else { + result->reply = FLX_REPLY_BAD_ACTION; + return; + } + + if (valid_data_length != msg->data_len) { + result->reply = FLX_REPLY_BAD_SIZE; + return; + } + + if (msg->option > OPTION_RANGES[msg->option]) { + result->reply = FLX_REPLY_BAD_OPTION; + return; + } + + out_buffer[1] = msg->option; + + if (valid_data_length != 0 && msg->data == NULL) { + result->reply = FLX_REPLY_INVALID_DATA; + return; + } + + for (int i = 0; i < msg->data_len; i++) { + if (msg->data[i] == NULL) { + result->reply = FLX_REPLY_INVALID_DATA; + return; + } + + int running_data_size = 0; + for (int j = 0; j < FLX_PKT_MINIMUM_SIZE - FLX_PKT_MAXIMUM_SIZE - data_size; j++) { + running_data_size += 1; + out_buffer[FLX_PKT_MINIMUM_SIZE + data_size + j] = msg->data[i][j]; + + if (msg->data[i][j] == 0) break; + } + + data_size += running_data_size; + } + + out_buffer[2] = data_size; + result->size = FLX_PKT_MINIMUM_SIZE + data_size; + result->reply = FLX_REPLY_VALID; + return; +} + +void flx_deserialize( + uint8_t in_buffer[FLX_PKT_MINIMUM_SIZE], + struct flx_msg *msg, + struct flx_serialize_result *result +) { + int valid_data_length = -1; + int data_size_index = 0; + int *data_sizes = NULL; + int data_size = 0; + int data_offset = FLX_PKT_MINIMUM_SIZE; + flx_serialize_result_init(result); + flx_msg_reset(msg); + + if (in_buffer[0] <= LAST_ACTION_INDEX) { + msg->action = in_buffer[0]; + valid_data_length = ACTION_SIZES[msg->action]; + } else { + result->reply = FLX_REPLY_BAD_ACTION; + return; + } + + if (in_buffer[1] > OPTION_RANGES[msg->action]) { + result->reply = FLX_REPLY_BAD_OPTION; + return; + } + + msg->option = in_buffer[1]; + + if (valid_data_length == 0) { + if (in_buffer[2] != 0) { + result->reply = FLX_REPLY_BAD_SIZE; + return; + } else { + result->reply = FLX_REPLY_VALID; + } + + return; + } + + msg->size = in_buffer[2]; + data_sizes = (int *)malloc(sizeof(int) * valid_data_length); + + for (int i = FLX_PKT_MINIMUM_SIZE; i < FLX_PKT_MAXIMUM_SIZE; i++) { + if (i > msg->size + FLX_PKT_MINIMUM_SIZE) { + free(data_sizes); + result->reply = FLX_REPLY_BAD_SIZE; + return; + } + + if (data_size_index == valid_data_length) { + if (data_size == 1) { + free(data_sizes); + result->reply = FLX_REPLY_BAD_SIZE; + return; + } + break; + } + + if (in_buffer[i] == 0) { + data_sizes[data_size_index] = ++data_size; + data_size_index++; + data_size = 0; + continue; + } + + if (in_buffer[i] < '!' || in_buffer[i] > '~') { + free(data_sizes); + result->reply = FLX_REPLY_INVALID_DATA; + return; + } + + data_size++; + } + + msg->data = (char**)malloc(sizeof(char *) * valid_data_length); + msg->data_len = valid_data_length; + + for (int i = 0; i < valid_data_length; i++) { + msg->data[i] = (char *)malloc(sizeof(char) * data_sizes[i]); + + for (int j = 0; j < data_sizes[i]; j++) { + msg->data[i][j] = in_buffer[j + data_offset]; + } + data_offset += data_sizes[i]; + } + + result->reply = FLX_REPLY_VALID; + result->size = FLX_PKT_MINIMUM_SIZE + msg->size; + free(data_sizes); + return; +} + diff --git a/libflex/libflex.h b/libflex/libflex.h new file mode 100644 index 0000000..5b69a3c --- /dev/null +++ b/libflex/libflex.h @@ -0,0 +1,71 @@ +#include + +typedef uint8_t flx_act; +typedef uint8_t flx_opt; + +#define FLX_ACT_WATCH 0x00 +#define FLX_ACT_QUIT 0x01 +#define FLX_ACT_NOTIFY 0x02 +#define FLX_ACT_REPLY 0x03 +#define FLX_ACT_STATUS 0x04 +#define FLX_ACT_UNSET 0xFF + +#define FLX_WATCH_ADD 0x00 +#define FLX_WATCH_REM 0x01 + +#define FLX_QUIT_USER 0x00 +#define FLX_QUIT_ERROR 0x01 + +#define FLX_NOTIFY_CREATE 0x00 +#define FLX_NOTIFY_DELETE 0x01 +#define FLX_NOTIFY_ACCESS 0x02 +#define FLX_NOTIFY_CLOSE 0x03 +#define FLX_NOTIFY_MODIFY 0x04 +#define FLX_NOTIFY_MOVE 0x05 + +#define FLX_REPLY_VALID 0x00 +#define FLX_REPLY_BAD_SIZE 0x01 +#define FLX_REPLY_BAD_ACTION 0x02 +#define FLX_REPLY_BAD_OPTION 0x03 +#define FLX_REPLY_BAD_PATH 0x04 +#define FLX_REPLY_INVALID_DATA 0x05 +#define FLX_REPLY_UNSET 0xFF + +#define FLX_STATUS_SUCCESS 0x00 +#define FLX_STATUS_ERR_INIT_INOTIFY 0x01 +#define FLX_STATUS_ERR_ADD_WATCH 0x02 +#define FLX_STATUS_ERR_READ_INOTIFY 0x03 + +#define FLX_UNSET_UNSET 0xFF + +#define FLX_PKT_MINIMUM_SIZE 3 +#define FLX_PKT_MAXIMUM_SIZE 255 + +#define FLX_DLEN_WATCH 1 +#define FLX_DLEN_QUIT 0 +#define FLX_DLEN_NOTIFY 2 +#define FLX_DLEN_REPLY 0 +#define FLX_DLEN_STATUS 0 +#define FLX_DLEN_UNSET 0 + + +struct flx_msg { + flx_act action; + flx_opt option; + uint8_t size; + + char **data; + uint32_t data_len; +}; + +struct flx_serialize_result { + int size; + flx_opt reply; +}; + +void flx_serialize_result_init(struct flx_serialize_result *result); +void flx_msg_init(struct flx_msg *msg); +void flx_msg_free(struct flx_msg *msg); +void flx_msg_reset(struct flx_msg *msg); +void flx_serialize(uint8_t out_buffer[FLX_PKT_MAXIMUM_SIZE], struct flx_msg *msg, struct flx_serialize_result *result); +void flx_deserialize(uint8_t in_buffer[FLX_PKT_MINIMUM_SIZE], struct flx_msg *msg, struct flx_serialize_result *result); diff --git a/rolexhound/main.c b/rolexhound/main.c new file mode 100644 index 0000000..4868517 --- /dev/null +++ b/rolexhound/main.c @@ -0,0 +1,190 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +struct daemon_state { + bool running; + int inotify_fd; + int inotify_wd; +}; +static struct daemon_state g_state = { + .running = true +}; + +void daemon_deinit(struct daemon_state *state) { + inotify_rm_watch(g_state.inotify_fd, g_state.inotify_wd); +} + +void signal_handler(int signal) { + g_state.running = false; + printf("Stoping daemon\n"); + daemon_deinit(&g_state); + exit(0); +} + +#define PORT 12345 + +int main() { + int server_fd = socket(AF_INET, SOCK_STREAM, 0); + if (server_fd == -1) { + perror("socket"); + return -1; + } + + int on = 1; + setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + struct sockaddr_in server_addr; + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = htonl(INADDR_ANY); + server_addr.sin_port = htons(PORT); + + if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) { + perror("bind"); + return -1; + } + + if (listen(server_fd, 1) == -1) { + perror("listen"); + return -1; + } + + printf("listening on 0.0.0.0:%d...\n", PORT); + + struct sockaddr_storage client_addr; + socklen_t client_addrlen; + int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_addrlen); + if (client_fd == -1) { + perror("accept"); + return -1; + } + + printf("Accepted connection!\n"); + + struct flx_msg *read_msg = malloc(sizeof(struct flx_msg)); + struct flx_msg *send_msg = malloc(sizeof(struct flx_msg)); + struct flx_serialize_result result; + + flx_msg_init(read_msg); + flx_msg_init(send_msg); + flx_serialize_result_init(&result); + + uint8_t socket_buffer[FLX_PKT_MAXIMUM_SIZE]; + while (true) { + int bytes_read = recv(client_fd, socket_buffer, sizeof(socket_buffer), 0); + if (bytes_read == 0 || bytes_read == -1) { + perror("recv"); + return -1; + } + + flx_deserialize(socket_buffer, read_msg, &result); + if (result.reply != FLX_REPLY_VALID) { + printf("Received error %x from client\n", result.reply); + + send_msg->action = FLX_ACT_REPLY; + send_msg->option = result.reply; + send_msg = 0; + + flx_serialize(socket_buffer, send_msg, &result); + write(client_fd, socket_buffer, result.size); + continue; + } + + if (read_msg->action != FLX_ACT_WATCH) { + continue; + } + + break; + } + + char *path = strdup(read_msg->data[0]); + + printf("Watching path: %s\n", path); + + int inotify_fd = inotify_init(); + if (inotify_fd == -1) { + perror("inotify_init"); + return -1; + } + + int inotify_wd = inotify_add_watch(inotify_fd, path, IN_CREATE | IN_DELETE | IN_ACCESS | IN_CLOSE_WRITE | IN_MODIFY | IN_MOVE_SELF); + if (inotify_fd == -1) { + perror("inotify_add_watch"); + return -1; + } + + char *base_path = strrchr(path, '/'); + if (base_path == NULL) { + base_path = path; + } else { + base_path += 1; + } + + signal(SIGABRT, signal_handler); + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); + + uint8_t buffer[4096]; + while (g_state.running) { + printf("Waiting for inotify event.\n"); + + int read_length = read(inotify_fd, buffer, sizeof(buffer)); + if (read_length == -1) { + perror("read"); + return -1; + } + + int buffer_used = 0; + while (buffer_used < read_length) { + struct inotify_event *event = (struct inotify_event*)&buffer[buffer_used]; + + if (event->mask & IN_CREATE) { + send_msg->option = FLX_NOTIFY_CREATE; + } + if (event->mask & IN_DELETE) { + send_msg->option = FLX_NOTIFY_DELETE; + } + if (event->mask & IN_ACCESS) { + send_msg->option = FLX_NOTIFY_ACCESS; + } + if (event->mask & IN_CLOSE_WRITE) { + send_msg->option = FLX_NOTIFY_CLOSE; + } + if (event->mask & IN_MODIFY) { + send_msg->option = FLX_NOTIFY_MODIFY; + } + if (event->mask & IN_MOVE_SELF) { + send_msg->option = FLX_NOTIFY_MOVE; + } + + buffer_used += sizeof(struct inotify_event) + event->len; + + if (send_msg->option == FLX_UNSET_UNSET) { + continue; + } + + send_msg->action = FLX_ACT_NOTIFY; + send_msg->data_len = FLX_DLEN_NOTIFY; + send_msg->data = malloc(sizeof(char*) * FLX_DLEN_NOTIFY); + + send_msg->data[0] = base_path; + send_msg->data[1] = path; + + flx_serialize(socket_buffer, send_msg, &result); + } + } + + daemon_deinit(&g_state); + + return 0; +} diff --git a/smartwatch/main.c b/smartwatch/main.c new file mode 100644 index 0000000..01fd7db --- /dev/null +++ b/smartwatch/main.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +#define PORT 12345 + +int main(int argc, char **argv) { + if (argc < 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return -1; + } + + char *path = argv[1]; + + int client_fd = socket(AF_INET, SOCK_STREAM, 0); + if (client_fd == -1) { + perror("socket"); + return -1; + } + + struct sockaddr_in server_addr; + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + server_addr.sin_port = htons(PORT); + + if (connect(client_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) { + perror("connect"); + return -1; + } + + uint8_t payload_buffer[256]; + struct flex_byte_buffer bb = flex_bb_init(payload_buffer, sizeof(payload_buffer)); + flex_bb_push_buffer8(&bb, (uint8_t*)path, strlen(path)); + + uint8_t send_buffer[512]; + int send_size = flex_serialize(send_buffer, sizeof(send_buffer), 1, bb.data, bb.size); + + write(client_fd, send_buffer, send_size); + + while (true) { + } + + close(client_fd); + + return 0; +}