ascii-chat 0.6.0
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
network/rate_limit/memory.c
Go to the documentation of this file.
1
9#include "common.h"
10#include "log/logging.h"
12#include "uthash.h"
13#include <stdlib.h>
14#include <string.h>
15
19typedef struct rate_event_s {
20 char key[256];
22 size_t count;
23 size_t capacity;
24 size_t head;
25 UT_hash_handle hh;
27
35
39static void make_key(const char *ip_address, rate_event_type_t event_type, char *key, size_t key_size) {
40 snprintf(key, key_size, "%s:%d", ip_address, event_type);
41}
42
46static void add_timestamp(rate_event_t *event, uint64_t timestamp) {
47 if (event->count < event->capacity) {
48 // Buffer not full yet
49 event->timestamps[event->count++] = timestamp;
50 } else {
51 // Buffer full, overwrite oldest
52 event->timestamps[event->head] = timestamp;
53 event->head = (event->head + 1) % event->capacity;
54 }
55}
56
60static uint32_t count_events_in_window(rate_event_t *event, uint64_t window_start_ms) {
61 uint32_t count = 0;
62
63 for (size_t i = 0; i < event->count; i++) {
64 if (event->timestamps[i] >= window_start_ms) {
65 count++;
66 }
67 }
68
69 return count;
70}
71
75static void cleanup_old_events(rate_event_t *event, uint64_t cutoff_ms) {
76 // Compact array by removing old timestamps
77 size_t write_idx = 0;
78
79 for (size_t read_idx = 0; read_idx < event->count; read_idx++) {
80 if (event->timestamps[read_idx] >= cutoff_ms) {
81 event->timestamps[write_idx++] = event->timestamps[read_idx];
82 }
83 }
84
85 event->count = write_idx;
86 event->head = 0; // Reset circular buffer head
87}
88
89static asciichat_error_t memory_check(void *backend_data, const char *ip_address, rate_event_type_t event_type,
90 const rate_limit_config_t *config, bool *allowed) {
91 memory_backend_t *backend = (memory_backend_t *)backend_data;
92
93 // Use provided config or default
94 const rate_limit_config_t *limit = config ? config : &DEFAULT_RATE_LIMITS[event_type];
95
96 // Get current time
98 uint64_t window_start_ms = now_ms - ((uint64_t)limit->window_secs * 1000);
99
100 // Create hash key
101 char key[256];
102 make_key(ip_address, event_type, key, sizeof(key));
103
104 mutex_lock(&backend->lock);
105
106 // Find event record
107 rate_event_t *event = NULL;
108 HASH_FIND_STR(backend->events, key, event);
109
110 uint32_t event_count = 0;
111
112 if (event) {
113 // Count events in window
114 event_count = count_events_in_window(event, window_start_ms);
115 }
116
117 mutex_unlock(&backend->lock);
118
119 // Check if limit exceeded
120 *allowed = (event_count < limit->max_events);
121
122 if (!*allowed) {
123 log_warn("Rate limit exceeded for %s (event: %s, count: %u/%u)", ip_address,
124 rate_limiter_event_type_string(event_type), event_count, limit->max_events);
125 }
126
127 return ASCIICHAT_OK;
128}
129
130static asciichat_error_t memory_record(void *backend_data, const char *ip_address, rate_event_type_t event_type) {
131 memory_backend_t *backend = (memory_backend_t *)backend_data;
132
133 // Get current time
135
136 // Create hash key
137 char key[256];
138 make_key(ip_address, event_type, key, sizeof(key));
139
140 mutex_lock(&backend->lock);
141
142 // Find or create event record
143 rate_event_t *event = NULL;
144 HASH_FIND_STR(backend->events, key, event);
145
146 if (!event) {
147 // Create new event record
148 event = SAFE_MALLOC(sizeof(rate_event_t), rate_event_t *);
149 if (!event) {
150 mutex_unlock(&backend->lock);
151 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate rate event");
152 }
153
154 memset(event, 0, sizeof(*event));
155 SAFE_STRNCPY(event->key, key, sizeof(event->key));
156
157 // Allocate timestamp buffer (default: 100 events)
158 event->capacity = 100;
159 event->timestamps = SAFE_MALLOC(sizeof(uint64_t) * event->capacity, uint64_t *);
160 if (!event->timestamps) {
161 SAFE_FREE(event);
162 mutex_unlock(&backend->lock);
163 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate timestamp buffer");
164 }
165
166 HASH_ADD_STR(backend->events, key, event);
167 }
168
169 // Add timestamp
170 add_timestamp(event, now_ms);
171
172 mutex_unlock(&backend->lock);
173
174 log_debug("Rate event recorded: %s - %s", ip_address, rate_limiter_event_type_string(event_type));
175 return ASCIICHAT_OK;
176}
177
178static asciichat_error_t memory_cleanup(void *backend_data, uint32_t max_age_secs) {
179 memory_backend_t *backend = (memory_backend_t *)backend_data;
180
181 // Default to 1 hour cleanup window
182 if (max_age_secs == 0) {
183 max_age_secs = 3600;
184 }
185
186 // Calculate cutoff time
188 uint64_t cutoff_ms = now_ms - ((uint64_t)max_age_secs * 1000);
189
190 mutex_lock(&backend->lock);
191
192 size_t total_removed = 0;
193 rate_event_t *event, *tmp;
194
195 HASH_ITER(hh, backend->events, event, tmp) {
196 size_t before_count = event->count;
197
198 // Remove old timestamps
199 cleanup_old_events(event, cutoff_ms);
200
201 total_removed += (before_count - event->count);
202
203 // Remove event record if empty
204 if (event->count == 0) {
205 HASH_DEL(backend->events, event);
206 SAFE_FREE(event->timestamps);
207 SAFE_FREE(event);
208 }
209 }
210
211 mutex_unlock(&backend->lock);
212
213 if (total_removed > 0) {
214 log_debug("Cleaned up %zu old rate events", total_removed);
215 }
216
217 return ASCIICHAT_OK;
218}
219
220static void memory_destroy(void *backend_data) {
221 memory_backend_t *backend = (memory_backend_t *)backend_data;
222 if (!backend) {
223 return;
224 }
225
226 // Free all event records
227 rate_event_t *event, *tmp;
228 HASH_ITER(hh, backend->events, event, tmp) {
229 HASH_DEL(backend->events, event);
230 SAFE_FREE(event->timestamps);
231 SAFE_FREE(event);
232 }
233
234 mutex_destroy(&backend->lock);
235 SAFE_FREE(backend);
236}
237
240 if (!backend) {
241 log_error("Failed to allocate memory backend");
242 return NULL;
243 }
244
245 memset(backend, 0, sizeof(*backend));
246
247 if (mutex_init(&backend->lock) != 0) {
248 log_error("Failed to initialize mutex");
249 SAFE_FREE(backend);
250 return NULL;
251 }
252
253 log_debug("Memory rate limiter backend initialized");
254 return backend;
255}
256
258 .check = memory_check,
259 .record = memory_record,
260 .cleanup = memory_cleanup,
261 .destroy = memory_destroy,
262};
🔌 Cross-platform abstraction layer umbrella header for ascii-chat
unsigned int uint32_t
Definition common.h:58
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:358
#define SAFE_FREE(ptr)
Definition common.h:320
#define SAFE_MALLOC(size, cast)
Definition common.h:208
unsigned long long uint64_t
Definition common.h:59
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it.
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:46
@ ERROR_MEMORY
Definition error_codes.h:53
@ ASCIICHAT_OK
Definition error_codes.h:48
#define log_warn(...)
Log a WARN message.
#define log_error(...)
Log an ERROR message.
#define log_debug(...)
Log a DEBUG message.
int mutex_init(mutex_t *mutex)
Initialize a mutex.
#define mutex_lock(mutex)
Lock a mutex (with debug tracking in debug builds)
Definition mutex.h:140
pthread_mutex_t mutex_t
Mutex type (POSIX: pthread_mutex_t)
Definition mutex.h:38
#define mutex_unlock(mutex)
Unlock a mutex (with debug tracking in debug builds)
Definition mutex.h:175
int mutex_destroy(mutex_t *mutex)
Destroy a mutex.
📝 Logging API with multiple log levels and terminal output control
void * memory_backend_create(void)
Create memory backend instance.
const rate_limiter_backend_ops_t memory_backend_ops
Memory backend operations vtable.
struct rate_event_s rate_event_t
Rate event record in memory.
🧠 In-memory rate limiting backend interface
uint64_t rate_limiter_get_time_ms(void)
Helper: Get current time in milliseconds.
Definition rate_limit.c:175
const char * rate_limiter_event_type_string(rate_event_type_t event_type)
Helper: Get event type string for logging.
Definition rate_limit.c:184
const rate_limit_config_t DEFAULT_RATE_LIMITS[RATE_EVENT_MAX]
Default rate limits for each event type.
Definition rate_limit.c:29
rate_event_type_t
Rate limit event types.
Definition rate_limit.h:46
Memory backend data.
mutex_t lock
Mutex for thread safety.
rate_event_t * events
Hash table of rate events.
Rate event record in memory.
char key[256]
Hash key: "ip_address:event_type".
size_t count
Number of events in buffer.
UT_hash_handle hh
uthash handle
size_t capacity
Buffer capacity.
size_t head
Head index (oldest event)
uint64_t * timestamps
Array of timestamps (circular buffer)
Rate limit configuration.
Definition rate_limit.h:66
uint32_t window_secs
Time window in seconds.
Definition rate_limit.h:68
Backend operations vtable.
Definition rate_limit.h:83
asciichat_error_t(* check)(void *backend_data, const char *ip_address, rate_event_type_t event_type, const rate_limit_config_t *config, bool *allowed)
Definition rate_limit.h:84
#️⃣ Wrapper for uthash.h that ensures common.h is included first