From 2d9166d84c1cc76c192e84553099967764ea7c0c Mon Sep 17 00:00:00 2001 From: tema5002 Date: Mon, 25 May 2026 21:51:35 +0200 Subject: [PATCH] Update fastfetch_server.c --- fastfetch_server.c | 189 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 161 insertions(+), 28 deletions(-) diff --git a/fastfetch_server.c b/fastfetch_server.c index b495413..a5a4987 100644 --- a/fastfetch_server.c +++ b/fastfetch_server.c @@ -1,19 +1,100 @@ -// gcc fastfetch_server.c -o fastfetch_server +// gcc fastfetch_server.c -o fastfetch_server -O3 -Wall -Wextra -lpthread #include #include #include +#include +#include +#include #include #include +#include -#define BUFFER_SIZE 0x10000 +#define RATE_LIMIT 5 -void handle_client(int client_fd) { - char output[BUFFER_SIZE]; +typedef struct { + uint8_t addr[16]; + int count; + double window_start; +} ratelimit_t; - FILE *fp = popen("fastfetch --pipe false 2>/dev/null", "r"); - size_t n = fp ? fread(output, 1, BUFFER_SIZE - 1, fp) : 0; +static struct { + ratelimit_t data[256]; + size_t next; + pthread_mutex_t lock; +} ratelimit_table = { {0}, 0, PTHREAD_MUTEX_INITIALIZER }; + +typedef struct pthread_input_t { + int client_fd; + float window; + const char* command; +} pthread_input_t; + +static double now(void) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec + ts.tv_nsec * 1e-9; +} + +static int rate_limited(const uint8_t addr[16], float window) { + double n = now(); + + pthread_mutex_lock(&ratelimit_table.lock); + + ratelimit_t* e = NULL; + for (size_t i = 0; i < 256; i++) { + if (memcmp(ratelimit_table.data[i].addr, addr, 16) == 0) { + e = &ratelimit_table.data[i]; + break; + } + } + + if (!e) { + e = &ratelimit_table.data[ratelimit_table.next++]; + memcpy(e->addr, addr, 16); + e->window_start = n; + e->count = 0; + } + + if (n - e->window_start >= window) { + e->window_start = n; + e->count = 0; + } + + const int allowed = e->count < RATE_LIMIT; + if (allowed) e->count++; + + pthread_mutex_unlock(&ratelimit_table.lock); + return allowed ? 0 : 1; +} + +static void* handle_client(void* _arg) { + pthread_input_t* arg = _arg; + int client_fd = arg->client_fd; + float window = arg->window; + const char* command = arg->command; + + struct sockaddr_in6 peer; + socklen_t peerlen = sizeof(peer); + getpeername(client_fd, (struct sockaddr*)&peer, &peerlen); + + if (rate_limited(peer.sin6_addr.s6_addr, window)) { + dprintf(client_fd, + "HTTP/1.1 429 Too Many Requests\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: 19\r\n" + "Connection: close\r\n" + "\r\n" + "Too Many Requests\r\n" + "HTTP/1.1 200 OK\r\n" + ); + goto cleanup; + } + + char fastfetch[16384]; + FILE* fp = popen(command, "r"); + size_t n = fp ? fread(fastfetch, sizeof(fastfetch[0]), sizeof(fastfetch) - 1, fp) : 0; if (fp) pclose(fp); - output[n] = '\0'; + fastfetch[n] = '\0'; dprintf(client_fd, "HTTP/1.1 200 OK\r\n" @@ -22,22 +103,75 @@ void handle_client(int client_fd) { "Connection: close\r\n" "\r\n" "%s", - n, output); + n, fastfetch); +cleanup: shutdown(client_fd, SHUT_WR); - char discard[1024]; while (recv(client_fd, discard, sizeof(discard), 0) > 0) {} - close(client_fd); - exit(0); + return NULL; } +#define streq(a, b) (strcmp((a), (b)) == 0) +#define exit_error(...) do { \ + fprintf(stderr, __VA_ARGS__); \ + exit(EXIT_FAILURE); \ +} while (0) + +#define exit_perror(s) do { \ + perror(s); \ + exit(EXIT_FAILURE); \ +} while (0) + int main(int argc, char *argv[]) { - const int port = argc > 1 ? atoi(argv[1]) : 80; + int port = 80; + float window = 60.0f; + const char* command = "fastfetch --pipe false 2>/dev/null"; + struct in6_addr bind_addr = in6addr_any; + + for (int i = 1; i < argc; i++) { + if (streq(argv[i], "--help")) { + puts("usage:\n" + "\tfastfetch_server --addr 127.0.01 (optional)\n" + "\t --window 60 (optional)\n" + "\t --hyfetch (optional)\n" + "\t port (optional)\n" + "\t --addr expects an address in an IPv4 or IPv6 format\n" + "\t --window sets a ratelimit delay in seconds\n" + "\t --hyfetch uses hyfetch with fastfetch backend instead of fastfetch\n" + "\t port specifies the port, 80 by default, might need superuser for\n" + "\t ports below 1024" + "get latest version with:\n" + "\t wget https://gitea.codersquack.nl/tema5002/.profile/raw/branch/main/fastfetch_server.c\n" + "\t gcc fastfetch_server.c -o fastfetch_server -O3 -Wall -Wextra -lpthread\n" + "so far tested on:\n" + "\t GNU/Linux on x86_32/x86_64 with glibc/musl"); + return 0; + } + if (streq(argv[i], "--addr") && i + 1 < argc) { + if (inet_pton(AF_INET6, argv[++i], &bind_addr) != 1) { + struct in_addr v4; + if (inet_pton(AF_INET, argv[i], &v5) != 1) exit_error("invalid address: %s\n", argv[i]); + memset(&bind_addr, 0, sizeof(bind_addr)); + bind_addr.s6_addr[10] = 0xff; + bind_addr.s6_addr[11] = 0xff; + memcpy(&bind_addr.s6_addr[12], &v4, 4); + } + } + else if (streq(argv[i], "--window") && i + 1 < argc) { + window = atof(argv[i++]); + } + else if (streq(argv[i], "--hyfetch")) { + command = "hyfetch --backend=fastfetch --args=\"--pipe false\" 2>/dev/null"; + } + else { + port = atoi(argv[i]); + } + } const int server_fd = socket(AF_INET6, SOCK_STREAM, 0); - if (server_fd < 0) return perror("socket"), 1; + if (server_fd < 0) exit_perror("socket"); int opt = 0; setsockopt(server_fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)); @@ -46,32 +180,31 @@ int main(int argc, char *argv[]) { const struct sockaddr_in6 addr = { .sin6_family = AF_INET6, - .sin6_addr = in6addr_any, - .sin6_port = htons(port) + .sin6_addr = bind_addr, + .sin6_port = htons(port) }; - if (bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) return perror("bind"), 1; - if (listen(server_fd, 128) < 0) return perror("listen"), 1; + if (bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) exit_perror("bind"); + if (listen(server_fd, 128) < 0) exit_perror("listen"); - printf("listening on port %d\n", port); + char addr_str[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &bind_addr, addr_str, sizeof(addr_str)); + printf("listening on [%s]:%d (limit: %d req / %fs per IP)\n", + addr_str, port, RATE_LIMIT, window); for (;;) { struct sockaddr_in6 client_addr; socklen_t addrlen = sizeof(client_addr); int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &addrlen); - if (client_fd < 0) continue; - pid_t pid = fork(); - if (pid == 0) { - close(server_fd); - handle_client(client_fd); - } - else if (pid > 0) { - close(client_fd); - } + pthread_input_t pi = {.client_fd = client_fd, .window = window, .command = command}; + + pthread_t tid; + if (pthread_create(&tid, NULL, handle_client, (void*)(intptr_t)&pi) == 0) + pthread_detach(tid); else { - perror("fork"); + perror("pthread_create"); close(client_fd); } }