kike: add a trivial flood detector

This commit is contained in:
Přemysl Eric Janouch 2014-08-10 02:22:52 +02:00
parent 126c07b70a
commit b8f002eaf5
1 changed files with 53 additions and 0 deletions

53
kike.c
View File

@ -92,6 +92,49 @@ setup_signal_handlers (void)
exit_fatal ("%s: %s", "sigaction", strerror (errno)); exit_fatal ("%s: %s", "sigaction", strerror (errno));
} }
// --- Rate limiter ------------------------------------------------------------
struct flood_detector
{
unsigned interval; ///< Interval for the limit
unsigned limit; ///< Maximum number of events allowed
time_t *timestamps; ///< Timestamps of last events
unsigned pos; ///< Index of the oldest event
};
static void
flood_detector_init (struct flood_detector *self,
unsigned interval, unsigned limit)
{
self->interval = interval;
self->limit = limit;
self->timestamps = xcalloc (limit + 1, sizeof *self->timestamps);
self->pos = 0;
}
static void
flood_detector_free (struct flood_detector *self)
{
free (self->timestamps);
}
static bool
flood_detector_check (struct flood_detector *self)
{
time_t now = time (NULL);
self->timestamps[self->pos++] = now;
if (self->pos > self->limit)
self->pos = 0;
time_t begin = now - self->interval;
size_t count = 0;
for (size_t i = 0; i <= self->limit; i++)
if (self->timestamps[i] >= begin)
count++;
return count <= self->limit;
}
// --- IRC token validation ---------------------------------------------------- // --- IRC token validation ----------------------------------------------------
// Use the enum only if applicable and a simple boolean isn't sufficient. // Use the enum only if applicable and a simple boolean isn't sufficient.
@ -277,6 +320,7 @@ struct client
unsigned mode; ///< User's mode unsigned mode; ///< User's mode
char *away_message; ///< Away message char *away_message; ///< Away message
time_t last_active; ///< Last PRIVMSG, to get idle time time_t last_active; ///< Last PRIVMSG, to get idle time
struct flood_detector antiflood; ///< Flood detector
}; };
static void static void
@ -287,6 +331,8 @@ client_init (struct client *self)
self->socket_fd = -1; self->socket_fd = -1;
str_init (&self->read_buffer); str_init (&self->read_buffer);
str_init (&self->write_buffer); str_init (&self->write_buffer);
// TODO: make this configurable and more fine-grained
flood_detector_init (&self->antiflood, 10, 20);
} }
static void static void
@ -306,6 +352,7 @@ client_free (struct client *self)
free (self->hostname); free (self->hostname);
free (self->away_message); free (self->away_message);
flood_detector_free (&self->antiflood);
} }
static void client_close_link (struct client *, const char *); static void client_close_link (struct client *, const char *);
@ -2299,6 +2346,12 @@ irc_process_message (const struct irc_message *msg,
if (c->closing_link) if (c->closing_link)
return; return;
if (!flood_detector_check (&c->antiflood))
{
client_close_link (c, "Excess flood");
return;
}
struct irc_command *cmd = str_map_find (&c->ctx->handlers, msg->command); struct irc_command *cmd = str_map_find (&c->ctx->handlers, msg->command);
if (!cmd) if (!cmd)
irc_send_reply (c, IRC_ERR_UNKNOWNCOMMAND, msg->command); irc_send_reply (c, IRC_ERR_UNKNOWNCOMMAND, msg->command);