From 907904d3435d72c627f1661f76c4ccdb52263d29 Mon Sep 17 00:00:00 2001 From: Colin Leroy Date: Thu, 23 Sep 2004 12:51:33 +0000 Subject: [PATCH] 2004-09-23 [colin] 0.9.12cvs103.1 * src/plugins/spamassassin/libspamc.c * src/plugins/spamassassin/libspamc.h * src/plugins/spamassassin/utils.c * src/plugins/spamassassin/utils.h Sync with HEAD (Update spamassassin to 3.0) --- ChangeLog-gtk2.claws | 8 + PATCHSETS | 1 + configure.ac | 4 +- src/plugins/spamassassin/libspamc.c | 1702 +++++++++++++-------------- src/plugins/spamassassin/libspamc.h | 149 ++- src/plugins/spamassassin/utils.c | 298 +++-- src/plugins/spamassassin/utils.h | 87 +- 7 files changed, 1215 insertions(+), 1034 deletions(-) diff --git a/ChangeLog-gtk2.claws b/ChangeLog-gtk2.claws index b9a2ceec9..0a0d8fc31 100644 --- a/ChangeLog-gtk2.claws +++ b/ChangeLog-gtk2.claws @@ -1,3 +1,11 @@ +2004-09-23 [colin] 0.9.12cvs103.1 + + * src/plugins/spamassassin/libspamc.c + * src/plugins/spamassassin/libspamc.h + * src/plugins/spamassassin/utils.c + * src/plugins/spamassassin/utils.h + Sync with HEAD (Update spamassassin to 3.0) + 2004-09-21 [colin] 0.9.12cvs102.3 * src/action.c diff --git a/PATCHSETS b/PATCHSETS index 58bf909ae..95fb0f408 100644 --- a/PATCHSETS +++ b/PATCHSETS @@ -173,3 +173,4 @@ ( cvs diff -u -r 1.396.2.9 -r 1.396.2.10 ChangeLog; cvs diff -u -r 1.2504.2.21 -r 1.2504.2.22 ChangeLog.claws; cvs diff -u -r 1.391.2.9 -r 1.391.2.10 ChangeLog.jp; cvs diff -u -r 1.654.2.204 -r 1.654.2.205 configure.ac; cvs diff -u -r 1.149.2.14 -r 1.149.2.15 src/inc.c; ) > 0.9.12cvs102.1.patchset ( cvs diff -u -r 1.7.2.7 -r 1.7.2.8 src/plugins/image_viewer/viewer.c; ) > 0.9.12cvs102.2.patchset ( cvs diff -u -r 1.12.2.12 -r 1.12.2.13 src/action.c; ) > 0.9.12cvs102.3.patchset +( cvs diff -u -r 1.4.2.1 -r 1.4.2.2 src/plugins/spamassassin/libspamc.c; cvs diff -u -r 1.4.2.1 -r 1.4.2.2 src/plugins/spamassassin/libspamc.h; cvs diff -u -r 1.3.2.1 -r 1.3.2.2 src/plugins/spamassassin/utils.c; cvs diff -u -r 1.3.2.1 -r 1.3.2.2 src/plugins/spamassassin/utils.h; ) > 0.9.12cvs103.1.patchset diff --git a/configure.ac b/configure.ac index 0f2963352..9e6af35b0 100644 --- a/configure.ac +++ b/configure.ac @@ -11,9 +11,9 @@ MINOR_VERSION=9 MICRO_VERSION=12 INTERFACE_AGE=0 BINARY_AGE=0 -EXTRA_VERSION=102 +EXTRA_VERSION=103 EXTRA_RELEASE= -EXTRA_GTK2_VERSION=.3 +EXTRA_GTK2_VERSION=.1 if test \( $EXTRA_VERSION -eq 0 \) -o \( "x$EXTRA_RELEASE" != "x" \); then VERSION=${MAJOR_VERSION}.${MINOR_VERSION}.${MICRO_VERSION}${EXTRA_RELEASE}${EXTRA_GTK2_VERSION} diff --git a/src/plugins/spamassassin/libspamc.c b/src/plugins/spamassassin/libspamc.c index 84d21da41..4d4798785 100644 --- a/src/plugins/spamassassin/libspamc.c +++ b/src/plugins/spamassassin/libspamc.c @@ -1,27 +1,45 @@ -/* - * This code is copyright 2001 by Craig Hughes - * Portions copyright 2002 by Brad Jorsch - * It is licensed under the same license as Perl itself. The text of this - * license is included in the SpamAssassin distribution in the file named - * "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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * */ #include "config.h" #include "libspamc.h" #include "utils.h" -#include #include #include #include #include +#ifdef _WIN32 +#define snprintf _snprintf +#define vsnprintf _vsnprintf +#define strcasecmp stricmp +#define sleep Sleep +#include +#else #include +#include #include #include #include #include #include #include +#define closesocket(x) close(x) +#endif #ifdef HAVE_SYSEXITS_H #include @@ -46,13 +64,13 @@ /* KAM 12-4-01 */ /* SJF 2003/04/25 - now test for macros directly */ #ifndef SHUT_RD -# define SHUT_RD 0 /* no more receptions */ +# define SHUT_RD 0 /* no more receptions */ #endif #ifndef SHUT_WR -# define SHUT_WR 1 /* no more transmissions */ +# define SHUT_WR 1 /* no more transmissions */ #endif #ifndef SHUT_RDWR -# define SHUT_RDWR 2 /* no more receptions or transmissions */ +# define SHUT_RDWR 2 /* no more receptions or transmissions */ #endif #ifndef HAVE_H_ERRNO @@ -72,13 +90,13 @@ extern char *optarg; #ifndef HAVE_EX__MAX /* jm: very conservative figure, should be well out of range on almost all NIXes */ -#define EX__MAX 200 +#define EX__MAX 200 #endif #undef DO_CONNECT_DEBUG_SYSLOGS /* or #define DO_CONNECT_DEBUG_SYSLOGS 1 */ -static const int ESC_PASSTHROUGHRAW = EX__MAX+666; +static const int ESC_PASSTHROUGHRAW = EX__MAX + 666; /* set EXPANSION_ALLOWANCE to something more than might be added to a message in X-headers and the report template */ @@ -91,14 +109,15 @@ 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.3"; /* "private" part of struct message. * we use this instead of the struct message directly, so that we * can add new members without affecting the ABI. */ -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 libspamc_timeout = 0; @@ -112,32 +131,30 @@ int libspamc_timeout = 0; * * This should ONLY be called when there is an error. */ -static int -translate_connect_errno(int err) +static int _translate_connect_errno(int err) { - switch (err) - { - case EBADF: - case EFAULT: - case ENOTSOCK: - case EISCONN: - case EADDRINUSE: - case EINPROGRESS: - case EALREADY: - case EAFNOSUPPORT: - return EX_SOFTWARE; + switch (err) { + case EBADF: + case EFAULT: + case ENOTSOCK: + case EISCONN: + case EADDRINUSE: + case EINPROGRESS: + case EALREADY: + case EAFNOSUPPORT: + return EX_SOFTWARE; - case ECONNREFUSED: - case ETIMEDOUT: - case ENETUNREACH: - return EX_UNAVAILABLE; + case ECONNREFUSED: + case ETIMEDOUT: + case ENETUNREACH: + return EX_UNAVAILABLE; - case EACCES: - return EX_NOPERM; + case EACCES: + return EX_NOPERM; - default: - return EX_SOFTWARE; - } + default: + return EX_SOFTWARE; + } } /* @@ -150,64 +167,70 @@ translate_connect_errno(int err) * * Upon failure we return one of the other EX_??? error codes. */ -static int opensocket(int type, int *psock) +static int _opensocket(int flags, int type, int *psock) { -const char *typename; -int proto = 0; + const char *typename; + int proto = 0; - assert(psock != 0); + assert(psock != 0); /*---------------------------------------------------------------- * Create a few induction variables that are implied by the socket * type given by the user. The typename is strictly used for debug * reporting. */ - if ( type == PF_UNIX ) - { - typename = "PF_UNIX"; - } - else - { - typename = "PF_INET"; - proto = IPPROTO_TCP; - } + if (type == PF_UNIX) { + typename = "PF_UNIX"; + } + else { + typename = "PF_INET"; + proto = IPPROTO_TCP; + } #ifdef DO_CONNECT_DEBUG_SYSLOGS - syslog (DEBUG_LEVEL, "dbg: create socket(%s)", typename); + libspamc_log(flags, DEBUG_LEVEL, "dbg: create socket(%s)", typename); #endif - if ( (*psock = socket(type, SOCK_STREAM, proto)) < 0 ) - { - int origerr; + if ((*psock = socket(type, SOCK_STREAM, proto)) +#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 = errno; /* take a copy before syslog() */ - - syslog (LOG_ERR, "socket(%s) to spamd failed: %m", typename); +#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 - switch (origerr) - { - case EPROTONOSUPPORT: - case EINVAL: - return EX_SOFTWARE; + switch (origerr) { + case EPROTONOSUPPORT: + case EINVAL: + return EX_SOFTWARE; - case EACCES: - return EX_NOPERM; + case EACCES: + return EX_NOPERM; - case ENFILE: - case EMFILE: - case ENOBUFS: - case ENOMEM: - return EX_OSERR; + case ENFILE: + case EMFILE: + case ENOBUFS: + case ENOMEM: + return EX_OSERR; - default: - return EX_SOFTWARE; - } + default: + return EX_SOFTWARE; } + } /*---------------------------------------------------------------- @@ -215,31 +238,39 @@ int proto = 0; * suggest this is probably not set */ #ifdef USE_TCP_NODELAY - { - int one = 1; - - if ( type == PF_INET - && setsockopt(*psock, 0, TCP_NODELAY, &one, sizeof one) != 0 ) - { - switch(errno) - { - case EBADF: - case ENOTSOCK: - case ENOPROTOOPT: - case EFAULT: - syslog(LOG_ERR, - "setsockopt(TCP_NODELAY) failed: %m"); - close (*psock); - return EX_SOFTWARE; - - default: - break; /* ignored */ - } - } + { + int one = 1; + + if (type == PF_INET + && setsockopt(*psock, 0, TCP_NODELAY, &one, sizeof one) != 0) { + int origerrno; +#ifndef _WIN32 + origerr = errno; +#else + origerrno = WSAGetLastError(); +#endif + switch (origerr) { + case EBADF: + case ENOTSOCK: + case ENOPROTOOPT: + case EFAULT: + libspamc_log(flags, LOG_ERR, +#ifndef _WIN32 + "setsockopt(TCP_NODELAY) failed: %s", strerror(origerr)); +#else + "setsockopt(TCP_NODELAY) failed: %d", origerr); +#endif + closesocket(*psock); + return EX_SOFTWARE; + + default: + break; /* ignored */ + } } + } #endif /* USE_TCP_NODELAY */ - return EX_OK; /* all is well */ + return EX_OK; /* all is well */ } /* @@ -250,55 +281,58 @@ int proto = 0; * file descriptor in *sockptr. Return is EX_OK if we did it, * and some other error code otherwise. */ -static int -try_to_connect_unix (struct transport *tp, int *sockptr) +static int _try_to_connect_unix(struct transport *tp, int *sockptr) { -int mysock, status, origerr; -struct sockaddr_un addrbuf; -int ret; +#ifndef _WIN32 + int mysock, status, origerr; + struct sockaddr_un addrbuf; + int ret; - assert(tp != 0); - assert(sockptr != 0); - assert(tp->socketpath != 0); + assert(tp != 0); + assert(sockptr != 0); + assert(tp->socketpath != 0); /*---------------------------------------------------------------- * If the socket itself can't be created, this is a fatal error. */ - if ( (ret = opensocket(PF_UNIX, &mysock)) != EX_OK ) - return ret; + if ((ret = _opensocket(tp->flags, PF_UNIX, &mysock)) != EX_OK) + return ret; - /* set up the UNIX domain socket */ - memset(&addrbuf, 0, sizeof addrbuf); - addrbuf.sun_family = AF_UNIX; - strncpy(addrbuf.sun_path, tp->socketpath, sizeof addrbuf.sun_path - 1); - addrbuf.sun_path[sizeof addrbuf.sun_path - 1] = '\0'; + /* set up the UNIX domain socket */ + memset(&addrbuf, 0, sizeof addrbuf); + addrbuf.sun_family = AF_UNIX; + strncpy(addrbuf.sun_path, tp->socketpath, sizeof addrbuf.sun_path - 1); + addrbuf.sun_path[sizeof addrbuf.sun_path - 1] = '\0'; #ifdef DO_CONNECT_DEBUG_SYSLOGS - syslog (DEBUG_LEVEL, "dbg: connect(AF_UNIX) to spamd at %s", - addrbuf.sun_path); + libspamc_log(tp->flags, DEBUG_LEVEL, "dbg: connect(AF_UNIX) to spamd at %s", + addrbuf.sun_path); #endif - status = connect(mysock, (struct sockaddr *) &addrbuf, sizeof(addrbuf)); + status = connect(mysock, (struct sockaddr *) &addrbuf, sizeof(addrbuf)); - origerr = errno; + origerr = errno; - if ( status >= 0 ) - { + if (status >= 0) { #ifdef DO_CONNECT_DEBUG_SYSLOGS - syslog(DEBUG_LEVEL, "dbg: connect(AF_UNIX) ok"); + libspamc_log(tp->flags, DEBUG_LEVEL, "dbg: connect(AF_UNIX) ok"); #endif - *sockptr = mysock; - - return EX_OK; - } + *sockptr = mysock; - syslog(LOG_ERR, "connect(AF_UNIX) to spamd %s failed: %m", - addrbuf.sun_path); + return EX_OK; + } - close(mysock); + libspamc_log(tp->flags, LOG_ERR, "connect(AF_UNIX) to spamd %s failed: %s", + addrbuf.sun_path, strerror(origerr)); + closesocket(mysock); - return translate_connect_errno(origerr); + return _translate_connect_errno(origerr); +#else + (void) tp; /* not used. suppress compiler warning */ + (void) sockptr; /* not used. suppress compiler warning */ + return EX_OSERR; +#endif } /* @@ -309,545 +343,346 @@ int ret; * list of IP addresses has already been randomized (if requested) * and limited to just one if fallback has been enabled. */ -static int -try_to_connect_tcp (const struct transport *tp, int *sockptr) +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; - 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++) - { - syslog(LOG_ERR, "dbg: %d/%d: %s", - numloops+1, tp->nhosts, inet_ntoa(tp->hosts[numloops])); - } + for (numloops = 0; numloops < tp->nhosts; numloops++) { + libspamc_log(tp->flags, LOG_ERR, "dbg: %d/%d: %s", + numloops + 1, tp->nhosts, inet_ntoa(tp->hosts[numloops])); + } #endif - for (numloops = 0; numloops < MAX_CONNECT_RETRIES; numloops++) - { + for (numloops = 0; numloops < MAX_CONNECT_RETRIES; numloops++) { struct sockaddr_in addrbuf; - const int hostix = numloops % tp->nhosts; - int status, mysock; - const char * ipaddr; + const int hostix = numloops % tp->nhosts; + int status, mysock; + const char *ipaddr; /*-------------------------------------------------------- * We always start by creating the socket, as we get only * one attempt to connect() on each one. If this fails, * we're done. */ - if ( (ret = opensocket(PF_INET, &mysock)) != EX_OK ) - return ret; + if ((ret = _opensocket(tp->flags, PF_INET, &mysock)) != EX_OK) + return ret; - memset(&addrbuf, 0, sizeof(addrbuf)); + memset(&addrbuf, 0, sizeof(addrbuf)); - addrbuf.sin_family = AF_INET; - addrbuf.sin_port = htons(tp->port); - addrbuf.sin_addr = tp->hosts[hostix]; + addrbuf.sin_family = AF_INET; + addrbuf.sin_port = htons(tp->port); + addrbuf.sin_addr = tp->hosts[hostix]; - ipaddr = inet_ntoa(addrbuf.sin_addr); + ipaddr = inet_ntoa(addrbuf.sin_addr); #ifdef DO_CONNECT_DEBUG_SYSLOGS - syslog (DEBUG_LEVEL, - "dbg: connect(AF_INET) to spamd at %s (try #%d of %d)", - ipaddr, - numloops+1, - MAX_CONNECT_RETRIES); + libspamc_log(tp->flags, DEBUG_LEVEL, + "dbg: connect(AF_INET) to spamd at %s (try #%d of %d)", + ipaddr, numloops + 1, MAX_CONNECT_RETRIES); #endif - status = connect(mysock, (struct sockaddr *)&addrbuf, sizeof(addrbuf)); + status = + connect(mysock, (struct sockaddr *) &addrbuf, sizeof(addrbuf)); - if (status != 0) - { - syslog (LOG_ERR, - "connect(AF_INET) to spamd at %s failed, retrying (#%d of %d): %m", - ipaddr, numloops+1, MAX_CONNECT_RETRIES); - - close(mysock); - - sleep(CONNECT_RETRY_SLEEP); - } - else - { -#ifdef DO_CONNECT_DEBUG_SYSLOGS - syslog(DEBUG_LEVEL, - "dbg: connect(AF_INET) to spamd at %s done", - ipaddr); + 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)); +#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); #endif - *sockptr = mysock; + closesocket(mysock); - return EX_OK; - } + sleep(CONNECT_RETRY_SLEEP); } - - syslog (LOG_ERR, "connection attempt to spamd aborted after %d retries", - MAX_CONNECT_RETRIES); - - return translate_connect_errno(origerr); -} - -#if 0 -static int -try_to_connect (const struct sockaddr *argaddr, struct hostent *hent, - int hent_port, int *sockptr) -{ -#ifdef USE_TCP_NODELAY - int value; -#endif - int mysock = -1; - int status = -1; - int origerr; - int numloops; - int hostnum = 0; - struct sockaddr_in addrbuf, *addr; - struct in_addr inaddrlist[256]; - + else { #ifdef DO_CONNECT_DEBUG_SYSLOGS - int dbgiter; char dbgbuf[2048]; int dbgbuflen = 0; -#endif - - /* NOTE: do not call syslog() (unless you are about to return) before - * we take a copy of the h_addr_list. - */ - - /* only one set of connection targets can be used. assert this */ - if (argaddr == NULL && hent == NULL) { - syslog (LOG_ERR, "oops! both NULL in try_to_connect"); - return EX_SOFTWARE; - } else if (argaddr != NULL && hent != NULL) { - syslog (LOG_ERR, "oops! both non-NULL in try_to_connect"); - return EX_SOFTWARE; - } - - /* take a copy of the h_addr_list part of the struct hostent */ - if (hent != NULL) { - memset (inaddrlist, 0, sizeof(inaddrlist)); - - for (hostnum=0; hent->h_addr_list[hostnum] != 0; hostnum++) { - -#ifdef DO_CONNECT_DEBUG_SYSLOGS - dbgbuflen += snprintf (dbgbuf+dbgbuflen, 2047-dbgbuflen, - "[%d %lx: %d.%d.%d.%d]", - hostnum, hent->h_addr_list[hostnum], - hent->h_addr_list[hostnum][0], - hent->h_addr_list[hostnum][1], - hent->h_addr_list[hostnum][2], - hent->h_addr_list[hostnum][3]); -#endif - - if (hostnum > 255) { - syslog (LOG_ERR, "too many address in hostent (%d), ignoring others", - hostnum); - break; - } - - if (hent->h_addr_list[hostnum] == NULL) { - /* shouldn't happen */ - syslog (LOG_ERR, "hent->h_addr_list[hostnum] == NULL! foo!"); - return EX_SOFTWARE; - } - -#ifdef DO_CONNECT_DEBUG_SYSLOGS - dbgbuflen += snprintf (dbgbuf+dbgbuflen, 2047-dbgbuflen, - "[%d: %d.%d.%d.%d] ", sizeof (struct in_addr), - hent->h_addr_list[hostnum][0], - hent->h_addr_list[hostnum][1], - hent->h_addr_list[hostnum][2], - hent->h_addr_list[hostnum][3]); -#endif - - memcpy ((void *) &(inaddrlist[hostnum]), - (void *) hent->h_addr_list[hostnum], - sizeof (struct in_addr)); - } - -#ifdef DO_CONNECT_DEBUG_SYSLOGS - syslog (LOG_DEBUG, "dbg: %d %s", hostnum, dbgbuf); dbgbuflen = 0; -#endif - } - - -#ifdef DO_CONNECT_DEBUG_SYSLOGS - for (dbgiter = 0; dbgiter < hostnum; dbgiter++) { - syslog (LOG_DEBUG, "dbg: host addr %d/%d = %lx at %lx", dbgiter, hostnum, - inaddrlist[dbgiter].s_addr, &(inaddrlist[dbgiter])); - } -#endif - - hent = NULL; /* cannot use hent after this point, syslog() may overwrite it */ - -#ifdef DO_CONNECT_DEBUG_SYSLOGS - syslog (LOG_DEBUG, "dbg: socket"); -#endif - - if(-1 == (mysock = socket(PF_INET,SOCK_STREAM,0))) - { - origerr = errno; /* take a copy before syslog() */ - syslog (LOG_ERR, "socket() to spamd failed: %m"); - switch(origerr) - { - case EPROTONOSUPPORT: - case EINVAL: - return EX_SOFTWARE; - case EACCES: - return EX_NOPERM; - case ENFILE: - case EMFILE: - case ENOBUFS: - case ENOMEM: - return EX_OSERR; - default: - return EX_SOFTWARE; - } - } - -#ifdef USE_TCP_NODELAY - /* TODO: should this be up above the connect()? */ - value = 1; /* make this explicit! */ - if(-1 == setsockopt(mysock,0,TCP_NODELAY,&value,sizeof(value))) - { - switch(errno) - { - case EBADF: - case ENOTSOCK: - case ENOPROTOOPT: - case EFAULT: - syslog (LOG_ERR, "setsockopt() to spamd failed: %m"); - close (mysock); - return EX_SOFTWARE; - - default: - break; /* ignored */ - } - } -#endif - - for (numloops=0; numloops < MAX_CONNECT_RETRIES; numloops++) { - -#ifdef DO_CONNECT_DEBUG_SYSLOGS - syslog (LOG_DEBUG, "dbg: connect() to spamd %d", numloops); -#endif - - if (argaddr != NULL) { - addr = (struct sockaddr_in *) argaddr; /* use the one provided */ - -#ifdef DO_CONNECT_DEBUG_SYSLOGS - syslog (LOG_DEBUG, "dbg: using argaddr"); -#endif - - } else { - /* cycle through the addrs in hent */ - memset(&addrbuf, 0, sizeof(addrbuf)); - addrbuf.sin_family=AF_INET; - addrbuf.sin_port=htons(hent_port); - - if (sizeof(addrbuf.sin_addr) != sizeof(struct in_addr)) { /* shouldn't happen */ - syslog (LOG_ERR, - "foo! sizeof(sockaddr.sin_addr) != sizeof(struct in_addr)"); - return EX_SOFTWARE; - } - -#ifdef DO_CONNECT_DEBUG_SYSLOGS - syslog (LOG_DEBUG, "dbg: cpy addr %d/%d at %lx", - numloops%hostnum, hostnum, &(inaddrlist[numloops % hostnum])); -#endif - - memcpy (&addrbuf.sin_addr, &(inaddrlist[numloops % hostnum]), - sizeof(addrbuf.sin_addr)); - addr = &addrbuf; - -#ifdef DO_CONNECT_DEBUG_SYSLOGS - syslog (LOG_DEBUG, "dbg: conn addr %d/%d = %lx", - numloops%hostnum, hostnum, addrbuf.sin_addr.s_addr); + libspamc_log(tp->flags, DEBUG_LEVEL, + "dbg: connect(AF_INET) to spamd at %s done", ipaddr); #endif + *sockptr = mysock; + return EX_OK; + } } -#ifdef DO_CONNECT_DEBUG_SYSLOGS - syslog (LOG_DEBUG, "dbg: connect() to spamd at %s", - inet_ntoa(((struct sockaddr_in *)addr)->sin_addr)); -#endif - status = connect(mysock,(const struct sockaddr *) addr, sizeof(*addr)); - -#ifdef DO_CONNECT_DEBUG_SYSLOGS - syslog (LOG_DEBUG, "dbg: connect() to spamd at %s done", - inet_ntoa(((struct sockaddr_in *)addr)->sin_addr)); -#endif - - if (status < 0) - { - origerr = errno; /* take a copy before syslog() */ - syslog (LOG_ERR, "connect() to spamd at %s failed, retrying (%d/%d): %m", - inet_ntoa(((struct sockaddr_in *)addr)->sin_addr), - numloops+1, MAX_CONNECT_RETRIES); - sleep(CONNECT_RETRY_SLEEP); + libspamc_log(tp->flags, LOG_ERR, "connection attempt to spamd aborted after %d retries", + MAX_CONNECT_RETRIES); - } else { - *sockptr = mysock; - return EX_OK; - } - } - - /* failed, even with a few retries */ - close (mysock); - syslog (LOG_ERR, "connection attempt to spamd aborted after %d retries", - MAX_CONNECT_RETRIES); - - switch(origerr) - { - case EBADF: - case EFAULT: - case ENOTSOCK: - case EISCONN: - case EADDRINUSE: - case EINPROGRESS: - case EALREADY: - case EAFNOSUPPORT: - return EX_SOFTWARE; - case ECONNREFUSED: - case ETIMEDOUT: - case ENETUNREACH: - return EX_UNAVAILABLE; - case EACCES: - return EX_NOPERM; - default: - return EX_SOFTWARE; - } + return _translate_connect_errno(origerr); } -#endif /* Aug 14, 2002 bj: Reworked things. Now we have message_read, message_write, * message_dump, lookup_host, message_filter, and message_process, and a bunch * of helper functions. */ -static void clear_message(struct message *m){ - m->type=MESSAGE_NONE; - m->raw=NULL; m->raw_len=0; - m->pre=NULL; m->pre_len=0; - m->msg=NULL; m->msg_len=0; - m->post=NULL; m->post_len=0; - m->is_spam=EX_TOOBIG; - m->score=0.0; m->threshold=0.0; - m->out=NULL; m->out_len=0; - m->content_length=-1; +static void _clear_message(struct message *m) +{ + m->type = MESSAGE_NONE; + m->raw = NULL; + m->raw_len = 0; + m->pre = NULL; + m->pre_len = 0; + m->msg = NULL; + m->msg_len = 0; + m->post = NULL; + m->post_len = 0; + m->is_spam = EX_TOOBIG; + m->score = 0.0; + m->threshold = 0.0; + m->out = NULL; + m->out_len = 0; + m->content_length = -1; } -static int -message_read_raw(int fd, struct message *m){ - clear_message(m); - if((m->raw=malloc(m->max_len+1))==NULL) return EX_OSERR; - m->raw_len=full_read(fd, m->raw, m->max_len+1, m->max_len+1); - if(m->raw_len<=0){ - free(m->raw); m->raw=NULL; m->raw_len=0; - return EX_IOERR; +static int _message_read_raw(int fd, struct message *m) +{ + _clear_message(m); + if ((m->raw = malloc(m->max_len + 1)) == NULL) + return EX_OSERR; + m->raw_len = full_read(fd, 1, m->raw, m->max_len + 1, m->max_len + 1); + if (m->raw_len <= 0) { + free(m->raw); + m->raw = NULL; + m->raw_len = 0; + return EX_IOERR; } - m->type=MESSAGE_ERROR; - if(m->raw_len>m->max_len) return EX_TOOBIG; - m->type=MESSAGE_RAW; - m->msg=m->raw; - m->msg_len=m->raw_len; - m->out=m->msg; - m->out_len=m->msg_len; + m->type = MESSAGE_ERROR; + if (m->raw_len > m->max_len) + return EX_TOOBIG; + m->type = MESSAGE_RAW; + m->msg = m->raw; + m->msg_len = m->raw_len; + m->out = m->msg; + m->out_len = m->msg_len; return EX_OK; } -static int message_read_bsmtp(int fd, struct message *m){ - off_t i, j; +static int _message_read_bsmtp(int fd, struct message *m) +{ + unsigned int i, j; char prev; - clear_message(m); - if((m->raw=malloc(m->max_len+1))==NULL) return EX_OSERR; + _clear_message(m); + if ((m->raw = malloc(m->max_len + 1)) == NULL) + return EX_OSERR; /* Find the DATA line */ - m->raw_len=full_read(fd, m->raw, m->max_len+1, m->max_len+1); - if(m->raw_len<=0){ - free(m->raw); m->raw=NULL; m->raw_len=0; - return EX_IOERR; + m->raw_len = full_read(fd, 1, m->raw, m->max_len + 1, m->max_len + 1); + if (m->raw_len <= 0) { + free(m->raw); + m->raw = NULL; + m->raw_len = 0; + return EX_IOERR; } - m->type=MESSAGE_ERROR; - if(m->raw_len>m->max_len) return EX_TOOBIG; - m->pre=m->raw; - for(i=0; iraw_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; - } + m->type = MESSAGE_ERROR; + if (m->raw_len > 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; + } } - if(m->msg==NULL) return EX_DATAERR; + if (m->msg == NULL) + return EX_DATAERR; /* Find the end-of-DATA line */ - prev='\n'; - for(i=j=0; imsg_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'){ - /* 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]=='.'){ - /* Escaping dot, eliminate. */ - prev='.'; - continue; - } /* Else an ordinary dot, drop down to ordinary char handler */ - } - prev=m->msg[i]; - m->msg[j++]=m->msg[i]; + prev = '\n'; + for (i = j = 0; i < 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') { + /* 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] == '.') { + /* Escaping dot, eliminate. */ + prev = '.'; + continue; + } /* Else an ordinary dot, drop down to ordinary char handler */ + } + prev = m->msg[i]; + m->msg[j++] = m->msg[i]; } - 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; return EX_OK; } -int message_read(int fd, int flags, struct message *m){ +int message_read(int fd, int flags, struct message *m) +{ libspamc_timeout = 0; /* create the "private" part of the struct message */ - m->priv = malloc (sizeof (struct libspamc_private_message)); + m->priv = malloc(sizeof(struct libspamc_private_message)); if (m->priv == NULL) { - syslog(LOG_ERR, "message_read: malloc failed"); - return EX_OSERR; + libspamc_log(flags, LOG_ERR, "message_read: malloc failed"); + return EX_OSERR; } m->priv->flags = flags; - switch(flags&SPAMC_MODE_MASK){ - case SPAMC_RAW_MODE: - return message_read_raw(fd, m); + switch (flags & SPAMC_MODE_MASK) { + case SPAMC_RAW_MODE: + return _message_read_raw(fd, m); - case SPAMC_BSMTP_MODE: - return message_read_bsmtp(fd, m); + case SPAMC_BSMTP_MODE: + return _message_read_bsmtp(fd, m); - default: - syslog(LOG_ERR, "message_read: Unknown mode %d\n", flags&SPAMC_MODE_MASK); - return EX_USAGE; + default: + libspamc_log(flags, LOG_ERR, "message_read: Unknown mode %d", + flags & SPAMC_MODE_MASK); + return EX_USAGE; } } -long message_write(int fd, struct message *m){ - long total=0; +long message_write(int fd, struct message *m) +{ + long total = 0; off_t i, j; off_t jlimit; char buffer[1024]; - if (m->priv->flags&SPAMC_CHECK_ONLY) { - if(m->is_spam==EX_ISSPAM || m->is_spam==EX_NOTSPAM){ - return full_write(fd, m->out, m->out_len); + if (m->priv->flags & SPAMC_CHECK_ONLY) { + if (m->is_spam == EX_ISSPAM || m->is_spam == EX_NOTSPAM) { + return full_write(fd, 1, m->out, m->out_len); - } else { - syslog(LOG_ERR, "oops! SPAMC_CHECK_ONLY is_spam: %d\n", m->is_spam); + } + else { + libspamc_log(m->priv->flags, LOG_ERR, "oops! SPAMC_CHECK_ONLY is_spam: %d", + m->is_spam); return -1; } } /* else we're not in CHECK_ONLY mode */ - switch(m->type){ - case MESSAGE_NONE: - syslog(LOG_ERR, "Cannot write this message, it's MESSAGE_NONE!\n"); - return -1; - - case MESSAGE_ERROR: - return full_write(fd, m->raw, m->raw_len); - - case MESSAGE_RAW: - return full_write(fd, m->out, m->out_len); - - case MESSAGE_BSMTP: - total=full_write(fd, m->pre, m->pre_len); - for(i=0; iout_len; ){ - jlimit = (off_t) (sizeof(buffer)/sizeof(*buffer)-4); - for(j=0; i < (off_t) m->out_len && - j < jlimit;) - { - if(i+1out_len && m->out[i]=='\n' && m->out[i+1]=='.'){ + switch (m->type) { + case MESSAGE_NONE: + libspamc_log(m->priv->flags, LOG_ERR, "Cannot write this message, it's MESSAGE_NONE!"); + return -1; + + case MESSAGE_ERROR: + return full_write(fd, 1, m->raw, m->raw_len); + + case MESSAGE_RAW: + return full_write(fd, 1, m->out, m->out_len); + + case MESSAGE_BSMTP: + total = full_write(fd, 1, m->pre, m->pre_len); + for (i = 0; i < m->out_len;) { + jlimit = (off_t) (sizeof(buffer) / sizeof(*buffer) - 4); + for (j = 0; i < (off_t) m->out_len && j < jlimit;) { + if (i + 1 < m->out_len && m->out[i] == '\n' + && m->out[i + 1] == '.') { if (j > jlimit - 4) { - break; /* avoid overflow */ + break; /* avoid overflow */ } - buffer[j++]=m->out[i++]; - buffer[j++]=m->out[i++]; - buffer[j++]='.'; - } else { - buffer[j++]=m->out[i++]; - } - } - total+=full_write(fd, buffer, j); - } - return total+full_write(fd, m->post, m->post_len); - - default: - syslog(LOG_ERR, "Unknown message type %d\n", m->type); - return -1; + buffer[j++] = m->out[i++]; + buffer[j++] = m->out[i++]; + buffer[j++] = '.'; + } + else { + buffer[j++] = m->out[i++]; + } + } + total += full_write(fd, 1, buffer, j); + } + return total + full_write(fd, 1, m->post, m->post_len); + + default: + libspamc_log(m->priv->flags, LOG_ERR, "Unknown message type %d", m->type); + return -1; } } -void message_dump(int in_fd, int out_fd, struct message *m){ +void message_dump(int in_fd, int out_fd, struct message *m) +{ char buf[8196]; int bytes; - - if(m!=NULL && m->type!=MESSAGE_NONE) { - message_write(out_fd, m); + + if (m != NULL && m->type != MESSAGE_NONE) { + message_write(out_fd, m); } - while((bytes=full_read(in_fd, buf, 8192, 8192))>0){ - if (bytes!=full_write(out_fd, buf, bytes)) { - syslog(LOG_ERR, "oops! message_dump of %d returned different", bytes); - } + while ((bytes = full_read(in_fd, 1, buf, 8192, 8192)) > 0) { + if (bytes != full_write(out_fd, 1, buf, bytes)) { + libspamc_log(m->priv->flags, LOG_ERR, "oops! message_dump of %d returned different", + bytes); + } } } static int -_spamc_read_full_line (struct message *m, int flags, SSL *ssl, int sock, - char *buf, int *lenp, int bufsiz) +_spamc_read_full_line(struct message *m, int flags, SSL * ssl, int sock, + char *buf, size_t *lenp, size_t bufsiz) { int failureval; int bytesread = 0; - int len; + size_t len; UNUSED_VARIABLE(m); + *lenp = 0; /* Now, read from spamd */ - for(len=0; len 0 && buf[len-1] == '\r') { + if (buf[len] == '\n') { + buf[len] = '\0'; + if (len > 0 && buf[len - 1] == '\r') { len--; - buf[len]='\0'; + buf[len] = '\0'; } *lenp = len; return EX_OK; } - - if(bytesread<=0){ - failureval = EX_IOERR; goto failure; - } } - syslog(LOG_ERR, "spamd responded with line of %d bytes, dying", len); + libspamc_log(flags, LOG_ERR, "spamd responded with line of %d bytes, dying", len); failureval = EX_TOOBIG; -failure: + failure: return failureval; } @@ -855,58 +690,67 @@ failure: * May 7 2003 jm: using %f is bad where LC_NUMERIC is "," in the locale. * work around using our own locale-independent float-parser code. */ -static float -_locale_safe_string_to_float (char *buf, int siz) +static float _locale_safe_string_to_float(char *buf, int siz) { - int is_neg; - char *cp, *dot; - int divider; - float ret, postdot; - - buf[siz-1] = '\0'; /* ensure termination */ - - /* ok, let's illustrate using "100.033" as an example... */ - - is_neg = 0; - if (*buf == '-') { is_neg = 1; } - - ret = (float) (strtol (buf, &dot, 10)); - if (dot == NULL) { return 0.0; } - if (dot != NULL && *dot != '.') { return ret; } - - /* ex: ret == 100.0 */ - - cp = (dot + 1); - postdot = (float) (strtol (cp, NULL, 10)); - if (postdot == 0.0) { return ret; } - - /* ex: postdot == 33.0, cp="033" */ - - /* now count the number of decimal places and figure out what power of 10 to use */ - divider = 1; - while (*cp != '\0') { - divider *= 10; cp++; - } - - /* ex: - * cp="033", divider=1 - * cp="33", divider=10 - * cp="3", divider=100 - * cp="", divider=1000 - */ - - if (is_neg) { - ret -= (postdot / ((float) divider)); - } else { - ret += (postdot / ((float) divider)); - } - /* ex: ret == 100.033, tada! ... hopefully */ - - return ret; + int is_neg; + char *cp, *dot; + int divider; + float ret, postdot; + + buf[siz - 1] = '\0'; /* ensure termination */ + + /* ok, let's illustrate using "100.033" as an example... */ + + is_neg = 0; + if (*buf == '-') { + is_neg = 1; + } + + ret = (float) (strtol(buf, &dot, 10)); + if (dot == NULL) { + return 0.0; + } + if (dot != NULL && *dot != '.') { + return ret; + } + + /* ex: ret == 100.0 */ + + cp = (dot + 1); + postdot = (float) (strtol(cp, NULL, 10)); + if (postdot == 0.0) { + return ret; + } + + /* ex: postdot == 33.0, cp="033" */ + + /* now count the number of decimal places and figure out what power of 10 to use */ + divider = 1; + while (*cp != '\0') { + divider *= 10; + cp++; + } + + /* ex: + * cp="033", divider=1 + * cp="33", divider=10 + * cp="3", divider=100 + * cp="", divider=1000 + */ + + if (is_neg) { + ret -= (postdot / ((float) divider)); + } + else { + ret += (postdot / ((float) divider)); + } + /* ex: ret == 100.033, tada! ... hopefully */ + + return ret; } static int -_handle_spamd_header (struct message *m, int flags, char *buf, int len) +_handle_spamd_header(struct message *m, int flags, char *buf, int len) { char is_spam[6]; char s_str[21], t_str[21]; @@ -918,278 +762,352 @@ _handle_spamd_header (struct message *m, int flags, char *buf, int len) * May 7 2003 jm: using %f is bad where LC_NUMERIC is "," in the locale. * work around using our own locale-independent float-parser code. */ - if (sscanf(buf, "Spam: %5s ; %20s / %20s", is_spam, s_str, t_str) == 3) - { - m->score = _locale_safe_string_to_float (s_str, 20); - m->threshold = _locale_safe_string_to_float (t_str, 20); + if (sscanf(buf, "Spam: %5s ; %20s / %20s", is_spam, s_str, t_str) == 3) { + m->score = _locale_safe_string_to_float(s_str, 20); + m->threshold = _locale_safe_string_to_float(t_str, 20); + + /* set bounds on these to ensure no buffer overflow in the sprintf */ + if (m->score > 1e10) + m->score = 1e10; + else if (m->score < -1e10) + m->score = -1e10; + if (m->threshold > 1e10) + m->threshold = 1e10; + else if (m->threshold < -1e10) + m->threshold = -1e10; /* Format is "Spam: x; y / x" */ - m->is_spam=strcasecmp("true", is_spam) == 0 ? EX_ISSPAM: EX_NOTSPAM; + m->is_spam = + strcasecmp("true", is_spam) == 0 ? EX_ISSPAM : EX_NOTSPAM; - if(flags&SPAMC_CHECK_ONLY) { - m->out_len=snprintf (m->out, m->max_len+EXPANSION_ALLOWANCE, - "%.1f/%.1f\n", m->score, m->threshold); + if (flags & SPAMC_CHECK_ONLY) { + m->out_len = sprintf(m->out, + "%.1f/%.1f\n", m->score, m->threshold); } else if ((flags & SPAMC_REPORT_IFSPAM && m->is_spam == EX_ISSPAM) - || (flags & SPAMC_REPORT)) - { - m->out_len=snprintf (m->out, m->max_len+EXPANSION_ALLOWANCE, - "%.1f/%.1f\n", m->score, m->threshold); + || (flags & SPAMC_REPORT)) { + m->out_len = sprintf(m->out, + "%.1f/%.1f\n", m->score, m->threshold); } return EX_OK; - } else if(sscanf(buf, "Content-length: %d", &m->content_length) == 1) { + } + else if (sscanf(buf, "Content-length: %d", &m->content_length) == 1) { if (m->content_length < 0) { - syslog(LOG_ERR, "spamd responded with bad Content-length '%s'", buf); + libspamc_log(flags, LOG_ERR, "spamd responded with bad Content-length '%s'", + buf); return EX_PROTOCOL; } return EX_OK; } - syslog(LOG_ERR, "spamd responded with bad header '%s'", buf); + libspamc_log(flags, LOG_ERR, "spamd responded with bad header '%s'", buf); return EX_PROTOCOL; } int message_filter(struct transport *tp, const char *username, - int flags, struct message *m) + int flags, struct message *m) { char buf[8192]; - int bufsiz = (sizeof(buf) / sizeof(*buf)) - 4; /* bit of breathing room */ - int len, i; + size_t bufsiz = (sizeof(buf) / sizeof(*buf)) - 4; /* bit of breathing room */ + size_t len; int sock = -1; int rc; char versbuf[20]; float version; int response; int failureval; - SSL_CTX* ctx; - SSL* ssl; + SSL_CTX *ctx = NULL; + SSL *ssl = NULL; SSL_METHOD *meth; - if (flags&SPAMC_USE_SSL) { + if (flags & SPAMC_USE_SSL) { #ifdef SPAMC_SSL - SSLeay_add_ssl_algorithms(); - meth = SSLv2_client_method(); - SSL_load_error_strings(); - ctx = SSL_CTX_new(meth); + SSLeay_add_ssl_algorithms(); + meth = SSLv2_client_method(); + SSL_load_error_strings(); + ctx = SSL_CTX_new(meth); #else - UNUSED_VARIABLE(ssl); - UNUSED_VARIABLE(meth); - UNUSED_VARIABLE(ctx); - syslog(LOG_ERR, "spamc not built with SSL support"); - return EX_SOFTWARE; + UNUSED_VARIABLE(ssl); + UNUSED_VARIABLE(meth); + UNUSED_VARIABLE(ctx); + libspamc_log(flags, LOG_ERR, "spamc not built with SSL support"); + return EX_SOFTWARE; #endif - } + } - m->is_spam=EX_TOOBIG; - if((m->out=malloc(m->max_len+EXPANSION_ALLOWANCE+1))==NULL){ - failureval = EX_OSERR; goto failure; + m->is_spam = EX_TOOBIG; + if ((m->out = malloc(m->max_len + EXPANSION_ALLOWANCE + 1)) == NULL) { + failureval = EX_OSERR; + goto failure; } - m->out_len=0; + m->out_len = 0; /* Build spamd protocol header */ - if(flags & SPAMC_CHECK_ONLY) - len=snprintf(buf, bufsiz, "CHECK %s\r\n", PROTOCOL_VERSION); - else if(flags & SPAMC_REPORT_IFSPAM) - len=snprintf(buf, bufsiz, "REPORT_IFSPAM %s\r\n", PROTOCOL_VERSION); - else if(flags & SPAMC_REPORT) - len=snprintf(buf, bufsiz, "REPORT %s\r\n", PROTOCOL_VERSION); - else if(flags & SPAMC_SYMBOLS) - len=snprintf(buf, bufsiz, "SYMBOLS %s\r\n", PROTOCOL_VERSION); + if (flags & SPAMC_CHECK_ONLY) + strcpy(buf, "CHECK "); + else if (flags & SPAMC_REPORT_IFSPAM) + strcpy(buf, "REPORT_IFSPAM "); + else if (flags & SPAMC_REPORT) + strcpy(buf, "REPORT "); + else if (flags & SPAMC_SYMBOLS) + strcpy(buf, "SYMBOLS "); else - len=snprintf(buf, bufsiz, "PROCESS %s\r\n", PROTOCOL_VERSION); + strcpy(buf, "PROCESS "); + + len = strlen(buf); + if (len + strlen(PROTOCOL_VERSION) + 2 >= bufsiz) { + free(m->out); + m->out = m->msg; + m->out_len = m->msg_len; + return EX_OSERR; + } + + strcat(buf, PROTOCOL_VERSION); + strcat(buf, "\r\n"); + len = strlen(buf); - if(len<0 || len >= bufsiz){ free(m->out); m->out=m->msg; m->out_len=m->msg_len; return EX_OSERR; } - if(username!=NULL){ - len+=i=snprintf(buf+len, bufsiz-len, "User: %s\r\n", username); - if(i<0 || len >= bufsiz){ free(m->out); m->out=m->msg; m->out_len=m->msg_len; return EX_OSERR; } + if (username != NULL) { + if (strlen(username) + 8 >= (bufsiz - len)) { + free(m->out); + m->out = m->msg; + m->out_len = m->msg_len; + 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))) { + free(m->out); + m->out = m->msg; + m->out_len = m->msg_len; + return EX_OSERR; } - len+=i=snprintf(buf+len, bufsiz-len, "Content-length: %d\r\n", m->msg_len); - if(i<0 || len >= bufsiz){ free(m->out); m->out=m->msg; m->out_len=m->msg_len; return EX_OSERR; } - len+=i=snprintf(buf+len, bufsiz-len, "\r\n"); - if(i<0 || len >= bufsiz){ free(m->out); m->out=m->msg; m->out_len=m->msg_len; return EX_OSERR; } + len += sprintf(buf + len, "Content-length: %d\r\n\r\n", m->msg_len); libspamc_timeout = m->timeout; - if ( tp->socketpath ) - rc = try_to_connect_unix(tp, &sock); + if (tp->socketpath) + rc = _try_to_connect_unix(tp, &sock); else - rc = try_to_connect_tcp(tp, &sock); + rc = _try_to_connect_tcp(tp, &sock); - if ( rc != EX_OK ) - { - free(m->out); m->out=m->msg; m->out_len=m->msg_len; - return i; + if (rc != EX_OK) { + free(m->out); + m->out = m->msg; + m->out_len = m->msg_len; + return rc; /* use the error code try_to_connect_*() gave us. */ } - if(flags&SPAMC_USE_SSL) { + if (flags & SPAMC_USE_SSL) { #ifdef SPAMC_SSL - ssl = SSL_new(ctx); - SSL_set_fd(ssl, sock); - SSL_connect(ssl); -#endif + ssl = SSL_new(ctx); + SSL_set_fd(ssl, sock); + SSL_connect(ssl); +#endif } /* Send to spamd */ - if(flags&SPAMC_USE_SSL) { + if (flags & SPAMC_USE_SSL) { #ifdef SPAMC_SSL - SSL_write(ssl, buf, len); - SSL_write(ssl, m->msg, m->msg_len); + SSL_write(ssl, buf, len); + SSL_write(ssl, m->msg, m->msg_len); #endif - } else { - full_write(sock, buf, len); - full_write(sock, m->msg, m->msg_len); - shutdown(sock, SHUT_WR); + } + else { + full_write(sock, 0, buf, len); + full_write(sock, 0, m->msg, m->msg_len); + shutdown(sock, SHUT_WR); } /* ok, now read and parse it. SPAMD/1.2 line first... */ - failureval = _spamc_read_full_line (m, flags, ssl, sock, buf, &len, bufsiz); - if (failureval != EX_OK) { goto failure; } + failureval = + _spamc_read_full_line(m, flags, ssl, sock, buf, &len, bufsiz); + if (failureval != EX_OK) { + goto failure; + } - if(sscanf(buf, "SPAMD/%18s %d %*s", versbuf, &response)!=2) { - syslog(LOG_ERR, "spamd responded with bad string '%s'", buf); - failureval = EX_PROTOCOL; goto failure; + if (sscanf(buf, "SPAMD/%18s %d %*s", versbuf, &response) != 2) { + libspamc_log(flags, LOG_ERR, "spamd responded with bad string '%s'", buf); + failureval = EX_PROTOCOL; + goto failure; } versbuf[19] = '\0'; - version = _locale_safe_string_to_float (versbuf, 20); + version = _locale_safe_string_to_float(versbuf, 20); if (version < 1.0) { - syslog(LOG_ERR, "spamd responded with bad version string '%s'", versbuf); - failureval = EX_PROTOCOL; goto failure; + libspamc_log(flags, LOG_ERR, "spamd responded with bad version string '%s'", + versbuf); + failureval = EX_PROTOCOL; + goto failure; } m->score = 0; m->threshold = 0; m->is_spam = EX_TOOBIG; while (1) { - failureval = _spamc_read_full_line (m, flags, ssl, sock, buf, &len, bufsiz); - if (failureval != EX_OK) { goto failure; } + failureval = + _spamc_read_full_line(m, flags, ssl, sock, buf, &len, bufsiz); + if (failureval != EX_OK) { + goto failure; + } if (len == 0 && buf[0] == '\0') { - break; /* end of headers */ + break; /* end of headers */ } if (_handle_spamd_header(m, flags, buf, len) < 0) { - failureval = EX_PROTOCOL; goto failure; + failureval = EX_PROTOCOL; + goto failure; } } - len = 0; /* overwrite those headers */ + len = 0; /* overwrite those headers */ - if (flags&SPAMC_CHECK_ONLY) { - close(sock); sock = -1; + if (flags & SPAMC_CHECK_ONLY) { + closesocket(sock); + sock = -1; if (m->is_spam == EX_TOOBIG) { - /* We should have gotten headers back... Damnit. */ - failureval = EX_PROTOCOL; goto failure; + /* We should have gotten headers back... Damnit. */ + failureval = EX_PROTOCOL; + goto failure; } return EX_OK; } else { if (m->content_length < 0) { /* should have got a length too. */ - failureval = EX_PROTOCOL; goto failure; + failureval = EX_PROTOCOL; + goto failure; } /* have we already got something in the buffer (e.g. REPORT and - * REPORT_IFSPAM both create a line from the "Spam:" hdr)? If + * REPORT_IFSPAM both create a line from the "Spam:" hdr)? If * so, add the size of that so our sanity check passes. */ if (m->out_len > 0) { m->content_length += 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); - } else{ - len = full_read (sock, m->out+m->out_len, - m->max_len+EXPANSION_ALLOWANCE+1-m->out_len, - m->max_len+EXPANSION_ALLOWANCE+1-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); + } + 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); } - if(len+m->out_len>m->max_len+EXPANSION_ALLOWANCE){ - failureval = EX_TOOBIG; goto failure; + if (len + m->out_len > m->max_len + EXPANSION_ALLOWANCE) { + failureval = EX_TOOBIG; + goto failure; } - m->out_len+=len; + m->out_len += len; shutdown(sock, SHUT_RD); - close(sock); sock = -1; + closesocket(sock); + sock = -1; } libspamc_timeout = 0; - if(m->out_len!=m->content_length) { - syslog(LOG_ERR, "failed sanity check, %d bytes claimed, %d bytes seen", - m->content_length, m->out_len); - failureval = EX_PROTOCOL; goto failure; + if (m->out_len != m->content_length) { + libspamc_log(flags, LOG_ERR, + "failed sanity check, %d bytes claimed, %d bytes seen", + m->content_length, m->out_len); + failureval = EX_PROTOCOL; + goto failure; } return EX_OK; -failure: - free(m->out); m->out=m->msg; m->out_len=m->msg_len; + failure: + free(m->out); + m->out = m->msg; + m->out_len = m->msg_len; if (sock != -1) { - close(sock); + closesocket(sock); } libspamc_timeout = 0; - if(flags&SPAMC_USE_SSL) { + if (flags & SPAMC_USE_SSL) { #ifdef SPAMC_SSL - SSL_free(ssl); - SSL_CTX_free(ctx); + SSL_free(ssl); + SSL_CTX_free(ctx); #endif } 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) +{ int ret; struct message m; - m.type=MESSAGE_NONE; - - m.max_len=max_size; - ret=message_read(in_fd, flags, &m); - if(ret!=EX_OK) goto FAIL; - ret=message_filter(trans, username, flags, &m); - if(ret!=EX_OK) goto FAIL; - if(message_write(out_fd, &m)<0) goto FAIL; - if(m.is_spam!=EX_TOOBIG) { - message_cleanup(&m); - return m.is_spam; + m.type = MESSAGE_NONE; + + m.max_len = max_size; + ret = message_read(in_fd, flags, &m); + if (ret != EX_OK) + goto FAIL; + ret = message_filter(trans, username, flags, &m); + if (ret != EX_OK) + goto FAIL; + if (message_write(out_fd, &m) < 0) + goto FAIL; + if (m.is_spam != EX_TOOBIG) { + message_cleanup(&m); + return m.is_spam; } message_cleanup(&m); return ret; -FAIL: - if(flags&SPAMC_CHECK_ONLY){ - full_write(out_fd, "0/0\n", 4); - message_cleanup(&m); - return EX_NOTSPAM; - } else { - message_dump(in_fd, out_fd, &m); - message_cleanup(&m); - return ret; + FAIL: + if (flags & SPAMC_CHECK_ONLY) { + full_write(out_fd, 1, "0/0\n", 4); + message_cleanup(&m); + return EX_NOTSPAM; + } + else { + message_dump(in_fd, out_fd, &m); + message_cleanup(&m); + return ret; } } -void message_cleanup(struct message *m) { - if (m->out != NULL && m->out != m->raw) free(m->out); - if (m->raw != NULL) free(m->raw); - if (m->priv != NULL) free(m->priv); - clear_message(m); +void message_cleanup(struct message *m) +{ + if (m->out != NULL && m->pre != NULL && m->out != m->pre+m->pre_len) + free(m->out); + if (m->raw != NULL) + free(m->raw); + if (m->priv != NULL) + free(m->priv); + _clear_message(m); } /* Aug 14, 2002 bj: Obsolete! */ -int process_message(struct transport *tp, char *username, int max_size, int in_fd, int out_fd, const int my_check_only, const int my_safe_fallback){ +int process_message(struct transport *tp, char *username, int max_size, + int in_fd, int out_fd, const int my_check_only, + const int my_safe_fallback) +{ int flags; - flags=SPAMC_RAW_MODE; - if(my_check_only) flags|=SPAMC_CHECK_ONLY; - if(my_safe_fallback) flags|=SPAMC_SAFE_FALLBACK; + flags = SPAMC_RAW_MODE; + if (my_check_only) + flags |= SPAMC_CHECK_ONLY; + if (my_safe_fallback) + flags |= SPAMC_SAFE_FALLBACK; return message_process(tp, username, max_size, in_fd, out_fd, flags); } @@ -1202,12 +1120,13 @@ int process_message(struct transport *tp, char *username, int max_size, int in_f */ void transport_init(struct transport *tp) { - assert(tp != 0); + assert(tp != 0); - memset(tp, 0, sizeof *tp); + memset(tp, 0, sizeof *tp); - tp->type = TRANSPORT_LOCALHOST; - tp->port = 783; + tp->type = TRANSPORT_LOCALHOST; + tp->port = 783; + tp->flags = 0; } /* @@ -1220,26 +1139,26 @@ void transport_init(struct transport *tp) * do anything unless */ -static void randomize_hosts(struct transport *tp) +static void _randomize_hosts(struct transport *tp) { -int rnum; + int rnum; - assert(tp != 0); + assert(tp != 0); - if ( tp->nhosts <= 1 ) return; + if (tp->nhosts <= 1) + return; - rnum = rand() % tp->nhosts; + rnum = rand() % tp->nhosts; - while ( rnum-- > 0 ) - { - struct in_addr tmp = tp->hosts[0]; - int i; + while (rnum-- > 0) { + struct in_addr tmp = tp->hosts[0]; + int i; - 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]; - tp->hosts[i-1] = tmp; - } + tp->hosts[i - 1] = tmp; + } } /* @@ -1259,59 +1178,68 @@ int rnum; */ int transport_setup(struct transport *tp, int flags) { -struct hostent *hp = 0; -char **addrp; - - assert(tp != 0); - - switch ( tp->type ) - { - case TRANSPORT_UNIX: - assert(tp->socketpath != 0); - return EX_OK; - - case TRANSPORT_LOCALHOST: - tp->hosts[0].s_addr = inet_addr("127.0.0.1"); - tp->nhosts = 1; - return EX_OK; - - case TRANSPORT_TCP: - if (NULL == (hp = gethostbyname(tp->hostname))) - { - int origherr = h_errno; /* take a copy before syslog() */ - - syslog (LOG_ERR, "gethostbyname(%s) failed: h_errno=%d", - tp->hostname, origherr); - switch (origherr) - { - case HOST_NOT_FOUND: - case NO_ADDRESS: - case NO_RECOVERY: - return EX_NOHOST; - case TRY_AGAIN: - return EX_TEMPFAIL; - default: - return EX_OSERR; - } - } + struct hostent *hp = 0; + char **addrp; + +#ifdef _WIN32 + // Start Winsock up + WSADATA wsaData; + int nCode; + if ((nCode = WSAStartup(MAKEWORD(1, 1), &wsaData)) != 0) { + printf("WSAStartup() returned error code %d\n", nCode); + return EX_OSERR; + } + +#endif + + tp->flags = flags; + + assert(tp != 0); + + switch (tp->type) { +#ifndef _WIN32 + case TRANSPORT_UNIX: + assert(tp->socketpath != 0); + return EX_OK; +#endif + case TRANSPORT_LOCALHOST: + tp->hosts[0].s_addr = inet_addr("127.0.0.1"); + tp->nhosts = 1; + return EX_OK; + + case TRANSPORT_TCP: + if (NULL == (hp = gethostbyname(tp->hostname))) { + int origherr = h_errno; /* take a copy before syslog() */ + + libspamc_log(flags, LOG_ERR, "gethostbyname(%s) failed: h_errno=%d", + tp->hostname, origherr); + switch (origherr) { + case HOST_NOT_FOUND: + case NO_ADDRESS: + case NO_RECOVERY: + return EX_NOHOST; + case TRY_AGAIN: + return EX_TEMPFAIL; + default: + return EX_OSERR; + } + } /*-------------------------------------------------------- * 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. */ - if ( hp->h_addr_list[0] == 0 ) - { - /* no hosts in this list */ - return EX_NOHOST; - } + if (hp->h_addr_list[0] == 0) { + /* no hosts in this list */ + return EX_NOHOST; + } - if ( hp->h_length != sizeof tp->hosts[0] - || hp->h_addrtype != AF_INET ) - { - /* FAIL - bad size/protocol/family? */ - return EX_NOHOST; - } + if (hp->h_length != sizeof tp->hosts[0] + || hp->h_addrtype != AF_INET) { + /* FAIL - bad size/protocol/family? */ + return EX_NOHOST; + } /*-------------------------------------------------------- * Copy all the IP addresses into our private structure. @@ -1319,20 +1247,19 @@ char **addrp; * means we won't ever walk all over the list with other * calls. */ - tp->nhosts = 0; + tp->nhosts = 0; - for (addrp = hp->h_addr_list; *addrp; addrp++) - { - if (tp->nhosts >= TRANSPORT_MAX_HOSTS-1) { - syslog (LOG_ERR, "hit limit of %d hosts, ignoring remainder", TRANSPORT_MAX_HOSTS-1); - break; - } + for (addrp = hp->h_addr_list; *addrp; addrp++) { + if (tp->nhosts >= TRANSPORT_MAX_HOSTS - 1) { + libspamc_log(flags, LOG_ERR, "hit limit of %d hosts, ignoring remainder", + TRANSPORT_MAX_HOSTS - 1); + break; + } - memcpy(&tp->hosts[tp->nhosts], *addrp, - sizeof tp->hosts[0]); + memcpy(&tp->hosts[tp->nhosts], *addrp, sizeof tp->hosts[0]); - tp->nhosts++; - } + tp->nhosts++; + } /*-------------------------------------------------------- * QUASI-LOAD-BALANCING @@ -1342,25 +1269,61 @@ char **addrp; * This may later be truncated to a single item. This is * meaningful only if we have more than one host. */ - if ( (flags & SPAMC_RANDOMIZE_HOSTS) && tp->nhosts > 1 ) - { - randomize_hosts(tp); - } + if ((flags & SPAMC_RANDOMIZE_HOSTS) && tp->nhosts > 1) { + _randomize_hosts(tp); + } /*-------------------------------------------------------- * If the user wants no fallback, simply truncate the host * list to just one - this pretends that this is the extent * of our connection list - then it's not a special case. */ - if ( !(flags & SPAMC_SAFE_FALLBACK) && tp->nhosts > 1 ) - { - /* truncating list */ - tp->nhosts = 1; - } + if (!(flags & SPAMC_SAFE_FALLBACK) && tp->nhosts > 1) { + /* truncating list */ + tp->nhosts = 1; } - return EX_OK; + } + return EX_OK; } +/* --------------------------------------------------------------------------- */ + +#define LOG_BUFSIZ 1023 + +void +libspamc_log (int flags, int level, char *msg, ...) +{ + va_list ap; + char buf[LOG_BUFSIZ+1]; + int len = 0; + + va_start(ap, msg); + + if ((flags & SPAMC_LOG_TO_STDERR) != 0) { + // create a log-line buffer + len = snprintf(buf, LOG_BUFSIZ, "spamc: "); + len += vsnprintf(buf+len, LOG_BUFSIZ-len, msg, ap); + + // avoid buffer overflow + if (len > (LOG_BUFSIZ-2)) { len = (LOG_BUFSIZ-3); } + + len += snprintf(buf+len, LOG_BUFSIZ-len, "\n"); + buf[LOG_BUFSIZ] = '\0'; /* ensure termination */ + (void) write (2, buf, len); + + } else { + vsnprintf(buf, LOG_BUFSIZ, msg, ap); + buf[LOG_BUFSIZ] = '\0'; /* ensure termination */ +#ifndef _WIN32 + syslog (level, "%s", buf); +#else + (void) level; /* not used. suppress compiler warning */ + fprintf (stderr, "%s\n", buf); +#endif + } + + va_end(ap); +} /* --------------------------------------------------------------------------- */ @@ -1373,52 +1336,57 @@ char **addrp; */ #ifdef LIBSPAMC_UNIT_TESTS -static void -_test_locale_safe_string_to_float_val (float input) { - char inputstr[99], cmpbuf1[99], cmpbuf2[99]; - float output; - - snprintf (inputstr, 99, "%f", input); - output = _locale_safe_string_to_float (inputstr, 99); - if (input == output) { return; } +static void _test_locale_safe_string_to_float_val(float input) +{ + char inputstr[99], cmpbuf1[99], cmpbuf2[99]; + float output; + + /* sprintf instead of snprintf is safe here because it is only a controlled test */ + sprintf(inputstr, "%f", input); + output = _locale_safe_string_to_float(inputstr, 99); + if (input == output) { + return; + } - /* could be a rounding error. print as string and compare those */ - snprintf (cmpbuf1, 98, "%f", input); - snprintf (cmpbuf2, 98, "%f", output); - if (!strcmp (cmpbuf1, cmpbuf2)) { return; } + /* could be a rounding error. print as string and compare those */ + sprintf(cmpbuf1, "%f", input); + sprintf(cmpbuf2, "%f", output); + if (!strcmp(cmpbuf1, cmpbuf2)) { + return; + } - printf ("FAIL: input=%f != output=%f\n", input, output); + printf("FAIL: input=%f != output=%f\n", input, output); } -static void -unit_test_locale_safe_string_to_float (void) { - float statictestset[] = { /* will try both +ve and -ve */ +static void unit_test_locale_safe_string_to_float(void) +{ + float statictestset[] = { /* will try both +ve and -ve */ 0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001, 9.1, 9.91, 9.991, 9.9991, 9.99991, 9.999991, - 0.0 /* end of set constant */ - }; - float num; - int i; - - printf ("starting unit_test_locale_safe_string_to_float\n"); - /* tests of precision */ - for (i = 0; statictestset[i] != 0.0; i++) { - _test_locale_safe_string_to_float_val (statictestset[i]); - _test_locale_safe_string_to_float_val (-statictestset[i]); - _test_locale_safe_string_to_float_val (1-statictestset[i]); - _test_locale_safe_string_to_float_val (1+statictestset[i]); - } - /* now exhaustive, in steps of 0.01 */ - for (num = -1000.0; num < 1000.0; num += 0.01) { - _test_locale_safe_string_to_float_val (num); - } - printf ("finished unit_test_locale_safe_string_to_float\n"); + 0.0 /* end of set constant */ + }; + float num; + int i; + + printf("starting unit_test_locale_safe_string_to_float\n"); + /* tests of precision */ + for (i = 0; statictestset[i] != 0.0; i++) { + _test_locale_safe_string_to_float_val(statictestset[i]); + _test_locale_safe_string_to_float_val(-statictestset[i]); + _test_locale_safe_string_to_float_val(1 - statictestset[i]); + _test_locale_safe_string_to_float_val(1 + statictestset[i]); + } + /* now exhaustive, in steps of 0.01 */ + for (num = -1000.0; num < 1000.0; num += 0.01) { + _test_locale_safe_string_to_float_val(num); + } + printf("finished unit_test_locale_safe_string_to_float\n"); } -void -do_libspamc_unit_tests (void) { - unit_test_locale_safe_string_to_float(); - exit (0); +void do_libspamc_unit_tests(void) +{ + unit_test_locale_safe_string_to_float(); + exit(0); } #endif /* LIBSPAMC_UNIT_TESTS */ diff --git a/src/plugins/spamassassin/libspamc.h b/src/plugins/spamassassin/libspamc.h index cf00e8aff..dd44ddc1f 100644 --- a/src/plugins/spamassassin/libspamc.h +++ b/src/plugins/spamassassin/libspamc.h @@ -1,19 +1,75 @@ -/* - * This code is copyright 2001 by Craig Hughes - * Conversion to a thread-safe shared library copyright 2002 Liam Widdowson - * Portions copyright 2002 by Brad Jorsch - * It is licensed under the same license as Perl itself. The text of this - * license is included in the SpamAssassin distribution in the file named - * "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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * */ #ifndef LIBSPAMC_H #define LIBSPAMC_H 1 +#include +#include #include +#ifdef _WIN32 +#ifdef _MSC_VER +/* ignore MSVC++ warnings that are annoying and hard to remove: + 4115 named type definition in parentheses + 4127 conditional expression is constant + 4514 unreferenced inline function removed + */ +#pragma warning( disable : 4115 4127 4514 ) +#endif +#include +#else +#include #include #include -#include -#include +#endif + +#ifdef _WIN32 +/* FIXME: This stuff has to go somewhere else */ + +#define EX_OK 0 +#define EX_USAGE 64 +#define EX_DATAERR 65 +#define EX_NOINPUT 66 +#define EX_NOUSER 67 +#define EX_NOHOST 68 +#define EX_UNAVAILABLE 69 +#define EX_SOFTWARE 70 +#define EX_OSERR 71 +#define EX_OSFILE 72 +#define EX_CANTCREAT 73 +#define EX_IOERR 74 +#define EX_TEMPFAIL 75 +#define EX_PROTOCOL 76 +#define EX_NOPERM 77 +#define EX_CONFIG 78 + +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 + +/* FIXME: This doesn't belong here either */ +#define LOG_EMERG 0 /* system is unusable */ +#define LOG_ALERT 1 /* action must be taken immediately */ +#define LOG_CRIT 2 /* critical conditions */ +#define LOG_ERR 3 /* error conditions */ +#define LOG_WARNING 4 /* warning conditions */ +#define LOG_NOTICE 5 /* normal but significant condition */ +#define LOG_INFO 6 /* informational */ +#define LOG_DEBUG 7 /* debug-level messages */ + +#endif #define EX_NOTSPAM 0 #define EX_ISSPAM 1 @@ -38,9 +94,12 @@ /* 2003/04/16 SJF: randomize hostname order (quasi load balancing) */ #define SPAMC_RANDOMIZE_HOSTS (1<<23) +/* log to stderr */ +#define SPAMC_LOG_TO_STDERR (1<<22) /* Aug 14, 2002 bj: A struct for storing a message-in-progress */ -typedef enum { +typedef enum +{ MESSAGE_NONE, MESSAGE_ERROR, MESSAGE_RAW, @@ -50,26 +109,32 @@ typedef enum { struct libspamc_private_message; -struct message { +struct message +{ /* Set before passing the struct on! */ - int max_len; /* messages larger than this will return EX_TOOBIG */ - int timeout; /* timeout for read() system calls */ + unsigned int max_len; /* messages larger than this will return EX_TOOBIG */ + int timeout; /* timeout for read() system calls */ /* Filled in by message_read */ message_type_t type; - char *raw; int raw_len; /* Raw message buffer */ - char *pre; int pre_len; /* Pre-message data (e.g. SMTP commands) */ - char *msg; int msg_len; /* The message */ - char *post; int post_len; /* Post-message data (e.g. SMTP commands) */ + char *raw; + unsigned int raw_len; /* Raw message buffer */ + char *pre; + int pre_len; /* Pre-message data (e.g. SMTP commands) */ + char *msg; + unsigned int msg_len; /* The message */ + char *post; + int post_len; /* Post-message data (e.g. SMTP commands) */ int content_length; /* Filled in by filter_message */ - int is_spam; /* EX_ISSPAM if the message is spam, EX_NOTSPAM - if not */ - float score, threshold; /* score and threshold */ - char *out; int out_len; /* Output from spamd. Either the filtered - message, or the check-only response. Or else, - a pointer to msg above. */ + int is_spam; /* EX_ISSPAM if the message is spam, EX_NOTSPAM + if not */ + float score, threshold; /* score and threshold */ + char *out; + int out_len; /* Output from spamd. Either the filtered + message, or the check-only response. Or else, + a pointer to msg above. */ /* these members added in SpamAssassin version 2.60: */ struct libspamc_private_message *priv; @@ -99,25 +164,27 @@ struct message { * code just loops over that same address. */ #define TRANSPORT_LOCALHOST 0x01 /* TCP to localhost only */ -#define TRANSPORT_TCP 0x02 /* standard TCP socket */ -#define TRANSPORT_UNIX 0x03 /* UNIX domain socket */ +#define TRANSPORT_TCP 0x02 /* standard TCP socket */ +#define TRANSPORT_UNIX 0x03 /* UNIX domain socket */ -#define TRANSPORT_MAX_HOSTS 256 /* max hosts we can failover between */ +#define TRANSPORT_MAX_HOSTS 256 /* max hosts we can failover between */ -struct transport { - int type; +struct transport +{ + int type; - const char *socketpath; /* for UNIX dommain socket */ - const char *hostname; /* for TCP sockets */ + const char *socketpath; /* for UNIX dommain socket */ + const char *hostname; /* for TCP sockets */ - unsigned short port; /* for TCP sockets */ + unsigned short port; /* for TCP sockets */ - struct in_addr hosts[TRANSPORT_MAX_HOSTS]; - int nhosts; + struct in_addr hosts[TRANSPORT_MAX_HOSTS]; + int nhosts; + int flags; }; extern void transport_init(struct transport *tp); -extern int transport_setup(struct transport *tp, int flags); +extern int transport_setup(struct transport *tp, int flags); /* Aug 14, 2002 bj: New interface functions */ @@ -138,7 +205,7 @@ long message_write(int out_fd, struct message *m); * no failover is done. */ int message_filter(struct transport *tp, const char *username, - int flags, struct message *m); + int flags, struct message *m); /* Dump the message. If there is any data in the message (typically, m->type * will be MESSAGE_ERROR) it will be message_writed. Then, fd_in will be piped @@ -149,16 +216,18 @@ void message_dump(int in_fd, int out_fd, struct message *m); /* Do a message_read->message_filter->message_write sequence, handling errors * appropriately with dump_message or appropriate CHECK_ONLY output. Returns * EX_OK or EX_ISSPAM/EX_NOTSPAM on success, some error EX on error. */ -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); /* Cleanup the resources we allocated for storing the message. Call after * you're done processing. */ void message_cleanup(struct message *m); /* Aug 14, 2002 bj: This is now legacy, don't use it. */ -int process_message(struct transport *tp, char *username, - int max_size, int in_fd, int out_fd, - const int check_only, const int safe_fallback); +int process_message(struct transport *tp, char *username, + int max_size, int in_fd, int out_fd, + const int check_only, const int safe_fallback); -#endif +void libspamc_log(int flags, int level, char *msg, ...); +#endif diff --git a/src/plugins/spamassassin/utils.c b/src/plugins/spamassassin/utils.c index b67ca673e..f42c961ca 100644 --- a/src/plugins/spamassassin/utils.c +++ b/src/plugins/spamassassin/utils.c @@ -1,17 +1,40 @@ -/* - * This code is copyright 2001 by Craig Hughes - * Portions copyright 2002 by Brad Jorsch - * It is licensed under the same license as Perl itself. The text of this - * license is included in the SpamAssassin distribution in the file named - * "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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * */ +#ifndef _WIN32 #include +#include +#include +#include +#else + +#ifdef _MSC_VER +/* ignore MSVC++ warnings that are annoying and hard to remove: + 4115 named type definition in parentheses + 4127 conditional expression is constant + 4514 unreferenced inline function removed + */ +#pragma warning( disable : 4115 4127 4514 ) +#endif + +#include +#endif #include #include -#include -#include -#include #include #include "utils.h" @@ -25,151 +48,194 @@ /* Apr 24, 2003 sjf: made full_read and full_write void* params */ /* -------------------------------------------------------------------------- */ +#ifndef _WIN32 +typedef void sigfunc(int); /* for signal handlers */ -typedef void sigfunc(int); /* for signal handlers */ - -sigfunc* sig_catch(int sig, void (*f)(int)) +sigfunc *sig_catch(int sig, void (*f) (int)) { - struct sigaction act, oact; - act.sa_handler = f; - act.sa_flags = 0; - sigemptyset(&act.sa_mask); - sigaction(sig, &act, &oact); - return oact.sa_handler; + struct sigaction act, oact; + act.sa_handler = f; + act.sa_flags = 0; + sigemptyset(&act.sa_mask); + sigaction(sig, &act, &oact); + return oact.sa_handler; } -static void catch_alrm(int x) { - UNUSED_VARIABLE(x); +static void catch_alrm(int x) +{ + UNUSED_VARIABLE(x); } +#endif -ssize_t -fd_timeout_read (int fd, void *buf, size_t nbytes) +int fd_timeout_read(int fd, char fdflag, void *buf, size_t nbytes) { - ssize_t nred; - sigfunc* sig; - - sig = sig_catch(SIGALRM, catch_alrm); - if (libspamc_timeout > 0) { - alarm(libspamc_timeout); - } + int nred; + int origerr; +#ifndef _WIN32 + sigfunc *sig; + + sig = sig_catch(SIGALRM, catch_alrm); + if (libspamc_timeout > 0) { + alarm(libspamc_timeout); + } +#endif - do { - nred = read (fd, buf, nbytes); - } while(nred < 0 && errno == EAGAIN); + do { + if (fdflag) { + nred = (int)read(fd, buf, nbytes); + origerr = errno; + } + else { + nred = (int)recv(fd, buf, nbytes, 0); +#ifndef _WIN32 + origerr = errno; +#else + origerr = WSAGetLastError(); +#endif + } + } while (nred < 0 && origerr == EWOULDBLOCK); - if(nred < 0 && errno == EINTR) - errno = ETIMEDOUT; +#ifndef _WIN32 + if (nred < 0 && origerr == EINTR) + errno = ETIMEDOUT; - if (libspamc_timeout > 0) { - alarm(0); - } + if (libspamc_timeout > 0) { + alarm(0); + } - /* restore old signal handler */ - sig_catch(SIGALRM, sig); + /* restore old signal handler */ + sig_catch(SIGALRM, sig); +#endif - return nred; + return nred; } -int -ssl_timeout_read (SSL *ssl, void *buf, int nbytes) +int ssl_timeout_read(SSL * ssl, void *buf, int nbytes) { - int nred; - sigfunc* sig; + int nred; + +#ifndef _WIN32 + sigfunc *sig; -#ifndef SPAMC_SSL - UNUSED_VARIABLE(ssl); - UNUSED_VARIABLE(buf); - UNUSED_VARIABLE(nbytes); + sig = sig_catch(SIGALRM, catch_alrm); + if (libspamc_timeout > 0) { + alarm(libspamc_timeout); + } #endif - sig = sig_catch(SIGALRM, catch_alrm); - if (libspamc_timeout > 0) { - alarm(libspamc_timeout); - } + do { - do { #ifdef SPAMC_SSL - nred = SSL_read (ssl, buf, nbytes); + nred = SSL_read(ssl, buf, nbytes); #else - nred = 0; /* never used */ + UNUSED_VARIABLE(ssl); + UNUSED_VARIABLE(buf); + UNUSED_VARIABLE(nbytes); + nred = 0; /* never used */ #endif - } while(nred < 0 && errno == EAGAIN); - if(nred < 0 && errno == EINTR) - errno = ETIMEDOUT; + } while (nred < 0 && errno == EWOULDBLOCK); + +#ifndef _WIN32 + if (nred < 0 && errno == EINTR) + errno = ETIMEDOUT; - if (libspamc_timeout > 0) { - alarm(0); - } + if (libspamc_timeout > 0) { + alarm(0); + } - /* restore old signal handler */ - sig_catch(SIGALRM, sig); + /* restore old signal handler */ + sig_catch(SIGALRM, sig); +#endif - return nred; + return nred; } /* -------------------------------------------------------------------------- */ -int -full_read (int fd, void *vbuf, int min, int len) +int full_read(int fd, char fdflag, void *vbuf, int min, int len) { - unsigned char *buf = (unsigned char *)vbuf; - int total; - int thistime; - - for (total = 0; total < min; ) { - thistime = fd_timeout_read (fd, buf+total, len-total); - - if (thistime < 0) { - return -1; - } else if (thistime == 0) { - /* EOF, but we didn't read the minimum. return what we've read - * so far and next read (if there is one) will return 0. */ - return total; + unsigned char *buf = (unsigned char *) vbuf; + int total; + int thistime; + + for (total = 0; total < min;) { + thistime = fd_timeout_read(fd, fdflag, buf + total, len - total); + + if (thistime < 0) { + if (total >= min) { + /* error, but we read *some*. return what we've read + * so far and next read (if there is one) will return -1. */ + return total; + } else { + return -1; + } + } + else if (thistime == 0) { + /* EOF, but we didn't read the minimum. return what we've read + * so far and next read (if there is one) will return 0. */ + return total; + } + + total += thistime; } - - total += thistime; - } - return total; + return total; } -int -full_read_ssl (SSL *ssl, unsigned char *buf, int min, int len) +int full_read_ssl(SSL * ssl, unsigned char *buf, int min, int len) { - int total; - int thistime; - - for (total = 0; total < min; ) { - thistime = ssl_timeout_read (ssl, buf+total, len-total); - - if (thistime < 0) { - return -1; - } else if (thistime == 0) { - /* EOF, but we didn't read the minimum. return what we've read - * so far and next read (if there is one) will return 0. */ - return total; + int total; + int thistime; + + for (total = 0; total < min;) { + thistime = ssl_timeout_read(ssl, buf + total, len - total); + + if (thistime < 0) { + if (total >= min) { + /* error, but we read *some*. return what we've read + * so far and next read (if there is one) will return -1. */ + return total; + } else { + return -1; + } + } + else if (thistime == 0) { + /* EOF, but we didn't read the minimum. return what we've read + * so far and next read (if there is one) will return 0. */ + return total; + } + + total += thistime; } - - total += thistime; - } - return total; + return total; } -int -full_write (int fd, const void *vbuf, int len) +int full_write(int fd, char fdflag, const void *vbuf, int len) { - const unsigned char *buf = (const unsigned char *)vbuf; - int total; - int thistime; - - for (total = 0; total < len; ) { - thistime = write (fd, buf+total, len-total); - - if (thistime < 0) { - if(EINTR == errno || EAGAIN == errno) continue; - return thistime; /* always an error for writes */ + const char *buf = (const char *) vbuf; + int total; + int thistime; + int origerr; + + for (total = 0; total < len;) { + if (fdflag) { + thistime = write(fd, buf + total, len - total); + origerr = errno; + } + else { + thistime = send(fd, buf + total, len - total, 0); +#ifndef _WIN32 + origerr = errno; +#else + origerr = WSAGetLastError(); +#endif + } + if (thistime < 0) { + if (EINTR == origerr || EWOULDBLOCK == origerr) + continue; + return thistime; /* always an error for writes */ + } + total += thistime; } - total += thistime; - } - return total; + return total; } diff --git a/src/plugins/spamassassin/utils.h b/src/plugins/spamassassin/utils.h index 4df7c5333..3a287546f 100644 --- a/src/plugins/spamassassin/utils.h +++ b/src/plugins/spamassassin/utils.h @@ -1,9 +1,26 @@ +/* <@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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + #ifndef UTILS_H #define UTILS_H #define UNUSED_VARIABLE(v) ((void)(v)) -extern int libspamc_timeout; /* default timeout in seconds */ +extern int libspamc_timeout; /* default timeout in seconds */ #ifdef SPAMC_SSL #include @@ -11,17 +28,69 @@ extern int libspamc_timeout; /* default timeout in seconds */ #include #include #else -typedef int SSL; /* fake type to avoid conditional compilation */ -typedef int SSL_CTX; -typedef int SSL_METHOD; +typedef int SSL; /* fake type to avoid conditional compilation */ +typedef int SSL_CTX; +typedef int SSL_METHOD; +#endif + +#ifdef _WIN32 +#include +// +// BSD-compatible socket error codes for Win32 +// + +#define EWOULDBLOCK WSAEWOULDBLOCK +#define EINPROGRESS WSAEINPROGRESS +#define EALREADY WSAEALREADY +#define ENOTSOCK WSAENOTSOCK +#define EDESTADDRREQ WSAEDESTADDRREQ +#define EMSGSIZE WSAEMSGSIZE +#define EPROTOTYPE WSAEPROTOTYPE +#define ENOPROTOOPT WSAENOPROTOOPT +#define EPROTONOSUPPORT WSAEPROTONOSUPPORT +#define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT +#define EOPNOTSUPP WSAEOPNOTSUPP +#define EPFNOSUPPORT WSAEPFNOSUPPORT +#define EAFNOSUPPORT WSAEAFNOSUPPORT +#define EADDRINUSE WSAEADDRINUSE +#define EADDRNOTAVAIL WSAEADDRNOTAVAIL +#define ENETDOWN WSAENETDOWN +#define ENETUNREACH WSAENETUNREACH +#define ENETRESET WSAENETRESET +#define ECONNABORTED WSAECONNABORTED +#define ECONNRESET WSAECONNRESET +#define ENOBUFS WSAENOBUFS +#define EISCONN WSAEISCONN +#define ENOTCONN WSAENOTCONN +#define ESHUTDOWN WSAESHUTDOWN +#define ETOOMANYREFS WSAETOOMANYREFS +#define ETIMEDOUT WSAETIMEDOUT +#define ECONNREFUSED WSAECONNREFUSED +#define ELOOP WSAELOOP +// #define ENAMETOOLONG WSAENAMETOOLONG +#define EHOSTDOWN WSAEHOSTDOWN +#define EHOSTUNREACH WSAEHOSTUNREACH +// #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! +#define HOST_NOT_FOUND WSAHOST_NOT_FOUND +#define TRY_AGAIN WSATRY_AGAIN +#define NO_RECOVERY WSANO_RECOVERY +#define NO_DATA WSANO_DATA + #endif -ssize_t fd_timeout_read (int fd, 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); /* these are fd-only, no SSL support */ -int full_read(int fd, void *buf, int min, int len); -int full_read_ssl(SSL *ssl, unsigned char *buf, int min, int len); -int full_write(int fd, const void *buf, int len); +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); +int full_write(int fd, char fdflag, const void *buf, int len); #endif -- 2.25.1