You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@spamassassin.apache.org by jm...@apache.org on 2006/06/29 19:58:03 UTC

svn commit: r418101 - in /spamassassin/trunk: spamc/libspamc.c spamc/libspamc.h t/spamc_l.t

Author: jm
Date: Thu Jun 29 10:58:02 2006
New Revision: 418101

URL: http://svn.apache.org/viewvc?rev=418101&view=rev
Log:
bug 4477: port spamc to support both IPv4 and IPv6; thanks to John Madden

Modified:
    spamassassin/trunk/spamc/libspamc.c
    spamassassin/trunk/spamc/libspamc.h
    spamassassin/trunk/t/spamc_l.t

Modified: spamassassin/trunk/spamc/libspamc.c
URL: http://svn.apache.org/viewvc/spamassassin/trunk/spamc/libspamc.c?rev=418101&r1=418100&r2=418101&view=diff
==============================================================================
--- spamassassin/trunk/spamc/libspamc.c (original)
+++ spamassassin/trunk/spamc/libspamc.c Thu Jun 29 10:58:02 2006
@@ -95,7 +95,10 @@
 #endif
 
 #undef DO_CONNECT_DEBUG_SYSLOGS
-/* or #define DO_CONNECT_DEBUG_SYSLOGS 1 */
+/*
+#define DO_CONNECT_DEBUG_SYSLOGS 1
+#define CONNECT_DEBUG_LEVEL LOG_DEBUG
+*/
 
 /* static const int ESC_PASSTHROUGHRAW = EX__MAX + 666;  No longer seems to be used */
 
@@ -168,10 +171,9 @@
  *
  *	Upon failure we return one of the other EX_??? error codes.
  */
-static int _opensocket(int flags, int type, int *psock)
+static int _opensocket(int flags, struct addrinfo *res, int *psock)
 {
     const char *typename;
-    int proto = 0;
 
 #ifdef _WIN32
     int socktout;
@@ -184,19 +186,26 @@
 	 * type given by the user. The typename is strictly used for debug
 	 * reporting.
 	 */
-    if (type == PF_UNIX) {
-	typename = "PF_UNIX";
-    }
-    else {
-	typename = "PF_INET";
-	proto = IPPROTO_TCP;
+    switch(res->ai_family) {
+       case PF_UNIX:
+          typename = "PF_UNIX";
+          break;
+       case PF_INET:
+          typename = "PF_INET";
+          break;
+       case PF_INET6:
+          typename = "PF_INET6";
+          break;
+       default:
+          typename = "Unknown";
+          break;
     }
 
 #ifdef DO_CONNECT_DEBUG_SYSLOGS
-    libspamc_log(flags, LOG_DEBUG, "dbg: create socket(%s)", typename);
+    libspamc_log(flags, CONNECT_DEBUG_LEVEL, "dbg: create socket(%s)", typename);
 #endif
 
-    if ((*psock = socket(type, SOCK_STREAM, proto))
+    if ((*psock = socket(res->ai_family, res->ai_socktype, res->ai_protocol))
 #ifndef _WIN32
 	< 0
 #else
@@ -315,16 +324,23 @@
 #ifndef _WIN32
     int mysock, status, origerr;
     struct sockaddr_un addrbuf;
+    struct addrinfo hints, *res;
     int ret;
 
     assert(tp != 0);
     assert(sockptr != 0);
     assert(tp->socketpath != 0);
 
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = PF_UNIX;
+    hints.ai_socktype = SOCK_STREAM;
+    hints.ai_protocol = 0;
+    res = &hints;
+
 	/*----------------------------------------------------------------
 	 * If the socket itself can't be created, this is a fatal error.
 	 */
-    if ((ret = _opensocket(tp->flags, PF_UNIX, &mysock)) != EX_OK)
+    if ((ret = _opensocket(tp->flags, res, &mysock)) != EX_OK)
 	return ret;
 
     /* set up the UNIX domain socket */
@@ -334,7 +350,7 @@
     addrbuf.sun_path[sizeof addrbuf.sun_path - 1] = '\0';
 
 #ifdef DO_CONNECT_DEBUG_SYSLOGS
-    libspamc_log(tp->flags, LOG_DEBUG, "dbg: connect(AF_UNIX) to spamd at %s",
+    libspamc_log(tp->flags, CONNECT_DEBUG_LEVEL, "dbg: connect(AF_UNIX) to spamd at %s",
 	   addrbuf.sun_path);
 #endif
 
@@ -344,7 +360,7 @@
 
     if (status >= 0) {
 #ifdef DO_CONNECT_DEBUG_SYSLOGS
-	libspamc_log(tp->flags, LOG_DEBUG, "dbg: connect(AF_UNIX) ok");
+	libspamc_log(tp->flags, CONNECT_DEBUG_LEVEL, "dbg: connect(AF_UNIX) ok");
 #endif
 
 	*sockptr = mysock;
@@ -377,78 +393,99 @@
     int numloops;
     int origerr = 0;
     int ret;
+    struct addrinfo *res = NULL;
+
+    char host[255]; /* hostname, for logging */
+    char port[255]; /* port, for logging */
+                  /* needs larger size to accomodate strings for
+                    * ports (eg. port 783 resolves to 'spamd' from
+                    * getnameinfo() */
 
     assert(tp != 0);
     assert(sockptr != 0);
     assert(tp->nhosts > 0);
 
-#ifdef DO_CONNECT_DEBUG_SYSLOGS
-    for (numloops = 0; numloops < tp->nhosts; numloops++) {
-	libspamc_log(tp->flags, LOG_ERR, "dbg: %d/%d: %s",
-		numloops + 1, tp->nhosts, inet_ntoa(tp->hosts[numloops]));
-    }
-#endif
-
     for (numloops = 0; numloops < MAX_CONNECT_RETRIES; numloops++) {
-	struct sockaddr_in addrbuf;
-	const int hostix = numloops % tp->nhosts;
-	int status, mysock;
-	const char *ipaddr;
-
-		/*--------------------------------------------------------
-		 * We always start by creating the socket, as we get only
-		 * one attempt to connect() on each one. If this fails,
-		 * we're done.
-		 */
-	if ((ret = _opensocket(tp->flags, PF_INET, &mysock)) != EX_OK)
-	    return ret;
+        const int hostix = numloops % tp->nhosts;
+        int status, mysock;
 
-	memset(&addrbuf, 0, sizeof(addrbuf));
+                /*--------------------------------------------------------
+                * We always start by creating the socket, as we get only
+                * one attempt to connect() on each one. If this fails,
+                * we're done.
+                */
+
+        res = tp->hosts[hostix];
+        while(res) {
+            char *family = NULL;
+            switch(res->ai_family) {
+            case AF_INET:
+                family = "AF_INET";
+                break;
+            case AF_INET6:
+                family = "AF_INET6";
+                break;
+            default:
+                family = "Unknown";
+                break;
+            }
 
-	addrbuf.sin_family = AF_INET;
-	addrbuf.sin_port = htons(tp->port);
-	addrbuf.sin_addr = tp->hosts[hostix];
+            if ((ret = _opensocket(tp->flags, res, &mysock)) != EX_OK) {
+                res = res->ai_next;
+                continue;
+            }
 
-	ipaddr = inet_ntoa(addrbuf.sin_addr);
+            getnameinfo(res->ai_addr, res->ai_addrlen,
+                  host, (sizeof(host)*sizeof(char)),
+                  port, (sizeof(port)*sizeof(char)),
+                  NI_NUMERICHOST);
 
 #ifdef DO_CONNECT_DEBUG_SYSLOGS
-	libspamc_log(tp->flags, LOG_DEBUG,
-	       "dbg: connect(AF_INET) to spamd at %s (try #%d of %d)",
-		ipaddr, numloops + 1, MAX_CONNECT_RETRIES);
+            libspamc_log(tp->flags, CONNECT_DEBUG_LEVEL,
+              "dbg: connect(%s) to spamd (host %s, port %s) (try #%d of %d)",
+                      family, host, port, numloops + 1, MAX_CONNECT_RETRIES);
 #endif
 
-	status =
-	    timeout_connect(mysock, (struct sockaddr *) &addrbuf, sizeof(addrbuf));
+              status = connect(mysock, res->ai_addr, res->ai_addrlen);
 
-	if (status != 0) {
+            if (status != 0) {
 #ifndef _WIN32
-	    origerr = errno;
-	    libspamc_log(tp->flags, LOG_ERR,
-		   "connect(AF_INET) to spamd at %s failed, retrying (#%d of %d): %s",
-		   ipaddr, numloops + 1, MAX_CONNECT_RETRIES, strerror(origerr));
+                  origerr = errno;
 #else
-	    origerr = WSAGetLastError();
-	    libspamc_log(tp->flags, LOG_ERR,
-		   "connect(AF_INET) to spamd at %s failed, retrying (#%d of %d): %d",
-		   ipaddr, numloops + 1, MAX_CONNECT_RETRIES, origerr);
+                  origerr = WSAGetLastError();
 #endif
-	    closesocket(mysock);
-
-	    sleep(CONNECT_RETRY_SLEEP);
-	}
-	else {
+                  closesocket(mysock);
+              } else {
 #ifdef DO_CONNECT_DEBUG_SYSLOGS
-	    libspamc_log(tp->flags, LOG_DEBUG,
-		   "dbg: connect(AF_INET) to spamd at %s done", ipaddr);
+                  libspamc_log(tp->flags, CONNECT_DEBUG_LEVEL,
+                          "dbg: connect(%s) to spamd done",family);
 #endif
-	    *sockptr = mysock;
+                  *sockptr = mysock;
 
-	    return EX_OK;
-	}
+                  return EX_OK;
+            }
+            res = res->ai_next;
+        }
+        sleep(CONNECT_RETRY_SLEEP);
+
+#ifndef _WIN32
+        libspamc_log(tp->flags, LOG_ERR,
+                "connect to spamd failed, retrying (#%d of %d): %s",
+                numloops+1, MAX_CONNECT_RETRIES, strerror(origerr));
+#else
+        libspamc_log(tp->flags, LOG_ERR,
+                "connect to spamd on %s failed, retrying (#%d of %d): %s",
+                family, host, numloops+1, MAX_CONNECT_RETRIES, origerr);
+#endif
+    } /* for(numloops...) */
+
+    for(numloops=0;numloops<tp->nhosts;numloops++) {
+        freeaddrinfo(tp->hosts[tp->nhosts]);
     }
 
-    libspamc_log(tp->flags, LOG_ERR, "connection attempt to spamd aborted after %d retries",
-	    MAX_CONNECT_RETRIES);
+    libspamc_log(tp->flags, LOG_ERR,
+              "connection attempt to spamd aborted after %d retries",
+              MAX_CONNECT_RETRIES);
 
     return _translate_connect_errno(origerr);
 }
@@ -1455,6 +1492,8 @@
 
 static void _randomize_hosts(struct transport *tp)
 {
+    struct addrinfo *tmp;
+    int i;
     int rnum;
 
     assert(tp != 0);
@@ -1465,8 +1504,7 @@
     rnum = rand() % tp->nhosts;
 
     while (rnum-- > 0) {
-        struct in_addr tmp = tp->hosts[0];
-        int i;
+        tmp = tp->hosts[0];
 
         for (i = 1; i < tp->nhosts; i++)
             tp->hosts[i - 1] = tp->hosts[i];
@@ -1492,10 +1530,11 @@
 */
 int transport_setup(struct transport *tp, int flags)
 {
-    struct hostent *hp;
+    struct addrinfo hints, *res; 
     char *hostlist, *hostname;
     int errbits;
-    char **addrp;
+    char port[6];
+    int origerr;
 
 #ifdef _WIN32
     /* Start Winsock up */
@@ -1511,6 +1550,12 @@
     assert(tp != NULL);
     tp->flags = flags;
 
+    snprintf(port, 6, "%d", tp->port);
+
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = PF_UNSPEC;
+    hints.ai_socktype = SOCK_STREAM;
+
     switch (tp->type) {
 #ifndef _WIN32
     case TRANSPORT_UNIX:
@@ -1518,7 +1563,8 @@
         return EX_OK;
 #endif
     case TRANSPORT_LOCALHOST:
-        tp->hosts[0].s_addr = inet_addr("127.0.0.1");
+        getaddrinfo("localhost", port, &hints, &res);
+        tp->hosts[0] = res;
         tp->nhosts = 1;
         return EX_OK;
 
@@ -1550,17 +1596,24 @@
                 *hostend = '\0';
             }
             
-            if ((hp = gethostbyname(hostname)) == NULL) {
-                int origerr = h_errno; /* take a copy before syslog() */
-                libspamc_log(flags, LOG_DEBUG, "gethostbyname(%s) failed: h_errno=%d",
-                    hostname, origerr);
-                switch (origerr) {
-                case TRY_AGAIN:
+            if ((origerr = getaddrinfo(hostname, port, &hints, &res))) {
+                libspamc_log(flags, LOG_DEBUG, 
+                      "getaddrinfo(%s) failed: %s",
+                      hostname, gai_strerror(origerr));
+                switch (origerr) { 
+                case EAI_AGAIN:
                     errbits |= 1;
                     break;
-                case HOST_NOT_FOUND:
-                case NO_ADDRESS:
-                case NO_RECOVERY:
+                case EAI_FAMILY: /*address family not supported*/
+                case EAI_SOCKTYPE: /*socket type not supported*/
+                case EAI_BADFLAGS: /*ai_flags is invalid*/
+                case EAI_NONAME: /*node or service unknown*/
+                case EAI_SERVICE: /*service not available*/
+                case EAI_ADDRFAMILY: /*no addresses in requested family*/
+                case EAI_NODATA: /*address exists, but no data*/
+                case EAI_MEMORY: /*out of memory*/
+                case EAI_FAIL: /*name server returned permanent error*/
+                case EAI_SYSTEM: /*system error, check errno*/
                     errbits |= 2;
                     break;
                 default:
@@ -1571,14 +1624,8 @@
                 goto nexthost; /* try next host in list */
             }
             
-            /* If we have no hosts at all, or if they are some other
-             * kind of address family besides IPv4, then we really
-             * just have no hosts at all. TODO: IPv6
-             */
-            if (hp->h_addr_list[0] == NULL
-             || hp->h_length != sizeof tp->hosts[0]
-             || hp->h_addrtype != AF_INET) {
-                /* no hosts/bad size/wrong family */
+            /* If we have no hosts at all */
+            if(res == NULL) {
                 errbits |= 1;
                 goto nexthost; /* try next host in list */
             }
@@ -1588,15 +1635,14 @@
              * means we won't ever walk all over the list with other
              * calls.
              */
-            for (addrp = hp->h_addr_list; *addrp; addrp++) {
-                if (tp->nhosts == TRANSPORT_MAX_HOSTS) {
-                    libspamc_log(flags, LOG_NOTICE, "hit limit of %d hosts, ignoring remainder",
-                        TRANSPORT_MAX_HOSTS);
-                    break;
-                }
-                memcpy(&tp->hosts[tp->nhosts], *addrp, hp->h_length);
-                tp->nhosts++;
+            if(tp->nhosts == TRANSPORT_MAX_HOSTS) {
+               libspamc_log(flags, LOG_NOTICE, 
+                     "hit limit of %d hosts, ignoring remainder",
+                     TRANSPORT_MAX_HOSTS);
+               break;
             }
+            tp->hosts[tp->nhosts] = res;
+            tp->nhosts++;
             
 nexthost:
             hostname = hostend;

Modified: spamassassin/trunk/spamc/libspamc.h
URL: http://svn.apache.org/viewvc/spamassassin/trunk/spamc/libspamc.h?rev=418101&r1=418100&r2=418101&view=diff
==============================================================================
--- spamassassin/trunk/spamc/libspamc.h (original)
+++ spamassassin/trunk/spamc/libspamc.h Thu Jun 29 10:58:02 2006
@@ -204,7 +204,7 @@
 
     unsigned short port;	/* for TCP sockets              */
 
-    struct in_addr hosts[TRANSPORT_MAX_HOSTS];
+    struct addrinfo *hosts[TRANSPORT_MAX_HOSTS];
     int nhosts;
     int flags;
 };

Modified: spamassassin/trunk/t/spamc_l.t
URL: http://svn.apache.org/viewvc/spamassassin/trunk/t/spamc_l.t?rev=418101&r1=418100&r2=418101&view=diff
==============================================================================
--- spamassassin/trunk/t/spamc_l.t (original)
+++ spamassassin/trunk/t/spamc_l.t Thu Jun 29 10:58:02 2006
@@ -15,7 +15,7 @@
 %patterns = (
 
 q{ hello world }, 'spamc_l',
-q{ spamc: connect(AF_INET) to spamd at 127.0.0.1 failed, retrying (#1 of 3): } . $errmsg, 'connfailed',
+q{ spamc: connect to spamd failed, retrying (#1 of 3): } . $errmsg, 'connfailed',
 
 );