diff options
-rw-r--r-- | src/kike.c | 57 |
1 files changed, 36 insertions, 21 deletions
@@ -37,7 +37,7 @@ static struct config_item g_config_table[] = { "ssl_cert", NULL, "Server SSL certificate (PEM)" }, { "ssl_key", NULL, "Server SSL private key (PEM)" }, - { "max_connections", NULL, "Maximum client connections" }, + { "max_connections", "0", "Global connection limit" }, { "ping_interval", "180", "Interval between PING's (sec)" }, { NULL, NULL, NULL } }; @@ -365,6 +365,7 @@ struct server_context int listen_fd; ///< Listening socket FD struct client *clients; ///< Clients SSL_CTX *ssl_ctx; ///< SSL context + unsigned n_clients; ///< Current number of connections struct str_map users; ///< Maps nicknames to clients struct str_map channels; ///< Maps channel names to data @@ -377,6 +378,7 @@ struct server_context struct str_map config; ///< Server configuration char *server_name; ///< Our server name unsigned ping_interval; ///< Ping interval in seconds + unsigned max_connections; ///< Max. connections allowed or 0 struct str_vector motd; ///< MOTD (none if empty) nl_catd catalog; ///< Message catalog for server msgs }; @@ -384,14 +386,10 @@ struct server_context static void server_context_init (struct server_context *self) { - str_map_init (&self->config); - self->config.free = free; - load_config_defaults (&self->config, g_config_table); - self->listen_fd = -1; self->clients = NULL; + self->n_clients = 0; - self->server_name = NULL; str_map_init (&self->users); self->users.key_xfrm = irc_strxfrm; // TODO: set channel_free() as the free function? @@ -404,6 +402,13 @@ server_context_init (struct server_context *self) self->quitting = false; self->polling = false; + str_map_init (&self->config); + self->config.free = free; + load_config_defaults (&self->config, g_config_table); + + self->server_name = NULL; + self->ping_interval = 0; + self->max_connections = 0; str_vector_init (&self->motd); self->catalog = (nl_catd) -1; } @@ -476,6 +481,7 @@ client_kill (struct client *c, const char *reason) c->socket_fd = -1; client_free (c); LIST_UNLINK (ctx->clients, c); + ctx->n_clients--; free (c); } @@ -1239,8 +1245,6 @@ on_irc_client_available (const struct pollfd *pfd, void *user_data) (void) pfd; struct server_context *ctx = user_data; - // TODO: stop accepting new connections when `max_connections' is reached - while (true) { // XXX: `struct sockaddr_storage' is not the most portable thing @@ -1252,9 +1256,8 @@ on_irc_client_available (const struct pollfd *pfd, void *user_data) { if (errno == EAGAIN) break; - if (errno == EINTR) - continue; - if (errno == ECONNABORTED) + if (errno == EINTR + || errno == ECONNABORTED) continue; // TODO: handle resource exhaustion (EMFILE, ENFILE) specially @@ -1263,6 +1266,14 @@ on_irc_client_available (const struct pollfd *pfd, void *user_data) exit_fatal ("%s: %s", "accept", strerror (errno)); } + if (ctx->max_connections != 0 + && ctx->n_clients >= ctx->max_connections) + { + print_debug ("connection limit reached, refusing connection"); + close (fd); + continue; + } + char host[NI_MAXHOST] = "unknown", port[NI_MAXSERV] = "unknown"; int err = getnameinfo ((struct sockaddr *) &peer, peer_len, host, sizeof host, port, sizeof port, NI_NUMERICSERV); @@ -1276,6 +1287,7 @@ on_irc_client_available (const struct pollfd *pfd, void *user_data) c->socket_fd = fd; c->hostname = xstrdup (host); LIST_PREPEND (ctx->clients, c); + ctx->n_clients++; set_blocking (fd, false); poller_set (&ctx->poller, fd, POLLIN, @@ -1434,16 +1446,19 @@ static bool irc_parse_config (struct server_context *ctx, struct error **e) { unsigned long ul; - - const char *ping_interval = str_map_find (&ctx->config, "ping_interval"); - hard_assert (ping_interval != NULL); // We have a default value for this - if (!xstrtoul (&ul, ping_interval, 10) || ul > UINT_MAX) - { - error_set (e, "invalid configuration value for `%s': %s", - "ping_interval", "the number is invalid or out of range"); - return false; - } - ctx->ping_interval = ul; +#define PARSE_UNSIGNED(name, min, max) \ + const char *name = str_map_find (&ctx->config, #name); \ + hard_assert (name != NULL); \ + if (!xstrtoul (&ul, name, 10) || ul > max || ul < min) \ + { \ + error_set (e, "invalid configuration value for `%s': %s", \ + #name, "the number is invalid or out of range"); \ + return false; \ + } \ + ctx->name = ul + + PARSE_UNSIGNED (ping_interval, 1, UINT_MAX); + PARSE_UNSIGNED (max_connections, 0, UINT_MAX); return true; } |