This commit is contained in:
2026-05-25 21:59:49 +02:00
parent b8b8471595
commit 3ff2c3f902

View File

@@ -24,6 +24,7 @@ static struct {
typedef struct pthread_input_t { typedef struct pthread_input_t {
int client_fd; int client_fd;
float window; float window;
int rate_limit;
const char* command; const char* command;
} pthread_input_t; } pthread_input_t;
@@ -33,7 +34,7 @@ static double now(void) {
return ts.tv_sec + ts.tv_nsec * 1e-9; return ts.tv_sec + ts.tv_nsec * 1e-9;
} }
static int rate_limited(const uint8_t addr[16], float window) { static int rate_limited(const uint8_t addr[16], float window, int rate_limit) {
double n = now(); double n = now();
pthread_mutex_lock(&ratelimit_table.lock); pthread_mutex_lock(&ratelimit_table.lock);
@@ -58,7 +59,7 @@ static int rate_limited(const uint8_t addr[16], float window) {
e->count = 0; e->count = 0;
} }
const int allowed = e->count < RATE_LIMIT; const int allowed = e->count < rate_limit;
if (allowed) e->count++; if (allowed) e->count++;
pthread_mutex_unlock(&ratelimit_table.lock); pthread_mutex_unlock(&ratelimit_table.lock);
@@ -69,13 +70,14 @@ static void* handle_client(void* _arg) {
pthread_input_t* arg = _arg; pthread_input_t* arg = _arg;
int client_fd = arg->client_fd; int client_fd = arg->client_fd;
float window = arg->window; float window = arg->window;
int rate_limit = arg->rate_limit;
const char* command = arg->command; const char* command = arg->command;
struct sockaddr_in6 peer; struct sockaddr_in6 peer;
socklen_t peerlen = sizeof(peer); socklen_t peerlen = sizeof(peer);
getpeername(client_fd, (struct sockaddr*)&peer, &peerlen); getpeername(client_fd, (struct sockaddr*)&peer, &peerlen);
if (rate_limited(peer.sin6_addr.s6_addr, window)) { if (rate_limited(peer.sin6_addr.s6_addr, window, rate_limit)) {
dprintf(client_fd, dprintf(client_fd,
"HTTP/1.1 429 Too Many Requests\r\n" "HTTP/1.1 429 Too Many Requests\r\n"
"Content-Type: text/plain\r\n" "Content-Type: text/plain\r\n"
@@ -83,7 +85,6 @@ static void* handle_client(void* _arg) {
"Connection: close\r\n" "Connection: close\r\n"
"\r\n" "\r\n"
"Too Many Requests\r\n" "Too Many Requests\r\n"
"HTTP/1.1 200 OK\r\n"
); );
goto cleanup; goto cleanup;
} }
@@ -125,6 +126,7 @@ cleanup:
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
int port = 80; int port = 80;
float window = 60.0f; float window = 60.0f;
int rate_limit = 5;
const char* command = "fastfetch --pipe false 2>/dev/null"; const char* command = "fastfetch --pipe false 2>/dev/null";
struct in6_addr bind_addr = in6addr_any; struct in6_addr bind_addr = in6addr_any;
@@ -133,10 +135,12 @@ int main(int argc, char *argv[]) {
puts("usage:\n" puts("usage:\n"
"\tfastfetch_server --addr 127.0.01 (optional)\n" "\tfastfetch_server --addr 127.0.01 (optional)\n"
"\t --window 60 (optional)\n" "\t --window 60 (optional)\n"
"\t --ratelimit 5 (optional)"
"\t --hyfetch (optional)\n" "\t --hyfetch (optional)\n"
"\t port (optional)\n" "\t port (optional)\n"
"\t --addr expects an address in an IPv4 or IPv6 format\n" "\t --addr expects an address in an IPv4 or IPv6 format\n"
"\t --window sets a ratelimit delay in seconds\n" "\t --window sets a rate limit delay in seconds\n"
"\t --ratelimit sets the rate limit amount"
"\t --hyfetch uses hyfetch with fastfetch backend instead of fastfetch\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 port specifies the port, 80 by default, might need superuser for\n"
"\t ports below 1024" "\t ports below 1024"
@@ -158,7 +162,10 @@ int main(int argc, char *argv[]) {
} }
} }
else if (streq(argv[i], "--window") && i + 1 < argc) { else if (streq(argv[i], "--window") && i + 1 < argc) {
window = atof(argv[i++]); window = atof(argv[++i]);
}
else if (streq(argv[i], "--ratelimit") && i + 1 < argc) {
rate_limit = atoi(argv[++i]);
} }
else if (streq(argv[i], "--hyfetch")) { else if (streq(argv[i], "--hyfetch")) {
command = "hyfetch --backend=fastfetch --args=\"--pipe false\" 2>/dev/null"; command = "hyfetch --backend=fastfetch --args=\"--pipe false\" 2>/dev/null";
@@ -188,7 +195,7 @@ int main(int argc, char *argv[]) {
char addr_str[INET6_ADDRSTRLEN]; char addr_str[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &bind_addr, addr_str, sizeof(addr_str)); inet_ntop(AF_INET6, &bind_addr, addr_str, sizeof(addr_str));
printf("listening on [%s]:%d (limit: %d req / %fs per IP)\n", printf("listening on [%s]:%d (limit: %d req / %fs per IP)\n",
addr_str, port, RATE_LIMIT, window); addr_str, port, rate_limit, window);
for (;;) { for (;;) {
struct sockaddr_in6 client_addr; struct sockaddr_in6 client_addr;
@@ -196,7 +203,7 @@ int main(int argc, char *argv[]) {
int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &addrlen); int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &addrlen);
if (client_fd < 0) continue; if (client_fd < 0) continue;
pthread_input_t pi = {.client_fd = client_fd, .window = window, .command = command}; pthread_input_t pi = {.client_fd = client_fd, .window = window, .rate_limit = rate_limit, .command = command};
pthread_t tid; pthread_t tid;
if (pthread_create(&tid, NULL, handle_client, (void*)(intptr_t)&pi) == 0) if (pthread_create(&tid, NULL, handle_client, (void*)(intptr_t)&pi) == 0)