diff --git a/src/kike.c b/src/kike.c index dde8d42..5a250b2 100644 --- a/src/kike.c +++ b/src/kike.c @@ -425,7 +425,9 @@ channel_get_mode (struct channel *self, bool disclose_secrets) struct server_context { - int listen_fd; ///< Listening socket FD + int listen_fds[1]; ///< Listening socket FD's + size_t n_listen_fds; ///< Number of listening sockets + struct client *clients; ///< Clients SSL_CTX *ssl_ctx; ///< SSL context unsigned n_clients; ///< Current number of connections @@ -450,7 +452,7 @@ struct server_context static void server_context_init (struct server_context *self) { - self->listen_fd = -1; + self->n_listen_fds = 0; self->clients = NULL; self->n_clients = 0; @@ -485,8 +487,8 @@ server_context_free (struct server_context *self) { str_map_free (&self->config); - if (self->listen_fd != -1) - xclose (self->listen_fd); + for (size_t i = 0; i < self->n_listen_fds; i++) + xclose (self->listen_fds[i]); if (self->ssl_ctx) SSL_CTX_free (self->ssl_ctx); @@ -763,12 +765,14 @@ irc_initiate_quit (struct server_context *ctx) if (!iter->closing_link) irc_close_link (iter, "Shutting down"); - ssize_t i = poller_find_by_fd (&ctx->poller, ctx->listen_fd); - if (soft_assert (i != -1)) - poller_remove_at_index (&ctx->poller, i); - if (ctx->listen_fd != -1) - xclose (ctx->listen_fd); - ctx->listen_fd = -1; + for (size_t i = 0; i < ctx->n_listen_fds; i++) + { + ssize_t index = poller_find_by_fd (&ctx->poller, ctx->listen_fds[i]); + if (soft_assert (index != -1)) + poller_remove_at_index (&ctx->poller, index); + xclose (ctx->listen_fds[i]); + } + ctx->n_listen_fds = 0; ctx->quitting = true; irc_try_finish_quit (ctx); @@ -2501,7 +2505,7 @@ on_irc_client_available (const struct pollfd *pfd, void *user_data) struct sockaddr_storage peer; socklen_t peer_len = sizeof peer; - int fd = accept (ctx->listen_fd, (struct sockaddr *) &peer, &peer_len); + int fd = accept (pfd->fd, (struct sockaddr *) &peer, &peer_len); if (fd == -1) { if (errno == EAGAIN) @@ -2774,8 +2778,47 @@ irc_initialize_server_name (struct server_context *ctx, struct error **e) return true; } +static int +irc_listen (struct addrinfo *gai_iter) +{ + int fd = socket (gai_iter->ai_family, + gai_iter->ai_socktype, gai_iter->ai_protocol); + if (fd == -1) + return -1; + set_cloexec (fd); + + int yes = 1; + soft_assert (setsockopt (fd, SOL_SOCKET, SO_KEEPALIVE, + &yes, sizeof yes) != -1); + soft_assert (setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, + &yes, sizeof yes) != -1); + + char real_host[NI_MAXHOST], real_port[NI_MAXSERV]; + real_host[0] = real_port[0] = '\0'; + int err = getnameinfo (gai_iter->ai_addr, gai_iter->ai_addrlen, + real_host, sizeof real_host, real_port, sizeof real_port, + NI_NUMERICHOST | NI_NUMERICSERV); + if (err) + print_debug ("%s: %s", "getnameinfo", gai_strerror (err)); + + if (bind (fd, gai_iter->ai_addr, gai_iter->ai_addrlen)) + print_error ("bind to %s:%s failed: %s", + real_host, real_port, strerror (errno)); + else if (listen (fd, 16 /* arbitrary number */)) + print_error ("listen at %s:%s failed: %s", + real_host, real_port, strerror (errno)); + else + { + print_status ("listening at %s:%s", real_host, real_port); + return fd; + } + + xclose (fd); + return -1; +} + static bool -irc_listen (struct server_context *ctx, struct error **e) +irc_setup_listen_fds (struct server_context *ctx, struct error **e) { const char *bind_host = str_map_find (&ctx->config, "bind_host"); const char *bind_port = str_map_find (&ctx->config, "bind_port"); @@ -2795,56 +2838,27 @@ irc_listen (struct server_context *ctx, struct error **e) return false; } - int sockfd; - char real_host[NI_MAXHOST], real_port[NI_MAXSERV]; - + int fd; for (gai_iter = gai_result; gai_iter; gai_iter = gai_iter->ai_next) { - sockfd = socket (gai_iter->ai_family, - gai_iter->ai_socktype, gai_iter->ai_protocol); - if (sockfd == -1) - continue; - set_cloexec (sockfd); - - int yes = 1; - soft_assert (setsockopt (sockfd, SOL_SOCKET, SO_KEEPALIVE, - &yes, sizeof yes) != -1); - soft_assert (setsockopt (sockfd, SOL_SOCKET, SO_REUSEADDR, - &yes, sizeof yes) != -1); - - real_host[0] = real_port[0] = '\0'; - err = getnameinfo (gai_iter->ai_addr, gai_iter->ai_addrlen, - real_host, sizeof real_host, real_port, sizeof real_port, - NI_NUMERICHOST | NI_NUMERICSERV); - if (err) - print_debug ("%s: %s", "getnameinfo", gai_strerror (err)); - - if (bind (sockfd, gai_iter->ai_addr, gai_iter->ai_addrlen)) - print_error ("bind to %s:%s failed: %s", - real_host, real_port, strerror (errno)); - else if (listen (sockfd, 16 /* arbitrary number */)) - print_error ("listen at %s:%s failed: %s", - real_host, real_port, strerror (errno)); - else + if (ctx->n_listen_fds >= N_ELEMENTS (ctx->listen_fds)) break; + if ((fd = irc_listen (gai_iter)) == -1) + continue; - xclose (sockfd); + ctx->listen_fds[ctx->n_listen_fds++] = fd; + set_blocking (fd, false); + poller_set (&ctx->poller, fd, POLLIN, + (poller_dispatcher_func) on_irc_client_available, ctx); + break; } - freeaddrinfo (gai_result); - if (!gai_iter) + if (!ctx->n_listen_fds) { error_set (e, "network setup failed"); return false; } - - set_blocking (sockfd, false); - ctx->listen_fd = sockfd; - poller_set (&ctx->poller, ctx->listen_fd, POLLIN, - (poller_dispatcher_func) on_irc_client_available, ctx); - - print_status ("listening at %s:%s", real_host, real_port); return true; } @@ -2979,7 +2993,7 @@ main (int argc, char *argv[]) || !irc_initialize_motd (&ctx, &e) || !irc_initialize_catalog (&ctx, &e) || !irc_parse_config (&ctx, &e) - || !irc_listen (&ctx, &e)) + || !irc_setup_listen_fds (&ctx, &e)) { print_error ("%s", e->message); error_free (e);