From 5bedd3918cc942ce4495f4418525c1d6f53cbfe7 Mon Sep 17 00:00:00 2001
From: Přemysl Janouch
Date: Sat, 2 Aug 2014 15:01:48 +0200
Subject: kike: implement connection limit
Somehow I'm not sure whether this thing is useful in any sense.
---
src/kike.c | 57 ++++++++++++++++++++++++++++++++++++---------------------
1 file changed, 36 insertions(+), 21 deletions(-)
diff --git a/src/kike.c b/src/kike.c
index aae5c18..9a744c2 100644
--- a/src/kike.c
+++ b/src/kike.c
@@ -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;
}
--
cgit v1.2.3-70-g09d2