diff --git a/kike.c b/kike.c index c390715..32be85c 100644 --- a/kike.c +++ b/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);