#include #include #include #include #include #include #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); } }