]> 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 b4d1936fc446aaccb84eb4b31f3c204e2b023e5c..1294e00ca7cf7d5add5494aa105d6abb5eda7a7c 100644 (file)
@@ -1,9 +1,10 @@
 /* <@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
  * 
  * </@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"
 #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 */
 #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
@@ -95,9 +108,25 @@ extern char *optarg;
 #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 */
@@ -110,7 +139,7 @@ static const int EXPANSION_ALLOWANCE = 16384;
  */
 
 /* 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
@@ -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() */
+    int alloced_size;           /* allocated space for the "out" buffer */
 };
 
 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.
  */
+#ifdef SPAMC_HAS_ADDRINFO
+static int _opensocket(int flags, struct addrinfo *res, int *psock)
+{
+#else
 static int _opensocket(int flags, int type, int *psock)
 {
-    const char *typename;
     int proto = 0;
-
+#endif
+    const char *typename;
+    int origerr;
 #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.
         */
+#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";
     }
@@ -191,30 +242,33 @@ static int _opensocket(int flags, int type, int *psock)
        typename = "PF_INET";
        proto = IPPROTO_TCP;
     }
+#endif
 
 #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
 
+#ifdef SPAMC_HAS_ADDRINFO
+    if ((*psock = socket(res->ai_family, res->ai_socktype, res->ai_protocol))
+#else
     if ((*psock = socket(type, SOCK_STREAM, proto))
+#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.
                 */
+       origerr = spamc_get_errno();
 #ifndef _WIN32
-       origerr = errno;        /* take a copy before syslog() */
        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
 
@@ -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)
     {
-        int origerrno;
 
-        origerrno = WSAGetLastError();
-        switch (origerrno)
+        origerr = spamc_get_errno();
+        switch (origerr)
         {
         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:
-            break;             /* ignored */
+            break;             /* ignored */
         }
     }
 #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) {
-           int origerrno;
-#ifndef _WIN32
-           origerr = errno;
-#else
-           origerrno = WSAGetLastError();
-#endif
+           origerr = spamc_get_errno();
            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;
+#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);
 
+#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 ((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,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
-    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
 
-    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
-       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;
@@ -377,78 +437,163 @@ static int _try_to_connect_tcp(const struct transport *tp, int *sockptr)
     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);
 
-#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
-       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
 
-       status =
-           connect(mysock, (struct sockaddr *) &addrbuf, sizeof(addrbuf));
+            if (status != 0) {
+                  origerr = spamc_get_errno();
+                  closesocket(mysock);
 
-       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));
+                  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
-           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
-           closesocket(mysock);
 
-           sleep(CONNECT_RETRY_SLEEP);
-       }
-       else {
+            } else {
 #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
-           *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);
 }
@@ -514,8 +659,9 @@ static int _message_read_raw(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* p;
 
     _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;
-    if (m->raw_len > m->max_len)
+    if (m->raw_len > (int) m->max_len)
        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;
 
+    /* ensure this is >= 0 */
+    if (m->msg_len < 0) {
+       return EX_SOFTWARE;
+    }
+
     /* 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 ((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;
            }
-           else if (m->msg[i + 1] == '.') {
+           else if ((int) (i+1) < m->msg_len && m->msg[i + 1] == '.') {
                /* 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];
     }
 
+    /* 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;
@@ -596,6 +753,12 @@ int message_read(int fd, int flags, struct message *m)
        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:
@@ -620,7 +783,7 @@ long message_write(int fd, struct message *m)
 
     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);
 
@@ -849,7 +1012,7 @@ _handle_spamd_header(struct message *m, int flags, char *buf, int len,
        }
        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;
        }
@@ -857,7 +1020,7 @@ _handle_spamd_header(struct message *m, int flags, char *buf, int len,
          *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;
        }
@@ -869,6 +1032,112 @@ _handle_spamd_header(struct message *m, int flags, char *buf, int len,
     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)
 {
@@ -885,14 +1154,31 @@ int message_filter(struct transport *tp, const char *username,
     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);
 
+    if ((flags & SPAMC_USE_ZLIB) != 0) {
+      zlib_on = 1;
+    }
+
     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
@@ -905,7 +1191,8 @@ int message_filter(struct transport *tp, const char *username,
     }
 
     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;
     }
@@ -921,6 +1208,10 @@ int message_filter(struct transport *tp, const char *username,
        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 ");
 
@@ -934,21 +1225,37 @@ int message_filter(struct transport *tp, const char *username,
     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)) {
-           _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);
-    }
-    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);
-       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;
 
@@ -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);
-       SSL_write(ssl, m->msg, m->msg_len);
+       SSL_write(ssl, towrite_buf, towrite_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);
     }
 
@@ -1005,6 +1312,14 @@ int message_filter(struct transport *tp, const char *username,
        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;
@@ -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,
-                               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,
-                           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;
        }
@@ -1087,6 +1399,12 @@ int message_filter(struct transport *tp, const char *username,
        goto failure;
     }
 
+    if (flags & SPAMC_HEADERS) {
+        if (_append_original_body(m, flags) != EX_OK) {
+            goto failure;
+        }
+    }
+
     return EX_OK;
 
   failure:
@@ -1105,7 +1423,6 @@ int message_filter(struct transport *tp, const char *username,
     return failureval;
 }
 
-
 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.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;
@@ -1181,7 +1504,8 @@ int message_tell(struct transport *tp, const char *username, int flags,
     }
 
     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;
     }
@@ -1256,11 +1580,11 @@ int message_tell(struct transport *tp, const char *username, int flags,
        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);
-       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;
 
@@ -1410,6 +1734,7 @@ void transport_init(struct transport *tp)
     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)
 {
+#ifdef SPAMC_HAS_ADDRINFO
+    struct addrinfo *tmp;
+#else
+    struct in_addr tmp;
+#endif
+    int i;
     int rnum;
 
     assert(tp != 0);
@@ -1434,9 +1765,9 @@ static void _randomize_hosts(struct transport *tp)
     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];
 
@@ -1461,13 +1792,19 @@ static void _randomize_hosts(struct transport *tp)
 */
 int transport_setup(struct transport *tp, int flags)
 {
+#ifdef SPAMC_HAS_ADDRINFO
+    struct addrinfo hints, *res, *addrp; 
+    char port[6];
+#else
     struct hostent *hp;
+    char **addrp;
+#endif
     char *hostlist, *hostname;
     int errbits;
-    char **addrp;
+    int origerr;
 
 #ifdef _WIN32
-    // Start Winsock up
+    /* Start Winsock up */
     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;
 
+#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:
@@ -1487,7 +1833,21 @@ int transport_setup(struct transport *tp, int flags)
         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");
+#endif
         tp->nhosts = 1;
         return EX_OK;
 
@@ -1518,7 +1878,42 @@ int transport_setup(struct transport *tp, int flags)
             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",
@@ -1539,15 +1934,18 @@ int transport_setup(struct transport *tp, int flags)
                 }
                 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]
-             || hp->h_addrtype != AF_INET) {
+             || hp->h_addrtype != AF_INET)
                 /* no hosts/bad size/wrong family */
+#endif
+            {
                 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.
              */
+#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",
@@ -1566,7 +1978,7 @@ int transport_setup(struct transport *tp, int flags)
                 memcpy(&tp->hosts[tp->nhosts], *addrp, hp->h_length);
                 tp->nhosts++;
             }
-            
+#endif            
 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) {
-        // 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);
 
-        // avoid buffer overflow
+        /* avoid buffer overflow */
         if (len > (LOG_BUFSIZ-2)) { len = (LOG_BUFSIZ-3); }
 
         len += snprintf(buf+len, LOG_BUFSIZ-len, "\n");
index be3d0fd90bbad18a415fe0fa0d1fdb97d45a4642..e26cf6fc953477100e492148e659c1a69a974916 100644 (file)
@@ -1,9 +1,10 @@
 /* <@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
  * 
  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 )
+#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>
+/* some platforms (Cygwin) don't implement getaddrinfo */
+#ifdef EAI_AGAIN
+#define SPAMC_HAS_ADDRINFO 1
+#endif
 #endif
 
-#ifdef _WIN32
+#if (defined(_WIN32) || !defined(_SYSEXITS_H))
 /* 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)
 
+/* 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_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
 {
@@ -134,11 +160,12 @@ struct message
     /* 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;
-    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;
@@ -195,11 +222,23 @@ struct transport
 
     unsigned short port;       /* for TCP sockets              */
 
+#ifdef SPAMC_HAS_ADDRINFO
+    struct addrinfo *hosts[TRANSPORT_MAX_HOSTS];
+#else
     struct in_addr hosts[TRANSPORT_MAX_HOSTS];
+#endif
     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);
 
diff --git a/utils.c b/utils.c
index f42c961ca86fe369ceb51e8695b58ed05eb6ead0..d69751f54cb96b646bf6051146c201a9b10b1ef9 100644 (file)
--- a/utils.c
+++ b/utils.c
@@ -1,9 +1,10 @@
 /* <@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
  * 
@@ -67,6 +68,33 @@ static void catch_alrm(int x)
 }
 #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;
diff --git a/utils.h b/utils.h
index 3a287546f0a9454e850105dcdd68c1b983b3f80c..7262d9abe936cd054a03d7743c4b42ad932347bb 100644 (file)
--- a/utils.h
+++ b/utils.h
@@ -1,9 +1,10 @@
 /* <@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
  * 
@@ -20,6 +21,8 @@
 
 #define UNUSED_VARIABLE(v)     ((void)(v))
 
+#include <stddef.h>
+
 extern int libspamc_timeout;   /* default timeout in seconds */
 
 #ifdef SPAMC_SSL
@@ -35,9 +38,9 @@ typedef int SSL_METHOD;
 
 #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
@@ -67,17 +70,17 @@ typedef int SSL_METHOD;
 #define ETIMEDOUT               WSAETIMEDOUT
 #define ECONNREFUSED            WSAECONNREFUSED
 #define ELOOP                   WSAELOOP
-// #define ENAMETOOLONG            WSAENAMETOOLONG
+/* #define ENAMETOOLONG            WSAENAMETOOLONG */
 #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
 
-// 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
@@ -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);
 
+/* 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);