summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar moon <moon@coffin.ir> 2025-09-13 23:13:07 +0330
committerGravatar moon <moon@coffin.ir> 2025-09-13 23:13:07 +0330
commit81440f9a4e2d8d105bade578bc1e11ec6780ca81 (patch)
tree5f5957256a61a7b604afd9c614bba42cbdedd108
parentnew things (diff)
feature impls + directories
-rw-r--r--Makefile15
-rwxr-xr-xall-in-one.sh4
-rw-r--r--plugin_example/plugin_example.c35
-rw-r--r--src/client.c (renamed from client.c)15
-rw-r--r--src/common.c (renamed from common.c)23
-rw-r--r--src/config.c99
-rw-r--r--src/external.h124
-rw-r--r--src/firehose.c (renamed from firehose.c)12
-rw-r--r--src/omen.c (renamed from omen.c)150
-rw-r--r--src/packet_storage.c (renamed from packet_storage.c)52
-rw-r--r--src/plugin.c163
-rw-r--r--src/ratelimit.c (renamed from ratelimit.c)4
-rw-r--r--src/security.c (renamed from security.c)12
-rw-r--r--src/server.c (renamed from server.c)251
-rw-r--r--src/sha256.c (renamed from sha256.c)10
15 files changed, 749 insertions, 220 deletions
diff --git a/Makefile b/Makefile
index fb50ec3..84dca71 100644
--- a/Makefile
+++ b/Makefile
@@ -2,18 +2,21 @@ clean:
rm -rf bin/*
mkdir bin 2> /dev/null || true
+sample_plugin:
+ clang plugin_example/plugin_example.c -shared -fPIC -o bin/plugin_example.so
+
client:
- clang client.c -o bin/client -lsodium -O3 -g
+ clang src/client.c -o bin/client -lsodium -lz -O3 -g
server:
- clang server.c -o bin/server -lsodium -O3 -g
+ clang src/server.c -o bin/server -lsodium -lz -O3 -g
release: clean
- clang client.c -o bin/client -O3
- clang server.c -o bin/server -O3
+ clang src/client.c -o bin/client -O3
+ clang src/server.c -o bin/server -O3
crun:
bin/client
-srun:
- bin/server \ No newline at end of file
+srun: sample_plugin
+ bin/server plugin=bin/plugin_example.so log=debug \ No newline at end of file
diff --git a/all-in-one.sh b/all-in-one.sh
new file mode 100755
index 0000000..8ea9762
--- /dev/null
+++ b/all-in-one.sh
@@ -0,0 +1,4 @@
+#!/usr/bin/env sh
+find . -type f -name "*.c" -exec sh -c 'echo "{}:"; echo "\"\"\""; cat "{}"; echo "
+\"\"\"
+"' \; > bin/compressed.txt \ No newline at end of file
diff --git a/plugin_example/plugin_example.c b/plugin_example/plugin_example.c
new file mode 100644
index 0000000..45a2131
--- /dev/null
+++ b/plugin_example/plugin_example.c
@@ -0,0 +1,35 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+//
+#include "../src/external.h"
+
+static void log_to_file(const char *message) {
+ FILE *log_file = fopen("bin/server_logs.log", "a");
+ if (log_file) {
+ fprintf(log_file, "%s\n", message);
+ fclose(log_file);
+ }
+}
+
+void MP_init_plugin() { // say hello to the user!
+ printf("hello from a plugin!\n");
+}
+
+void MP_modify_server_stats(struct relay_info *server_stats) {
+ // return a random number as motd each time
+ char str[] = "hello! random number: X";
+ str[ sizeof(str) - 2 ] = (rand() % 10) + '0';
+ memcpy(server_stats->motd, str, sizeof(str));
+}
+
+void MP_custom_log(int level, const char *msg) {
+ // log stuff to a file
+ char log_msg[ 512 ];
+ snprintf(log_msg, sizeof(log_msg), "[level %d] %s", level, msg);
+ log_to_file(log_msg);
+}
+
+void MP_on_server_shutdown() { // goodbye!
+ printf("bye!\n");
+} \ No newline at end of file
diff --git a/client.c b/src/client.c
index 5270204..ac47307 100644
--- a/client.c
+++ b/src/client.c
@@ -356,16 +356,13 @@ int main() {
printf(" Neighbors: %u\n", info.statistics.neighbors);
printf(" Held Packets: %lu\n", info.statistics.held_packets);
- FILE* temp_file = fopen("test_packet.txt", "w");
- if (temp_file) {
- fprintf(temp_file, "this is a test message from the moonpaste client.");
- fclose(temp_file);
- }
-
keypair author = generate_keypair();
- struct data_packet new_packet = create_packet("test_packet.txt", author, 0, NULL);
- new_packet = run_pow(new_packet, info.capabilities.leading_zeros);
+ const char input[] = "this is a test message from the moonpaste client.";
+
+ struct data_packet new_packet
+ = create_packet_from_bytes((byte*) input, sizeof(input), 0, NULL, author);
+ new_packet = run_pow(new_packet, info.capabilities.leading_zeros);
printf("hash: " hash_fmt "\n", hash_ref(new_packet.hash));
printf("pushing new packet to server...\n");
@@ -428,8 +425,6 @@ int main() {
printf("\nstarting firehose stream. Press Ctrl+C to exit.\n");
get_firehose_stream("127.0.0.1", 8040, 0, 10);
- remove("test_packet.txt");
-
return 0;
}
diff --git a/common.c b/src/common.c
index 27f2734..e679cbe 100644
--- a/common.c
+++ b/src/common.c
@@ -388,7 +388,7 @@ bool check_leading_zeros(const hash _hash, byte required_zeros) {
return true;
}
-uint64_t get_current_time_ms() {
+uint64_t get_current_time_ms(void) {
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
return (uint64_t) ts.tv_sec * 1000 + (uint64_t) ts.tv_nsec / 1000000;
@@ -425,9 +425,9 @@ static inline void write_u16_le(byte *b, uint16_t v) {
b[ 1 ] = (v >> 8) & 0xFF;
}
-struct relay_info run_server_audit(const char* ip, int port) {
+struct relay_info run_server_audit(const char *ip, int port) {
byte data[] = { audit_server, 0, 0, 0, 0, 0, 0, 0 };
- byte* response = NULL;
+ byte *response = NULL;
size_t resp_len = 0;
bool success;
@@ -445,4 +445,19 @@ struct relay_info run_server_audit(const char* ip, int port) {
return info;
}
-pthread_mutex_t packet_storage_mutex = PTHREAD_MUTEX_INITIALIZER; \ No newline at end of file
+pthread_mutex_t packet_storage_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+pthread_mutex_t log_io_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+enum { _debug = 1, _info = 2, _warn = 3, _error = 4 } log_level = _error; // error only by default
+const char* log_level_names[] = { "debug", "info", "warn", "error" };
+#define log(__level, fmt, ...) \
+ ({ \
+ char msg[ 4096 ]; \
+ snprintf(msg, sizeof(msg), fmt __VA_OPT__(, ) __VA_ARGS__); \
+ pthread_mutex_lock(&log_io_mutex); \
+ if (__level >= log_level) \
+ fprintf(stderr, "[%s] %s\n", log_level_names[ __level - 1 ], msg); \
+ if (custom_log_func) custom_log_func(__level, msg); \
+ pthread_mutex_unlock(&log_io_mutex); \
+ })
diff --git a/src/config.c b/src/config.c
new file mode 100644
index 0000000..ef55387
--- /dev/null
+++ b/src/config.c
@@ -0,0 +1,99 @@
+#include <ctype.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+//
+
+// .
+// .
+// warning: slices are not NULL-terminated, and should not be modified!
+// strdup them if you need to make modifications.
+typedef struct slice {
+ char* ref;
+ size_t len;
+} slice_t;
+
+typedef struct pair {
+ slice_t key;
+ slice_t value;
+} pair_t;
+
+pair_t* global_arg_table = NULL;
+size_t args_count = 0;
+
+void parse_arguments(int argc, char** argv) {
+ if (argc <= 1) return;
+
+ global_arg_table = calloc(argc - 1, sizeof(pair_t));
+
+ for (int i = 1; i < argc; i++) {
+ char* current = argv[ i ];
+ char* eq_sign = strchr(current, '=');
+ if (eq_sign == NULL) {
+ printf(
+ "argument error: "
+ "moonpaste expects named arguments with values. example: 'foo=bar'\n"
+ " in '%s'",
+ current);
+ exit(1);
+ }
+
+ // key
+ size_t key_len = eq_sign - current;
+ size_t key_start = 0;
+ while (key_start < key_len && isspace(current[ key_start ])) { // trim start
+ key_start++;
+ key_len--;
+ }
+ while (key_len > 0 && isspace(current[ key_start + key_len - 1 ])) { // trim end
+ key_len--;
+ }
+
+ if (key_len == 0) {
+ printf(
+ "argument error: empty key\n"
+ " in '%s'\n",
+ current);
+ exit(1);
+ }
+
+ // value
+ size_t value_start = eq_sign - current + 1;
+ size_t value_len = strlen(current) - value_start;
+ while (value_start < strlen(current) && isspace(current[ value_start ])) { // trim start
+ value_start++;
+ value_len--;
+ }
+ while (value_len > 0 && isspace(current[ value_start + value_len - 1 ])) { // trim end
+ value_len--;
+ }
+
+ // store
+ global_arg_table[ args_count ].key.ref = current + key_start;
+ global_arg_table[ args_count ].key.len = key_len;
+ global_arg_table[ args_count ].value.ref = current + value_start;
+ global_arg_table[ args_count ].value.len = value_len;
+ args_count++;
+ }
+}
+
+// get a config option's value from argv.
+// if mandatory == true -> program errors
+char* get_config(char* key, bool mandatory) {
+ size_t len = strlen(key);
+ for (int i = 0; i < args_count; i++) {
+ if (global_arg_table[ i ].key.len != len) continue;
+ if (strncmp(key, global_arg_table[ i ].key.ref, len) != 0) continue;
+ char* new_str = malloc(global_arg_table[ i ].value.len);
+ memcpy(new_str, global_arg_table[ i ].value.ref, global_arg_table[ i ].value.len);
+ return new_str; // free me!
+ }
+
+ if (!mandatory) return NULL;
+ fprintf(stderr, "program expected argument %s but did not receive it.\n", key);
+ // __builtin_unreachable();
+ return NULL;
+} \ No newline at end of file
diff --git a/src/external.h b/src/external.h
new file mode 100644
index 0000000..e145f91
--- /dev/null
+++ b/src/external.h
@@ -0,0 +1,124 @@
+#pragma once
+//
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+//
+
+typedef uint8_t byte;
+
+typedef byte ipv4[ 4 ];
+typedef byte ipv6[ 16 ];
+
+typedef byte hash[ 32 ];
+typedef byte public_key[ 32 ];
+typedef byte private_key[ 32 ];
+typedef byte signature[ 64 ];
+
+const uint16_t default_port = 8040;
+
+enum client_input {
+ // ask server for its information (responds with audit_response)
+ audit_server = 0x9a,
+
+ // ask server for a random set of neighbors
+ // (input uint8_t neighbor_count, responds with neighbors_info)
+ get_neighbors = 0x20,
+
+ // monitor incoming packets
+ // (input struct firehose_request, responds with firehose_reply)
+ firehose = 0xf1,
+
+ // give a packet to the relay
+ // (input struct data_packet, responds with push_packet_result)
+ push_packet = 0x88,
+
+ // get a packet from the server
+ // (input hash, responds with get_packet_result)
+ get_packet = 0x87,
+
+ // register a new neighbor relay to this server
+ // (input struct relay_neighbor, responds with register_neighbor_result)
+ register_neighbor = 0x73,
+};
+
+enum server_reply {
+ // pretty self-explanatory.
+ error = 0x1,
+
+ // type `struct relay_info`
+ audit_response = 0xaf,
+ // type `struct relay_neighbor[ neighbor_count ]`
+ neighbors_info = 0x43,
+ // type `uint64_t new_offset, struct data_packet[ count ]`
+ firehose_reply = 0x6c,
+ // type `bool result_code`
+ push_packet_result = 0x4a,
+ // type `struct data_packet`
+ get_packet_result = 0x42,
+ // type `bool result_code`
+ register_neighbor_result = 0x6e,
+};
+
+typedef uint8_t get_neighbors_request;
+
+struct firehose_request {
+ uint8_t count;
+ uint64_t offset;
+};
+
+typedef uint64_t firehose_reply_new_offset;
+
+struct relay_neighbor {
+ bool active; // set to 0 to indicate empty quick connect slot
+
+ bool has_ipv4; // whether the neighbor supports ipv4 connection
+ bool has_ipv6; // whether the neighbor supports ipv6 connection
+ ipv4 v4_addr; // ipv4 address of the neighbor (if supported)
+ ipv6 v6_addr; // ipv6 address of the neighbor (if supported)
+};
+
+struct relay_info {
+ // -- version info --
+ byte major_version;
+ byte minor_version;
+ // -- motd --
+ byte motd[ 128 ];
+ // -- capabilities --
+ struct {
+ uint16_t retention_hours; // how many hours packets will be retained for
+ bool can_store; // whether or not the relay is accepting new packets
+ byte leading_zeros; // relay challenge level
+ bool accepts_plain; // whether or not the relay accepts unencrypted packets
+ } capabilities;
+ // -- statistics --
+ struct {
+ uint16_t neighbors; // current neighboring relay count for this relay
+ uint64_t held_packets; // how many packets the relay is holding
+ struct relay_neighbor quick_connect[ 4 ]; // four best neighbors for quick connection
+ } statistics;
+};
+
+// do not change
+#define MAX_RECIPIENTS 32
+
+struct data_packet {
+ // nonce + hash for PoW
+ uint32_t nonce;
+ hash hash;
+
+ // content inside to be nonce + hashed
+ struct {
+ bool encrypted; // whether the contents is encrypted or plaintext
+ uint64_t created_time_ms; // unix timestamp for creation time in milliseconds
+ public_key author; // message author
+ public_key author_signing_pub; // message signing pubkey
+ uint8_t recipient_count; // number of recipients. leave as 0 for public.
+ public_key recipients[ 32 ]; // up to 32 recipients for packet
+ byte data[ 65536 ]; // packet data
+ signature signature; // data signature
+ } contents;
+};
+
+const size_t CONTENTS_SIZE = 1 + 8 + 32 + 32 + 1 + (32 * MAX_RECIPIENTS) + 65536 + 64;
+const size_t PACKET_SIZE = 4 + 32 + (CONTENTS_SIZE); \ No newline at end of file
diff --git a/firehose.c b/src/firehose.c
index b2931a7..6a4a134 100644
--- a/firehose.c
+++ b/src/firehose.c
@@ -24,13 +24,13 @@ typedef struct {
static fire_buffer firehose_buffer;
-void firehose_init();
+void firehose_init(void);
void firehose_push(hash new_hash, uint64_t storage_index);
-uint16_t firehose_get_count();
-uint64_t firehose_get_total_packets();
+uint16_t firehose_get_count(void);
+uint64_t firehose_get_total_packets(void);
uint16_t firehose_get_packets(uint64_t offset, uint16_t count, water_splash* output_entries);
-void firehose_init() {
+void firehose_init(void) {
memset(firehose_buffer.entries, 0, sizeof(firehose_buffer.entries));
firehose_buffer.head = 0;
firehose_buffer.tail = 0;
@@ -59,8 +59,8 @@ void firehose_push(hash new_hash, uint64_t storage_index) {
pthread_mutex_unlock(&firehose_buffer.mutex);
}
-uint16_t firehose_get_count() { return firehose_buffer.count; }
-uint64_t firehose_get_total_packets() { return firehose_buffer.total_packets; }
+uint16_t firehose_get_count(void) { return firehose_buffer.count; }
+uint64_t firehose_get_total_packets(void) { return firehose_buffer.total_packets; }
uint16_t firehose_get_packets(uint64_t offset, uint16_t count, water_splash* output_entries) {
pthread_mutex_lock(&firehose_buffer.mutex);
diff --git a/omen.c b/src/omen.c
index 98da86a..222e702 100644
--- a/omen.c
+++ b/src/omen.c
@@ -19,10 +19,6 @@
typedef uint8_t byte;
typedef void (*tome_ptr)(
char *ip_addr, size_t addr_size, byte *in, size_t in_len, byte **out, size_t *out_len);
-struct server_task {
- int fd;
- struct server_task *next;
-};
#define MAX_BUFFER 1 << 18
#define MAX_BODY 1 << 22
@@ -32,12 +28,16 @@ struct server_task {
int worker_count = 64;
-pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER;
-pthread_cond_t queue_cond = PTHREAD_COND_INITIALIZER;
-struct server_task *head = NULL;
-struct server_task *tail = NULL;
-size_t queue_size = 0;
-tome_ptr global_handler = NULL;
+pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER;
+pthread_cond_t queue_cond = PTHREAD_COND_INITIALIZER;
+
+static int ring_fds[ MAX_CONNECTIONS ];
+static char ring_ips[ MAX_CONNECTIONS ][ INET6_ADDRSTRLEN ];
+static size_t ring_head = 0;
+static size_t ring_tail = 0;
+size_t queue_size = 0;
+
+tome_ptr global_handler = NULL;
// utils
@@ -82,7 +82,8 @@ static int read_until_headers(int fd, char **out_buf, size_t *out_len) {
free(buf);
return -1;
} else if (r == 0) {
- len = len;
+ // TODO: why?
+ // len = len;
break;
}
len += (size_t) r;
@@ -169,57 +170,19 @@ static int send_http_response(int fd, const byte *payload, size_t payload_len) {
void *omen_bound(void *arg) {
(void) arg;
while (1) {
- int client_fd = -1;
- struct sockaddr_storage client_addr;
- socklen_t client_addr_len = sizeof(client_addr);
+ int client_fd = -1;
+ char client_ip[ INET6_ADDRSTRLEN ] = { 0 };
pthread_mutex_lock(&queue_mutex);
- while (head == NULL) { pthread_cond_wait(&queue_cond, &queue_mutex); }
- struct server_task *task = head;
- head = head->next;
- if (head == NULL) tail = NULL;
- queue_size--;
- client_fd = task->fd;
+ while (queue_size == 0) { pthread_cond_wait(&queue_cond, &queue_mutex); }
- if (getpeername(client_fd, (struct sockaddr *) &client_addr, &client_addr_len) < 0) {
- memset(&client_addr, 0, sizeof(client_addr));
- }
-
- free(task);
+ client_fd = ring_fds[ ring_head ];
+ strncpy(client_ip, ring_ips[ ring_head ], INET6_ADDRSTRLEN - 1);
+ client_ip[ INET6_ADDRSTRLEN - 1 ] = '\0';
+ ring_head = (ring_head + 1) % MAX_CONNECTIONS;
+ queue_size--;
pthread_mutex_unlock(&queue_mutex);
- char client_ip[ INET6_ADDRSTRLEN ] = { 0 };
- if (client_addr.ss_family == AF_INET) {
- struct sockaddr_in *addr4 = (struct sockaddr_in *) &client_addr;
- inet_ntop(AF_INET, &addr4->sin_addr, client_ip, INET_ADDRSTRLEN);
- } else if (client_addr.ss_family == AF_INET6) {
- struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) &client_addr;
- inet_ntop(AF_INET6, &addr6->sin6_addr, client_ip, INET6_ADDRSTRLEN);
- }
-
- if (!ratelimit_check(client_ip)) {
- usleep(250 * 1000);
-
- struct server_task *new_task = malloc(sizeof(struct server_task));
- if (new_task) {
- new_task->fd = client_fd;
- new_task->next = NULL;
-
- pthread_mutex_lock(&queue_mutex);
- if (tail) tail->next = new_task;
- else
- head = new_task;
- tail = new_task;
- queue_size++;
- pthread_cond_signal(&queue_cond);
- pthread_mutex_unlock(&queue_mutex);
- } else {
- close(client_fd);
- }
-
- continue;
- }
-
struct timeval timeout = { 5, 0 };
setsockopt(client_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
@@ -339,7 +302,7 @@ int omen(int port, tome_ptr handler) {
}
if (listen(server_fd, MAX_CONNECTIONS) < 0) {
- perror("Listen failed");
+ perror("listen failed");
close(server_fd);
return -1;
}
@@ -365,11 +328,29 @@ int omen(int port, tome_ptr handler) {
socklen_t len = sizeof(client_addr);
int client_fd = accept(server_fd, (struct sockaddr *) &client_addr, &len);
if (client_fd < 0) {
- if (errno != EINTR) { perror("Accept failed"); }
+ if (errno != EINTR) { perror("accept failed"); }
continue;
}
- printf("request\n");
+ char client_ip[ INET6_ADDRSTRLEN ] = { 0 };
+ if (client_addr.ss_family == AF_INET) {
+ struct sockaddr_in *addr4 = (struct sockaddr_in *) &client_addr;
+ inet_ntop(AF_INET, &addr4->sin_addr, client_ip, sizeof(client_ip));
+ } else if (client_addr.ss_family == AF_INET6) {
+ struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) &client_addr;
+ if (IN6_IS_ADDR_V4MAPPED(&addr6->sin6_addr)) {
+ struct in_addr v4;
+ memcpy(&v4.s_addr, &addr6->sin6_addr.s6_addr[ 12 ], sizeof(v4.s_addr));
+ inet_ntop(AF_INET, &v4, client_ip, sizeof(client_ip));
+ } else {
+ inet_ntop(AF_INET6, &addr6->sin6_addr, client_ip, sizeof(client_ip));
+ }
+ } else {
+ strncpy(client_ip, "0.0.0.0", sizeof(client_ip) - 1);
+ client_ip[ sizeof(client_ip) - 1 ] = '\0';
+ }
+
+ if (!ratelimit_check(client_ip)) { usleep(250 * 1000); }
pthread_mutex_lock(&queue_mutex);
if (queue_size >= (size_t) MAX_CONNECTIONS) {
@@ -383,18 +364,10 @@ int omen(int port, tome_ptr handler) {
continue;
}
- struct server_task *new_task = malloc(sizeof(struct server_task));
- if (!new_task) {
- close(client_fd);
- pthread_mutex_unlock(&queue_mutex);
- continue;
- }
- new_task->fd = client_fd;
- new_task->next = NULL;
- if (tail) tail->next = new_task;
- else
- head = new_task;
- tail = new_task;
+ ring_fds[ ring_tail ] = client_fd;
+ strncpy(ring_ips[ ring_tail ], client_ip, INET6_ADDRSTRLEN - 1);
+ ring_ips[ ring_tail ][ INET6_ADDRSTRLEN - 1 ] = '\0';
+ ring_tail = (ring_tail + 1) % MAX_CONNECTIONS;
queue_size++;
pthread_cond_signal(&queue_cond);
pthread_mutex_unlock(&queue_mutex);
@@ -416,11 +389,11 @@ int study(
size_t *resp_len) {
bool is_ipv6 = strchr(ip, ':') != NULL;
int domain = is_ipv6 ? AF_INET6 : AF_INET;
- printf("Attempting %s connection to %s:%d\n", is_ipv6 ? "IPv6" : "IPv4", ip, port);
+ // printf("attempting %s connection to %s:%d\n", is_ipv6 ? "ipv6" : "ipv4", ip, port);
int sock = socket(domain, SOCK_STREAM, 0);
if (sock < 0) {
- printf("Socket creation failed: %s\n", strerror(errno));
+ printf("socket creation failed: %s\n", strerror(errno));
return -1;
}
@@ -441,7 +414,7 @@ int study(
}
if (inet_pton(domain, ip, addr_ptr) != 1) {
- printf("Invalid %s address: %s\n", is_ipv6 ? "IPv6" : "IPv4", ip);
+ printf("invalid %s address: %s\n", is_ipv6 ? "ipv6" : "ipv4", ip);
close(sock);
return -1;
}
@@ -449,22 +422,21 @@ int study(
struct timeval timeout = { 5, 0 };
if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0
|| setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) < 0) {
- printf("Setsockopt failed: %s\n", strerror(errno));
+ printf("setsockopt failed: %s\n", strerror(errno));
close(sock);
return -1;
}
- printf("Connecting to %s:%d\n", ip, port);
+ // printf("connecting to %s:%d\n", ip, port);
if (connect(sock, (struct sockaddr *) &addr, addr_len) < 0) {
- printf("Connect failed: %s\n", strerror(errno));
+ printf("connect failed: %s\n", strerror(errno));
close(sock);
return -1;
}
if (data_len > MAX_BODY) data_len = MAX_BODY;
char req_hdr[ 256 ];
- // Use [ ] for IPv6 in Host header
- int hdr_n;
+ int hdr_n;
if (is_ipv6) {
hdr_n = snprintf(
req_hdr,
@@ -481,15 +453,15 @@ int study(
data_len);
}
if (hdr_n < 0 || (size_t) hdr_n >= sizeof(req_hdr)) {
- printf("Failed to format HTTP header\n");
+ printf("failed to format HTTP header\n");
close(sock);
return -1;
}
- printf("Sending request: %.*s\n", hdr_n, req_hdr);
+ // printf("sending request: %.*s\n", hdr_n, req_hdr);
if (write_all(sock, req_hdr, (size_t) hdr_n) < 0
|| (data_len > 0 && write_all(sock, data, data_len) < 0)) {
- printf("Write failed: %s\n", strerror(errno));
+ printf("write failed: %s\n", strerror(errno));
close(sock);
return -1;
}
@@ -497,14 +469,14 @@ int study(
char *hdr_buf = NULL;
size_t hdr_len = 0;
if (read_until_headers(sock, &hdr_buf, &hdr_len) < 0) {
- printf("Failed to read headers: %s\n", strerror(errno));
+ printf("failed to read headers: %s\n", strerror(errno));
close(sock);
free(hdr_buf);
return -1;
}
if (memmem(hdr_buf, hdr_len, "HTTP/1.1 418 I'm a teapot", 34) != NULL) {
- printf("Server responded with 418 I'm a teapot\n");
+ // printf("server responded with 418 I'm a teapot\n");
close(sock);
free(hdr_buf);
return -2;
@@ -521,7 +493,7 @@ int study(
if ((size_t) cl > MAX_BODY) cl = MAX_BODY;
body = malloc((size_t) cl);
if (!body) {
- printf("Failed to allocate body buffer\n");
+ printf("failed to allocate body buffer\n");
free(hdr_buf);
close(sock);
return -1;
@@ -534,7 +506,7 @@ int study(
}
if ((size_t) body_len < (size_t) cl) {
if (read_exact(sock, body + body_len, (size_t) cl - body_len) < 0) {
- printf("Failed to read body: %s\n", strerror(errno));
+ printf("failed to read body: %s\n", strerror(errno));
free(body);
free(hdr_buf);
close(sock);
@@ -576,6 +548,6 @@ int study(
*resp_len = 0;
free(body);
}
- printf("Response received, length: %zu\n", body_len);
+ // printf("response received, length: %zu\n", body_len);
return 0;
-} \ No newline at end of file
+}
diff --git a/packet_storage.c b/src/packet_storage.c
index 30e047f..1094afd 100644
--- a/packet_storage.c
+++ b/src/packet_storage.c
@@ -1,6 +1,8 @@
#include <pthread.h>
#include <stdint.h>
#include <stdlib.h>
+// dependency: zlib
+#include <zlib.h>
//
#include "common.c"
@@ -27,45 +29,37 @@ struct data_packet_entry {
}* packet_storage;
// TODO: yes, i know it's weird, but this has to be zero
-#define minus_one (uint64_t) 0
+#define minus_one ((uint64_t) 0)
-struct data_packet* load_packet(const hash packet_hash) {
- char filename[ 128 ];
- sprintf(filename, "packets/" hash_fmt ".bin", hash_ref(packet_hash));
-
- FILE* file = fopen(filename, "rb");
+struct data_packet* load_packet(FILE* file) {
if (!file) { return NULL; }
fseek(file, 0, SEEK_END);
size_t file_size = ftell(file);
fseek(file, 0, SEEK_SET);
- if (file_size != PACKET_SIZE) {
+ if (file_size == 0) {
fclose(file);
return NULL;
}
- byte* buffer = malloc(file_size);
- if (!buffer) {
- fclose(file);
- return NULL;
- }
+ byte comp_buf[ file_size ];
- size_t r = fread(buffer, 1, file_size, file);
+ size_t r = fread(comp_buf, 1, file_size, file);
fclose(file);
- if (r != file_size) {
- free(buffer);
- return NULL;
- }
+ if (r != file_size) { return NULL; }
+
+ byte buffer[ PACKET_SIZE ];
+
+ uLongf dest_len = (uLongf) PACKET_SIZE;
+ int unres = uncompress(buffer, &dest_len, comp_buf, (uLongf) file_size);
+
+ if (unres != Z_OK || dest_len != (uLongf) PACKET_SIZE) { return NULL; }
bool success;
struct data_packet* packet = malloc(sizeof(struct data_packet));
- if (!packet) {
- free(buffer);
- return NULL;
- }
- *packet = s2data_packet(file_size, buffer, &success);
- free(buffer);
+ if (!packet) { return NULL; }
+ *packet = s2data_packet(PACKET_SIZE, buffer, &success);
if (!success) {
free(packet);
@@ -77,7 +71,7 @@ struct data_packet* load_packet(const hash packet_hash) {
uint64_t set_data_packet(struct data_packet* new_packet);
-struct data_packet_entry* get_data_packet(hash _hash) {
+struct data_packet_entry* get_data_packet(const hash _hash) {
uint64_t hash_val;
memcpy(&hash_val, _hash, sizeof(uint64_t));
uint64_t cur_index = hash_val % max_storage;
@@ -97,7 +91,11 @@ struct data_packet_entry* get_data_packet(hash _hash) {
}
pthread_mutex_unlock(&packet_storage_mutex);
- struct data_packet* packet = load_packet(_hash);
+ char filename[ 128 ];
+ sprintf(filename, "packets/" hash_fmt ".bin", hash_ref(_hash));
+ FILE* file = fopen(filename, "rb");
+
+ struct data_packet* packet = load_packet(file);
if (packet) {
uint64_t add_idx = set_data_packet(packet);
@@ -105,7 +103,7 @@ struct data_packet_entry* get_data_packet(hash _hash) {
free(packet);
return &packet_storage[ add_idx ];
} else {
- __thread static struct data_packet_entry temp_entry;
+ static __thread struct data_packet_entry temp_entry;
temp_entry.packet = *packet;
free(packet);
return &temp_entry;
@@ -232,4 +230,4 @@ bool _delete_at_index(uint64_t index) {
current_entry->prev = minus_one;
return true;
-} \ No newline at end of file
+}
diff --git a/src/plugin.c b/src/plugin.c
new file mode 100644
index 0000000..cfee5d4
--- /dev/null
+++ b/src/plugin.c
@@ -0,0 +1,163 @@
+#pragma once
+
+#include <dlfcn.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+//
+#include "common.c"
+
+typedef uint8_t byte;
+
+// contracts
+
+void __init_plugin_func(void);
+typedef typeof(__init_plugin_func) init_plugin;
+
+struct relay_info;
+struct relay_neighbor;
+struct data_packet;
+
+// runs when the server wants to respond to a server audit request
+void __modify_server_stats_func(struct relay_info *server_stats);
+typedef typeof(__modify_server_stats_func) modify_server_stats;
+
+// runs when the server wants to decide whether to add a neighbor by request or not
+bool __decide_neighbor_registry_func(struct relay_neighbor new_neighbor);
+typedef typeof(__decide_neighbor_registry_func) decide_neighbor_registry;
+
+// runs when the server wants to decide whether to propagate
+bool __decide_packet_propogation_func(struct data_packet packet);
+typedef typeof(__decide_packet_propogation_func) decide_packet_propogation;
+
+// runs before the paste_server function performs any logic
+void __paste_server_start_func(
+ char **ip_addr_ref,
+ size_t *ip_size_ref,
+ byte **in_ref,
+ size_t *in_len_ref,
+ byte ***out_ref,
+ size_t **out_len_ref);
+typedef typeof(__paste_server_start_func) paste_server_start;
+
+// runs when the server wants to decide whether to save the packet to disk
+bool __decide_packet_save_to_disk_func(struct data_packet packet);
+typedef typeof(__decide_packet_save_to_disk_func) decide_packet_save_to_disk;
+
+// runs when the server wants a plugin to modify a packet (e.g., keepalive, run PoW, etc)
+void __modify_packet_func(struct data_packet *packet);
+typedef typeof(__modify_packet_func) modify_packet;
+
+// runs when the server wants to decide whether to wipe a packet
+bool __decide_packet_cleanup_func(struct data_packet packet);
+typedef typeof(__decide_packet_cleanup_func) decide_packet_cleanup;
+
+// runs when the server logs a message
+void __custom_log_func(int level, const char *msg);
+typedef typeof(__custom_log_func) custom_log;
+
+// runs when the server is shutting down
+void __on_server_shutdown_func(void);
+typedef typeof(__on_server_shutdown_func) on_server_shutdown;
+
+// data
+
+enum plugin_handler_code_types {
+ code_modify_server_stats = 1,
+ code_decide_neighbor_registry = 2,
+ code_decide_packet_propogation = 3,
+ code_paste_server_start = 4,
+ code_decide_packet_save_to_disk = 5,
+ code_modify_packet = 6,
+ code_decide_packet_cleanup = 7,
+ code_on_server_shutdown = 8,
+ code_custom_log = 9
+};
+
+void *plugin_handle = NULL;
+init_plugin *init_plugin_func = NULL;
+modify_server_stats *modify_server_stats_func = NULL;
+decide_neighbor_registry *decide_neighbor_registry_func = NULL;
+decide_packet_propogation *decide_packet_propogation_func = NULL;
+paste_server_start *paste_server_start_func = NULL;
+decide_packet_save_to_disk *decide_packet_save_to_disk_func = NULL;
+modify_packet *modify_packet_func = NULL;
+decide_packet_cleanup *decide_packet_cleanup_func = NULL;
+custom_log *custom_log_func = NULL;
+on_server_shutdown *on_server_shutdown_func = NULL; // unused for now
+
+pthread_mutex_t plugin_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+// implementation
+
+void load_plugins(const char *plugin_path) {
+ pthread_mutex_lock(&plugin_mutex);
+ if (plugin_handle) {
+ dlclose(plugin_handle);
+ plugin_handle = NULL;
+ init_plugin_func = NULL;
+ modify_server_stats_func = NULL;
+ decide_neighbor_registry_func = NULL;
+ decide_packet_propogation_func = NULL;
+ paste_server_start_func = NULL;
+ decide_packet_save_to_disk_func = NULL;
+ modify_packet_func = NULL;
+ decide_packet_cleanup_func = NULL;
+ custom_log_func = NULL;
+ on_server_shutdown_func = NULL;
+ }
+
+ plugin_handle = dlopen(plugin_path, RTLD_NOW);
+ if (!plugin_handle) {
+ fprintf(stderr, "%s\n", dlerror());
+ pthread_mutex_unlock(&plugin_mutex);
+ return;
+ }
+
+ if (init_plugin_func) {
+ init_plugin_func();
+ log(_info, "loaded %s\n", plugin_path);
+ } else {
+ log(_error, "%s has no MP_init_plugin", plugin_path);
+ dlclose(plugin_handle);
+ plugin_handle = NULL;
+ pthread_mutex_unlock(&plugin_mutex);
+ return;
+ }
+
+ init_plugin_func = dlsym(plugin_handle, "MP_init_plugin");
+ modify_server_stats_func = dlsym(plugin_handle, "MP_modify_server_stats");
+ decide_neighbor_registry_func = dlsym(plugin_handle, "MP_decide_neighbor_registry");
+ decide_packet_propogation_func = dlsym(plugin_handle, "MP_decide_packet_propogation");
+ paste_server_start_func = dlsym(plugin_handle, "MP_paste_server_start");
+ decide_packet_save_to_disk_func = dlsym(plugin_handle, "MP_decide_packet_save_to_disk");
+ modify_packet_func = dlsym(plugin_handle, "MP_modify_packet");
+ decide_packet_cleanup_func = dlsym(plugin_handle, "MP_decide_packet_cleanup");
+ custom_log_func = dlsym(plugin_handle, "MP_custom_log");
+ on_server_shutdown_func = dlsym(plugin_handle, "MP_on_server_shutdown");
+
+ pthread_mutex_unlock(&plugin_mutex);
+}
+
+void cleanup_plugins(void) {
+ pthread_mutex_lock(&plugin_mutex);
+ if (plugin_handle) {
+ if (on_server_shutdown_func) { on_server_shutdown_func(); }
+ dlclose(plugin_handle);
+ plugin_handle = NULL;
+ init_plugin_func = NULL;
+ modify_server_stats_func = NULL;
+ decide_neighbor_registry_func = NULL;
+ decide_packet_propogation_func = NULL;
+ paste_server_start_func = NULL;
+ decide_packet_save_to_disk_func = NULL;
+ modify_packet_func = NULL;
+ decide_packet_cleanup_func = NULL;
+ custom_log_func = NULL;
+ on_server_shutdown_func = NULL;
+ }
+ pthread_mutex_unlock(&plugin_mutex);
+}
diff --git a/ratelimit.c b/src/ratelimit.c
index 38f756e..c6f0e01 100644
--- a/ratelimit.c
+++ b/src/ratelimit.c
@@ -23,7 +23,7 @@ struct rl_table {
struct rl_table* global_ratelimit = NULL;
-void ratelimit_init() {
+void ratelimit_init(void) {
if (global_ratelimit != NULL) return;
struct rl_table* table = malloc(sizeof(struct rl_table));
@@ -105,4 +105,4 @@ void ratelimit_free(struct rl_table* table) {
pthread_mutex_destroy(&table->mutex);
free(table);
}
-} \ No newline at end of file
+}
diff --git a/security.c b/src/security.c
index 35f2a81..7afdb27 100644
--- a/security.c
+++ b/src/security.c
@@ -42,7 +42,7 @@ typedef struct {
bool has_called_sodium_init = false;
-void init_sec() {
+void init_sec(void) {
if (unlikely(!has_called_sodium_init)) {
if (sodium_init() == -1) {
fprintf(stderr, "sodium init failed\n");
@@ -52,7 +52,7 @@ void init_sec() {
}
}
-keypair generate_keypair() {
+keypair generate_keypair(void) {
init_sec();
keypair new;
crypto_box_keypair(new.encryption_pub, new.encryption_priv);
@@ -100,7 +100,11 @@ encrypt_and_sign(plain_payload input, keypair author_keys, public_key target_enc
return output;
}
-plain_payload decrypt(secure_payload input, keypair target_keys, public_key author_public, public_key author_signing_public) {
+plain_payload decrypt(
+ secure_payload input,
+ keypair target_keys,
+ public_key author_public,
+ public_key author_signing_public) {
init_sec();
plain_payload output;
@@ -158,4 +162,4 @@ bool verify_raw_signature(
init_sec();
if (msg == NULL || signature == NULL || author_signing_public == NULL) return false;
return crypto_sign_verify_detached(signature, msg, msg_len, author_signing_public) == 0;
-} \ No newline at end of file
+}
diff --git a/server.c b/src/server.c
index a88b6f8..a246fe5 100644
--- a/server.c
+++ b/src/server.c
@@ -1,28 +1,21 @@
#include <dirent.h>
#include <fcntl.h>
+#include <pthread.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
+// dependency: zlib
+#include <zlib.h>
//
#include "common.c"
+#include "config.c"
+#include "firehose.c"
#include "omen.c"
+#include "packet_storage.c"
+#include "plugin.c"
#include "security.c"
#include "sha256.c"
//
-#include "firehose.c"
-#include "packet_storage.c"
-
-pthread_mutex_t log_io_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-enum { _debug = 1, _info = 2, _warn = 3, _error = 4 } log_level = _debug;
-const char* log_level_names[] = { "debug", "info", "warn", "error" };
-#define log(__level, fmt, ...) \
- ({ \
- pthread_mutex_lock(&log_io_mutex); \
- if (__level >= log_level) \
- fprintf(stderr, "[%s] " fmt, (#__level) + 1 __VA_OPT__(, ) __VA_ARGS__); \
- pthread_mutex_unlock(&log_io_mutex); \
- })
const uint64_t max_neighbors = 1 << 10; // probably more than enough for a lot of folks
@@ -32,7 +25,7 @@ struct relay_neighbor* neighbor_storage;
bool* empty_neighbor_slots;
pthread_mutex_t neighbor_storage_mutex = PTHREAD_MUTEX_INITIALIZER;
-struct {
+struct server_stats {
byte major_version;
byte minor_version;
byte motd[ 128 ];
@@ -50,7 +43,11 @@ struct {
};
bool add_neighbor(struct relay_neighbor new_neighbor) {
- log(_info, "adding neighbor\n");
+ if (decide_neighbor_registry_func) {
+ if (!decide_neighbor_registry_func(new_neighbor)) return false;
+ }
+
+ log(_info, "adding neighbor");
pthread_mutex_lock(&neighbor_storage_mutex);
if (neighbor_count >= max_neighbors) {
@@ -91,11 +88,16 @@ bool add_neighbor(struct relay_neighbor new_neighbor) {
void* propagate_packet(void* _packet) {
struct data_packet packet = *(struct data_packet*) _packet;
- log(_debug, "propagating packet " hash_fmt "\n", hash_ref(packet.hash));
- size_t send_len = 1 + sizeof(struct data_packet);
- byte* send_data = calloc(send_len, sizeof(byte));
- send_data[ 0 ] = push_packet;
+ if (decide_packet_propogation_func) {
+ if (!decide_packet_propogation_func(packet)) return NULL;
+ }
+
+ log(_debug, "propagating packet " hash_fmt, hash_ref(packet.hash));
+
+ size_t send_len = 1 + sizeof(struct data_packet);
+ byte send_data[ send_len * sizeof(byte) ];
+ send_data[ 0 ] = push_packet;
data_packet2s(packet, send_data + 1);
pthread_mutex_lock(&neighbor_storage_mutex);
@@ -131,14 +133,13 @@ void* propagate_packet(void* _packet) {
ip_string, default_port, send_data, send_len, &response_data, &response_len);
}
- if (result != 0) { fprintf(stderr, "failed to send packet to neighbor\n"); }
+ if (result != 0) { log(_error, "failed to send packet to neighbor"); }
if (response_data) { free(response_data); }
}
}
pthread_mutex_unlock(&neighbor_storage_mutex);
- free(send_data);
free(_packet);
return NULL;
}
@@ -153,7 +154,14 @@ void save_packet(const struct data_packet packet);
void paste_server(
char* ip_addr, size_t ip_size, byte* in, size_t in_len, byte** out, size_t* out_len) {
- log(_info, "<%.*s> incoming request with size %li\n", (int) ip_size, ip_addr, in_len);
+ log(_info, "<%.*s> incoming request with size %li", (int) ip_size, ip_addr, in_len);
+
+ if (paste_server_start_func) {
+ paste_server_start_func(&ip_addr, &ip_size, &in, &in_len, &out, &out_len);
+ }
+
+ // tip: if you want the server to not mandate the 8 bytes minimum, modify in_len using the
+ // plugin.
if (in_len < 8) { // server mandates 8 bytes minimum. pad it if you don't need it!
reply(17, strdup("\x01input too small"));
@@ -164,7 +172,7 @@ void paste_server(
switch (input_type) {
case audit_server: {
- log(_info, "<%.*s> request type: audit\n", (int) ip_size, ip_addr);
+ log(_info, "<%.*s> request type: audit", (int) ip_size, ip_addr);
byte* output_data = calloc(1 + 237, sizeof(byte));
if (!output_data) {
@@ -198,11 +206,13 @@ void paste_server(
info.statistics.quick_connect[ i ] = neighbor_storage[ i ];
}
+ memcpy(info.motd, server_stats.motd, 128);
+
+ if (modify_server_stats_func) { modify_server_stats_func(&info); }
+
pthread_mutex_unlock(&packet_storage_mutex);
pthread_mutex_unlock(&neighbor_storage_mutex);
- memcpy(info.motd, server_stats.motd, 128);
-
relay_info2s(info, output_data + 1);
reply(1 + 237, output_data);
@@ -212,7 +222,7 @@ void paste_server(
case get_neighbors: {
byte count = in[ 1 ];
log(_info,
- "<%.*s> request type: get neighbors, count %i\n",
+ "<%.*s> request type: get neighbors, count %i",
(int) ip_size,
ip_addr,
count);
@@ -257,7 +267,7 @@ void paste_server(
s2hash(32, in + 1, &success, packet_hash);
log(_info,
- "<%.*s> request type: get packet with hash " hash_fmt "\n",
+ "<%.*s> request type: get packet with hash " hash_fmt,
(int) ip_size,
ip_addr,
hash_ref(packet_hash));
@@ -305,7 +315,7 @@ void paste_server(
struct data_packet packet = s2data_packet(in_len - 1, in + 1, &success);
log(_info,
- "<%.*s> request type: push packet with hash " hash_fmt "\n",
+ "<%.*s> request type: push packet with hash " hash_fmt,
(int) ip_size,
ip_addr,
hash_ref(packet.hash));
@@ -316,14 +326,23 @@ void paste_server(
output_data[ 0 ] = push_packet_result;
if (!success) {
- log(_warn, "<%.*s> malformed data\n", (int) ip_size, ip_addr);
+ log(_warn, "<%.*s> malformed data", (int) ip_size, ip_addr);
output_data[ 1 ] = false;
reply(output_len, output_data);
return;
}
+ if (modify_packet_func) {
+ // things you can do here:
+ // - prefer certain packet authors
+ // - run PoW
+ // - increase leading zeroes
+ // - recompute the hash
+ modify_packet_func(&packet);
+ }
+
if (!check_leading_zeros(packet.hash, server_stats.leading_zeros)) {
- log(_warn, "bad leads\n");
+ log(_warn, "bad leads");
output_data[ 1 ] = false;
reply(output_len, output_data);
return;
@@ -347,7 +366,7 @@ void paste_server(
struct sha256 calculated_hash_struct = sha256_hash(hash_input_buffer, hash_input_len);
if (memcmp(calculated_hash_struct.hash, packet.hash, sizeof(hash)) != 0) {
log(_warn,
- "bad hash " hash_fmt " vs " hash_fmt "\n",
+ "bad hash " hash_fmt " vs " hash_fmt,
hash_ref(calculated_hash_struct.hash),
hash_ref(packet.hash));
output_data[ 1 ] = false;
@@ -357,7 +376,7 @@ void paste_server(
uint64_t current_time = get_current_time_ms();
if (packet.contents.created_time_ms > current_time) {
- log(_warn, "future\n");
+ log(_warn, "packet timestamp is in the future");
output_data[ 1 ] = false;
reply(output_len, output_data);
return;
@@ -365,14 +384,14 @@ void paste_server(
if (current_time - packet.contents.created_time_ms
> server_stats.retention_hours * (60 * 60 * 1000)) {
- log(_warn, "expired\n");
+ log(_warn, "packet expired");
output_data[ 1 ] = false;
reply(output_len, output_data);
return;
}
if (!packet.contents.encrypted && !server_stats.accepts_plain) {
- log(_warn, "plaintext not accepted by server\n");
+ log(_warn, "plaintext not accepted by server");
output_data[ 1 ] = false;
reply(output_len, output_data);
return;
@@ -398,7 +417,7 @@ void paste_server(
}
if (!sig_ok) {
- log(_warn, "bad signature\n");
+ log(_warn, "bad signature");
output_data[ 1 ] = false;
reply(output_len, output_data);
return;
@@ -452,7 +471,7 @@ void paste_server(
if (unlikely(req.count > 64)) req.count = 64; // clamp
log(_info,
- "<%.*s> request type: firehose count %i offset %li\n",
+ "<%.*s> request type: firehose count %i offset %li",
(int) ip_size,
ip_addr,
req.count,
@@ -500,7 +519,7 @@ void paste_server(
return;
}
- log(_info, "<%.*s> request type: neighbor registration\n", (int) ip_size, ip_addr);
+ log(_info, "<%.*s> request type: neighbor registration", (int) ip_size, ip_addr);
char ip_string[ INET6_ADDRSTRLEN ];
@@ -533,7 +552,7 @@ void paste_server(
} break;
default: {
- log(_info, "<%.*s> request type: invalid method\n", (int) ip_size, ip_addr);
+ log(_info, "<%.*s> request type: invalid method", (int) ip_size, ip_addr);
byte* output_data = calloc(1 + 14, sizeof(byte));
output_data[ 0 ] = error;
@@ -562,7 +581,7 @@ void* alloc_map(uint64_t size) {
alloc = mmap(NULL, size, RW, PA, -1, 0);
if (alloc != MAP_FAILED) {
// hugepages failed, remove flag
- printf("[note] no hugepages support present\n");
+ printf("[note] no hugepages support present");
try_hugepages = false;
}
}
@@ -580,30 +599,60 @@ volatile bool disk_persistence_thread_running = true;
bool file_exists(const char* filename) { return access(filename, F_OK) == 0; }
void save_packet(const struct data_packet packet) {
- log(_debug, "saving packet " hash_fmt " to disk\n", hash_ref(packet.hash));
+ if (decide_packet_save_to_disk_func) {
+ if (!decide_packet_save_to_disk_func(packet)) return;
+ }
+
+ log(_debug, "saving packet " hash_fmt " to disk", hash_ref(packet.hash));
mkdir("packets", 0755);
- char filename[ 128 ];
- sprintf(filename, "packets/" hash_fmt ".bin", hash_ref(packet.hash));
+ char filename_tmp[ 256 ];
+ char filename_final[ 256 ];
+ snprintf(
+ filename_tmp, sizeof(filename_tmp), "packets/" hash_fmt ".bin.tmp", hash_ref(packet.hash));
+ snprintf(
+ filename_final, sizeof(filename_final), "packets/" hash_fmt ".bin", hash_ref(packet.hash));
- FILE* file = fopen(filename, "wb");
- if (!file) {
- perror("failed to save packet");
+ /* serialize packet into buffer */
+ byte buffer[ PACKET_SIZE ];
+ data_packet2s(packet, buffer);
+
+ /* compress */
+ uLongf comp_bound = compressBound(PACKET_SIZE);
+ byte comp_buf[ comp_bound ];
+
+ uLongf comp_len = comp_bound;
+ int zres = compress2(comp_buf, &comp_len, buffer, PACKET_SIZE, Z_BEST_COMPRESSION);
+ if (zres != Z_OK) {
+ log(_error, "save_packet: compress2 failed (%d)", zres);
return;
}
- byte* buffer = malloc(PACKET_SIZE);
- if (!buffer) {
- fclose(file);
+ FILE* f = fopen(filename_tmp, "wb");
+ if (!f) {
+ perror("save_packet: fopen tmp");
return;
}
- data_packet2s(packet, buffer);
- size_t written = fwrite(buffer, 1, PACKET_SIZE, file);
- if (written != PACKET_SIZE) { perror("failed to write full packet to disk"); }
- free(buffer);
- fclose(file);
+ if (fwrite(comp_buf, 1, comp_len, f) != comp_len) {
+ perror("save_packet: write failed");
+ fclose(f);
+ unlink(filename_tmp);
+ return;
+ }
+
+ fflush(f);
+#if defined(__unix__) || defined(__APPLE__)
+ int fd = fileno(f);
+ if (fd >= 0) fsync(fd);
+#endif
+ fclose(f);
+
+ if (rename(filename_tmp, filename_final) != 0) {
+ perror("save_packet: rename failed");
+ unlink(filename_tmp);
+ }
}
void save_neighbors() {
@@ -645,13 +694,13 @@ void load_neighbors() {
}
fclose(file);
- log(_info, "loaded %li neighbors\n", count);
+ log(_info, "loaded %li neighbors", count);
}
void* fs_persistence(void* arg) {
while (disk_persistence_thread_running) {
sleep(2 * 60); // 2m
- log(_info, "saving neighbors...\n");
+ log(_info, "saving neighbors...");
save_neighbors();
}
@@ -662,18 +711,29 @@ void* cleanup_packets(void* arg) {
while (packet_cleanup_thread_running) {
sleep(30 * 60); // 30m
- log(_warn, "running cleanup\n");
+ log(_warn, "running cleanup");
uint64_t current_time_ms = get_current_time_ms();
uint64_t retention_ms = (uint64_t) server_stats.retention_hours * 60 * 60 * 1000;
// memory
for (uint64_t i = 0; i < max_storage; i++) {
pthread_mutex_lock(&packet_storage_mutex);
- if (packet_storage[ i ].is_full
- && current_time_ms - packet_storage[ i ].packet.contents.created_time_ms
- > retention_ms) {
+
+ if (!packet_storage[ i ].is_full) {
+ pthread_mutex_unlock(&packet_storage_mutex);
+ continue;
+ }
+
+ bool result
+ = decide_packet_cleanup_func
+ ? decide_packet_cleanup_func(packet_storage[ i ].packet) // delegate to user
+ : ( // do it natively
+ current_time_ms - packet_storage[ i ].packet.contents.created_time_ms
+ > retention_ms);
+
+ if (result) {
log(_debug,
- "cleaning up old packet hashed " hash_fmt "\n",
+ "cleaning up old packet hashed " hash_fmt,
hash_ref(packet_storage[ i ].packet.hash));
char filename[ 128 ];
@@ -682,11 +742,12 @@ void* cleanup_packets(void* arg) {
"packets/" hash_fmt ".bin",
hash_ref(packet_storage[ i ].packet.hash));
- if (remove(filename) != 0) log(_error, "failed to delete packet %s\n", filename);
+ if (remove(filename) != 0) log(_error, "failed to delete packet %s", filename);
packet_storage[ i ].is_full = false;
packet_count--;
}
+
pthread_mutex_unlock(&packet_storage_mutex);
}
@@ -701,10 +762,25 @@ void* cleanup_packets(void* arg) {
if (entry->d_type == DT_REG) {
char filepath[ 256 ];
snprintf(filepath, sizeof(filepath), "packets/%s", entry->d_name);
+
+ FILE* fd = decide_packet_cleanup_func ? fopen(filepath, "rb") : NULL;
+ struct data_packet* new = decide_packet_cleanup_func ? load_packet(fd) : NULL;
+
struct stat st;
- if (stat(filepath, &st) == 0
- && current_time_ms - (uint64_t) st.st_mtime * 1000 > retention_ms) {
- if (remove(filepath) != 0) log(_error, "failed to delete %s\n", filepath);
+ bool result = decide_packet_cleanup_func
+ ? decide_packet_cleanup_func(*new) // delegate to user
+ : ( // do it natively
+ stat(filepath, &st) == 0
+ && current_time_ms - (uint64_t) st.st_mtime * 1000
+ > retention_ms);
+
+ if (decide_packet_cleanup_func) {
+ fclose(fd);
+ free(new);
+ }
+
+ if (result) {
+ if (remove(filepath) != 0) log(_error, "failed to delete %s", filepath);
}
}
}
@@ -767,7 +843,7 @@ void* check_neighbors(void* arg) {
if (result != 0 || response_data[ 0 ] != audit_response) {
empty_neighbor_slots[ i ] = true;
neighbor_count--;
- log(_warn, "removed dead neighbor\n");
+ log(_warn, "removed dead neighbor");
}
if (response_data) { free(response_data); }
@@ -780,7 +856,41 @@ void* check_neighbors(void* arg) {
return NULL;
}
-int main() {
+int main(int argc, char** argv) {
+ parse_arguments(argc, argv);
+
+ char* __log_level = get_config("log", false);
+ if (__log_level) {
+ if (isdigit(*__log_level)) {
+ int _log_level = atoi(__log_level);
+ if (_log_level < 1 || _log_level > 4) {
+ fprintf(
+ stderr,
+ "invalid log level %d (1 = debug, 2 = info, 3 = warn, 4 = error)\n",
+ _log_level);
+ exit(1);
+ }
+ log_level = _log_level;
+ } else {
+ if (strcmp(__log_level, "debug") == 0) log_level = 1;
+ else if (strcmp(__log_level, "info") == 0)
+ log_level = 2;
+ else if (strcmp(__log_level, "warn") == 0)
+ log_level = 3;
+ else if (strcmp(__log_level, "error") == 0)
+ log_level = 4;
+ else {
+ fprintf(
+ stderr,
+ "invalid log level %s (1 = debug, 2 = info, 3 = warn, 4 = error)\n",
+ __log_level);
+ exit(1);
+ }
+ }
+
+ free(__log_level);
+ }
+
fprintf(
stderr,
"-- moonpaste server v%i.%i --\n"
@@ -820,6 +930,13 @@ int main() {
firehose_init();
+ // load the plugins!
+ char* plugin_path = get_config("plugin", false);
+ if (plugin_path) {
+ load_plugins(plugin_path);
+ free(plugin_path);
+ }
+
omen(default_port, paste_server);
packet_cleanup_thread_running = false;
diff --git a/sha256.c b/src/sha256.c
index 0d19110..11ef912 100644
--- a/sha256.c
+++ b/src/sha256.c
@@ -30,7 +30,7 @@ static const uint32_t K[ 64 ] = {
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};
-static void compress(uint32_t state[ 8 ], const byte block[ 64 ]) {
+static void sha256_compress(uint32_t state[ 8 ], const byte block[ 64 ]) {
uint32_t w[ 64 ];
int i;
for (i = 0; i < 16; i++) {
@@ -72,7 +72,7 @@ struct sha256 {
uint32_t state[ 8 ] = { 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 };
size_t off = 0;
- for (; off + 64 <= in_len; off += 64) { compress(state, in + off); }
+ for (; off + 64 <= in_len; off += 64) { sha256_compress(state, in + off); }
byte pad[ 128 ];
memset(pad, 0, sizeof(pad));
size_t rem = in_len - off;
@@ -91,8 +91,8 @@ struct sha256 {
pad[ pad_len - 6 ] = (bit_len >> 40) & 0xff;
pad[ pad_len - 7 ] = (bit_len >> 48) & 0xff;
pad[ pad_len - 8 ] = (bit_len >> 56) & 0xff;
- compress(state, pad);
- if (use_two_blocks) { compress(state, pad + 64); }
+ sha256_compress(state, pad);
+ if (use_two_blocks) { sha256_compress(state, pad + 64); }
for (int i = 0; i < 8; i++) {
out.hash[ 4 * i ] = (state[ i ] >> 24) & 0xff;
out.hash[ 4 * i + 1 ] = (state[ i ] >> 16) & 0xff;
@@ -101,4 +101,4 @@ struct sha256 {
}
return out;
-} \ No newline at end of file
+}