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

Reply via email to