summaryrefslogtreecommitdiff
path: root/src/ratelimit.c
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 /src/ratelimit.c
parentnew things (diff)
feature impls + directories
Diffstat (limited to 'src/ratelimit.c')
-rw-r--r--src/ratelimit.c108
1 files changed, 108 insertions, 0 deletions
diff --git a/src/ratelimit.c b/src/ratelimit.c
new file mode 100644
index 0000000..c6f0e01
--- /dev/null
+++ b/src/ratelimit.c
@@ -0,0 +1,108 @@
+#include <pthread.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#define TABLE_SIZE 2048
+#define MAX_KEY_LEN 46
+#define STALE_SECONDS 30
+
+struct rl_entry {
+ char ip[ MAX_KEY_LEN ];
+ time_t last_request;
+ int count;
+ int in_use;
+};
+
+struct rl_table {
+ struct rl_entry entries[ TABLE_SIZE ];
+ pthread_mutex_t mutex;
+};
+
+struct rl_table* global_ratelimit = NULL;
+
+void ratelimit_init(void) {
+ if (global_ratelimit != NULL) return;
+
+ struct rl_table* table = malloc(sizeof(struct rl_table));
+ if (!table) {
+ perror("failed to alloc ratelimit table");
+ exit(1);
+ }
+ memset(table->entries, 0, sizeof(table->entries));
+ pthread_mutex_init(&table->mutex, NULL);
+
+ global_ratelimit = table;
+}
+
+static uint32_t hash_ip(const char* ip) {
+ uint32_t hash = 5381;
+ while (*ip) hash = (hash << 5) + hash + *ip++;
+ return hash % TABLE_SIZE;
+}
+
+int ratelimit_check(const char* ip) {
+ ratelimit_init();
+ pthread_mutex_lock(&global_ratelimit->mutex);
+
+ time_t now = time(NULL);
+ uint32_t index = hash_ip(ip);
+ uint32_t probes = 0;
+
+ while (probes < TABLE_SIZE) {
+ struct rl_entry* entry = &global_ratelimit->entries[ index ];
+
+ if (!entry->in_use) {
+ strncpy(entry->ip, ip, MAX_KEY_LEN - 1);
+ entry->ip[ MAX_KEY_LEN - 1 ] = '\0';
+ entry->last_request = now;
+ entry->count = 1;
+ entry->in_use = 1;
+ pthread_mutex_unlock(&global_ratelimit->mutex);
+ return 1;
+ }
+
+ if (strcmp(entry->ip, ip) == 0) {
+ if (now > entry->last_request) {
+ entry->last_request = now;
+ entry->count = 1;
+ } else if (entry->count >= 4) {
+ pthread_mutex_unlock(&global_ratelimit->mutex);
+ return 0;
+ } else {
+ entry->count++;
+ }
+ pthread_mutex_unlock(&global_ratelimit->mutex);
+ return 1;
+ }
+
+ if (entry->in_use && (now - entry->last_request) > STALE_SECONDS) {
+ entry->in_use = 0;
+ entry->count = 0;
+ entry->last_request = 0;
+ entry->ip[ 0 ] = '\0';
+ strncpy(entry->ip, ip, MAX_KEY_LEN - 1);
+ entry->ip[ MAX_KEY_LEN - 1 ] = '\0';
+ entry->last_request = now;
+ entry->count = 1;
+ entry->in_use = 1;
+ pthread_mutex_unlock(&global_ratelimit->mutex);
+ return 1;
+ }
+
+ index = (index + 1) % TABLE_SIZE;
+ probes++;
+ }
+
+ pthread_mutex_unlock(&global_ratelimit->mutex);
+ return 0;
+}
+
+void ratelimit_free(struct rl_table* table) {
+ if (table) {
+ pthread_mutex_destroy(&table->mutex);
+ free(table);
+ }
+}