ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
sqlite.c
Go to the documentation of this file.
1
8#include <ascii-chat/network/rate_limit/sqlite.h>
9#include <ascii-chat/log/logging.h>
10#include <ascii-chat/util/time.h>
11#include <sqlite3.h>
12#include <string.h>
13
17typedef struct {
18 sqlite3 *db;
20
21static asciichat_error_t sqlite_check(void *backend_data, const char *ip_address, rate_event_type_t event_type,
22 const rate_limit_config_t *config, bool *allowed) {
23 sqlite_backend_t *backend = (sqlite_backend_t *)backend_data;
24
25 // Use provided config or default
26 const rate_limit_config_t *limit = config ? config : &DEFAULT_RATE_LIMITS[event_type];
27
28 // Get current time in nanoseconds
29 uint64_t now_ns = time_get_realtime_ns();
30 uint64_t window_start_ns = now_ns - ((uint64_t)limit->window_secs * NS_PER_SEC_INT);
31
32 // Count events in the time window
33 const char *sql = "SELECT COUNT(*) FROM rate_events "
34 "WHERE ip_address = ? AND event_type = ? AND timestamp >= ?";
35
36 sqlite3_stmt *stmt = NULL;
37 int rc = sqlite3_prepare_v2(backend->db, sql, -1, &stmt, NULL);
38 if (rc != SQLITE_OK) {
39 return SET_ERRNO(ERROR_CONFIG, "Failed to prepare rate limit query: %s", sqlite3_errmsg(backend->db));
40 }
41
42 sqlite3_bind_text(stmt, 1, ip_address, -1, SQLITE_STATIC);
43 sqlite3_bind_text(stmt, 2, rate_limiter_event_type_string(event_type), -1, SQLITE_STATIC);
44 sqlite3_bind_int64(stmt, 3, (sqlite3_int64)window_start_ns);
45
46 rc = sqlite3_step(stmt);
47 if (rc != SQLITE_ROW) {
48 sqlite3_finalize(stmt);
49 return SET_ERRNO(ERROR_CONFIG, "Failed to execute rate limit query: %s", sqlite3_errmsg(backend->db));
50 }
51
52 uint32_t event_count = (uint32_t)sqlite3_column_int(stmt, 0);
53 sqlite3_finalize(stmt);
54
55 // Check if limit exceeded
56 *allowed = (event_count < limit->max_events);
57
58 if (!*allowed) {
59 log_warn("Rate limit exceeded for %s (event: %s, count: %u/%u)", ip_address,
60 rate_limiter_event_type_string(event_type), event_count, limit->max_events);
61 }
62
63 return ASCIICHAT_OK;
64}
65
66static asciichat_error_t sqlite_record(void *backend_data, const char *ip_address, rate_event_type_t event_type) {
67 sqlite_backend_t *backend = (sqlite_backend_t *)backend_data;
68
69 // Get current time in nanoseconds
70 uint64_t now_ns = time_get_realtime_ns();
71
72 // Insert event
73 const char *sql = "INSERT INTO rate_events (ip_address, event_type, timestamp) VALUES (?, ?, ?)";
74
75 sqlite3_stmt *stmt = NULL;
76 int rc = sqlite3_prepare_v2(backend->db, sql, -1, &stmt, NULL);
77 if (rc != SQLITE_OK) {
78 return SET_ERRNO(ERROR_CONFIG, "Failed to prepare rate limit insert: %s", sqlite3_errmsg(backend->db));
79 }
80
81 sqlite3_bind_text(stmt, 1, ip_address, -1, SQLITE_STATIC);
82 sqlite3_bind_text(stmt, 2, rate_limiter_event_type_string(event_type), -1, SQLITE_STATIC);
83 sqlite3_bind_int64(stmt, 3, (sqlite3_int64)now_ns);
84
85 rc = sqlite3_step(stmt);
86 sqlite3_finalize(stmt);
87
88 if (rc != SQLITE_DONE) {
89 return SET_ERRNO(ERROR_CONFIG, "Failed to record rate event: %s", sqlite3_errmsg(backend->db));
90 }
91
92 log_debug("Rate event recorded: %s - %s", ip_address, rate_limiter_event_type_string(event_type));
93 return ASCIICHAT_OK;
94}
95
96static asciichat_error_t sqlite_cleanup(void *backend_data, uint32_t max_age_secs) {
97 sqlite_backend_t *backend = (sqlite_backend_t *)backend_data;
98
99 // Default to 1 hour cleanup window
100 if (max_age_secs == 0) {
101 max_age_secs = SEC_PER_HOUR;
102 }
103
104 // Calculate cutoff time in nanoseconds
105 uint64_t now_ns = time_get_realtime_ns();
106 uint64_t cutoff_ns = now_ns - ((uint64_t)max_age_secs * NS_PER_SEC_INT);
107
108 // Delete old events
109 const char *sql = "DELETE FROM rate_events WHERE timestamp < ?";
110
111 sqlite3_stmt *stmt = NULL;
112 int rc = sqlite3_prepare_v2(backend->db, sql, -1, &stmt, NULL);
113 if (rc != SQLITE_OK) {
114 return SET_ERRNO(ERROR_CONFIG, "Failed to prepare rate limit cleanup: %s", sqlite3_errmsg(backend->db));
115 }
116
117 sqlite3_bind_int64(stmt, 1, (sqlite3_int64)cutoff_ns);
118
119 rc = sqlite3_step(stmt);
120 int changes = sqlite3_changes(backend->db);
121 sqlite3_finalize(stmt);
122
123 if (rc != SQLITE_DONE) {
124 return SET_ERRNO(ERROR_CONFIG, "Failed to cleanup rate events: %s", sqlite3_errmsg(backend->db));
125 }
126
127 if (changes > 0) {
128 log_debug("Cleaned up %d old rate events", changes);
129 }
130
131 return ASCIICHAT_OK;
132}
133
134static void sqlite_destroy(void *backend_data) {
135 sqlite_backend_t *backend = (sqlite_backend_t *)backend_data;
136 if (!backend) {
137 return;
138 }
139
140 // Note: We don't close the database here because it's owned by the caller
141 // (ACDS server manages the database lifecycle)
142 free(backend);
143}
144
145void *sqlite_backend_create(const char *db_path) {
146 (void)db_path; // Database is provided externally, not created here
147
148 sqlite_backend_t *backend = malloc(sizeof(sqlite_backend_t));
149 if (!backend) {
150 log_error("Failed to allocate SQLite backend");
151 return NULL;
152 }
153
154 memset(backend, 0, sizeof(*backend));
155
156 // Database handle will be set after creation by ACDS
157 // This is a placeholder backend that will be initialized later
158 log_debug("SQLite rate limiter backend allocated (database will be set externally)");
159 return backend;
160}
161
167void sqlite_backend_set_db(void *backend_data, sqlite3 *db) {
168 sqlite_backend_t *backend = (sqlite_backend_t *)backend_data;
169 if (backend) {
170 backend->db = db;
171 }
172}
173
174const rate_limiter_backend_ops_t sqlite_backend_ops = {
175 .check = sqlite_check,
176 .record = sqlite_record,
177 .cleanup = sqlite_cleanup,
178 .destroy = sqlite_destroy,
179};
const char * rate_limiter_event_type_string(rate_event_type_t event_type)
Get event type string for logging.
Definition rate_limit.c:176
const rate_limit_config_t DEFAULT_RATE_LIMITS[RATE_EVENT_MAX]
Definition rate_limit.c:30
void sqlite_backend_set_db(void *backend_data, sqlite3 *db)
Set SQLite database handle for backend.
Definition sqlite.c:167
void * sqlite_backend_create(const char *db_path)
Definition sqlite.c:145
const rate_limiter_backend_ops_t sqlite_backend_ops
Definition sqlite.c:174
SQLite backend data.
Definition sqlite.c:17
sqlite3 * db
SQLite database handle.
Definition sqlite.c:18
uint64_t time_get_realtime_ns(void)
Definition util/time.c:59