ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
rate_limit.h File Reference

🚦 Rate limiting API with pluggable backends More...

Go to the source code of this file.

Data Structures

struct  rate_limit_config_t
 Rate limit configuration. More...
 
struct  rate_limiter_backend_ops_t
 Backend operations vtable. More...
 

Typedefs

typedef struct rate_limiter_s rate_limiter_t
 Opaque rate limiter handle.
 

Enumerations

enum  rate_event_type_t {
  RATE_EVENT_SESSION_CREATE = 0 , RATE_EVENT_SESSION_LOOKUP = 1 , RATE_EVENT_SESSION_JOIN = 2 , RATE_EVENT_CONNECTION = 3 ,
  RATE_EVENT_IMAGE_FRAME = 4 , RATE_EVENT_AUDIO = 5 , RATE_EVENT_PING = 6 , RATE_EVENT_CLIENT_JOIN = 7 ,
  RATE_EVENT_CONTROL = 8 , RATE_EVENT_MAX
}
 Rate limit event types. More...
 

Functions

rate_limiter_t * rate_limiter_create_memory (void)
 Create in-memory rate limiter.
 
rate_limiter_t * rate_limiter_create_sqlite (const char *db_path)
 Create SQLite-backed rate limiter.
 
void rate_limiter_set_sqlite_db (rate_limiter_t *limiter, void *db)
 Set SQLite database handle for rate limiter.
 
void rate_limiter_destroy (rate_limiter_t *limiter)
 Destroy rate limiter and free resources.
 
asciichat_error_t rate_limiter_check (rate_limiter_t *limiter, const char *ip_address, rate_event_type_t event_type, const rate_limit_config_t *config, bool *allowed)
 Check if an event from an IP address should be rate limited.
 
asciichat_error_t rate_limiter_record (rate_limiter_t *limiter, const char *ip_address, rate_event_type_t event_type)
 Record a rate limit event.
 
asciichat_error_t rate_limiter_prune (rate_limiter_t *limiter, uint32_t max_age_secs)
 Clean up old rate limit events.
 
const char * rate_limiter_event_type_string (rate_event_type_t event_type)
 Get event type string for logging/database storage.
 

Variables

const rate_limit_config_t DEFAULT_RATE_LIMITS [RATE_EVENT_MAX]
 Default rate limits for each event type.
 

Detailed Description

🚦 Rate limiting API with pluggable backends

Supports two backends:

  • Memory: Thread-safe in-memory tracking with uthash (for ascii-chat server)
  • SQLite: Persistent database tracking (for acds discovery server)

Example usage:

// Create in-memory rate limiter
// Or SQLite-backed limiter
// Check rate limit
bool allowed = false;
rate_limiter_check(limiter, "192.168.1.100", RATE_EVENT_SESSION_CREATE, NULL, &allowed);
if (allowed) {
rate_limiter_record(limiter, "192.168.1.100", RATE_EVENT_SESSION_CREATE);
// Process request
} else {
// Reject request
}
// Cleanup (call periodically)
rate_limiter_prune(limiter, 3600);
// Destroy
asciichat_error_t rate_limiter_record(rate_limiter_t *limiter, const char *ip_address, rate_event_type_t event_type)
Record a rate limit event.
Definition rate_limit.c:145
void rate_limiter_destroy(rate_limiter_t *limiter)
Destroy rate limiter and free resources.
Definition rate_limit.c:116
rate_limiter_t * rate_limiter_create_memory(void)
Create in-memory rate limiter.
Definition rate_limit.c:73
rate_limiter_t * rate_limiter_create_sqlite(const char *db_path)
Create SQLite-backed rate limiter.
Definition rate_limit.c:90
asciichat_error_t rate_limiter_check(rate_limiter_t *limiter, const char *ip_address, rate_event_type_t event_type, const rate_limit_config_t *config, bool *allowed)
Check if an event from an IP address should be rate limited.
Definition rate_limit.c:128
asciichat_error_t rate_limiter_prune(rate_limiter_t *limiter, uint32_t max_age_secs)
Clean up old rate limit events.
Definition rate_limit.c:161
@ RATE_EVENT_SESSION_CREATE
Session creation.
Definition rate_limit.h:48
Rate limiter structure.
Definition rate_limit.c:64

Definition in file rate_limit.h.

Typedef Documentation

◆ rate_limiter_t

Opaque rate limiter handle.

Backend implementation is hidden from users.

Definition at line 76 of file rate_limit.h.

Enumeration Type Documentation

◆ rate_event_type_t

Rate limit event types.

Enumerator
RATE_EVENT_SESSION_CREATE 

Session creation.

RATE_EVENT_SESSION_LOOKUP 

Session lookup.

RATE_EVENT_SESSION_JOIN 

Session join.

RATE_EVENT_CONNECTION 

New connection.

RATE_EVENT_IMAGE_FRAME 

Image frame from client (PACKET_TYPE_IMAGE_FRAME)

RATE_EVENT_AUDIO 

Audio packet (PACKET_TYPE_AUDIO, PACKET_TYPE_AUDIO_BATCH)

RATE_EVENT_PING 

Ping/pong keepalive (PACKET_TYPE_PING, PACKET_TYPE_PONG)

RATE_EVENT_CLIENT_JOIN 

Client join request (PACKET_TYPE_CLIENT_JOIN)

RATE_EVENT_CONTROL 

Control packets (CAPABILITIES, STREAM_START/STOP, LEAVE)

RATE_EVENT_MAX 

Sentinel value.

Definition at line 46 of file rate_limit.h.

46 {
47 // ACDS discovery server events
51
52 // ascii-chat server events
56 RATE_EVENT_PING = 6,
59
rate_event_type_t
Rate limit event types.
Definition rate_limit.h:46
@ RATE_EVENT_SESSION_LOOKUP
Session lookup.
Definition rate_limit.h:49
@ RATE_EVENT_CONTROL
Control packets (CAPABILITIES, STREAM_START/STOP, LEAVE)
Definition rate_limit.h:58
@ RATE_EVENT_SESSION_JOIN
Session join.
Definition rate_limit.h:50
@ RATE_EVENT_CONNECTION
New connection.
Definition rate_limit.h:53
@ RATE_EVENT_PING
Ping/pong keepalive (PACKET_TYPE_PING, PACKET_TYPE_PONG)
Definition rate_limit.h:56
@ RATE_EVENT_AUDIO
Audio packet (PACKET_TYPE_AUDIO, PACKET_TYPE_AUDIO_BATCH)
Definition rate_limit.h:55
@ RATE_EVENT_MAX
Sentinel value.
Definition rate_limit.h:60
@ RATE_EVENT_IMAGE_FRAME
Image frame from client (PACKET_TYPE_IMAGE_FRAME)
Definition rate_limit.h:54
@ RATE_EVENT_CLIENT_JOIN
Client join request (PACKET_TYPE_CLIENT_JOIN)
Definition rate_limit.h:57

Function Documentation

◆ rate_limiter_check()

asciichat_error_t rate_limiter_check ( rate_limiter_t *  limiter,
const char *  ip_address,
rate_event_type_t  event_type,
const rate_limit_config_t *  config,
bool *  allowed 
)

Check if an event from an IP address should be rate limited.

Uses sliding window: counts events in the last window_secs seconds.

Parameters
limiterRate limiter instance
ip_addressIP address string (IPv4 or IPv6)
event_typeType of event
configRate limit configuration (NULL = use defaults)
[out]allowedTrue if event is allowed, false if rate limited
Returns
ASCIICHAT_OK on success, error code otherwise

Definition at line 128 of file rate_limit.c.

129 {
130 if (!limiter || !limiter->ops || !limiter->ops->check) {
131 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid rate limiter");
132 }
133
134 if (!ip_address || !allowed) {
135 return SET_ERRNO(ERROR_INVALID_PARAM, "ip_address or allowed is NULL");
136 }
137
138 if (event_type >= RATE_EVENT_MAX) {
139 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid event_type: %d", event_type);
140 }
141
142 return limiter->ops->check(limiter->backend_data, ip_address, event_type, config, allowed);
143}
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
@ ERROR_INVALID_PARAM
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
void * backend_data
Backend-specific data.
Definition rate_limit.c:66
const rate_limiter_backend_ops_t * ops
Backend operations.
Definition rate_limit.c:65

References rate_limiter_s::backend_data, rate_limiter_backend_ops_t::check, ERROR_INVALID_PARAM, rate_limiter_s::ops, RATE_EVENT_MAX, and SET_ERRNO.

Referenced by check_and_record_rate_limit().

◆ rate_limiter_create_memory()

rate_limiter_t * rate_limiter_create_memory ( void  )

Create in-memory rate limiter.

Thread-safe implementation using uthash and mutexes. Suitable for ascii-chat server where persistence is not needed.

Returns
Rate limiter instance or NULL on failure

Definition at line 73 of file rate_limit.c.

73 {
74 rate_limiter_t *limiter = malloc(sizeof(rate_limiter_t));
75 if (!limiter) {
76 log_error("Failed to allocate rate limiter");
77 return NULL;
78 }
79
81 if (!limiter->backend_data) {
82 free(limiter);
83 return NULL;
84 }
85
86 limiter->ops = &memory_backend_ops;
87 return limiter;
88}
#define log_error(...)
Log an ERROR message.
Definition log/log.h:587
void * memory_backend_create(void)
Create memory backend instance.
const rate_limiter_backend_ops_t memory_backend_ops
Memory backend operations vtable.

References rate_limiter_s::backend_data, log_error, memory_backend_create(), memory_backend_ops, and rate_limiter_s::ops.

◆ rate_limiter_create_sqlite()

rate_limiter_t * rate_limiter_create_sqlite ( const char *  db_path)

Create SQLite-backed rate limiter.

Persistent implementation using SQLite database. Suitable for acds discovery server where persistence is needed.

Parameters
db_pathPath to SQLite database (NULL = externally managed database)
Returns
Rate limiter instance or NULL on failure

Definition at line 90 of file rate_limit.c.

90 {
91 rate_limiter_t *limiter = malloc(sizeof(rate_limiter_t));
92 if (!limiter) {
93 log_error("Failed to allocate rate limiter");
94 return NULL;
95 }
96
97 limiter->backend_data = sqlite_backend_create(db_path);
98 if (!limiter->backend_data) {
99 free(limiter);
100 return NULL;
101 }
102
103 limiter->ops = &sqlite_backend_ops;
104 return limiter;
105}
void * sqlite_backend_create(const char *db_path)
Create SQLite backend instance.
Definition sqlite.c:145
const rate_limiter_backend_ops_t sqlite_backend_ops
SQLite backend operations vtable.
Definition sqlite.c:174

References rate_limiter_s::backend_data, log_error, rate_limiter_s::ops, sqlite_backend_create(), and sqlite_backend_ops.

Referenced by acds_server_init().

◆ rate_limiter_destroy()

void rate_limiter_destroy ( rate_limiter_t *  limiter)

Destroy rate limiter and free resources.

Parameters
limiterRate limiter instance (NULL-safe)

Definition at line 116 of file rate_limit.c.

116 {
117 if (!limiter) {
118 return;
119 }
120
121 if (limiter->ops && limiter->ops->destroy) {
122 limiter->ops->destroy(limiter->backend_data);
123 }
124
125 free(limiter);
126}
void(* destroy)(void *backend_data)
Definition rate_limit.h:91

References rate_limiter_s::backend_data, rate_limiter_backend_ops_t::destroy, and rate_limiter_s::ops.

Referenced by acds_server_init(), and acds_server_shutdown().

◆ rate_limiter_event_type_string()

const char * rate_limiter_event_type_string ( rate_event_type_t  event_type)

Get event type string for logging/database storage.

Parameters
event_typeEvent type enum value
Returns
Event type name string (e.g., "session_create", "connection")

Get event type string for logging/database storage.

Helper: Get event type string for logging.

Definition at line 176 of file rate_limit.c.

176 {
177 if (event_type >= RATE_EVENT_MAX) {
178 return "unknown";
179 }
180 return event_type_strings[event_type];
181}

References RATE_EVENT_MAX.

◆ rate_limiter_prune()

asciichat_error_t rate_limiter_prune ( rate_limiter_t *  limiter,
uint32_t  max_age_secs 
)

Clean up old rate limit events.

Deletes events older than the specified age to prevent unbounded growth. Should be called periodically (e.g., every 5 minutes).

Parameters
limiterRate limiter instance
max_age_secsDelete events older than this (0 = use default 1 hour)
Returns
ASCIICHAT_OK on success, error code otherwise

Definition at line 161 of file rate_limit.c.

161 {
162 if (!limiter || !limiter->ops || !limiter->ops->cleanup) {
163 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid rate limiter");
164 }
165
166 return limiter->ops->cleanup(limiter->backend_data, max_age_secs);
167}
asciichat_error_t(* cleanup)(void *backend_data, uint32_t max_age_secs)
Definition rate_limit.h:89

References rate_limiter_s::backend_data, rate_limiter_backend_ops_t::cleanup, ERROR_INVALID_PARAM, rate_limiter_s::ops, and SET_ERRNO.

◆ rate_limiter_record()

asciichat_error_t rate_limiter_record ( rate_limiter_t *  limiter,
const char *  ip_address,
rate_event_type_t  event_type 
)

Record a rate limit event.

Should be called after rate_limiter_check() returns allowed=true.

Parameters
limiterRate limiter instance
ip_addressIP address string
event_typeType of event
Returns
ASCIICHAT_OK on success, error code otherwise

Definition at line 145 of file rate_limit.c.

145 {
146 if (!limiter || !limiter->ops || !limiter->ops->record) {
147 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid rate limiter");
148 }
149
150 if (!ip_address) {
151 return SET_ERRNO(ERROR_INVALID_PARAM, "ip_address is NULL");
152 }
153
154 if (event_type >= RATE_EVENT_MAX) {
155 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid event_type: %d", event_type);
156 }
157
158 return limiter->ops->record(limiter->backend_data, ip_address, event_type);
159}
asciichat_error_t(* record)(void *backend_data, const char *ip_address, rate_event_type_t event_type)
Definition rate_limit.h:87

References rate_limiter_s::backend_data, ERROR_INVALID_PARAM, rate_limiter_s::ops, RATE_EVENT_MAX, rate_limiter_backend_ops_t::record, and SET_ERRNO.

Referenced by check_and_record_rate_limit().

◆ rate_limiter_set_sqlite_db()

void rate_limiter_set_sqlite_db ( rate_limiter_t *  limiter,
void *  db 
)

Set SQLite database handle for rate limiter.

For SQLite-backed rate limiters where the database lifecycle is managed externally (e.g., ACDS manages its own database). Must be called after rate_limiter_create_sqlite(NULL).

Parameters
limiterRate limiter instance (must be SQLite backend)
dbSQLite database handle

Definition at line 107 of file rate_limit.c.

107 {
108 if (!limiter || !limiter->backend_data) {
109 return;
110 }
111
112 // Call the SQLite backend function to set the database handle
113 sqlite_backend_set_db(limiter->backend_data, (sqlite3 *)db);
114}
void sqlite_backend_set_db(void *backend_data, sqlite3 *db)
Set SQLite database handle for backend.
Definition sqlite.c:167

References rate_limiter_s::backend_data, and sqlite_backend_set_db().

Referenced by acds_server_init().

Variable Documentation

◆ DEFAULT_RATE_LIMITS

const rate_limit_config_t DEFAULT_RATE_LIMITS[RATE_EVENT_MAX]
extern

Default rate limits for each event type.

Definition at line 30 of file rate_limit.c.

30 {
31// ACDS discovery server limits
32#ifdef NDEBUG
33 // Release mode: production limits (144 FPS video, 172 FPS audio)
34 [RATE_EVENT_SESSION_CREATE] = {.max_events = 10, .window_secs = 60}, // 10 creates per minute
35 [RATE_EVENT_SESSION_LOOKUP] = {.max_events = 30, .window_secs = 60}, // 30 lookups per minute
36 [RATE_EVENT_SESSION_JOIN] = {.max_events = 20, .window_secs = 60}, // 20 joins per minute
37 [RATE_EVENT_CONNECTION] = {.max_events = 50, .window_secs = 60}, // 50 connections per minute
38 [RATE_EVENT_IMAGE_FRAME] = {.max_events = 8640, .window_secs = 60}, // 8640 frames/min = 144 FPS
39 [RATE_EVENT_AUDIO] = {.max_events = 10320, .window_secs = 60}, // 10320 packets/min = 172 FPS
40 [RATE_EVENT_PING] = {.max_events = 120, .window_secs = 60}, // 120 pings/min = 2 Hz max
41 [RATE_EVENT_CLIENT_JOIN] = {.max_events = 10, .window_secs = 60}, // 10 joins per minute
42 [RATE_EVENT_CONTROL] = {.max_events = 100, .window_secs = 60}, // 100 control packets/min
43#else
44 // Debug mode: slightly relaxed limits for development/testing (1.5x production limits)
45 [RATE_EVENT_SESSION_CREATE] = {.max_events = 15, .window_secs = 60}, // 15 creates per minute
46 [RATE_EVENT_SESSION_LOOKUP] = {.max_events = 45, .window_secs = 60}, // 45 lookups per minute
47 [RATE_EVENT_SESSION_JOIN] = {.max_events = 30, .window_secs = 60}, // 30 joins per minute
48 [RATE_EVENT_CONNECTION] = {.max_events = 75, .window_secs = 60}, // 75 connections per minute
49 [RATE_EVENT_IMAGE_FRAME] = {.max_events = 12960, .window_secs = 60}, // 12960 frames/min = 216 FPS
50 [RATE_EVENT_AUDIO] = {.max_events = 15480, .window_secs = 60}, // 15480 packets/min = 258 FPS
51 [RATE_EVENT_PING] = {.max_events = 180, .window_secs = 60}, // 180 pings/min = 3 Hz
52 [RATE_EVENT_CLIENT_JOIN] = {.max_events = 25, .window_secs = 60}, // 25 joins per minute (for testing reconnects)
53 [RATE_EVENT_CONTROL] = {.max_events = 150, .window_secs = 60}, // 150 control packets/min
54#endif
55};