kike: add a trivial flood detector
This commit is contained in:
parent
126c07b70a
commit
b8f002eaf5
53
kike.c
53
kike.c
|
@ -92,6 +92,49 @@ setup_signal_handlers (void)
|
|||
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 ----------------------------------------------------
|
||||
|
||||
// Use the enum only if applicable and a simple boolean isn't sufficient.
|
||||
|
@ -277,6 +320,7 @@ struct client
|
|||
unsigned mode; ///< User's mode
|
||||
char *away_message; ///< Away message
|
||||
time_t last_active; ///< Last PRIVMSG, to get idle time
|
||||
struct flood_detector antiflood; ///< Flood detector
|
||||
};
|
||||
|
||||
static void
|
||||
|
@ -287,6 +331,8 @@ client_init (struct client *self)
|
|||
self->socket_fd = -1;
|
||||
str_init (&self->read_buffer);
|
||||
str_init (&self->write_buffer);
|
||||
// TODO: make this configurable and more fine-grained
|
||||
flood_detector_init (&self->antiflood, 10, 20);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -306,6 +352,7 @@ client_free (struct client *self)
|
|||
|
||||
free (self->hostname);
|
||||
free (self->away_message);
|
||||
flood_detector_free (&self->antiflood);
|
||||
}
|
||||
|
||||
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)
|
||||
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);
|
||||
if (!cmd)
|
||||
irc_send_reply (c, IRC_ERR_UNKNOWNCOMMAND, msg->command);
|
||||
|
|
Loading…
Reference in New Issue