]> diplodocus.org Git - mdeliver/commitdiff
Update libspam.[ch] utils.[ch] from spamassassin 3.1.0 to 3.2.5 .
authorepg@pretzelnet.org <>
Sun, 8 Mar 2009 09:20:24 +0000 (01:20 -0800)
committerepg@pretzelnet.org <>
Sun, 8 Mar 2009 09:20:24 +0000 (01:20 -0800)
libspamc.c
libspamc.h
utils.c
utils.h

index b4d1936..1294e00 100644 (file)
@@ -1,9 +1,10 @@
 /* <@LICENSE>
 /* <@LICENSE>
- * Copyright 2004 Apache Software Foundation
- * 
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at:
  * 
  *     http://www.apache.org/licenses/LICENSE-2.0
  * 
  * 
  *     http://www.apache.org/licenses/LICENSE-2.0
  * 
  * </@LICENSE>
  */
 
  * </@LICENSE>
  */
 
+/* 
+  Compile with extra warnings -- gcc only, not suitable for use as default:
+
+  gcc -Wextra -Wdeclaration-after-statement -Wall -g -O2 spamc/spamc.c \
+  spamc/getopt.c spamc/libspamc.c spamc/utils.c -o spamc/spamc -ldl -lz
+ */
+
 #include "config.h"
 #include "libspamc.h"
 #include "utils.h"
 #include "config.h"
 #include "libspamc.h"
 #include "utils.h"
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
 #endif
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
 #endif
-
-/* FIXME: Make this configurable */
-#define MAX_CONNECT_RETRIES 3
-#define CONNECT_RETRY_SLEEP 1
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
 
 /* RedHat 5.2 doesn't define Shutdown 2nd Parameter Constants */
 /* KAM 12-4-01 */
 
 /* RedHat 5.2 doesn't define Shutdown 2nd Parameter Constants */
 /* KAM 12-4-01 */
 #define h_errno errno
 #endif
 
 #define h_errno errno
 #endif
 
+#ifdef _WIN32
+#define spamc_get_errno()   WSAGetLastError()
+#else
+#define spamc_get_errno()   errno
+#endif
+
 #ifndef HAVE_OPTARG
 extern char *optarg;
 #endif
 #ifndef HAVE_OPTARG
 extern char *optarg;
 #endif
@@ -95,9 +108,25 @@ extern char *optarg;
 #endif
 
 #undef DO_CONNECT_DEBUG_SYSLOGS
 #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;
+/* bug 4477 comment 14 */
+#ifdef NI_MAXHOST
+#define SPAMC_MAXHOST NI_MAXHOST
+#else
+#define SPAMC_MAXHOST 256
+#endif
+
+#ifdef NI_MAXSERV
+#define SPAMC_MAXSERV NI_MAXSERV
+#else
+#define SPAMC_MAXSERV 256
+#endif
+
+/* static const int ESC_PASSTHROUGHRAW = EX__MAX + 666;  No longer seems to be used */
 
 /* set EXPANSION_ALLOWANCE to something more than might be
    added to a message in X-headers and the report template */
 
 /* set EXPANSION_ALLOWANCE to something more than might be
    added to a message in X-headers and the report template */
@@ -110,7 +139,7 @@ static const int EXPANSION_ALLOWANCE = 16384;
  */
 
 /* Set the protocol version that this spamc speaks */
  */
 
 /* Set the protocol version that this spamc speaks */
-static const char *PROTOCOL_VERSION = "SPAMC/1.3";
+static const char *PROTOCOL_VERSION = "SPAMC/1.4";
 
 /* "private" part of struct message.
  * we use this instead of the struct message directly, so that we
 
 /* "private" part of struct message.
  * we use this instead of the struct message directly, so that we
@@ -119,6 +148,7 @@ static const char *PROTOCOL_VERSION = "SPAMC/1.3";
 struct libspamc_private_message
 {
     int flags;                 /* copied from "flags" arg to message_read() */
 struct libspamc_private_message
 {
     int flags;                 /* copied from "flags" arg to message_read() */
+    int alloced_size;           /* allocated space for the "out" buffer */
 };
 
 int libspamc_timeout = 0;
 };
 
 int libspamc_timeout = 0;
@@ -168,11 +198,16 @@ static int _translate_connect_errno(int err)
  *
  *     Upon failure we return one of the other EX_??? error codes.
  */
  *
  *     Upon failure we return one of the other EX_??? error codes.
  */
+#ifdef SPAMC_HAS_ADDRINFO
+static int _opensocket(int flags, struct addrinfo *res, int *psock)
+{
+#else
 static int _opensocket(int flags, int type, int *psock)
 {
 static int _opensocket(int flags, int type, int *psock)
 {
-    const char *typename;
     int proto = 0;
     int proto = 0;
-
+#endif
+    const char *typename;
+    int origerr;
 #ifdef _WIN32
     int socktout;
 #endif
 #ifdef _WIN32
     int socktout;
 #endif
@@ -184,6 +219,22 @@ static int _opensocket(int flags, int type, int *psock)
         * type given by the user. The typename is strictly used for debug
         * reporting.
         */
         * type given by the user. The typename is strictly used for debug
         * reporting.
         */
+#ifdef SPAMC_HAS_ADDRINFO
+    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;
+    }
+#else
     if (type == PF_UNIX) {
        typename = "PF_UNIX";
     }
     if (type == PF_UNIX) {
        typename = "PF_UNIX";
     }
@@ -191,30 +242,33 @@ static int _opensocket(int flags, int type, int *psock)
        typename = "PF_INET";
        proto = IPPROTO_TCP;
     }
        typename = "PF_INET";
        proto = IPPROTO_TCP;
     }
+#endif
 
 #ifdef DO_CONNECT_DEBUG_SYSLOGS
 
 #ifdef DO_CONNECT_DEBUG_SYSLOGS
-    libspamc_log(flags, DEBUG_LEVEL, "dbg: create socket(%s)", typename);
+    libspamc_log(flags, CONNECT_DEBUG_LEVEL, "dbg: create socket(%s)", typename);
 #endif
 
 #endif
 
+#ifdef SPAMC_HAS_ADDRINFO
+    if ((*psock = socket(res->ai_family, res->ai_socktype, res->ai_protocol))
+#else
     if ((*psock = socket(type, SOCK_STREAM, proto))
     if ((*psock = socket(type, SOCK_STREAM, proto))
+#endif
 #ifndef _WIN32
        < 0
 #else
        == INVALID_SOCKET
 #endif
        ) {
 #ifndef _WIN32
        < 0
 #else
        == INVALID_SOCKET
 #endif
        ) {
-       int origerr;
 
                /*--------------------------------------------------------
                 * At this point we had a failure creating the socket, and
                 * this is pretty much fatal. Translate the error reason
                 * into something the user can understand.
                 */
 
                /*--------------------------------------------------------
                 * At this point we had a failure creating the socket, and
                 * this is pretty much fatal. Translate the error reason
                 * into something the user can understand.
                 */
+       origerr = spamc_get_errno();
 #ifndef _WIN32
 #ifndef _WIN32
-       origerr = errno;        /* take a copy before syslog() */
        libspamc_log(flags, LOG_ERR, "socket(%s) to spamd failed: %s", typename, strerror(origerr));
 #else
        libspamc_log(flags, LOG_ERR, "socket(%s) to spamd failed: %s", typename, strerror(origerr));
 #else
-       origerr = WSAGetLastError();
        libspamc_log(flags, LOG_ERR, "socket(%s) to spamd failed: %d", typename, origerr);
 #endif
 
        libspamc_log(flags, LOG_ERR, "socket(%s) to spamd failed: %d", typename, origerr);
 #endif
 
@@ -243,21 +297,20 @@ static int _opensocket(int flags, int type, int *psock)
     if (type == PF_INET
         && setsockopt(*psock, SOL_SOCKET, SO_RCVTIMEO, (char *)&socktout, sizeof(socktout)) != 0)
     {
     if (type == PF_INET
         && setsockopt(*psock, SOL_SOCKET, SO_RCVTIMEO, (char *)&socktout, sizeof(socktout)) != 0)
     {
-        int origerrno;
 
 
-        origerrno = WSAGetLastError();
-        switch (origerrno)
+        origerr = spamc_get_errno();
+        switch (origerr)
         {
         case EBADF:
         case ENOTSOCK:
         case ENOPROTOOPT:
         case EFAULT:
         {
         case EBADF:
         case ENOTSOCK:
         case ENOPROTOOPT:
         case EFAULT:
-            libspamc_log(flags, LOG_ERR, "setsockopt(SO_RCVTIMEO) failed: %d", origerrno);
+            libspamc_log(flags, LOG_ERR, "setsockopt(SO_RCVTIMEO) failed: %d", origerr);
             closesocket(*psock);
             return EX_SOFTWARE;
 
         default:
             closesocket(*psock);
             return EX_SOFTWARE;
 
         default:
-            break;             /* ignored */
+            break;             /* ignored */
         }
     }
 #endif
         }
     }
 #endif
@@ -272,12 +325,7 @@ static int _opensocket(int flags, int type, int *psock)
 
        if (type == PF_INET
            && setsockopt(*psock, 0, TCP_NODELAY, &one, sizeof one) != 0) {
 
        if (type == PF_INET
            && setsockopt(*psock, 0, TCP_NODELAY, &one, sizeof one) != 0) {
-           int origerrno;
-#ifndef _WIN32
-           origerr = errno;
-#else
-           origerrno = WSAGetLastError();
-#endif
+           origerr = spamc_get_errno();
            switch (origerr) {
            case EBADF:
            case ENOTSOCK:
            switch (origerr) {
            case EBADF:
            case ENOTSOCK:
@@ -315,16 +363,28 @@ static int _try_to_connect_unix(struct transport *tp, int *sockptr)
 #ifndef _WIN32
     int mysock, status, origerr;
     struct sockaddr_un addrbuf;
 #ifndef _WIN32
     int mysock, status, origerr;
     struct sockaddr_un addrbuf;
+#ifdef SPAMC_HAS_ADDRINFO
+    struct addrinfo hints, *res;
+#else
+    int res = PF_UNIX;
+#endif
     int ret;
 
     assert(tp != 0);
     assert(sockptr != 0);
     assert(tp->socketpath != 0);
 
     int ret;
 
     assert(tp != 0);
     assert(sockptr != 0);
     assert(tp->socketpath != 0);
 
+#ifdef SPAMC_HAS_ADDRINFO
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = PF_UNIX;
+    hints.ai_socktype = SOCK_STREAM;
+    hints.ai_protocol = 0;
+    res = &hints;
+#endif
        /*----------------------------------------------------------------
         * If the socket itself can't be created, this is a fatal error.
         */
        /*----------------------------------------------------------------
         * 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 */
        return ret;
 
     /* set up the UNIX domain socket */
@@ -334,17 +394,17 @@ static int _try_to_connect_unix(struct transport *tp, int *sockptr)
     addrbuf.sun_path[sizeof addrbuf.sun_path - 1] = '\0';
 
 #ifdef DO_CONNECT_DEBUG_SYSLOGS
     addrbuf.sun_path[sizeof addrbuf.sun_path - 1] = '\0';
 
 #ifdef DO_CONNECT_DEBUG_SYSLOGS
-    libspamc_log(tp->flags, DEBUG_LEVEL, "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
 
           addrbuf.sun_path);
 #endif
 
-    status = connect(mysock, (struct sockaddr *) &addrbuf, sizeof(addrbuf));
+    status = timeout_connect(mysock, (struct sockaddr *) &addrbuf, sizeof(addrbuf));
 
     origerr = errno;
 
     if (status >= 0) {
 #ifdef DO_CONNECT_DEBUG_SYSLOGS
 
     origerr = errno;
 
     if (status >= 0) {
 #ifdef DO_CONNECT_DEBUG_SYSLOGS
-       libspamc_log(tp->flags, DEBUG_LEVEL, "dbg: connect(AF_UNIX) ok");
+       libspamc_log(tp->flags, CONNECT_DEBUG_LEVEL, "dbg: connect(AF_UNIX) ok");
 #endif
 
        *sockptr = mysock;
 #endif
 
        *sockptr = mysock;
@@ -377,78 +437,163 @@ static int _try_to_connect_tcp(const struct transport *tp, int *sockptr)
     int numloops;
     int origerr = 0;
     int ret;
     int numloops;
     int origerr = 0;
     int ret;
+#ifdef SPAMC_HAS_ADDRINFO
+    struct addrinfo *res = NULL;
+#else
+    int res = PF_INET;
+#endif
+
+    char host[SPAMC_MAXHOST-1]; /* hostname, for logging */
+    char port[SPAMC_MAXSERV-1]; /* port, for logging */
+
+    int connect_retries, retry_sleep;
 
     assert(tp != 0);
     assert(sockptr != 0);
     assert(tp->nhosts > 0);
 
 
     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
+    /* default values */
+    retry_sleep = tp->retry_sleep;
+    connect_retries = tp->connect_retries;
+    if (connect_retries == 0) {
+      connect_retries = 3;
+    }
+    if (retry_sleep < 0) {
+      retry_sleep = 1;
+    }
+
+    for (numloops = 0; numloops < connect_retries; numloops++) {
+        const int hostix = numloops % tp->nhosts;
+        int status, mysock;
+
+                /*--------------------------------------------------------
+                * We always start by creating the socket, as we get only
+                * one attempt to connect() on each one. If this fails,
+                * we're done.
+                */
+
+#ifdef SPAMC_HAS_ADDRINFO
+        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;
+            }
 
 
-    for (numloops = 0; numloops < MAX_CONNECT_RETRIES; numloops++) {
-       struct sockaddr_in addrbuf;
-       const int hostix = numloops % tp->nhosts;
-       int status, mysock;
-       const char *ipaddr;
+            if ((ret = _opensocket(tp->flags, res, &mysock)) != EX_OK) {
+                res = res->ai_next;
+                continue;
+            }
 
 
-               /*--------------------------------------------------------
-                * 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;
+            getnameinfo(res->ai_addr, res->ai_addrlen,
+                  host, sizeof(host),
+                  port, sizeof(port),
+                  NI_NUMERICHOST|NI_NUMERICSERV);
 
 
-       memset(&addrbuf, 0, sizeof(addrbuf));
+#ifdef DO_CONNECT_DEBUG_SYSLOGS
+            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, connect_retries);
+#endif
 
 
-       addrbuf.sin_family = AF_INET;
-       addrbuf.sin_port = htons(tp->port);
-       addrbuf.sin_addr = tp->hosts[hostix];
+            /* this is special-cased so that we have an address we can
+             * safely use as an "always fail" test case */
+            if (!strcmp(host, "255.255.255.255")) {
+              libspamc_log(tp->flags, LOG_ERR,
+                          "connect to spamd on %s failed, broadcast addr",
+                          host);
+              status = -1;
+            }
+            else {
+              status = timeout_connect(mysock, res->ai_addr, res->ai_addrlen);
+            }
 
 
-       ipaddr = inet_ntoa(addrbuf.sin_addr);
+#else
+           struct sockaddr_in addrbuf;
+           const char *ipaddr;
+           const char* family="AF_INET";
+           if ((ret = _opensocket(tp->flags, PF_INET, &mysock)) != EX_OK)
+             return ret;
+           
+           memset(&addrbuf, 0, sizeof(addrbuf));
+           
+           addrbuf.sin_family = AF_INET;
+           addrbuf.sin_port = htons(tp->port);
+           addrbuf.sin_addr = tp->hosts[hostix];
+           
+           ipaddr = inet_ntoa(addrbuf.sin_addr);
+
+            /* make a copy in host, for logging (bug 5577) */
+            strncpy (host, ipaddr, sizeof(host) - 1);
 
 #ifdef DO_CONNECT_DEBUG_SYSLOGS
 
 #ifdef DO_CONNECT_DEBUG_SYSLOGS
-       libspamc_log(tp->flags, DEBUG_LEVEL,
-              "dbg: connect(AF_INET) to spamd at %s (try #%d of %d)",
-               ipaddr, numloops + 1, MAX_CONNECT_RETRIES);
+           libspamc_log(tp->flags, LOG_DEBUG,
+                        "dbg: connect(AF_INET) to spamd at %s (try #%d of %d)",
+                        ipaddr, numloops + 1, connect_retries);
+#endif
+
+            /* this is special-cased so that we have an address we can
+             * safely use as an "always fail" test case */
+            if (!strcmp(ipaddr, "255.255.255.255")) {
+              libspamc_log(tp->flags, LOG_ERR,
+                          "connect to spamd on %s failed, broadcast addr",
+                          ipaddr);
+              status = -1;
+            }
+            else {
+              status = timeout_connect(mysock, (struct sockaddr *) &addrbuf,
+                        sizeof(addrbuf));
+            }
+
 #endif
 
 #endif
 
-       status =
-           connect(mysock, (struct sockaddr *) &addrbuf, sizeof(addrbuf));
+            if (status != 0) {
+                  origerr = spamc_get_errno();
+                  closesocket(mysock);
 
 
-       if (status != 0) {
 #ifndef _WIN32
 #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));
+                  libspamc_log(tp->flags, LOG_ERR,
+                      "connect to spamd on %s failed, retrying (#%d of %d): %s",
+                      host, numloops+1, connect_retries, strerror(origerr));
 #else
 #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);
+                  libspamc_log(tp->flags, LOG_ERR,
+                      "connect to spamd on %s failed, retrying (#%d of %d): %d",
+                      host, numloops+1, connect_retries, origerr);
 #endif
 #endif
-           closesocket(mysock);
 
 
-           sleep(CONNECT_RETRY_SLEEP);
-       }
-       else {
+            } else {
 #ifdef DO_CONNECT_DEBUG_SYSLOGS
 #ifdef DO_CONNECT_DEBUG_SYSLOGS
-           libspamc_log(tp->flags, DEBUG_LEVEL,
-                  "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
 #endif
-           *sockptr = mysock;
+                  *sockptr = mysock;
 
 
-           return EX_OK;
-       }
+                  return EX_OK;
+            }
+#ifdef SPAMC_HAS_ADDRINFO
+            res = res->ai_next;
+        }
+#endif
+        sleep(retry_sleep);
+    } /* for(numloops...) */
+
+#ifdef SPAMC_HAS_ADDRINFO
+    for(numloops=0;numloops<tp->nhosts;numloops++) {
+        freeaddrinfo(tp->hosts[numloops]);
     }
     }
+#endif
 
 
-    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",
+              connect_retries);
 
     return _translate_connect_errno(origerr);
 }
 
     return _translate_connect_errno(origerr);
 }
@@ -514,8 +659,9 @@ static int _message_read_raw(int fd, struct message *m)
 
 static int _message_read_bsmtp(int fd, struct message *m)
 {
 
 static int _message_read_bsmtp(int fd, struct message *m)
 {
-    unsigned int i, j;
+    unsigned int i, j, p_len;
     char prev;
     char prev;
+    char* p;
 
     _clear_message(m);
     if ((m->raw = malloc(m->max_len + 1)) == NULL)
 
     _clear_message(m);
     if ((m->raw = malloc(m->max_len + 1)) == NULL)
@@ -530,44 +676,52 @@ static int _message_read_bsmtp(int fd, struct message *m)
        return EX_IOERR;
     }
     m->type = MESSAGE_ERROR;
        return EX_IOERR;
     }
     m->type = MESSAGE_ERROR;
-    if (m->raw_len > m->max_len)
+    if (m->raw_len > (int) m->max_len)
        return EX_TOOBIG;
        return EX_TOOBIG;
-    m->pre = m->raw;
-    for (i = 0; i < m->raw_len - 6; i++) {
-       if ((m->raw[i] == '\n') &&
-           (m->raw[i + 1] == 'D' || m->raw[i + 1] == 'd') &&
-           (m->raw[i + 2] == 'A' || m->raw[i + 2] == 'a') &&
-           (m->raw[i + 3] == 'T' || m->raw[i + 3] == 't') &&
-           (m->raw[i + 4] == 'A' || m->raw[i + 4] == 'a') &&
-           ((m->raw[i + 5] == '\r' && m->raw[i + 6] == '\n')
-            || m->raw[i + 5] == '\n')) {
-           /* Found it! */
-           i += 6;
-           if (m->raw[i - 1] == '\r')
-               i++;
-           m->pre_len = i;
-           m->msg = m->raw + i;
-           m->msg_len = m->raw_len - i;
-           break;
+    p = m->pre = m->raw;
+    /* Search for \nDATA\n which marks start of actual message */
+    while ((p_len = (m->raw_len - (p - m->raw))) > 8) { /* leave room for at least \nDATA\n.\n */
+      char* q = memchr(p, '\n', p_len - 8);  /* find next \n then see if start of \nDATA\n */
+      if (q == NULL) break;
+      q++;
+      if (((q[0]|0x20) == 'd') && /* case-insensitive ASCII comparison */
+         ((q[1]|0x20) == 'a') &&
+         ((q[2]|0x20) == 't') &&
+         ((q[3]|0x20) == 'a')) {
+       q+=4;
+       if (q[0] == '\r') ++q;
+       if (*(q++) == '\n') {  /* leave q at start of message if we found it */
+         m->msg = q;
+         m->pre_len = q - m->raw;
+         m->msg_len = m->raw_len - m->pre_len;
+         break;
        }
        }
+      }
+      p = q; /* the above code ensures no other '\n' comes before q */
     }
     if (m->msg == NULL)
        return EX_DATAERR;
 
     }
     if (m->msg == NULL)
        return EX_DATAERR;
 
+    /* ensure this is >= 0 */
+    if (m->msg_len < 0) {
+       return EX_SOFTWARE;
+    }
+
     /* Find the end-of-DATA line */
     prev = '\n';
     /* Find the end-of-DATA line */
     prev = '\n';
-    for (i = j = 0; i < m->msg_len; i++) {
+    for (i = j = 0; i < (unsigned int) m->msg_len; i++) {
        if (prev == '\n' && m->msg[i] == '.') {
            /* Dot at the beginning of a line */
        if (prev == '\n' && m->msg[i] == '.') {
            /* Dot at the beginning of a line */
-           if ((m->msg[i + 1] == '\r' && m->msg[i + 2] == '\n')
-               || m->msg[i + 1] == '\n') {
+            if (((int) (i+1) == m->msg_len)
+                || ((int) (i+1) < m->msg_len && m->msg[i + 1] == '\n')
+                || ((int) (i+2) < m->msg_len && m->msg[i + 1] == '\r' && m->msg[i + 2] == '\n')) {
                /* Lone dot! That's all, folks */
                m->post = m->msg + i;
                m->post_len = m->msg_len - i;
                m->msg_len = j;
                break;
            }
                /* Lone dot! That's all, folks */
                m->post = m->msg + i;
                m->post_len = m->msg_len - i;
                m->msg_len = j;
                break;
            }
-           else if (m->msg[i + 1] == '.') {
+           else if ((int) (i+1) < m->msg_len && m->msg[i + 1] == '.') {
                /* Escaping dot, eliminate. */
                prev = '.';
                continue;
                /* Escaping dot, eliminate. */
                prev = '.';
                continue;
@@ -577,6 +731,9 @@ static int _message_read_bsmtp(int fd, struct message *m)
        m->msg[j++] = m->msg[i];
     }
 
        m->msg[j++] = m->msg[i];
     }
 
+    /* if bad format with no end "\n.\n", error out */
+    if (m->post == NULL)
+       return EX_DATAERR;
     m->type = MESSAGE_BSMTP;
     m->out = m->msg;
     m->out_len = m->msg_len;
     m->type = MESSAGE_BSMTP;
     m->out = m->msg;
     m->out_len = m->msg_len;
@@ -596,6 +753,12 @@ int message_read(int fd, int flags, struct message *m)
        return EX_OSERR;
     }
     m->priv->flags = flags;
        return EX_OSERR;
     }
     m->priv->flags = flags;
+    m->priv->alloced_size = 0;
+
+    if (flags & SPAMC_PING) {
+      _clear_message(m);
+      return EX_OK;
+    }
 
     switch (flags & SPAMC_MODE_MASK) {
     case SPAMC_RAW_MODE:
 
     switch (flags & SPAMC_MODE_MASK) {
     case SPAMC_RAW_MODE:
@@ -620,7 +783,7 @@ long message_write(int fd, struct message *m)
 
     assert(m != NULL);
 
 
     assert(m != NULL);
 
-    if (m->priv->flags & SPAMC_CHECK_ONLY) {
+    if (m->priv->flags & (SPAMC_CHECK_ONLY|SPAMC_PING)) {
        if (m->is_spam == EX_ISSPAM || m->is_spam == EX_NOTSPAM) {
            return full_write(fd, 1, m->out, m->out_len);
 
        if (m->is_spam == EX_ISSPAM || m->is_spam == EX_NOTSPAM) {
            return full_write(fd, 1, m->out, m->out_len);
 
@@ -849,7 +1012,7 @@ _handle_spamd_header(struct message *m, int flags, char *buf, int len,
        }
        return EX_OK;
     }
        }
        return EX_OK;
     }
-    else if (sscanf(buf, "DidSet: %s", didset_ret) == 1) {
+    else if (sscanf(buf, "DidSet: %14s", didset_ret) == 1) {
       if (strstr(didset_ret, "local")) {
          *didtellflags |= SPAMC_SET_LOCAL;
        }
       if (strstr(didset_ret, "local")) {
          *didtellflags |= SPAMC_SET_LOCAL;
        }
@@ -857,7 +1020,7 @@ _handle_spamd_header(struct message *m, int flags, char *buf, int len,
          *didtellflags |= SPAMC_SET_REMOTE;
        }
     }
          *didtellflags |= SPAMC_SET_REMOTE;
        }
     }
-    else if (sscanf(buf, "DidRemove: %s", didremove_ret) == 1) {
+    else if (sscanf(buf, "DidRemove: %14s", didremove_ret) == 1) {
         if (strstr(didremove_ret, "local")) {
          *didtellflags |= SPAMC_REMOVE_LOCAL;
        }
         if (strstr(didremove_ret, "local")) {
          *didtellflags |= SPAMC_REMOVE_LOCAL;
        }
@@ -869,6 +1032,112 @@ _handle_spamd_header(struct message *m, int flags, char *buf, int len,
     return EX_OK;
 }
 
     return EX_OK;
 }
 
+static int
+_zlib_compress (char *m_msg, int m_msg_len,
+        unsigned char **zlib_buf, int *zlib_bufsiz, int flags)
+{
+    int rc;
+    int len, totallen;
+
+#ifndef HAVE_LIBZ
+
+    UNUSED_VARIABLE(rc);
+    UNUSED_VARIABLE(len);
+    UNUSED_VARIABLE(totallen);
+    libspamc_log(flags, LOG_ERR, "spamc not built with zlib support");
+    return EX_SOFTWARE;
+
+#else
+    z_stream strm;
+
+    UNUSED_VARIABLE(flags);
+
+    /* worst-case, according to http://www.zlib.org/zlib_tech.html ;
+      * same as input, plus 5 bytes per 16k, plus 6 bytes.  this should
+      * be plenty */
+    *zlib_bufsiz = (int) (m_msg_len * 1.0005) + 1024;
+    *zlib_buf = (unsigned char *) malloc (*zlib_bufsiz);
+    if (*zlib_buf == NULL) {
+        return EX_OSERR;
+    }
+
+    strm.zalloc = Z_NULL;
+    strm.zfree = Z_NULL;
+    strm.opaque = Z_NULL;
+    rc = deflateInit(&strm, 3);
+    if (rc != Z_OK) {
+        return EX_OSERR;
+    }
+
+    strm.avail_in = m_msg_len;
+    strm.next_in = (unsigned char *) m_msg;
+    strm.avail_out = *zlib_bufsiz;
+    strm.next_out = (unsigned char *) *zlib_buf;
+
+    totallen = 0;
+    do {
+        rc = deflate(&strm, Z_FINISH);
+        assert(rc != Z_STREAM_ERROR);
+        len = (size_t) (*zlib_bufsiz - strm.avail_out);
+        strm.next_out += len;
+        totallen += len;
+    } while (strm.avail_out == 0);
+
+    *zlib_bufsiz = totallen;
+    return EX_OK;
+
+#endif
+}
+
+int
+_append_original_body (struct message *m, int flags)
+{
+    char *cp, *cpend, *bodystart;
+    int bodylen, outspaceleft, towrite;
+
+    /* at this stage, m->out now contains the rewritten headers.
+     * find and append the raw message's body, up to m->priv->alloced_size
+     * bytes.
+     */
+
+#define CRNLCRNL        "\r\n\r\n"
+#define CRNLCRNL_LEN    4
+#define NLNL            "\n\n"
+#define NLNL_LEN        2
+
+    cpend = m->raw + m->raw_len;
+    bodystart = NULL;
+
+    for (cp = m->raw; cp < cpend; cp++) {
+        if (*cp == '\r' && cpend - cp >= CRNLCRNL_LEN && 
+                            !strncmp(cp, CRNLCRNL, CRNLCRNL_LEN))
+        {
+            bodystart = cp + CRNLCRNL_LEN;
+            break;
+        }
+        else if (*cp == '\n' && cpend - cp >= NLNL_LEN && 
+                           !strncmp(cp, NLNL, NLNL_LEN))
+        {
+            bodystart = cp + NLNL_LEN;
+            break;
+        }
+    }
+
+    if (bodystart == NULL) {
+        libspamc_log(flags, LOG_ERR, "failed to find end-of-headers");
+        return EX_SOFTWARE;
+    }
+
+    bodylen = cpend - bodystart;
+    outspaceleft = (m->priv->alloced_size-1) - m->out_len;
+    towrite = (bodylen < outspaceleft ? bodylen : outspaceleft);
+
+    /* copy in the body; careful not to overflow */
+    strncpy (m->out + m->out_len, bodystart, towrite);
+    m->out_len += towrite;
+    return EX_OK;
+}
+
 int message_filter(struct transport *tp, const char *username,
                    int flags, struct message *m)
 {
 int message_filter(struct transport *tp, const char *username,
                    int flags, struct message *m)
 {
@@ -885,14 +1154,31 @@ int message_filter(struct transport *tp, const char *username,
     SSL_CTX *ctx = NULL;
     SSL *ssl = NULL;
     SSL_METHOD *meth;
     SSL_CTX *ctx = NULL;
     SSL *ssl = NULL;
     SSL_METHOD *meth;
+    char zlib_on = 0;
+    unsigned char *zlib_buf = NULL;
+    int zlib_bufsiz = 0;
+    unsigned char *towrite_buf;
+    int towrite_len;
 
     assert(tp != NULL);
     assert(m != NULL);
 
 
     assert(tp != NULL);
     assert(m != NULL);
 
+    if ((flags & SPAMC_USE_ZLIB) != 0) {
+      zlib_on = 1;
+    }
+
     if (flags & SPAMC_USE_SSL) {
 #ifdef SPAMC_SSL
        SSLeay_add_ssl_algorithms();
     if (flags & SPAMC_USE_SSL) {
 #ifdef SPAMC_SSL
        SSLeay_add_ssl_algorithms();
-       meth = SSLv2_client_method();
+       if ((flags & SPAMC_SSLV2) && (flags & SPAMC_SSLV3)) {
+         meth = TLSv1_client_method(); /* both flag bits on means use TLSv1 */
+       } else if (flags & SPAMC_SSLV2) {
+         meth = SSLv2_client_method();
+       } else if (flags & SPAMC_SSLV3) {
+         meth = SSLv3_client_method();
+       } else {
+         meth = SSLv23_client_method(); /* no flag bits, default SSLv23 */
+       }
        SSL_load_error_strings();
        ctx = SSL_CTX_new(meth);
 #else
        SSL_load_error_strings();
        ctx = SSL_CTX_new(meth);
 #else
@@ -905,7 +1191,8 @@ int message_filter(struct transport *tp, const char *username,
     }
 
     m->is_spam = EX_TOOBIG;
     }
 
     m->is_spam = EX_TOOBIG;
-    if ((m->outbuf = malloc(m->max_len + EXPANSION_ALLOWANCE + 1)) == NULL) {
+    m->priv->alloced_size = m->max_len + EXPANSION_ALLOWANCE + 1;
+    if ((m->outbuf = malloc(m->priv->alloced_size)) == NULL) {
        failureval = EX_OSERR;
        goto failure;
     }
        failureval = EX_OSERR;
        goto failure;
     }
@@ -921,6 +1208,10 @@ int message_filter(struct transport *tp, const char *username,
        strcpy(buf, "REPORT ");
     else if (flags & SPAMC_SYMBOLS)
        strcpy(buf, "SYMBOLS ");
        strcpy(buf, "REPORT ");
     else if (flags & SPAMC_SYMBOLS)
        strcpy(buf, "SYMBOLS ");
+    else if (flags & SPAMC_PING)
+       strcpy(buf, "PING ");
+    else if (flags & SPAMC_HEADERS)
+       strcpy(buf, "HEADERS ");
     else
        strcpy(buf, "PROCESS ");
 
     else
        strcpy(buf, "PROCESS ");
 
@@ -934,21 +1225,37 @@ int message_filter(struct transport *tp, const char *username,
     strcat(buf, "\r\n");
     len = strlen(buf);
 
     strcat(buf, "\r\n");
     len = strlen(buf);
 
-    if (username != NULL) {
+    towrite_buf = (unsigned char *) m->msg;
+    towrite_len = (int) m->msg_len;
+    if (zlib_on) {
+        if (_zlib_compress(m->msg, m->msg_len, &zlib_buf, &zlib_bufsiz, flags) != EX_OK)
+        {
+            return EX_OSERR;
+        }
+        towrite_buf = zlib_buf;
+        towrite_len = zlib_bufsiz;
+    }
+
+    if (!(flags & SPAMC_PING)) {
+      if (username != NULL) {
        if (strlen(username) + 8 >= (bufsiz - len)) {
        if (strlen(username) + 8 >= (bufsiz - len)) {
-           _use_msg_for_out(m);
-           return EX_OSERR;
+          _use_msg_for_out(m);
+          return EX_OSERR;
        }
        strcpy(buf + len, "User: ");
        strcat(buf + len, username);
        strcat(buf + len, "\r\n");
        len += strlen(buf + len);
        }
        strcpy(buf + len, "User: ");
        strcat(buf + len, username);
        strcat(buf + len, "\r\n");
        len += strlen(buf + len);
-    }
-    if ((m->msg_len > 9999999) || ((len + 27) >= (bufsiz - len))) {
+      }
+      if (zlib_on) {
+       len += snprintf(buf + len, 8192-len, "Compress: zlib\r\n");
+      }
+      if ((m->msg_len > SPAMC_MAX_MESSAGE_LEN) || ((len + 27) >= (bufsiz - len))) {
        _use_msg_for_out(m);
        _use_msg_for_out(m);
-       return EX_OSERR;
+       return EX_DATAERR;
+      }
+      len += snprintf(buf + len, 8192-len, "Content-length: %d\r\n\r\n", (int) towrite_len);
     }
     }
-    len += sprintf(buf + len, "Content-length: %d\r\n\r\n", m->msg_len);
 
     libspamc_timeout = m->timeout;
 
 
     libspamc_timeout = m->timeout;
 
@@ -974,12 +1281,12 @@ int message_filter(struct transport *tp, const char *username,
     if (flags & SPAMC_USE_SSL) {
 #ifdef SPAMC_SSL
        SSL_write(ssl, buf, len);
     if (flags & SPAMC_USE_SSL) {
 #ifdef SPAMC_SSL
        SSL_write(ssl, buf, len);
-       SSL_write(ssl, m->msg, m->msg_len);
+       SSL_write(ssl, towrite_buf, towrite_len);
 #endif
     }
     else {
        full_write(sock, 0, buf, len);
 #endif
     }
     else {
        full_write(sock, 0, buf, len);
-       full_write(sock, 0, m->msg, m->msg_len);
+       full_write(sock, 0, towrite_buf, towrite_len);
        shutdown(sock, SHUT_WR);
     }
 
        shutdown(sock, SHUT_WR);
     }
 
@@ -1005,6 +1312,14 @@ int message_filter(struct transport *tp, const char *username,
        goto failure;
     }
 
        goto failure;
     }
 
+    if (flags & SPAMC_PING) {
+       closesocket(sock);
+       sock = -1;
+        m->out_len = sprintf(m->out, "SPAMD/%s %d\n", versbuf, response);
+        m->is_spam = EX_NOTSPAM;
+        return EX_OK;
+    }
+
     m->score = 0;
     m->threshold = 0;
     m->is_spam = EX_TOOBIG;
     m->score = 0;
     m->threshold = 0;
     m->is_spam = EX_TOOBIG;
@@ -1054,20 +1369,17 @@ int message_filter(struct transport *tp, const char *username,
 
        if (flags & SPAMC_USE_SSL) {
            len = full_read_ssl(ssl, (unsigned char *) m->out + m->out_len,
 
        if (flags & SPAMC_USE_SSL) {
            len = full_read_ssl(ssl, (unsigned char *) m->out + m->out_len,
-                               m->max_len + EXPANSION_ALLOWANCE + 1 -
-                               m->out_len,
-                               m->max_len + EXPANSION_ALLOWANCE + 1 -
-                               m->out_len);
+                               m->priv->alloced_size - m->out_len,
+                               m->priv->alloced_size - m->out_len);
        }
        else {
            len = full_read(sock, 0, m->out + m->out_len,
        }
        else {
            len = full_read(sock, 0, m->out + m->out_len,
-                           m->max_len + EXPANSION_ALLOWANCE + 1 - m->out_len,
-                           m->max_len + EXPANSION_ALLOWANCE + 1 -
-                           m->out_len);
+                           m->priv->alloced_size - m->out_len,
+                           m->priv->alloced_size - m->out_len);
        }
 
 
        }
 
 
-       if (len + m->out_len > m->max_len + EXPANSION_ALLOWANCE) {
+       if (len + m->out_len > (m->priv->alloced_size-1)) {
            failureval = EX_TOOBIG;
            goto failure;
        }
            failureval = EX_TOOBIG;
            goto failure;
        }
@@ -1087,6 +1399,12 @@ int message_filter(struct transport *tp, const char *username,
        goto failure;
     }
 
        goto failure;
     }
 
+    if (flags & SPAMC_HEADERS) {
+        if (_append_original_body(m, flags) != EX_OK) {
+            goto failure;
+        }
+    }
+
     return EX_OK;
 
   failure:
     return EX_OK;
 
   failure:
@@ -1105,7 +1423,6 @@ int message_filter(struct transport *tp, const char *username,
     return failureval;
 }
 
     return failureval;
 }
 
-
 int message_process(struct transport *trans, char *username, int max_size,
                    int in_fd, int out_fd, const int flags)
 {
 int message_process(struct transport *trans, char *username, int max_size,
                    int in_fd, int out_fd, const int flags)
 {
@@ -1116,7 +1433,13 @@ int message_process(struct transport *trans, char *username, int max_size,
 
     m.type = MESSAGE_NONE;
 
 
     m.type = MESSAGE_NONE;
 
-    m.max_len = max_size;
+    /* enforce max_size being unsigned, therefore >= 0 */
+    if (max_size < 0) {
+       ret = EX_SOFTWARE;
+        goto FAIL;
+    }
+    m.max_len = (unsigned int) max_size;
+
     ret = message_read(in_fd, flags, &m);
     if (ret != EX_OK)
         goto FAIL;
     ret = message_read(in_fd, flags, &m);
     if (ret != EX_OK)
         goto FAIL;
@@ -1181,7 +1504,8 @@ int message_tell(struct transport *tp, const char *username, int flags,
     }
 
     m->is_spam = EX_TOOBIG;
     }
 
     m->is_spam = EX_TOOBIG;
-    if ((m->outbuf = malloc(m->max_len + EXPANSION_ALLOWANCE + 1)) == NULL) {
+    m->priv->alloced_size = m->max_len + EXPANSION_ALLOWANCE + 1;
+    if ((m->outbuf = malloc(m->priv->alloced_size)) == NULL) {
        failureval = EX_OSERR;
        goto failure;
     }
        failureval = EX_OSERR;
        goto failure;
     }
@@ -1256,11 +1580,11 @@ int message_tell(struct transport *tp, const char *username, int flags,
        strcat(buf + len, "\r\n");
        len += strlen(buf + len);
     }
        strcat(buf + len, "\r\n");
        len += strlen(buf + len);
     }
-    if ((m->msg_len > 9999999) || ((len + 27) >= (bufsiz - len))) {
+    if ((m->msg_len > SPAMC_MAX_MESSAGE_LEN) || ((len + 27) >= (bufsiz - len))) {
        _use_msg_for_out(m);
        _use_msg_for_out(m);
-       return EX_OSERR;
+       return EX_DATAERR;
     }
     }
-    len += sprintf(buf + len, "Content-length: %d\r\n\r\n", m->msg_len);
+    len += sprintf(buf + len, "Content-length: %d\r\n\r\n", (int) m->msg_len);
 
     libspamc_timeout = m->timeout;
 
 
     libspamc_timeout = m->timeout;
 
@@ -1410,6 +1734,7 @@ void transport_init(struct transport *tp)
     tp->type = TRANSPORT_LOCALHOST;
     tp->port = 783;
     tp->flags = 0;
     tp->type = TRANSPORT_LOCALHOST;
     tp->port = 783;
     tp->flags = 0;
+    tp->retry_sleep = -1;
 }
 
 /*
 }
 
 /*
@@ -1424,6 +1749,12 @@ void transport_init(struct transport *tp)
 
 static void _randomize_hosts(struct transport *tp)
 {
 
 static void _randomize_hosts(struct transport *tp)
 {
+#ifdef SPAMC_HAS_ADDRINFO
+    struct addrinfo *tmp;
+#else
+    struct in_addr tmp;
+#endif
+    int i;
     int rnum;
 
     assert(tp != 0);
     int rnum;
 
     assert(tp != 0);
@@ -1434,9 +1765,9 @@ static void _randomize_hosts(struct transport *tp)
     rnum = rand() % tp->nhosts;
 
     while (rnum-- > 0) {
     rnum = rand() % tp->nhosts;
 
     while (rnum-- > 0) {
-        struct in_addr tmp = tp->hosts[0];
-        int i;
+        tmp = tp->hosts[0];
 
 
+        /* TODO: free using freeaddrinfo() */
         for (i = 1; i < tp->nhosts; i++)
             tp->hosts[i - 1] = tp->hosts[i];
 
         for (i = 1; i < tp->nhosts; i++)
             tp->hosts[i - 1] = tp->hosts[i];
 
@@ -1461,13 +1792,19 @@ static void _randomize_hosts(struct transport *tp)
 */
 int transport_setup(struct transport *tp, int flags)
 {
 */
 int transport_setup(struct transport *tp, int flags)
 {
+#ifdef SPAMC_HAS_ADDRINFO
+    struct addrinfo hints, *res, *addrp; 
+    char port[6];
+#else
     struct hostent *hp;
     struct hostent *hp;
+    char **addrp;
+#endif
     char *hostlist, *hostname;
     int errbits;
     char *hostlist, *hostname;
     int errbits;
-    char **addrp;
+    int origerr;
 
 #ifdef _WIN32
 
 #ifdef _WIN32
-    // Start Winsock up
+    /* Start Winsock up */
     WSADATA wsaData;
     int nCode;
     if ((nCode = WSAStartup(MAKEWORD(1, 1), &wsaData)) != 0) {
     WSADATA wsaData;
     int nCode;
     if ((nCode = WSAStartup(MAKEWORD(1, 1), &wsaData)) != 0) {
@@ -1480,6 +1817,15 @@ int transport_setup(struct transport *tp, int flags)
     assert(tp != NULL);
     tp->flags = flags;
 
     assert(tp != NULL);
     tp->flags = flags;
 
+#ifdef SPAMC_HAS_ADDRINFO
+    snprintf(port, 6, "%d", tp->port);
+
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_flags = 0;
+    hints.ai_family = PF_UNSPEC;
+    hints.ai_socktype = SOCK_STREAM;
+#endif
+
     switch (tp->type) {
 #ifndef _WIN32
     case TRANSPORT_UNIX:
     switch (tp->type) {
 #ifndef _WIN32
     case TRANSPORT_UNIX:
@@ -1487,7 +1833,21 @@ int transport_setup(struct transport *tp, int flags)
         return EX_OK;
 #endif
     case TRANSPORT_LOCALHOST:
         return EX_OK;
 #endif
     case TRANSPORT_LOCALHOST:
+#ifdef SPAMC_HAS_ADDRINFO
+        /* getaddrinfo(NULL) will look up the loopback address.
+         * bug 5057: unfortunately, it's the IPv6 loopback address on
+         * linux!  Be explicit, and force IPv4 using "127.0.0.1".
+         */
+        if ((origerr = getaddrinfo("127.0.0.1", port, &hints, &res)) != 0) {
+            libspamc_log(flags, LOG_ERR, 
+                  "getaddrinfo(127.0.0.1) failed: %s",
+                  gai_strerror(origerr));
+            return EX_OSERR;
+        }
+        tp->hosts[0] = res;
+#else
         tp->hosts[0].s_addr = inet_addr("127.0.0.1");
         tp->hosts[0].s_addr = inet_addr("127.0.0.1");
+#endif
         tp->nhosts = 1;
         return EX_OK;
 
         tp->nhosts = 1;
         return EX_OK;
 
@@ -1518,7 +1878,42 @@ int transport_setup(struct transport *tp, int flags)
             if (hostend != NULL) {
                 *hostend = '\0';
             }
             if (hostend != NULL) {
                 *hostend = '\0';
             }
-            
+#ifdef SPAMC_HAS_ADDRINFO            
+            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 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*/
+/* work around Cygwin IPv6 patch - err codes not defined in Windows aren't in patch */
+#ifdef HAVE_EAI_ADDRFAMILY
+                case EAI_ADDRFAMILY: /*no addresses in requested family*/
+#endif
+#ifdef HAVE_EAI_SYSTEM
+                case EAI_SYSTEM: /*system error, check errno*/
+#endif
+#ifdef HAVE_EAI_NODATA
+                case EAI_NODATA: /*address exists, but no data*/
+#endif
+                case EAI_MEMORY: /*out of memory*/
+                case EAI_FAIL: /*name server returned permanent error*/
+                    errbits |= 2;
+                    break;
+                default:
+                    /* should not happen, all errors are checked above */
+                    free(hostlist);
+                    return EX_OSERR;
+                }
+                goto nexthost; /* try next host in list */
+            }
+#else
             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",
             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",
@@ -1539,15 +1934,18 @@ int transport_setup(struct transport *tp, int flags)
                 }
                 goto nexthost; /* try next host in list */
             }
                 }
                 goto nexthost; /* try next host in list */
             }
+#endif
             
             
-            /* 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 we have no hosts at all */
+#ifdef SPAMC_HAS_ADDRINFO
+            if(res == NULL)
+#else
             if (hp->h_addr_list[0] == NULL
              || hp->h_length != sizeof tp->hosts[0]
             if (hp->h_addr_list[0] == NULL
              || hp->h_length != sizeof tp->hosts[0]
-             || hp->h_addrtype != AF_INET) {
+             || hp->h_addrtype != AF_INET)
                 /* no hosts/bad size/wrong family */
                 /* no hosts/bad size/wrong family */
+#endif
+            {
                 errbits |= 1;
                 goto nexthost; /* try next host in list */
             }
                 errbits |= 1;
                 goto nexthost; /* try next host in list */
             }
@@ -1557,6 +1955,20 @@ int transport_setup(struct transport *tp, int flags)
              * means we won't ever walk all over the list with other
              * calls.
              */
              * means we won't ever walk all over the list with other
              * calls.
              */
+#ifdef SPAMC_HAS_ADDRINFO
+            if(tp->nhosts == TRANSPORT_MAX_HOSTS) {
+               libspamc_log(flags, LOG_NOTICE, 
+                     "hit limit of %d hosts, ignoring remainder",
+                     TRANSPORT_MAX_HOSTS);
+               break;
+            }
+            for (addrp = res; addrp != NULL; ) {
+                tp->hosts[tp->nhosts] = addrp;
+                addrp = addrp->ai_next;     /* before NULLing ai_next */
+                tp->hosts[tp->nhosts]->ai_next = NULL;
+                tp->nhosts++;
+            }
+#else
             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",
             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",
@@ -1566,7 +1978,7 @@ int transport_setup(struct transport *tp, int flags)
                 memcpy(&tp->hosts[tp->nhosts], *addrp, hp->h_length);
                 tp->nhosts++;
             }
                 memcpy(&tp->hosts[tp->nhosts], *addrp, hp->h_length);
                 tp->nhosts++;
             }
-            
+#endif            
 nexthost:
             hostname = hostend;
         } while (hostname != NULL);
 nexthost:
             hostname = hostend;
         } while (hostname != NULL);
@@ -1627,11 +2039,11 @@ libspamc_log (int flags, int level, char *msg, ...)
     va_start(ap, msg);
 
     if ((flags & SPAMC_LOG_TO_STDERR) != 0) {
     va_start(ap, msg);
 
     if ((flags & SPAMC_LOG_TO_STDERR) != 0) {
-        // create a log-line buffer
+        /* create a log-line buffer */
         len = snprintf(buf, LOG_BUFSIZ, "spamc: ");
         len += vsnprintf(buf+len, LOG_BUFSIZ-len, msg, ap);
 
         len = snprintf(buf, LOG_BUFSIZ, "spamc: ");
         len += vsnprintf(buf+len, LOG_BUFSIZ-len, msg, ap);
 
-        // avoid buffer overflow
+        /* avoid buffer overflow */
         if (len > (LOG_BUFSIZ-2)) { len = (LOG_BUFSIZ-3); }
 
         len += snprintf(buf+len, LOG_BUFSIZ-len, "\n");
         if (len > (LOG_BUFSIZ-2)) { len = (LOG_BUFSIZ-3); }
 
         len += snprintf(buf+len, LOG_BUFSIZ-len, "\n");
index be3d0fd..e26cf6f 100644 (file)
@@ -1,9 +1,10 @@
 /* <@LICENSE>
 /* <@LICENSE>
- * Copyright 2004 Apache Software Foundation
- * 
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at:
  * 
  *     http://www.apache.org/licenses/LICENSE-2.0
  * 
  * 
  *     http://www.apache.org/licenses/LICENSE-2.0
  * 
  4115 named type definition in parentheses
  4127 conditional expression is constant
  4514 unreferenced inline function removed
  4115 named type definition in parentheses
  4127 conditional expression is constant
  4514 unreferenced inline function removed
+ 4996 deprecated "unsafe" functions (bug 4855)
  */
 #pragma warning( disable : 4115 4127 4514 )
  */
 #pragma warning( disable : 4115 4127 4514 )
+#if (_MSC_VER >= 1400)  /* VC8+ */
+#pragma warning( disable : 4996 )
+#endif
+
 #endif
 #include <winsock.h>
 #else
 #include <netdb.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #endif
 #include <winsock.h>
 #else
 #include <netdb.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
+/* some platforms (Cygwin) don't implement getaddrinfo */
+#ifdef EAI_AGAIN
+#define SPAMC_HAS_ADDRINFO 1
+#endif
 #endif
 
 #endif
 
-#ifdef _WIN32
+#if (defined(_WIN32) || !defined(_SYSEXITS_H))
 /* FIXME: This stuff has to go somewhere else */
 
 #define EX_OK           0
 /* FIXME: This stuff has to go somewhere else */
 
 #define EX_OK           0
 /* May 5, 2005 NP: added list reporting support */
 #define SPAMC_REPORT_MSG      (1<<20)
 
 /* May 5, 2005 NP: added list reporting support */
 #define SPAMC_REPORT_MSG      (1<<20)
 
+/* Oct 21, 2005 sidney: added ping test */
+#define SPAMC_PING      (1<<19)
+
+/* Jan 1, 2007 sidney: added SSL protocol versions */
+/* no flags means use default of SSL_v23 */
+/* Set both flags to specify TSL_v1 */
+#define SPAMC_SSLV2 (1<<18)
+#define SPAMC_SSLV3 (1<<17)
+
+/* Nov 30, 2006 jm: add -z, zlib support */
+#define SPAMC_USE_ZLIB        (1<<16)
+
+/* Jan 16, 2007 jm: get markup headers from spamd */
+#define SPAMC_HEADERS         (1<<15)
 
 #define SPAMC_MESSAGE_CLASS_SPAM 1
 #define SPAMC_MESSAGE_CLASS_HAM  2
 
 #define SPAMC_MESSAGE_CLASS_SPAM 1
 #define SPAMC_MESSAGE_CLASS_HAM  2
 #define SPAMC_REMOVE_LOCAL       4
 #define SPAMC_REMOVE_REMOTE      8
 
 #define SPAMC_REMOVE_LOCAL       4
 #define SPAMC_REMOVE_REMOTE      8
 
+#define SPAMC_MAX_MESSAGE_LEN     (256 * 1024 * 1024)     /* see bug 4928 */
+
 /* Aug 14, 2002 bj: A struct for storing a message-in-progress */
 typedef enum
 {
 /* Aug 14, 2002 bj: A struct for storing a message-in-progress */
 typedef enum
 {
@@ -134,11 +160,12 @@ struct message
     /* Filled in by message_read */
     message_type_t type;
     char *raw;
     /* Filled in by message_read */
     message_type_t type;
     char *raw;
-    unsigned int raw_len;              /* Raw message buffer */
+    int raw_len;               /* Raw message buffer */
+    /* note: do not make "raw_len" in particular unsigned! see bug 4593 */
     char *pre;
     int pre_len;               /* Pre-message data (e.g. SMTP commands) */
     char *msg;
     char *pre;
     int pre_len;               /* Pre-message data (e.g. SMTP commands) */
     char *msg;
-    unsigned int msg_len;              /* The message */
+    int msg_len;               /* The message */
     char *post;
     int post_len;              /* Post-message data (e.g. SMTP commands) */
     int content_length;
     char *post;
     int post_len;              /* Post-message data (e.g. SMTP commands) */
     int content_length;
@@ -195,11 +222,23 @@ struct transport
 
     unsigned short port;       /* for TCP sockets              */
 
 
     unsigned short port;       /* for TCP sockets              */
 
+#ifdef SPAMC_HAS_ADDRINFO
+    struct addrinfo *hosts[TRANSPORT_MAX_HOSTS];
+#else
     struct in_addr hosts[TRANSPORT_MAX_HOSTS];
     struct in_addr hosts[TRANSPORT_MAX_HOSTS];
+#endif
     int nhosts;
     int flags;
     int nhosts;
     int flags;
+
+    /* added in SpamAssassin 3.2.0 */
+    int connect_retries;
+    int retry_sleep;
 };
 
 };
 
+/* Initialise and setup transport-specific context for the connection
+ * to spamd.  Note that this may leak a small amount of string data for
+ * the remote hostname (bug 5531) if called repeatedly; SpamAssassin
+ * 3.3.0 will include a new API to free this leakage. */
 extern void transport_init(struct transport *tp);
 extern int transport_setup(struct transport *tp, int flags);
 
 extern void transport_init(struct transport *tp);
 extern int transport_setup(struct transport *tp, int flags);
 
diff --git a/utils.c b/utils.c
index f42c961..d69751f 100644 (file)
--- a/utils.c
+++ b/utils.c
@@ -1,9 +1,10 @@
 /* <@LICENSE>
 /* <@LICENSE>
- * Copyright 2004 Apache Software Foundation
- * 
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at:
  * 
  *     http://www.apache.org/licenses/LICENSE-2.0
  * 
  * 
  *     http://www.apache.org/licenses/LICENSE-2.0
  * 
@@ -67,6 +68,33 @@ static void catch_alrm(int x)
 }
 #endif
 
 }
 #endif
 
+int timeout_connect (int sockfd, const struct sockaddr *serv_addr, size_t addrlen)
+{
+    int ret;
+
+#ifndef _WIN32
+    sigfunc* sig;
+
+    sig = sig_catch(SIGALRM, catch_alrm);
+    if (libspamc_timeout > 0) {
+      alarm(libspamc_timeout);
+    }
+#endif
+
+    ret = connect(sockfd, serv_addr, addrlen);
+
+#ifndef _WIN32
+    if (libspamc_timeout > 0) {
+      alarm(0);
+    }
+  
+    /* restore old signal handler */
+    sig_catch(SIGALRM, sig);
+#endif
+  
+    return ret;
+}
+
 int fd_timeout_read(int fd, char fdflag, void *buf, size_t nbytes)
 {
     int nred;
 int fd_timeout_read(int fd, char fdflag, void *buf, size_t nbytes)
 {
     int nred;
diff --git a/utils.h b/utils.h
index 3a28754..7262d9a 100644 (file)
--- a/utils.h
+++ b/utils.h
@@ -1,9 +1,10 @@
 /* <@LICENSE>
 /* <@LICENSE>
- * Copyright 2004 Apache Software Foundation
- * 
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at:
  * 
  *     http://www.apache.org/licenses/LICENSE-2.0
  * 
  * 
  *     http://www.apache.org/licenses/LICENSE-2.0
  * 
@@ -20,6 +21,8 @@
 
 #define UNUSED_VARIABLE(v)     ((void)(v))
 
 
 #define UNUSED_VARIABLE(v)     ((void)(v))
 
+#include <stddef.h>
+
 extern int libspamc_timeout;   /* default timeout in seconds */
 
 #ifdef SPAMC_SSL
 extern int libspamc_timeout;   /* default timeout in seconds */
 
 #ifdef SPAMC_SSL
@@ -35,9 +38,9 @@ typedef int SSL_METHOD;
 
 #ifdef _WIN32
 #include <winsock.h>
 
 #ifdef _WIN32
 #include <winsock.h>
-//
-// BSD-compatible socket error codes for Win32
-//
+/*
+ * BSD-compatible socket error codes for Win32
+ */
 
 #define EWOULDBLOCK             WSAEWOULDBLOCK
 #define EINPROGRESS             WSAEINPROGRESS
 
 #define EWOULDBLOCK             WSAEWOULDBLOCK
 #define EINPROGRESS             WSAEINPROGRESS
@@ -67,17 +70,17 @@ typedef int SSL_METHOD;
 #define ETIMEDOUT               WSAETIMEDOUT
 #define ECONNREFUSED            WSAECONNREFUSED
 #define ELOOP                   WSAELOOP
 #define ETIMEDOUT               WSAETIMEDOUT
 #define ECONNREFUSED            WSAECONNREFUSED
 #define ELOOP                   WSAELOOP
-// #define ENAMETOOLONG            WSAENAMETOOLONG
+/* #define ENAMETOOLONG            WSAENAMETOOLONG */
 #define EHOSTDOWN               WSAEHOSTDOWN
 #define EHOSTUNREACH            WSAEHOSTUNREACH
 #define EHOSTDOWN               WSAEHOSTDOWN
 #define EHOSTUNREACH            WSAEHOSTUNREACH
-// #define ENOTEMPTY               WSAENOTEMPTY
+/* #define ENOTEMPTY               WSAENOTEMPTY */
 #define EPROCLIM                WSAEPROCLIM
 #define EUSERS                  WSAEUSERS
 #define EDQUOT                  WSAEDQUOT
 #define ESTALE                  WSAESTALE
 #define EREMOTE                 WSAEREMOTE
 
 #define EPROCLIM                WSAEPROCLIM
 #define EUSERS                  WSAEUSERS
 #define EDQUOT                  WSAEDQUOT
 #define ESTALE                  WSAESTALE
 #define EREMOTE                 WSAEREMOTE
 
-// NOTE: these are not errno constants in UNIX!
+/* NOTE: these are not errno constants in UNIX! */
 #define HOST_NOT_FOUND          WSAHOST_NOT_FOUND
 #define TRY_AGAIN               WSATRY_AGAIN
 #define NO_RECOVERY             WSANO_RECOVERY
 #define HOST_NOT_FOUND          WSAHOST_NOT_FOUND
 #define TRY_AGAIN               WSATRY_AGAIN
 #define NO_RECOVERY             WSANO_RECOVERY
@@ -88,6 +91,9 @@ typedef int SSL_METHOD;
 int fd_timeout_read(int fd, char fdflag, void *, size_t);
 int ssl_timeout_read(SSL * ssl, void *, int);
 
 int fd_timeout_read(int fd, char fdflag, void *, size_t);
 int ssl_timeout_read(SSL * ssl, void *, int);
 
+/* uses size_t instead of socket_t because socket_t not defined on some platforms */
+int timeout_connect (int sockfd, const struct sockaddr *serv_addr, size_t addrlen);
+
 /* these are fd-only, no SSL support */
 int full_read(int fd, char fdflag, void *buf, int min, int len);
 int full_read_ssl(SSL * ssl, unsigned char *buf, int min, int len);
 /* these are fd-only, no SSL support */
 int full_read(int fd, char fdflag, void *buf, int min, int len);
 int full_read_ssl(SSL * ssl, unsigned char *buf, int min, int len);