kike: implement connection limit
Somehow I'm not sure whether this thing is useful in any sense.
This commit is contained in:
parent
2fe3c7ed45
commit
5bedd3918c
55
src/kike.c
55
src/kike.c
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user