Server will now bind in two ports: one for tcp and one
for tcp6. It only will fail if both protocol fails.
A future patch will add configuration options to allow
the user to disable each protocol individually.
Signed-off-by: Richard Maciel <[email protected]>
---
src/tcsd/svrside.c | 304 ++++++++++++++++++++++++++++++++++++++++++-----------
1 file changed, 241 insertions(+), 63 deletions(-)
diff --git a/src/tcsd/svrside.c b/src/tcsd/svrside.c
index 08ca1c8..ef56eb5 100644
--- a/src/tcsd/svrside.c
+++ b/src/tcsd/svrside.c
@@ -27,6 +27,7 @@
#include <arpa/inet.h>
#include <errno.h>
#include <getopt.h>
+#include <sys/select.h>
#include "trousers/tss.h"
#include "trousers_types.h"
#include "tcs_tsp.h"
@@ -46,6 +47,13 @@ extern char *optarg;
int sd;
char *tcsd_config_file = NULL;
+struct srv_sock_info {
+ int sd;
+ int domain; // AF_INET or AF_INET6
+ socklen_t addr_len;
+};
+#define MAX_IP_PROTO 2
+
static void
tcsd_shutdown(void)
{
@@ -211,17 +219,181 @@ reload_config(void)
return result;
}
+int setup_ipv4_socket(struct srv_sock_info *ssi)
+{
+ struct sockaddr_in serv_addr;
+ int sd, opt;
+
+ ssi->sd = -1;
+
+ // Initialization of IPv4 socket.
+ sd = socket(AF_INET, SOCK_STREAM, 0);
+ if (sd < 0) {
+ LogWarn("Failed IPv4 socket: %s", strerror(errno));
+ goto err;
+ }
+
+ memset(&serv_addr, 0, sizeof (serv_addr));
+ serv_addr.sin_family = AF_INET;
+ serv_addr.sin_port = htons(tcsd_options.port);
+
+ /* If no remote_ops are defined, restrict connections to localhost
+ * only at the socket. */
+ if (tcsd_options.remote_ops[0] == 0)
+ serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ else
+ serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ opt = 1;
+ setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+ if (bind(sd, (struct sockaddr *) &serv_addr, sizeof (serv_addr)) < 0) {
+ LogWarn("Failed IPv4 bind: %s", strerror(errno));
+ goto err;
+ }
+
+ if (listen(sd, TCSD_MAX_SOCKETS_QUEUED) < 0) {
+ LogWarn("Failed IPv4 listen: %s", strerror(errno));
+ goto err;
+ }
+
+ ssi->domain = AF_INET;
+ ssi->sd = sd;
+ ssi->addr_len = sizeof(serv_addr);
+
+ return 0;
+
+ err:
+ if (sd != -1)
+ close(sd);
+
+ return -1;
+}
+
+int setup_ipv6_socket(struct srv_sock_info *ssi)
+{
+ struct sockaddr_in6 serv6_addr;
+ int sd6, opt;
+
+ ssi->sd = -1;
+
+ sd6 = socket(AF_INET6, SOCK_STREAM, 0);
+ if (sd6 < 0) {
+ LogWarn("Failed IPv6 socket: %s", strerror(errno));
+ goto err;
+ }
+
+ memset(&serv6_addr, 0, sizeof (serv6_addr));
+ serv6_addr.sin6_family = AF_INET6;
+ serv6_addr.sin6_port = htons(tcsd_options.port);
+
+ /* If no remote_ops are defined, restrict connections to localhost
+ * only at the socket. */
+ if (tcsd_options.remote_ops[0] == 0)
+ serv6_addr.sin6_addr = in6addr_loopback;
+ else
+ serv6_addr.sin6_addr = in6addr_any;
+
+#ifdef __linux__
+ opt = 1;
+ if(setsockopt(sd6, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)) == -1)
{
+ LogWarn("Could not set IPv6 socket option properly.\n");
+ goto err;
+ }
+#endif
+
+ opt = 1;
+ setsockopt(sd6, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+ if (bind(sd6, (struct sockaddr *) &serv6_addr, sizeof (serv6_addr)) <
0) {
+ LogWarn("Failed IPv6 bind: %s", strerror(errno));
+ goto err;
+ }
+
+ if (listen(sd6, TCSD_MAX_SOCKETS_QUEUED) < 0) {
+ LogWarn("Failed IPv6 listen: %s", strerror(errno));
+ goto err;
+ }
+
+ ssi->domain = AF_INET6;
+ ssi->sd = sd6;
+ ssi->addr_len = sizeof(serv6_addr);
+
+ return 0;
+
+ err:
+ if (sd != -1)
+ close(sd6);
+
+ return -1;
+}
+
+int setup_server_sockets(struct srv_sock_info ssi[])
+{
+ int i=0;
+
+ ssi[0].sd = ssi[1].sd = -1;
+ // Only enqueue sockets successfully bound.
+ if (setup_ipv4_socket(&ssi[i]) == 0)
+ i++;
+ setup_ipv6_socket(&ssi[i]);
+
+ // It's only a failure if both sockets are unavailable.
+ if ((ssi[0].sd == -1) && (ssi[1].sd == -1)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+char *fetch_hostname(struct sockaddr_storage *client_addr, socklen_t socklen)
+{
+ char buf[NI_MAXHOST];
+
+ if (getnameinfo((struct sockaddr *)client_addr, socklen, buf,
+ sizeof(buf), NULL, 0, 0) != 0) {
+ LogWarn("Could not retrieve client address info");
+ return strdup("<Invalid client address>");
+ } else {
+ return strdup(buf);
+ }
+}
+
+void prepare_for_select(struct srv_sock_info *socks_info, int *num_fds,
+ fd_set *rdfd_set, int *nfds)
+{
+ int i;
+
+ FD_ZERO(rdfd_set);
+ *num_fds = 0;
+ *nfds = 0;
+ // Filter out socket descriptors in the queue that
+ // has the -1 value.
+ for (i=0; i < MAX_IP_PROTO; i++) {
+ if (socks_info[i].sd == -1)
+ break;
+
+ FD_SET(socks_info[i].sd, rdfd_set);
+ (*num_fds)++;
+ if (*nfds < socks_info[i].sd) // grab highest sd for select call
+ *nfds = socks_info[i].sd;
+ }
+}
int
main(int argc, char **argv)
{
- struct sockaddr_in serv_addr, client_addr;
TSS_RESULT result;
- int newsd, c, option_index = 0;
- unsigned client_len;
+ int newsd, c, rv, option_index = 0;
+ int i;
+ socklen_t client_len;
char *hostname = NULL;
+ fd_set rdfd_set;
+ int num_fds = 0;
+ int nfds = 0;
+ int stor_errno;
+ sigset_t sigmask, termmask, oldsigmask;
+ struct sockaddr_storage client_addr;
+ struct srv_sock_info socks_info[MAX_IP_PROTO];
struct passwd *pwd;
- struct hostent *client_hostent = NULL;
struct option long_options[] = {
{"help", 0, NULL, 'h'},
{"foreground", 0, NULL, 'f'},
@@ -256,30 +428,7 @@ main(int argc, char **argv)
if ((result = tcsd_startup()))
return (int)result;
- sd = socket(AF_INET, SOCK_STREAM, 0);
- if (sd < 0) {
- LogError("Failed socket: %s", strerror(errno));
- return -1;
- }
-
- memset(&serv_addr, 0, sizeof (serv_addr));
- serv_addr.sin_family = AF_INET;
- serv_addr.sin_port = htons(tcsd_options.port);
-
- /* If no remote_ops are defined, restrict connections to localhost
- * only at the socket. */
- if (tcsd_options.remote_ops[0] == 0)
- serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
- else
- serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
-
- c = 1;
- setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &c, sizeof(c));
- if (bind(sd, (struct sockaddr *) &serv_addr, sizeof (serv_addr)) < 0) {
- LogError("Failed bind: %s", strerror(errno));
- return -1;
- }
-#ifndef SOLARIS
+ #ifndef SOLARIS
pwd = getpwnam(TSS_USER_NAME);
if (pwd == NULL) {
if (errno == 0) {
@@ -292,12 +441,12 @@ main(int argc, char **argv)
}
setuid(pwd->pw_uid);
#endif
- if (listen(sd, TCSD_MAX_SOCKETS_QUEUED) < 0) {
- LogError("Failed listen: %s", strerror(errno));
+
+ if (setup_server_sockets(socks_info) == -1) {
+ LogError("Could not create sockets to listen to connections.
Aborting...");
return -1;
}
- client_len = (unsigned)sizeof(client_addr);
-
+
if (getenv("TCSD_FOREGROUND") == NULL) {
if (daemon(0, 0) == -1) {
perror("daemon");
@@ -307,46 +456,75 @@ main(int argc, char **argv)
}
LogInfo("%s: TCSD up and running.", PACKAGE_STRING);
- do {
- newsd = accept(sd, (struct sockaddr *) &client_addr,
&client_len);
- if (newsd < 0) {
- if (errno == EINTR) {
- if (term)
- break;
- else if (hup) {
- if (reload_config() != TSS_SUCCESS)
- LogError("Failed reloading
config");
- }
- continue;
- } else {
- LogError("Failed accept: %s", strerror(errno));
- continue;
- }
- }
- LogDebug("accepted socket %i", newsd);
- if ((client_hostent = gethostbyaddr((char *)
&client_addr.sin_addr,
-
sizeof(client_addr.sin_addr),
- AF_INET)) == NULL) {
- char buf[16];
- uint32_t addr = htonl(client_addr.sin_addr.s_addr);
+ sigemptyset(&sigmask);
+ sigaddset(&sigmask, SIGTERM);
+ sigaddset(&sigmask, SIGHUP);
- snprintf(buf, 16, "%d.%d.%d.%d", (addr & 0xff000000)
>> 24,
- (addr & 0x00ff0000) >> 16, (addr &
0x0000ff00) >> 8,
- addr & 0x000000ff);
+ sigemptyset(&termmask);
+ sigaddset(&termmask, SIGTERM);
- LogWarn("Host name for connecting IP %s could not be
resolved", buf);
- hostname = strdup(buf);
- } else {
- hostname = strdup(client_hostent->h_name);
+ do {
+ prepare_for_select(socks_info, &num_fds, &rdfd_set, &nfds);
+ // Sanity check
+ if (num_fds == 0) {
+ LogError("No server sockets available to listen
connections. Aborting...");
+ return -1;
}
- tcsd_thread_create(newsd, hostname);
- hostname = NULL;
+ // Block TERM and HUP signals to prevent race condition
+ if (sigprocmask(SIG_BLOCK, &sigmask, &oldsigmask) == -1) {
+ LogError("Error setting interrupt mask before accept");
+ }
+
+ // TERM and HUP are blocked here, so its safe to test flags.
if (hup) {
+ // Config reading can be slow, so unmask SIGTERM.
+ if (sigprocmask(SIG_UNBLOCK, &termmask, NULL) == -1) {
+ LogError("Error unblocking SIGTERM before
config reload");
+ }
if (reload_config() != TSS_SUCCESS)
LogError("Failed reloading config");
+ if (sigprocmask(SIG_BLOCK, &termmask, NULL) == -1) {
+ LogError("Error blocking SIGTERM after config
reload");
+ }
+ }
+ if (term)
+ break;
+
+ // Select IPv4 and IPv6 socket descriptors with appropriate
sigmask.
+ LogDebug("Waiting for connections");
+ rv = pselect(nfds+1, &rdfd_set, NULL, NULL, NULL, &oldsigmask);
+ stor_errno = errno; // original mask must be set ASAP, so store
errno.
+ if (sigprocmask(SIG_SETMASK, &oldsigmask, NULL) == -1) {
+ LogError("Error reseting signal mask to the original
configuration.");
+ }
+ if (rv == -1) {
+ if (stor_errno != EINTR) {
+ LogError("Error monitoring server socket
descriptors.");
+ return -1;
+ }
+ continue;
}
+
+ for (i=0; i < num_fds; i++) { // accept connections from all IP
versions (with valid sd)
+ if (!FD_ISSET(socks_info[i].sd, &rdfd_set)) {
+ continue;
+ }
+ client_len = socks_info[i].addr_len;
+ newsd = accept(socks_info[i].sd, (struct sockaddr *)
&client_addr, &client_len);
+ if (newsd < 0) {
+ if (errno != EINTR)
+ LogError("Failed accept: %s",
strerror(errno));
+ continue;
+ }
+ LogDebug("accepted socket %i", newsd);
+
+ hostname = fetch_hostname(&client_addr, client_len);
+
+ tcsd_thread_create(newsd, hostname);
+ hostname = NULL;
+ } // for (i=0; i < MAX_IP_PROTO; i++)
} while (term ==0);
/* To close correctly, we must receive a SIGTERM */
--
1.8.1.4
------------------------------------------------------------------------------
Introducing Performance Central, a new site from SourceForge and
AppDynamics. Performance Central is your source for news, insights,
analysis and resources for efficient Application Performance Management.
Visit us today!
http://pubads.g.doubleclick.net/gampad/clk?id=48897511&iu=/4140/ostg.clktrk
_______________________________________________
TrouSerS-tech mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/trousers-tech