kike: implement connection limit

Somehow I'm not sure whether this thing is useful in any sense.
This commit is contained in:
Přemysl Eric Janouch 2014-08-02 15:01:48 +02:00
parent 2fe3c7ed45
commit 5bedd3918c

View File

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