Imported Upstream version 2.6.0p2
[debian/amanda] / common-src / dgram.c
index 60ba12369b0699299b56961b80c60fcea5426c5f..7fbabb0c517339be80df497ff4b9d06a1306c4aa 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
- * Copyright (c) 1991-1998 University of Maryland at College Park
+ * Copyright (c) 1991-1999 University of Maryland at College Park
  * All Rights Reserved.
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  *                        University of Maryland at College Park
  */
 /* 
- * $Id: dgram.c,v 1.11.2.3.4.3.2.4 2002/11/12 19:19:03 martinea Exp $
+ * $Id: dgram.c,v 1.32 2006/07/05 19:54:20 martinea Exp $
  *
  * library routines to marshall/send, recv/unmarshall UDP packets
  */
 #include "amanda.h"
+#include "arglist.h"
 #include "dgram.h"
 #include "util.h"
+#include "conffile.h"
+#include "sockaddr-util.h"
 
-void dgram_socket(dgram, socket)
-dgram_t *dgram;
-int socket;
+void
+dgram_socket(
+    dgram_t *  dgram,
+    int                socket)
 {
-    if(socket < 0 || socket >= FD_SETSIZE) {
-       error("dgram_socket: socket %d out of range (0 .. %d)\n",
-             socket, FD_SETSIZE-1);
+    if(socket < 0 || socket >= (int)FD_SETSIZE) {
+       error(_("dgram_socket: socket %d out of range (0 .. %d)\n"),
+             socket, (int)FD_SETSIZE-1);
+        /*NOTREACHED*/
     }
     dgram->socket = socket;
 }
 
 
-int dgram_bind(dgram, portp)
-dgram_t *dgram;
-int *portp;
+int
+dgram_bind(
+    dgram_t *  dgram,
+    sa_family_t family,
+    in_port_t *        portp)
 {
-    int s;
-    socklen_t len;
-    struct sockaddr_in name;
+    int s, retries;
+    socklen_t_equiv len;
+    sockaddr_union name;
     int save_errno;
+    int *portrange;
 
-    if((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+    portrange = getconf_intrange(CNF_RESERVED_UDP_PORT);
+    *portp = (in_port_t)0;
+    if((s = socket(family, SOCK_DGRAM, 0)) == -1) {
        save_errno = errno;
-       dbprintf(("%s: dgram_bind: socket() failed: %s\n",
-                 debug_prefix(NULL),
-                 strerror(save_errno)));
+       dbprintf(_("dgram_bind: socket() failed: %s\n"),
+                 strerror(save_errno));
        errno = save_errno;
        return -1;
     }
-    if(s < 0 || s >= FD_SETSIZE) {
-       dbprintf(("%s: dgram_bind: socket out of range: %d\n",
-                 debug_prefix(NULL),
-                 s));
+    if(s < 0 || s >= (int)FD_SETSIZE) {
+       dbprintf(_("dgram_bind: socket out of range: %d\n"),
+                 s);
        aclose(s);
        errno = EMFILE;                         /* out of range */
        return -1;
     }
 
-    memset(&name, 0, sizeof(name));
-    name.sin_family = AF_INET;
-    name.sin_addr.s_addr = INADDR_ANY;
+    SU_INIT(&name, family);
+    SU_SET_INADDR_ANY(&name);
 
     /*
      * If a port range was specified, we try to get a port in that
      * range first.  Next, we try to get a reserved port.  If that
      * fails, we just go for any port.
+     * 
+     * In all cases, not to use port that's assigned to other services. 
      *
      * 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.
      */
-#ifdef UDPPORTRANGE
-    if (bind_portrange(s, &name, UDPPORTRANGE) == 0)
-       goto out;
-#endif
-
-    if (bind_portrange(s, &name, 512, IPPORT_RESERVED - 1) == 0)
-       goto out;
+    for (retries = 0; ; retries++) {
+       if (bind_portrange(s, &name, portrange[0], portrange[1], "udp") == 0)
+           goto out;
+       dbprintf(_("dgram_bind: Could not bind to port in range: %d - %d.\n"),
+                 portrange[0], portrange[1]);
+       if (retries >= BIND_CYCLE_RETRIES) {
+           dbprintf(_("dgram_bind: Giving up...\n"));
+           break;
+       }
 
-    name.sin_port = INADDR_ANY;
-    if (bind(s, (struct sockaddr *)&name, (socklen_t)sizeof name) == -1) {
-       save_errno = errno;
-       dbprintf(("%s: dgram_bind: bind(INADDR_ANY) failed: %s\n",
-                 debug_prefix(NULL),
-                 strerror(save_errno)));
-       errno = save_errno;
-       aclose(s);
-       return -1;
+       dbprintf(_("dgram_bind: Retrying entire range after 10 second delay.\n"));
+       sleep(15);
     }
 
+    save_errno = errno;
+    dbprintf(_("dgram_bind: bind(in6addr_any) failed: %s\n"),
+                 strerror(save_errno));
+    aclose(s);
+    errno = save_errno;
+    return -1;
+
 out:
     /* find out what name was actually used */
 
-    len = (socklen_t) sizeof name;
+    len = (socklen_t_equiv)sizeof(name);
     if(getsockname(s, (struct sockaddr *)&name, &len) == -1) {
        save_errno = errno;
-       dbprintf(("%s: dgram_bind: getsockname() failed: %s\n",
-                 debug_prefix(NULL),
-                 strerror(save_errno)));
+       dbprintf(_("dgram_bind: getsockname() failed: %s\n"), strerror(save_errno));
        errno = save_errno;
        aclose(s);
        return -1;
     }
-    *portp = ntohs(name.sin_port);
+    *portp = SU_GET_PORT(&name);
     dgram->socket = s;
-    dbprintf(("%s: dgram_bind: socket bound to %s.%d\n",
-             debug_prefix_time(NULL),
-             inet_ntoa(name.sin_addr),
-             *portp));
+
+    dbprintf(_("dgram_bind: socket %d bound to %s\n"),
+             dgram->socket, str_sockaddr(&name));
     return 0;
 }
 
 
-int dgram_send_addr(addr, dgram)
-struct sockaddr_in addr;
-dgram_t *dgram;
+int
+dgram_send_addr(
+    sockaddr_union     *addr,
+    dgram_t *          dgram)
 {
-    int s;
+    int s, rc;
     int socket_opened;
-    struct sockaddr_in addr_save;
     int save_errno;
     int max_wait;
     int wait_count;
+#if defined(USE_REUSEADDR)
+    const int on = 1;
+    int r;
+#endif
 
+    dbprintf(_("dgram_send_addr(addr=%p, dgram=%p)\n"),
+             addr, dgram);
+    dump_sockaddr(addr);
+    dbprintf(_("dgram_send_addr: %p->socket = %d\n"),
+             dgram, dgram->socket);
     if(dgram->socket != -1) {
        s = dgram->socket;
        socket_opened = 0;
     } else {
-       if((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+       if((s = socket(SU_GET_FAMILY(addr), SOCK_DGRAM, 0)) == -1) {
            save_errno = errno;
-           dbprintf(("%s: dgram_send_addr: socket() failed: %s\n",
-                     debug_prefix(NULL),
-                     strerror(save_errno)));
+           dbprintf(_("dgram_send_addr: socket() failed: %s\n"),
+                     strerror(save_errno));
            errno = save_errno;
            return -1;
        }
        socket_opened = 1;
-    }
-
-    if(s < 0 || s >= FD_SETSIZE) {
-       dbprintf(("%s: dgram_send_addr: socket out of range: %d\n",
-                 debug_prefix(NULL),
-                 s));
-       if(socket_opened) {
-           aclose(s);
+#ifdef USE_REUSEADDR
+       r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+               (void *)&on, (socklen_t_equiv)sizeof(on));
+       if (r < 0) {
+           dbprintf(_("dgram_send_addr: setsockopt(SO_REUSEADDR) failed: %s\n"),
+                     strerror(errno));
        }
-       errno = EMFILE;                         /* out of range */
-       return -1;
+#endif
     }
 
-    memcpy(&addr_save, &addr, sizeof(addr));
-    max_wait = 300 / 5;                                /* five minutes */
-    wait_count = 0;
-    while(sendto(s,
+    if(s < 0 || s >= (int)FD_SETSIZE) {
+       dbprintf(_("dgram_send_addr: socket out of range: %d\n"), s);
+       errno = EMFILE;                         /* out of range */
+       rc = -1;
+    } else {
+       max_wait = 300 / 5;                             /* five minutes */
+       wait_count = 0;
+       rc = 0;
+       while(sendto(s,
                 dgram->data,
                 dgram->len,
                 0, 
-                (struct sockaddr *)&addr,
-                (socklen_t) sizeof(struct sockaddr_in)) == -1) {
+                (struct sockaddr *)addr,
+                SS_LEN(addr)) == -1) {
 #ifdef ECONNREFUSED
-       if(errno == ECONNREFUSED && wait_count++ < max_wait) {
-           sleep(5);
-           dbprintf(("%s: dgram_send_addr: sendto(%s.%d): retry %d after ECONNREFUSED\n",
-                     debug_prefix_time(NULL),
-                     inet_ntoa(addr_save.sin_addr),
-                     (int) ntohs(addr.sin_port),
-                     wait_count));
-           continue;
-       }
+           if(errno == ECONNREFUSED && wait_count++ < max_wait) {
+               sleep(5);
+               dbprintf(_("dgram_send_addr: sendto(%s): retry %d after ECONNREFUSED\n"),
+                     str_sockaddr(addr),
+                     wait_count);
+               continue;
+           }
+#endif
+#ifdef EAGAIN
+           if(errno == EAGAIN && wait_count++ < max_wait) {
+               sleep(5);
+               dbprintf(_("dgram_send_addr: sendto(%s): retry %d after EAGAIN\n"),
+                     str_sockaddr(addr),
+                     wait_count);
+               continue;
+           }
 #endif
-       save_errno = errno;
-       dbprintf(("%s: dgram_send_addr: sendto(%s.%d) failed: %s \n",
-                 debug_prefix_time(NULL),
-                 inet_ntoa(addr_save.sin_addr),
-                 (int) ntohs(addr.sin_port),
-                 strerror(save_errno)));
-       errno = save_errno;
-        return -1;
-    }
-
-    if(socket_opened) {
-       if(close(s) == -1) {
            save_errno = errno;
-           dbprintf(("%s: dgram_send_addr: close(%s.%d): failed: %s\n",
-                     debug_prefix(NULL),
-                     inet_ntoa(addr_save.sin_addr),
-                     (int) ntohs(addr.sin_port),
-                     strerror(save_errno)));
+           dbprintf(_("dgram_send_addr: sendto(%s) failed: %s \n"),
+                 str_sockaddr(addr),
+                 strerror(save_errno));
            errno = save_errno;
-           return -1;
+           rc = -1;
+           break;
        }
-       s = -1;
     }
 
-    return 0;
-}
-
-
-int dgram_send(hostname, port, dgram)
-char *hostname;
-int port;
-dgram_t *dgram;
-{
-    struct sockaddr_in name;
-    struct hostent *hp;
-    int save_errno;
-
-    if((hp = gethostbyname(hostname)) == 0) {
+    if(socket_opened) {
        save_errno = errno;
-       dbprintf(("%s: dgram_send: gethostbyname(%s) failed\n",
-                 debug_prefix_time(NULL),
-                 hostname));
+       if(close(s) == -1) {
+           dbprintf(_("dgram_send_addr: close(%s): failed: %s\n"),
+                     str_sockaddr(addr),
+                     strerror(errno));
+           /*
+            * Calling function should not care that the close failed.
+            * It does care about the send results though.
+            */
+       }
        errno = save_errno;
-       return -1;
     }
-    memcpy(&name.sin_addr, hp->h_addr, hp->h_length);
-    name.sin_family = AF_INET;
-    name.sin_port = htons(port);
 
-    return dgram_send_addr(name, dgram);
+    return rc;
 }
 
 
-int dgram_recv(dgram, timeout, fromaddr)
-dgram_t *dgram;
-int timeout;
-struct sockaddr_in *fromaddr;
+
+ssize_t
+dgram_recv(
+    dgram_t *          dgram,
+    int                        timeout,
+    sockaddr_union *fromaddr)
 {
-    fd_set ready;
+    SELECT_ARG_TYPE ready;
     struct timeval to;
     ssize_t size;
     int sock;
-    socklen_t addrlen;
-    int nfound;
+    socklen_t_equiv addrlen;
+    ssize_t nfound;
     int save_errno;
 
     sock = dgram->socket;
@@ -256,28 +258,26 @@ struct sockaddr_in *fromaddr;
     to.tv_sec = timeout;
     to.tv_usec = 0;
 
-    nfound = select(sock+1, (SELECT_ARG_TYPE *)&ready, NULL, NULL, &to);
+    dbprintf(_("dgram_recv(dgram=%p, timeout=%u, fromaddr=%p)\n"),
+               dgram, timeout, fromaddr);
+    
+    nfound = (ssize_t)select(sock+1, &ready, NULL, NULL, &to);
     if(nfound <= 0 || !FD_ISSET(sock, &ready)) {
        save_errno = errno;
        if(nfound < 0) {
-           dbprintf(("%s: dgram_recv: select() failed: %s\n",
-                     debug_prefix_time(NULL),
-                     strerror(save_errno)));
+           dbprintf(_("dgram_recv: select() failed: %s\n"), strerror(save_errno));
        } else if(nfound == 0) {
-           dbprintf(("%s: dgram_recv: timeout after %d second%s\n",
-                     debug_prefix_time(NULL),
-                     timeout,
-                     (timeout == 1) ? "" : "s"));
+           dbprintf(plural(_("dgram_recv: timeout after %d second\n"),
+                           _("dgram_recv: timeout after %d seconds\n"),
+                           timeout),
+                    timeout);
            nfound = 0;
        } else if (!FD_ISSET(sock, &ready)) {
            int i;
 
            for(i = 0; i < sock + 1; i++) {
                if(FD_ISSET(i, &ready)) {
-                   dbprintf(("%s: dgram_recv: got fd %d instead of %d\n",
-                             debug_prefix_time(NULL),
-                             i,
-                             sock));
+                   dbprintf(_("dgram_recv: got fd %d instead of %d\n"), i, sock);
                }
            }
            save_errno = EBADF;
@@ -287,68 +287,75 @@ struct sockaddr_in *fromaddr;
        return nfound;
     }
 
-    addrlen = (socklen_t) sizeof(struct sockaddr_in);
-    size = recvfrom(sock, dgram->data, MAX_DGRAM, 0,
+    addrlen = (socklen_t_equiv)sizeof(sockaddr_union);
+    size = recvfrom(sock, dgram->data, (size_t)MAX_DGRAM, 0,
                    (struct sockaddr *)fromaddr, &addrlen);
     if(size == -1) {
        save_errno = errno;
-       dbprintf(("%s: dgram_recv: recvfrom() failed: %s\n",
-                 debug_prefix(NULL),
-                 strerror(save_errno)));
+       dbprintf(_("dgram_recv: recvfrom() failed: %s\n"), strerror(save_errno));
        errno = save_errno;
        return -1;
     }
-    dgram->len = size;
+    dump_sockaddr(fromaddr);
+    dgram->len = (size_t)size;
     dgram->data[size] = '\0';
     dgram->cur = dgram->data;
     return size;
 }
 
 
-void dgram_zero(dgram)
-dgram_t *dgram;
+void
+dgram_zero(
+    dgram_t *  dgram)
 {
     dgram->cur = dgram->data;
     dgram->len = 0;
     *(dgram->cur) = '\0';
 }
 
-dgram_t *debug_dgram_alloc(s, l)
-char *s;
-int l;
+printf_arglist_function1(int dgram_cat, dgram_t *, dgram, const char *, fmt)
 {
-    dgram_t *p;
-
-    malloc_enter(dbmalloc_caller_loc(s, l));
-    p = (dgram_t *) alloc(sizeof(dgram_t));
-    dgram_zero(p);
-    p->socket = -1;
-    malloc_leave(dbmalloc_caller_loc(s, l));
+    ssize_t bufsize;
+    va_list argp;
+    int len;
 
-    return p;
-}
+    assert(dgram != NULL);
+    assert(fmt != NULL);
 
+    assert(dgram->len == (size_t)(dgram->cur - dgram->data));
+    assert(dgram->len < SIZEOF(dgram->data));
 
-void dgram_cat(dgram, str)
-dgram_t *dgram;
-const char *str;
-{
-    int len = strlen(str);
+    bufsize = (ssize_t)(sizeof(dgram->data) - dgram->len);
+    if (bufsize <= 0)
+       return -1;
 
-    if(dgram->len + len > MAX_DGRAM) len = MAX_DGRAM - dgram->len;
-    strncpy(dgram->cur, str, len);
-    dgram->cur += len;
-    dgram->len += len;
-    *(dgram->cur) = '\0';
+    arglist_start(argp, fmt);
+    len = g_vsnprintf(dgram->cur, (size_t)bufsize, fmt, argp);
+    arglist_end(argp);
+    if(len < 0) {
+       return -1;
+    } else if((ssize_t)len > bufsize) {
+       dgram->len = sizeof(dgram->data);
+       dgram->cur = dgram->data + dgram->len;
+       return -1;
+    }
+    else {
+       dgram->len += len;
+       dgram->cur = dgram->data + dgram->len;
+    }
+    return 0;
 }
 
-void dgram_eatline(dgram)
-dgram_t *dgram;
+void
+dgram_eatline(
+    dgram_t *  dgram)
 {
     char *p = dgram->cur;
     char *end = dgram->data + dgram->len;
 
-    while(p < end && *p && *p != '\n') p++;
-    if(*p == '\n') p++;
+    while(p < end && *p && *p != '\n')
+       p++;
+    if(*p == '\n')
+       p++;
     dgram->cur = p;
 }