Imported Upstream version 3.2.0
[debian/amanda] / common-src / stream.c
index 8ec116f6eebdf34907b0637740c50737ebd3ca86..9f93cceb8ac76fe404b30edb47f676980b659844 100644 (file)
@@ -25,7 +25,7 @@
  *                        University of Maryland at College Park
  */
 /*
- * $Id: stream.c,v 1.39.2.1 2006/12/12 14:56:38 martinea Exp $
+ * $Id: stream.c,v 1.39 2006/08/24 01:57:15 paddy_s Exp $
  *
  * functions for managing stream sockets
  */
@@ -33,6 +33,9 @@
 #include "dgram.h"
 #include "stream.h"
 #include "util.h"
+#include "conffile.h"
+#include "security-util.h"
+#include "sockaddr-util.h"
 
 /* local functions */
 static void try_socksize(int sock, int which, size_t size);
@@ -42,26 +45,45 @@ static int stream_client_internal(const char *hostname, in_port_t port,
 
 int
 stream_server(
+    int family,
     in_port_t *portp,
     size_t sendsize,
     size_t recvsize,
     int    priv)
 {
     int server_socket, retries;
-    socklen_t len;
+    socklen_t_equiv len;
 #if defined(SO_KEEPALIVE) || defined(USE_REUSEADDR)
     const int on = 1;
     int r;
 #endif
-    struct sockaddr_in server;
+    sockaddr_union server;
     int save_errno;
+    int *portrange;
+    socklen_t_equiv socklen;
+    int socket_family;
 
     *portp = USHRT_MAX;                                /* in case we error exit */
-    if((server_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
+    if (family == -1) {
+       socket_family = AF_NATIVE;
+    } else {
+       socket_family = family;
+    }
+    g_debug("stream_server opening socket with family %d (requested family was %d)", socket_family, family);
+    server_socket = socket(socket_family, SOCK_STREAM, 0);
+
+#ifdef WORKING_IPV6
+    /* if that address family actually isn't supported, just try AF_INET */
+    if (server_socket == -1 && errno == EAFNOSUPPORT) {
+       g_debug("stream_server retrying socket with AF_INET");
+       socket_family = AF_INET;
+       server_socket = socket(AF_INET, SOCK_STREAM, 0);
+    }
+#endif
+    if (server_socket == -1) {
        save_errno = errno;
-       dbprintf(("%s: stream_server: socket() failed: %s\n",
-                 debug_prefix(NULL),
-                 strerror(save_errno)));
+       g_debug(_("stream_server: socket() failed: %s"),
+                 strerror(save_errno));
        errno = save_errno;
        return -1;
     }
@@ -69,23 +91,21 @@ stream_server(
        aclose(server_socket);
        errno = EMFILE;                         /* out of range */
        save_errno = errno;
-       dbprintf(("%s: stream_server: socket out of range: %d\n",
-                 debug_prefix(NULL),
-                 server_socket));
+       g_debug(_("stream_server: socket out of range: %d"),
+                 server_socket);
        errno = save_errno;
        return -1;
     }
-    memset(&server, 0, SIZEOF(server));
-    server.sin_family = (sa_family_t)AF_INET;
-    server.sin_addr.s_addr = INADDR_ANY;
+
+    SU_INIT(&server, socket_family);
+    SU_SET_INADDR_ANY(&server);
 
 #ifdef USE_REUSEADDR
     r = setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR,
-       (void *)&on, (socklen_t)sizeof(on));
+       (void *)&on, (socklen_t_equiv)sizeof(on));
     if (r < 0) {
-       dbprintf(("%s: stream_server: setsockopt(SO_REUSEADDR) failed: %s\n",
-                 debug_prefix(NULL),
-                 strerror(errno)));
+       g_debug(_("stream_server: setsockopt(SO_REUSEADDR) failed: %s"),
+                 strerror(errno));
     }
 #endif
 
@@ -104,40 +124,37 @@ stream_server(
      * is within the range it requires.
      */
     for (retries = 0; ; retries++) {
-#ifdef TCPPORTRANGE
-       if (bind_portrange(server_socket, &server, TCPPORTRANGE, "tcp") == 0)
-           goto out;
-       dbprintf(("%s: stream_server: Could not bind to port in range: %d - %d.\n",
-                 debug_prefix(NULL), TCPPORTRANGE));
-#endif
+       if (priv) {
+           portrange = getconf_intrange(CNF_RESERVED_TCP_PORT);
+       } else {
+           portrange = getconf_intrange(CNF_UNRESERVED_TCP_PORT);
+       }
 
-       if(priv) {
-           if (bind_portrange(server_socket, &server,
-                          (in_port_t)512, (in_port_t)(IPPORT_RESERVED - 1), "tcp") == 0)
+       if (portrange[0] != 0 && portrange[1] != 0) {
+           if (bind_portrange(server_socket, &server, (in_port_t)portrange[0],
+                              (in_port_t)portrange[1], "tcp") == 0)
                goto out;
-           dbprintf(("%s: stream_server: Could not bind to port in range 512 - %d.\n",
-                     debug_prefix(NULL), IPPORT_RESERVED - 1));
+           g_debug(_("stream_server: Could not bind to port in range: %d - %d."),
+                     portrange[0], portrange[1]);
+       } else {
+           socklen = SS_LEN(&server);
+           if (bind(server_socket, (struct sockaddr *)&server, socklen) == 0)
+               goto out;
+           g_debug(_("stream_server: Could not bind to any port: %s"),
+                     strerror(errno));
        }
 
-       server.sin_port = INADDR_ANY;
-       if (bind(server_socket, (struct sockaddr *)&server, (socklen_t)sizeof(server)) == 0)
-           goto out;
-       dbprintf(("%s: stream_server: Could not bind to any port: %s\n",
-                 debug_prefix(NULL), strerror(errno)));
-
        if (retries >= BIND_CYCLE_RETRIES)
            break;
 
-       dbprintf(("%s: stream_server: Retrying entire range after 10 second delay.\n",
-                 debug_prefix(NULL)));
+       g_debug(_("stream_server: Retrying entire range after 10 second delay."));
 
        sleep(15);
     }
 
     save_errno = errno;
-    dbprintf(("%s: stream_server: bind(INADDR_ANY) failed: %s\n",
-                 debug_prefix(NULL),
-                 strerror(save_errno)));
+    g_debug(_("stream_server: bind(in6addr_any) failed: %s"),
+                 strerror(save_errno));
     aclose(server_socket);
     errno = save_errno;
     return -1;
@@ -150,9 +167,8 @@ out:
     len = SIZEOF(server);
     if(getsockname(server_socket, (struct sockaddr *)&server, &len) == -1) {
        save_errno = errno;
-       dbprintf(("%s: stream_server: getsockname() failed: %s\n",
-                 debug_prefix(NULL),
-                 strerror(save_errno)));
+       g_debug(_("stream_server: getsockname() failed: %s"),
+                 strerror(save_errno));
        aclose(server_socket);
        errno = save_errno;
        return -1;
@@ -160,23 +176,20 @@ out:
 
 #ifdef SO_KEEPALIVE
     r = setsockopt(server_socket, SOL_SOCKET, SO_KEEPALIVE,
-       (void *)&on, (socklen_t)sizeof(on));
+       (void *)&on, (socklen_t_equiv)sizeof(on));
     if(r == -1) {
        save_errno = errno;
-       dbprintf(("%s: stream_server: setsockopt(SO_KEEPALIVE) failed: %s\n",
-                 debug_prefix(NULL),
-                 strerror(save_errno)));
+       g_debug(_("stream_server: setsockopt(SO_KEEPALIVE) failed: %s"),
+                 strerror(save_errno));
         aclose(server_socket);
        errno = save_errno;
         return -1;
     }
 #endif
 
-    *portp = (in_port_t)ntohs(server.sin_port);
-    dbprintf(("%s: stream_server: waiting for connection: %s.%d\n",
-             debug_prefix_time(NULL),
-             inet_ntoa(server.sin_addr),
-             *portp));
+    *portp = SU_GET_PORT(&server);
+    g_debug(_("stream_server: waiting for connection: %s"),
+             str_sockaddr(&server));
     return server_socket;
 }
 
@@ -190,88 +203,67 @@ stream_client_internal(
     int nonblock,
     int priv)
 {
-    struct sockaddr_in svaddr, claddr;
-    struct hostent *hostp;
-    int save_errno;
+    sockaddr_union svaddr, claddr;
+    int save_errno = 0;
     char *f;
-    int client_socket;
+    int client_socket = 0;
+    int *portrange = NULL;
+    int result;
+    struct addrinfo *res, *res_addr;
 
     f = priv ? "stream_client_privileged" : "stream_client";
 
-    if((hostp = gethostbyname(hostname)) == NULL) {
-       save_errno = EHOSTUNREACH;
-       dbprintf(("%s: %s: gethostbyname(%s) failed\n",
-                 debug_prefix(NULL),
-                 f,
-                 hostname));
-       errno = save_errno;
+    result = resolve_hostname(hostname, SOCK_STREAM, &res, NULL);
+    if(result != 0) {
+       g_debug(_("resolve_hostname(%s): %s"), hostname, gai_strerror(result));
+       errno = EHOSTUNREACH;
+       return -1;
+    }
+    if(!res) {
+       g_debug(_("resolve_hostname(%s): no results"), hostname);
+       errno = EHOSTUNREACH;
        return -1;
     }
 
-    memset(&svaddr, 0, SIZEOF(svaddr));
-    svaddr.sin_family = (sa_family_t)AF_INET;
-    svaddr.sin_port = (in_port_t)htons(port);
-    memcpy(&svaddr.sin_addr, hostp->h_addr, (size_t)hostp->h_length);
-
+    for (res_addr = res; res_addr != NULL; res_addr = res_addr->ai_next) {
+       /* copy the first (preferred) address we found */
+       copy_sockaddr(&svaddr, (sockaddr_union *)res_addr->ai_addr);
+       SU_SET_PORT(&svaddr, port);
 
-    memset(&claddr, 0, SIZEOF(claddr));
-    claddr.sin_family = (sa_family_t)AF_INET;
-    claddr.sin_addr.s_addr = INADDR_ANY;
+       SU_INIT(&claddr, SU_GET_FAMILY(&svaddr));
+       SU_SET_INADDR_ANY(&claddr);
 
-    /*
-     * If a privileged port range was requested, we try to get a port in
-     * that range first and fail if it is not available.  Next, we try
-     * to get a port in the range built in when Amanda was configured.
-     * If that fails, we just go for any port.
-     *
-     * It is up to the caller to make sure we have the proper permissions
-     * to get the desired port, and to make sure we return a port that
-     * is within the range it requires.
-     */
-    if (priv) {
-#ifdef LOW_TCPPORTRANGE
-       client_socket = connect_portrange(&claddr, LOW_TCPPORTRANGE,
-                                          "tcp", &svaddr, nonblock);
-#else
-       client_socket = connect_portrange(&claddr, (socklen_t)512,
-                                         (socklen_t)(IPPORT_RESERVED - 1),
-                                          "tcp", &svaddr, nonblock);
-#endif
-                                         
+       /*
+        * If a privileged port range was requested, we try to get a port in
+        * that range first and fail if it is not available.  Next, we try
+        * to get a port in the range built in when Amanda was configured.
+        * If that fails, we just go for any port.
+        *
+        * It is up to the caller to make sure we have the proper permissions
+        * to get the desired port, and to make sure we return a port that
+        * is within the range it requires.
+        */
+       if (priv) {
+           portrange = getconf_intrange(CNF_RESERVED_TCP_PORT);
+       } else {
+           portrange = getconf_intrange(CNF_UNRESERVED_TCP_PORT);
+       }
+       client_socket = connect_portrange(&claddr, (in_port_t)portrange[0],
+                                         (in_port_t)portrange[1],
+                                         "tcp", &svaddr, nonblock);
+       save_errno = errno;
        if (client_socket > 0)
-           goto out;
-
-#ifdef LOW_TCPPORTRANGE
-       dbprintf((
-               "%s: stream_client: Could not bind to port in range %d-%d.\n",
-               debug_prefix(NULL), LOW_TCPPORTRANGE));
-#else
-       dbprintf((
-               "%s: stream_client: Could not bind to port in range 512-%d.\n",
-               debug_prefix(NULL), IPPORT_RESERVED - 1));
-#endif
+           break;
     }
 
-#ifdef TCPPORTRANGE
-    client_socket = connect_portrange(&claddr, TCPPORTRANGE,
-                                      "tcp", &svaddr, nonblock);
-    if(client_socket > 0)
+    freeaddrinfo(res);
+                                         
+    if (client_socket > 0)
        goto out;
 
-    dbprintf(("%s: stream_client: Could not bind to port in range %d - %d.\n",
-             debug_prefix(NULL), TCPPORTRANGE));
-#endif
+    g_debug(_("stream_client: Could not bind to port in range %d-%d."),
+             portrange[0], portrange[1]);
 
-    client_socket = connect_portrange(&claddr, (socklen_t)(IPPORT_RESERVED+1),
-                                     (socklen_t)(65535),
-                                      "tcp", &svaddr, nonblock);
-
-    if(client_socket > 0)
-       goto out;
-
-    save_errno = errno;
-    dbprintf(("%s: stream_client: Could not bind to any port: %s\n",
-             debug_prefix(NULL), strerror(save_errno)));
     errno = save_errno;
     return -1;
 
@@ -279,7 +271,7 @@ out:
     try_socksize(client_socket, SO_SNDBUF, sendsize);
     try_socksize(client_socket, SO_RCVBUF, recvsize);
     if (localport != NULL)
-       *localport = (in_port_t)ntohs(claddr.sin_port);
+       *localport = SU_GET_PORT(&claddr);
     return client_socket;
 }
 
@@ -320,8 +312,16 @@ stream_client(
 }
 
 /* don't care about these values */
-static struct sockaddr_in addr;
-static socklen_t addrlen;
+static sockaddr_union addr;
+static socklen_t_equiv addrlen;
+
+static gboolean
+stream_accept_prolong(
+    gpointer data)
+{
+    time_t *tp = data;
+    return time(NULL) <= *tp;
+}
 
 int
 stream_accept(
@@ -330,95 +330,72 @@ stream_accept(
     size_t sendsize,
     size_t recvsize)
 {
-    SELECT_ARG_TYPE readset;
-    struct timeval tv;
-    int nfound, connected_socket;
+    time_t timeout_time;
+    int connected_socket;
     int save_errno;
-    int ntries = 0;
+    in_port_t port;
 
     assert(server_socket >= 0);
 
-    do {
-       ntries++;
-       memset(&tv, 0, SIZEOF(tv));
-       tv.tv_sec = timeout;
-       memset(&readset, 0, SIZEOF(readset));
-       FD_ZERO(&readset);
-       FD_SET(server_socket, &readset);
-       nfound = select(server_socket+1, &readset, NULL, NULL, &tv);
-       if(nfound <= 0 || !FD_ISSET(server_socket, &readset)) {
-           save_errno = errno;
-           if(nfound < 0) {
-               dbprintf(("%s: stream_accept: select() failed: %s\n",
-                     debug_prefix_time(NULL),
-                     strerror(save_errno)));
-           } else if(nfound == 0) {
-               dbprintf(("%s: stream_accept: timeout after %d second%s\n",
-                     debug_prefix_time(NULL),
-                     timeout,
-                     (timeout == 1) ? "" : "s"));
-               errno = ENOENT;                 /* ??? */
-               return -1;
-           } else if (!FD_ISSET(server_socket, &readset)) {
-               int i;
-
-               for(i = 0; i < server_socket + 1; i++) {
-                   if(FD_ISSET(i, &readset)) {
-                       dbprintf(("%s: stream_accept: got fd %d instead of %d\n",
-                             debug_prefix_time(NULL),
-                             i,
-                             server_socket));
-                   }
-               }
-               save_errno = EBADF;
-           }
-           if (ntries > 5) {
-               errno = save_errno;
-               return -1;
-           }
-        }
-    } while (nfound <= 0);
+    /* set the time we want to stop accepting */
+    timeout_time = time(NULL) + timeout;
 
     while(1) {
-       addrlen = (socklen_t)sizeof(struct sockaddr);
-       connected_socket = accept(server_socket,
+       addrlen = (socklen_t_equiv)sizeof(sockaddr_union);
+       connected_socket = interruptible_accept(server_socket,
                                  (struct sockaddr *)&addr,
-                                 &addrlen);
+                                 &addrlen, stream_accept_prolong,
+                                 &timeout_time);
        if(connected_socket < 0) {
+           if (errno == 0) {
+               g_debug(plural(_("stream_accept: timeout after %d second"),
+                              _("stream_accept: timeout after %d seconds"),
+                             timeout),
+                       timeout);
+               errno = ETIMEDOUT;
+               return -1;
+           }
            break;
        }
-       dbprintf(("%s: stream_accept: connection from %s.%d\n",
-                 debug_prefix_time(NULL),
-                 inet_ntoa(addr.sin_addr),
-                 ntohs(addr.sin_port)));
+       g_debug(_("stream_accept: connection from %s"),
+                 str_sockaddr(&addr));
        /*
         * Make certain we got an inet connection and that it is not
         * from port 20 (a favorite unauthorized entry tool).
         */
-       if((addr.sin_family == (sa_family_t)AF_INET)
-         && (ntohs(addr.sin_port) != (in_port_t)20)) {
-           try_socksize(connected_socket, SO_SNDBUF, sendsize);
-           try_socksize(connected_socket, SO_RCVBUF, recvsize);
-           return connected_socket;
-       }
-       if(addr.sin_family != (sa_family_t)AF_INET) {
-           dbprintf(("%s: family is %d instead of %d(AF_INET): ignored\n",
-                     debug_prefix_time(NULL),
-                     addr.sin_family,
-                     AF_INET));
-       }
-       if(ntohs(addr.sin_port) == 20) {
-           dbprintf(("%s: remote port is %d: ignored\n",
-                     debug_prefix_time(NULL),
-                     ntohs(addr.sin_port)));
+       if (SU_GET_FAMILY(&addr) == AF_INET
+#ifdef WORKING_IPV6
+           || SU_GET_FAMILY(&addr) == AF_INET6
+#endif
+           ){
+           port = SU_GET_PORT(&addr);
+           if (port != (in_port_t)20) {
+               try_socksize(connected_socket, SO_SNDBUF, sendsize);
+               try_socksize(connected_socket, SO_RCVBUF, recvsize);
+               return connected_socket;
+           } else {
+               g_debug(_("remote port is %u: ignored"),
+                         (unsigned int)port);
+           }
+       } else {
+#ifdef WORKING_IPV6
+           g_debug(_("family is %d instead of %d(AF_INET)"
+                     " or %d(AF_INET6): ignored"),
+                     SU_GET_FAMILY(&addr),
+                     AF_INET, AF_INET6);
+#else
+           g_debug(_("family is %d instead of %d(AF_INET)"
+                     ": ignored"),
+                     SU_GET_FAMILY(&addr),
+                     AF_INET);
+#endif
        }
        aclose(connected_socket);
     }
 
     save_errno = errno;
-    dbprintf(("%s: stream_accept: accept() failed: %s\n",
-             debug_prefix_time(NULL),
-             strerror(save_errno)));
+    g_debug(_("stream_accept: accept() failed: %s"),
+             strerror(save_errno));
     errno = save_errno;
     return -1;
 }
@@ -430,26 +407,26 @@ try_socksize(
     size_t size)
 {
     size_t origsize;
+    int    isize;
 
     if (size == 0)
        return;
 
     origsize = size;
+    isize = size;
     /* keep trying, get as big a buffer as possible */
-    while((size > 1024) &&
+    while((isize > 1024) &&
          (setsockopt(sock, SOL_SOCKET,
-                     which, (void *) &size, (socklen_t)sizeof(int)) < 0)) {
-       size -= 1024;
+                     which, (void *) &isize, (socklen_t_equiv)sizeof(isize)) < 0)) {
+       isize -= 1024;
     }
-    if(size > 1024) {
-       dbprintf(("%s: try_socksize: %s buffer size is %zu\n",
-                 debug_prefix(NULL),
-                 (which == SO_SNDBUF) ? "send" : "receive",
-                 size));
+    if(isize > 1024) {
+       g_debug(_("try_socksize: %s buffer size is %d"),
+                 (which == SO_SNDBUF) ? _("send") : _("receive"),
+                 isize);
     } else {
-       dbprintf(("%s: try_socksize: could not allocate %s buffer of %zu\n",
-                 debug_prefix(NULL),
-                 (which == SO_SNDBUF) ? "send" : "receive",
-                 origsize));
+       g_debug(_("try_socksize: could not allocate %s buffer of %zu"),
+                 (which == SO_SNDBUF) ? _("send") : _("receive"),
+                 origsize);
     }
 }