2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to you under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at:
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
20 Compile with extra warnings -- gcc only, not suitable for use as default:
22 gcc -Wextra -Wdeclaration-after-statement -Wall -g -O2 spamc/spamc.c \
23 spamc/getopt.c spamc/libspamc.c spamc/utils.c -o spamc/spamc -ldl -lz
27 #include "claws-features.h"
36 #define snprintf _snprintf
37 #define vsnprintf _vsnprintf
38 #define strcasecmp stricmp
44 #include <sys/types.h>
45 #include <sys/socket.h>
46 #include <netinet/in.h>
48 #include <netinet/tcp.h>
49 #include <arpa/inet.h>
50 #define closesocket(x) close(x)
53 #ifdef HAVE_SYSEXITS_H
59 #ifdef HAVE_SYS_ERRNO_H
60 #include <sys/errno.h>
65 #ifdef HAVE_SYS_TIME_H
72 /* must load *after* errno.h, Bug 6697 */
75 /* RedHat 5.2 doesn't define Shutdown 2nd Parameter Constants */
77 /* SJF 2003/04/25 - now test for macros directly */
79 # define SHUT_RD 0 /* no more receptions */
82 # define SHUT_WR 1 /* no more transmissions */
85 # define SHUT_RDWR 2 /* no more receptions or transmissions */
93 #define spamc_get_errno() WSAGetLastError()
95 #define spamc_get_errno() errno
102 #ifndef HAVE_INADDR_NONE
103 #define INADDR_NONE ((in_addr_t) 0xffffffff)
106 /* jm: turned off for now, it should not be necessary. */
107 #undef USE_TCP_NODELAY
110 /* jm: very conservative figure, should be well out of range on almost all NIXes */
114 #undef DO_CONNECT_DEBUG_SYSLOGS
116 #define DO_CONNECT_DEBUG_SYSLOGS 1
117 #define CONNECT_DEBUG_LEVEL LOG_DEBUG
120 /* bug 4477 comment 14 */
122 #define SPAMC_MAXHOST NI_MAXHOST
124 #define SPAMC_MAXHOST 256
128 #define SPAMC_MAXSERV NI_MAXSERV
130 #define SPAMC_MAXSERV 256
133 /* static const int ESC_PASSTHROUGHRAW = EX__MAX + 666; No longer seems to be used */
135 /* set EXPANSION_ALLOWANCE to something more than might be
136 added to a message in X-headers and the report template */
137 static const int EXPANSION_ALLOWANCE = 16384;
139 /* set NUM_CHECK_BYTES to number of bytes that have to match at beginning and end
140 of the data streams before and after processing by spamd
141 Aug 7 2002 jm: no longer seems to be used
142 static const int NUM_CHECK_BYTES = 32;
145 /* Set the protocol version that this spamc speaks */
146 static const char *PROTOCOL_VERSION = "SPAMC/1.5";
148 /* "private" part of struct message.
149 * we use this instead of the struct message directly, so that we
150 * can add new members without affecting the ABI.
152 struct libspamc_private_message
154 int flags; /* copied from "flags" arg to message_read() */
155 int alloced_size; /* allocated space for the "out" buffer */
157 void (*spamc_header_callback)(struct message *m, int flags, char *buf, int len);
158 void (*spamd_header_callback)(struct message *m, int flags, const char *buf, int len);
161 void (*libspamc_log_callback)(int flags, int level, char *msg, va_list args) = NULL;
163 int libspamc_timeout = 0;
164 int libspamc_connect_timeout = 0; /* Sep 8, 2008 mrgus: separate connect timeout */
167 * translate_connect_errno()
169 * Given a UNIX error number obtained (probably) from "connect(2)",
170 * translate this to a failure code. This module is shared by both
171 * transport modules - UNIX and TCP.
173 * This should ONLY be called when there is an error.
175 static int _translate_connect_errno(int err)
191 return EX_UNAVAILABLE;
204 * Given a socket family (PF_INET or PF_INET6 or PF_UNIX), try to
205 * create this socket and store the FD in the pointed-to place.
206 * If it's successful, do any other setup required to make the socket
207 * ready to use, such as setting TCP_NODELAY mode, and in any case
208 * we return EX_OK if all is well.
210 * Upon failure we return one of the other EX_??? error codes.
212 #ifdef SPAMC_HAS_ADDRINFO
213 static int _opensocket(int flags, struct addrinfo *res, int *psock)
216 static int _opensocket(int flags, int type, int *psock)
220 const char *typename;
228 /*----------------------------------------------------------------
229 * Create a few induction variables that are implied by the socket
230 * type given by the user. The typename is strictly used for debug
233 #ifdef SPAMC_HAS_ADDRINFO
234 switch(res->ai_family) {
236 typename = "PF_UNIX";
239 typename = "PF_INET";
242 typename = "PF_INET6";
245 typename = "Unknown";
249 if (type == PF_UNIX) {
250 typename = "PF_UNIX";
253 typename = "PF_INET";
258 #ifdef DO_CONNECT_DEBUG_SYSLOGS
259 libspamc_log(flags, CONNECT_DEBUG_LEVEL, "dbg: create socket(%s)", typename);
262 #ifdef SPAMC_HAS_ADDRINFO
263 if ((*psock = socket(res->ai_family, res->ai_socktype, res->ai_protocol))
265 if ((*psock = socket(type, SOCK_STREAM, proto))
274 /*--------------------------------------------------------
275 * At this point we had a failure creating the socket, and
276 * this is pretty much fatal. Translate the error reason
277 * into something the user can understand.
279 origerr = spamc_get_errno();
281 libspamc_log(flags, LOG_ERR, "socket(%s) to spamd failed: %s", typename, strerror(origerr));
283 libspamc_log(flags, LOG_ERR, "socket(%s) to spamd failed: %d", typename, origerr);
287 case EPROTONOSUPPORT:
306 /* bug 4344: makes timeout functional on Win32 */
307 socktout = libspamc_timeout * 1000;
309 && setsockopt(*psock, SOL_SOCKET, SO_RCVTIMEO, (char *)&socktout, sizeof(socktout)) != 0)
312 origerr = spamc_get_errno();
319 libspamc_log(flags, LOG_ERR, "setsockopt(SO_RCVTIMEO) failed: %d", origerr);
329 /*----------------------------------------------------------------
330 * Do a bit of setup on the TCP socket if required. Notes above
331 * suggest this is probably not set
333 #ifdef USE_TCP_NODELAY
337 if ( ( type == PF_INET
341 ) && setsockopt(*psock, 0, TCP_NODELAY, &one, sizeof one) != 0) {
342 origerr = spamc_get_errno();
348 libspamc_log(flags, LOG_ERR,
350 "setsockopt(TCP_NODELAY) failed: %s", strerror(origerr));
352 "setsockopt(TCP_NODELAY) failed: %d", origerr);
362 #endif /* USE_TCP_NODELAY */
364 return EX_OK; /* all is well */
368 * try_to_connect_unix()
370 * Given a transport handle that implies using a UNIX domain
371 * socket, try to make a connection to it and store the resulting
372 * file descriptor in *sockptr. Return is EX_OK if we did it,
373 * and some other error code otherwise.
375 static int _try_to_connect_unix(struct transport *tp, int *sockptr)
378 int mysock, status, origerr;
379 struct sockaddr_un addrbuf;
380 #ifdef SPAMC_HAS_ADDRINFO
381 struct addrinfo hints, *res;
388 assert(sockptr != 0);
389 assert(tp->socketpath != 0);
391 #ifdef SPAMC_HAS_ADDRINFO
392 memset(&hints, 0, sizeof(hints));
393 hints.ai_family = PF_UNIX;
394 hints.ai_socktype = SOCK_STREAM;
395 hints.ai_protocol = 0;
398 /*----------------------------------------------------------------
399 * If the socket itself can't be created, this is a fatal error.
401 if ((ret = _opensocket(tp->flags, res, &mysock)) != EX_OK)
404 /* set up the UNIX domain socket */
405 memset(&addrbuf, 0, sizeof addrbuf);
406 addrbuf.sun_family = AF_UNIX;
407 strncpy(addrbuf.sun_path, tp->socketpath, sizeof addrbuf.sun_path - 1);
408 addrbuf.sun_path[sizeof addrbuf.sun_path - 1] = '\0';
410 #ifdef DO_CONNECT_DEBUG_SYSLOGS
411 libspamc_log(tp->flags, CONNECT_DEBUG_LEVEL, "dbg: connect(AF_UNIX) to spamd at %s",
415 status = timeout_connect(mysock, (struct sockaddr *) &addrbuf, sizeof(addrbuf));
420 #ifdef DO_CONNECT_DEBUG_SYSLOGS
421 libspamc_log(tp->flags, CONNECT_DEBUG_LEVEL, "dbg: connect(AF_UNIX) ok");
429 libspamc_log(tp->flags, LOG_ERR, "connect(AF_UNIX) to spamd using --socket='%s' failed: %s",
430 addrbuf.sun_path, strerror(origerr));
433 return _translate_connect_errno(origerr);
435 (void) tp; /* not used. suppress compiler warning */
436 (void) sockptr; /* not used. suppress compiler warning */
442 * try_to_connect_tcp()
444 * Given a transport that implies a TCP connection, either to
445 * localhost or a list of IP addresses, attempt to connect. The
446 * list of IP addresses has already been randomized (if requested)
447 * and limited to just one if fallback has been enabled.
449 static int _try_to_connect_tcp(const struct transport *tp, int *sockptr)
454 #ifdef SPAMC_HAS_ADDRINFO
455 struct addrinfo *res = NULL;
456 char port[SPAMC_MAXSERV-1]; /* port, for logging */
460 char host[SPAMC_MAXHOST-1]; /* hostname, for logging */
461 int connect_retries, retry_sleep;
464 assert(sockptr != 0);
465 assert(tp->nhosts > 0);
468 retry_sleep = tp->retry_sleep;
469 connect_retries = tp->connect_retries;
470 if (connect_retries == 0) {
473 if (retry_sleep < 0) {
477 for (numloops = 0; numloops < connect_retries; numloops++) {
478 const int hostix = numloops % tp->nhosts;
482 /*--------------------------------------------------------
483 * We always start by creating the socket, as we get only
484 * one attempt to connect() on each one. If this fails,
488 #ifdef SPAMC_HAS_ADDRINFO
489 res = tp->hosts[hostix];
492 switch(res->ai_family) {
504 if ((ret = _opensocket(tp->flags, res, &mysock)) != EX_OK) {
509 getnameinfo(res->ai_addr, res->ai_addrlen,
512 NI_NUMERICHOST|NI_NUMERICSERV);
514 #ifdef DO_CONNECT_DEBUG_SYSLOGS
515 libspamc_log(tp->flags, CONNECT_DEBUG_LEVEL,
516 "dbg: connect(%s) to spamd (host %s, port %s) (try #%d of %d)",
517 family, host, port, numloops + 1, connect_retries);
520 /* this is special-cased so that we have an address we can
521 * safely use as an "always fail" test case */
522 if (!strcmp(host, "255.255.255.255")) {
523 libspamc_log(tp->flags, LOG_ERR,
524 "connect to spamd on %s failed, broadcast addr",
529 status = timeout_connect(mysock, res->ai_addr, res->ai_addrlen);
530 if (status != 0) origerr = spamc_get_errno();
534 struct sockaddr_in addrbuf;
536 const char* family="AF_INET";
537 if ((ret = _opensocket(tp->flags, PF_INET, &mysock)) != EX_OK)
540 memset(&addrbuf, 0, sizeof(addrbuf));
542 addrbuf.sin_family = AF_INET;
543 addrbuf.sin_port = htons(tp->port);
544 addrbuf.sin_addr = tp->hosts[hostix];
546 ipaddr = inet_ntoa(addrbuf.sin_addr);
548 /* make a copy in host, for logging (bug 5577) */
549 strncpy (host, ipaddr, sizeof(host) - 1);
551 #ifdef DO_CONNECT_DEBUG_SYSLOGS
552 libspamc_log(tp->flags, LOG_DEBUG,
553 "dbg: connect(AF_INET) to spamd at %s (try #%d of %d)",
554 ipaddr, numloops + 1, connect_retries);
557 /* this is special-cased so that we have an address we can
558 * safely use as an "always fail" test case */
559 if (!strcmp(ipaddr, "255.255.255.255")) {
560 libspamc_log(tp->flags, LOG_ERR,
561 "connect to spamd on %s failed, broadcast addr",
566 status = timeout_connect(mysock, (struct sockaddr *) &addrbuf,
568 if (status != 0) origerr = spamc_get_errno();
576 innocent = origerr == ECONNREFUSED && numloops+1 < tp->nhosts;
577 libspamc_log(tp->flags, innocent ? LOG_DEBUG : LOG_ERR,
578 "connect to spamd on %s failed, retrying (#%d of %d): %s",
579 host, numloops+1, connect_retries,
588 #ifdef DO_CONNECT_DEBUG_SYSLOGS
589 libspamc_log(tp->flags, CONNECT_DEBUG_LEVEL,
590 "dbg: connect(%s) to spamd done",family);
596 #ifdef SPAMC_HAS_ADDRINFO
600 if (numloops+1 < connect_retries && !innocent) sleep(retry_sleep);
601 } /* for(numloops...) */
603 libspamc_log(tp->flags, LOG_ERR,
604 "connection attempt to spamd aborted after %d retries",
607 return _translate_connect_errno(origerr);
610 /* Aug 14, 2002 bj: Reworked things. Now we have message_read, message_write,
611 * message_dump, lookup_host, message_filter, and message_process, and a bunch
612 * of helper functions.
615 static void _clear_message(struct message *m)
617 m->type = MESSAGE_NONE;
626 m->is_spam = EX_TOOBIG;
632 m->content_length = -1;
635 static void _free_zlib_buffer(unsigned char **zlib_buf, int *zlib_bufsiz)
644 static void _use_msg_for_out(struct message *m)
650 m->out_len = m->msg_len;
653 static int _message_read_raw(int fd, struct message *m)
656 if ((m->raw = malloc(m->max_len + 1)) == NULL)
658 m->raw_len = full_read(fd, 1, m->raw, m->max_len + 1, m->max_len + 1);
659 if (m->raw_len <= 0) {
665 m->type = MESSAGE_ERROR;
666 if (m->raw_len > (int) m->max_len)
668 libspamc_log(m->priv->flags, LOG_NOTICE,
669 "skipped message, greater than max message size (%d bytes)",
673 m->type = MESSAGE_RAW;
675 m->msg_len = m->raw_len;
677 m->out_len = m->msg_len;
681 static int _message_read_bsmtp(int fd, struct message *m)
683 unsigned int i, j, p_len;
688 if ((m->raw = malloc(m->max_len + 1)) == NULL)
691 /* Find the DATA line */
692 m->raw_len = full_read(fd, 1, m->raw, m->max_len + 1, m->max_len + 1);
693 if (m->raw_len <= 0) {
699 m->type = MESSAGE_ERROR;
700 if (m->raw_len > (int) m->max_len)
703 /* Search for \nDATA\n which marks start of actual message */
704 while ((p_len = (m->raw_len - (p - m->raw))) > 8) { /* leave room for at least \nDATA\n.\n */
705 char* q = memchr(p, '\n', p_len - 8); /* find next \n then see if start of \nDATA\n */
706 if (q == NULL) break;
708 if (((q[0]|0x20) == 'd') && /* case-insensitive ASCII comparison */
709 ((q[1]|0x20) == 'a') &&
710 ((q[2]|0x20) == 't') &&
711 ((q[3]|0x20) == 'a')) {
713 if (q[0] == '\r') ++q;
714 if (*(q++) == '\n') { /* leave q at start of message if we found it */
716 m->pre_len = q - m->raw;
717 m->msg_len = m->raw_len - m->pre_len;
721 p = q; /* the above code ensures no other '\n' comes before q */
726 /* ensure this is >= 0 */
727 if (m->msg_len < 0) {
731 /* Find the end-of-DATA line */
733 for (i = j = 0; i < (unsigned int) m->msg_len; i++) {
734 if (prev == '\n' && m->msg[i] == '.') {
735 /* Dot at the beginning of a line */
736 if (((int) (i+1) == m->msg_len)
737 || ((int) (i+1) < m->msg_len && m->msg[i + 1] == '\n')
738 || ((int) (i+2) < m->msg_len && m->msg[i + 1] == '\r' && m->msg[i + 2] == '\n')) {
739 /* Lone dot! That's all, folks */
740 m->post = m->msg + i;
741 m->post_len = m->msg_len - i;
745 else if ((int) (i+1) < m->msg_len && m->msg[i + 1] == '.') {
746 /* Escaping dot, eliminate. */
749 } /* Else an ordinary dot, drop down to ordinary char handler */
752 m->msg[j++] = m->msg[i];
755 /* if bad format with no end "\n.\n", error out */
758 m->type = MESSAGE_BSMTP;
760 m->out_len = m->msg_len;
764 int message_read(int fd, int flags, struct message *m)
768 libspamc_timeout = 0;
770 /* create the "private" part of the struct message */
771 m->priv = malloc(sizeof(struct libspamc_private_message));
772 if (m->priv == NULL) {
773 libspamc_log(flags, LOG_ERR, "message_read: malloc failed");
776 m->priv->flags = flags;
777 m->priv->alloced_size = 0;
778 m->priv->spamc_header_callback = 0;
779 m->priv->spamd_header_callback = 0;
781 if (flags & SPAMC_PING) {
786 switch (flags & SPAMC_MODE_MASK) {
788 return _message_read_raw(fd, m);
790 case SPAMC_BSMTP_MODE:
791 return _message_read_bsmtp(fd, m);
794 libspamc_log(flags, LOG_ERR, "message_read: Unknown mode %d",
795 flags & SPAMC_MODE_MASK);
800 long message_write(int fd, struct message *m)
809 if (m->priv->flags & (SPAMC_CHECK_ONLY|SPAMC_PING)) {
810 if (m->is_spam == EX_ISSPAM || m->is_spam == EX_NOTSPAM) {
811 return full_write(fd, 1, m->out, m->out_len);
815 libspamc_log(m->priv->flags, LOG_ERR, "oops! SPAMC_CHECK_ONLY is_spam: %d",
821 /* else we're not in CHECK_ONLY mode */
824 libspamc_log(m->priv->flags, LOG_ERR, "Cannot write this message, it's MESSAGE_NONE!");
828 return full_write(fd, 1, m->raw, m->raw_len);
831 return full_write(fd, 1, m->out, m->out_len);
834 total = full_write(fd, 1, m->pre, m->pre_len);
835 for (i = 0; i < m->out_len;) {
836 jlimit = (off_t) (sizeof(buffer) / sizeof(*buffer) - 4);
837 for (j = 0; i < (off_t) m->out_len && j < jlimit;) {
838 if (i + 1 < m->out_len && m->out[i] == '\n'
839 && m->out[i + 1] == '.') {
840 if (j > jlimit - 4) {
841 break; /* avoid overflow */
843 buffer[j++] = m->out[i++];
844 buffer[j++] = m->out[i++];
848 buffer[j++] = m->out[i++];
851 total += full_write(fd, 1, buffer, j);
853 return total + full_write(fd, 1, m->post, m->post_len);
856 libspamc_log(m->priv->flags, LOG_ERR, "Unknown message type %d", m->type);
861 void message_dump(int in_fd, int out_fd, struct message *m, int flags)
867 libspamc_log(flags, LOG_ERR, "oops! message_dump called with NULL message");
871 if (m->type != MESSAGE_NONE) {
872 message_write(out_fd, m);
875 while ((bytes = full_read(in_fd, 1, buf, 8192, 8192)) > 0) {
876 if (bytes != full_write(out_fd, 1, buf, bytes)) {
877 libspamc_log(flags, LOG_ERR, "oops! message_dump of %d returned different",
884 _spamc_read_full_line(struct message *m, int flags, SSL * ssl, int sock,
885 char *buf, size_t *lenp, size_t bufsiz)
894 /* Now, read from spamd */
895 for (len = 0; len < bufsiz - 1; len++) {
896 if (flags & SPAMC_USE_SSL) {
897 bytesread = ssl_timeout_read(ssl, buf + len, 1);
900 bytesread = fd_timeout_read(sock, 0, buf + len, 1);
903 if (bytesread <= 0) {
904 failureval = EX_IOERR;
908 if (buf[len] == '\n') {
910 if (len > 0 && buf[len - 1] == '\r') {
919 libspamc_log(flags, LOG_ERR, "spamd responded with line of %d bytes, dying", len);
920 failureval = EX_TOOBIG;
927 * May 7 2003 jm: using %f is bad where LC_NUMERIC is "," in the locale.
928 * work around using our own locale-independent float-parser code.
930 static float _locale_safe_string_to_float(char *buf, int siz)
937 buf[siz - 1] = '\0'; /* ensure termination */
939 /* ok, let's illustrate using "100.033" as an example... */
946 ret = (float) (strtol(buf, &dot, 10));
950 if (dot != NULL && *dot != '.') {
954 /* ex: ret == 100.0 */
957 postdot = (float) (strtol(cp, NULL, 10));
958 /* note: don't compare floats == 0.0, it's unsafe. use a range */
959 if (postdot >= -0.00001 && postdot <= 0.00001) {
963 /* ex: postdot == 33.0, cp="033" */
965 /* now count the number of decimal places and figure out what power of 10 to use */
967 while (*cp != '\0') {
973 * cp="033", divider=1
974 * cp="33", divider=10
975 * cp="3", divider=100
976 * cp="", divider=1000
980 ret -= (postdot / ((float) divider));
983 ret += (postdot / ((float) divider));
985 /* ex: ret == 100.033, tada! ... hopefully */
991 _handle_spamd_header(struct message *m, int flags, char *buf, int len,
992 unsigned int *didtellflags)
995 char s_str[21], t_str[21];
997 char didremove_ret[15];
999 UNUSED_VARIABLE(len);
1001 /* Feb 12 2003 jm: actually, I think sccanf is working fine here ;)
1002 * let's stick with it for this parser.
1003 * May 7 2003 jm: using %f is bad where LC_NUMERIC is "," in the locale.
1004 * work around using our own locale-independent float-parser code.
1006 if (sscanf(buf, "Spam: %5s ; %20s / %20s", is_spam, s_str, t_str) == 3) {
1007 m->score = _locale_safe_string_to_float(s_str, 20);
1008 m->threshold = _locale_safe_string_to_float(t_str, 20);
1010 /* set bounds on these to ensure no buffer overflow in the sprintf */
1011 if (m->score > 1e10)
1013 else if (m->score < -1e10)
1015 if (m->threshold > 1e10)
1016 m->threshold = 1e10;
1017 else if (m->threshold < -1e10)
1018 m->threshold = -1e10;
1020 /* Format is "Spam: x; y / x" */
1022 strcasecmp("true", is_spam) == 0 ? EX_ISSPAM : EX_NOTSPAM;
1024 if (flags & SPAMC_CHECK_ONLY) {
1025 m->out_len = sprintf(m->out,
1026 "%.1f/%.1f\n", m->score, m->threshold);
1028 else if ((flags & SPAMC_REPORT_IFSPAM && m->is_spam == EX_ISSPAM)
1029 || (flags & SPAMC_REPORT)) {
1030 m->out_len = sprintf(m->out,
1031 "%.1f/%.1f\n", m->score, m->threshold);
1036 else if (sscanf(buf, "Content-length: %d", &m->content_length) == 1) {
1037 if (m->content_length < 0) {
1038 libspamc_log(flags, LOG_ERR, "spamd responded with bad Content-length '%s'",
1044 else if (sscanf(buf, "DidSet: %14s", didset_ret) == 1) {
1045 if (strstr(didset_ret, "local")) {
1046 *didtellflags |= SPAMC_SET_LOCAL;
1048 if (strstr(didset_ret, "remote")) {
1049 *didtellflags |= SPAMC_SET_REMOTE;
1052 else if (sscanf(buf, "DidRemove: %14s", didremove_ret) == 1) {
1053 if (strstr(didremove_ret, "local")) {
1054 *didtellflags |= SPAMC_REMOVE_LOCAL;
1056 if (strstr(didremove_ret, "remote")) {
1057 *didtellflags |= SPAMC_REMOVE_REMOTE;
1060 else if (m->priv->spamd_header_callback != NULL)
1061 m->priv->spamd_header_callback(m, flags, buf, len);
1067 _zlib_compress (char *m_msg, int m_msg_len,
1068 unsigned char **zlib_buf, int *zlib_bufsiz, int flags)
1075 UNUSED_VARIABLE(m_msg);
1076 UNUSED_VARIABLE(m_msg_len);
1077 UNUSED_VARIABLE(zlib_buf);
1078 UNUSED_VARIABLE(zlib_bufsiz);
1079 UNUSED_VARIABLE(rc);
1080 UNUSED_VARIABLE(len);
1081 UNUSED_VARIABLE(totallen);
1082 libspamc_log(flags, LOG_ERR, "spamc not built with zlib support");
1088 UNUSED_VARIABLE(flags);
1090 /* worst-case, according to http://www.zlib.org/zlib_tech.html ;
1091 * same as input, plus 5 bytes per 16k, plus 6 bytes. this should
1093 *zlib_bufsiz = (int) (m_msg_len * 1.0005) + 1024;
1094 *zlib_buf = (unsigned char *) malloc (*zlib_bufsiz);
1095 if (*zlib_buf == NULL) {
1099 strm.zalloc = Z_NULL;
1100 strm.zfree = Z_NULL;
1101 strm.opaque = Z_NULL;
1102 rc = deflateInit(&strm, 3);
1107 strm.avail_in = m_msg_len;
1108 strm.next_in = (unsigned char *) m_msg;
1109 strm.avail_out = *zlib_bufsiz;
1110 strm.next_out = (unsigned char *) *zlib_buf;
1114 rc = deflate(&strm, Z_FINISH);
1115 assert(rc != Z_STREAM_ERROR);
1116 len = (size_t) (*zlib_bufsiz - strm.avail_out);
1117 strm.next_out += len;
1119 } while (strm.avail_out == 0);
1121 *zlib_bufsiz = totallen;
1128 _append_original_body (struct message *m, int flags)
1130 char *cp, *cpend, *bodystart;
1131 int bodylen, outspaceleft, towrite;
1133 /* at this stage, m->out now contains the rewritten headers.
1134 * find and append the raw message's body, up to m->priv->alloced_size
1138 #define CRNLCRNL "\r\n\r\n"
1139 #define CRNLCRNL_LEN 4
1143 cpend = m->raw + m->raw_len;
1146 for (cp = m->raw; cp < cpend; cp++) {
1147 if (*cp == '\r' && cpend - cp >= CRNLCRNL_LEN &&
1148 !strncmp(cp, CRNLCRNL, CRNLCRNL_LEN))
1150 bodystart = cp + CRNLCRNL_LEN;
1153 else if (*cp == '\n' && cpend - cp >= NLNL_LEN &&
1154 !strncmp(cp, NLNL, NLNL_LEN))
1156 bodystart = cp + NLNL_LEN;
1161 if (bodystart == NULL) {
1162 libspamc_log(flags, LOG_ERR, "failed to find end-of-headers");
1166 bodylen = cpend - bodystart;
1167 outspaceleft = (m->priv->alloced_size-1) - m->out_len;
1168 towrite = (bodylen < outspaceleft ? bodylen : outspaceleft);
1170 /* copy in the body; careful not to overflow */
1171 strncpy (m->out + m->out_len, bodystart, towrite);
1172 m->out_len += towrite;
1176 int message_filter(struct transport *tp, const char *username,
1177 int flags, struct message *m)
1180 size_t bufsiz = (sizeof(buf) / sizeof(*buf)) - 4; /* bit of breathing room */
1187 int failureval = EX_SOFTWARE;
1188 unsigned int throwaway;
1189 SSL_CTX *ctx = NULL;
1191 const SSL_METHOD *meth;
1193 unsigned char *zlib_buf = NULL;
1194 int zlib_bufsiz = 0;
1195 unsigned char *towrite_buf;
1197 int filter_retry_count;
1198 int filter_retry_sleep;
1200 #ifdef SPAMC_HAS_ADDRINFO
1201 struct addrinfo *tmphost;
1203 struct in_addr tmphost;
1210 if ((flags & SPAMC_USE_ZLIB) != 0) {
1214 if (flags & SPAMC_USE_SSL) {
1216 SSLeay_add_ssl_algorithms();
1217 meth = SSLv23_client_method();
1218 SSL_load_error_strings();
1219 ctx = SSL_CTX_new(meth);
1221 UNUSED_VARIABLE(ssl);
1222 UNUSED_VARIABLE(meth);
1223 UNUSED_VARIABLE(ctx);
1224 libspamc_log(flags, LOG_ERR, "spamc not built with SSL support");
1229 m->is_spam = EX_TOOBIG;
1231 if (m->outbuf != NULL)
1233 m->priv->alloced_size = m->max_len + EXPANSION_ALLOWANCE + 1;
1234 if ((m->outbuf = malloc(m->priv->alloced_size)) == NULL) {
1235 failureval = EX_OSERR;
1241 /* If the spamd filter takes too long and we timeout, then
1242 * retry again. This gets us around a hung child thread
1243 * in spamd or a problem on a spamd host in a multi-host
1244 * setup. If there is more than one destination host
1245 * we move to the next host on each attempt.
1248 /* default values */
1249 filter_retry_sleep = tp->filter_retry_sleep;
1250 filter_retries = tp->filter_retries;
1251 if (filter_retries == 0) {
1254 if (filter_retry_sleep < 0) {
1255 filter_retry_sleep = 1;
1258 /* filterloop - Ensure that we run through this at least
1259 * once, and again if there are errors
1261 filter_retry_count = 0;
1262 while ((filter_retry_count==0) ||
1263 ((filter_retry_count<tp->filter_retries) && (failureval == EX_IOERR)))
1265 if (filter_retry_count != 0){
1266 /* Ensure that the old socket gets closed */
1272 /* Move to the next host in the list, if nhosts>1 */
1273 if (tp->nhosts > 1) {
1274 tmphost = tp->hosts[0];
1276 /* TODO: free using freeaddrinfo() */
1277 for (nhost_counter = 1; nhost_counter < tp->nhosts; nhost_counter++) {
1278 tp->hosts[nhost_counter - 1] = tp->hosts[nhost_counter];
1281 tp->hosts[nhost_counter - 1] = tmphost;
1284 /* Now sleep the requested amount */
1285 sleep(filter_retry_sleep);
1288 filter_retry_count++;
1290 /* Build spamd protocol header */
1291 if (flags & SPAMC_CHECK_ONLY)
1292 strcpy(buf, "CHECK ");
1293 else if (flags & SPAMC_REPORT_IFSPAM)
1294 strcpy(buf, "REPORT_IFSPAM ");
1295 else if (flags & SPAMC_REPORT)
1296 strcpy(buf, "REPORT ");
1297 else if (flags & SPAMC_SYMBOLS)
1298 strcpy(buf, "SYMBOLS ");
1299 else if (flags & SPAMC_PING)
1300 strcpy(buf, "PING ");
1301 else if (flags & SPAMC_HEADERS)
1302 strcpy(buf, "HEADERS ");
1304 strcpy(buf, "PROCESS ");
1307 if (len + strlen(PROTOCOL_VERSION) + 2 >= bufsiz) {
1308 _use_msg_for_out(m);
1312 strcat(buf, PROTOCOL_VERSION);
1313 strcat(buf, "\r\n");
1316 towrite_buf = (unsigned char *) m->msg;
1317 towrite_len = (int) m->msg_len;
1319 if (_zlib_compress(m->msg, m->msg_len, &zlib_buf, &zlib_bufsiz, flags) != EX_OK)
1321 _free_zlib_buffer(&zlib_buf, &zlib_bufsiz);
1324 towrite_buf = zlib_buf;
1325 towrite_len = zlib_bufsiz;
1328 if (!(flags & SPAMC_PING)) {
1329 if (username != NULL) {
1330 if (strlen(username) + 8 >= (bufsiz - len)) {
1331 _use_msg_for_out(m);
1333 _free_zlib_buffer(&zlib_buf, &zlib_bufsiz);
1337 strcpy(buf + len, "User: ");
1338 strcat(buf + len, username);
1339 strcat(buf + len, "\r\n");
1340 len += strlen(buf + len);
1343 len += snprintf(buf + len, 8192-len, "Compress: zlib\r\n");
1345 if ((m->msg_len > SPAMC_MAX_MESSAGE_LEN) || ((len + 27) >= (bufsiz - len))) {
1346 _use_msg_for_out(m);
1348 _free_zlib_buffer(&zlib_buf, &zlib_bufsiz);
1352 len += snprintf(buf + len, 8192-len, "Content-length: %d\r\n", (int) towrite_len);
1354 /* bug 6187, PING needs empty line too, bumps protocol version to 1.5 */
1355 len += snprintf(buf + len, 8192-len, "\r\n");
1357 libspamc_timeout = m->timeout;
1358 libspamc_connect_timeout = m->connect_timeout; /* Sep 8, 2008 mrgus: separate connect timeout */
1361 rc = _try_to_connect_unix(tp, &sock);
1363 rc = _try_to_connect_tcp(tp, &sock);
1366 _use_msg_for_out(m);
1368 _free_zlib_buffer(&zlib_buf, &zlib_bufsiz);
1370 return rc; /* use the error code try_to_connect_*() gave us. */
1373 if (flags & SPAMC_USE_SSL) {
1376 SSL_set_fd(ssl, sock);
1382 if (flags & SPAMC_USE_SSL) {
1384 SSL_write(ssl, buf, len);
1385 SSL_write(ssl, towrite_buf, towrite_len);
1389 full_write(sock, 0, buf, len);
1390 full_write(sock, 0, towrite_buf, towrite_len);
1391 shutdown(sock, SHUT_WR);
1395 * bug 6025: zlib buffer not freed if compression is used
1398 _free_zlib_buffer(&zlib_buf, &zlib_bufsiz);
1401 /* ok, now read and parse it. SPAMD/1.2 line first... */
1403 _spamc_read_full_line(m, flags, ssl, sock, buf, &len, bufsiz);
1404 } /* end of filterloop */
1406 if (failureval != EX_OK) {
1410 if (sscanf(buf, "SPAMD/%18s %d %*s", versbuf, &response) != 2) {
1411 libspamc_log(flags, LOG_ERR, "spamd responded with bad string '%s'", buf);
1412 failureval = EX_PROTOCOL;
1417 version = _locale_safe_string_to_float(versbuf, 20);
1418 if (version < 1.0) {
1419 libspamc_log(flags, LOG_ERR, "spamd responded with bad version string '%s'",
1421 failureval = EX_PROTOCOL;
1425 if (flags & SPAMC_PING) {
1428 m->out_len = sprintf(m->out, "SPAMD/%s %d\n", versbuf, response);
1429 m->is_spam = EX_NOTSPAM;
1435 m->is_spam = EX_TOOBIG;
1438 _spamc_read_full_line(m, flags, ssl, sock, buf, &len, bufsiz);
1439 if (failureval != EX_OK) {
1443 if (len == 0 && buf[0] == '\0') {
1444 break; /* end of headers */
1447 if (_handle_spamd_header(m, flags, buf, len, &throwaway) < 0) {
1448 failureval = EX_PROTOCOL;
1453 len = 0; /* overwrite those headers */
1455 if (flags & SPAMC_CHECK_ONLY) {
1458 if (m->is_spam == EX_TOOBIG) {
1459 /* We should have gotten headers back... Damnit. */
1460 failureval = EX_PROTOCOL;
1466 if (m->content_length < 0) {
1467 /* should have got a length too. */
1468 failureval = EX_PROTOCOL;
1472 /* have we already got something in the buffer (e.g. REPORT and
1473 * REPORT_IFSPAM both create a line from the "Spam:" hdr)? If
1474 * so, add the size of that so our sanity check passes.
1476 if (m->out_len > 0) {
1477 m->content_length += m->out_len;
1480 if (flags & SPAMC_USE_SSL) {
1481 len = full_read_ssl(ssl, (unsigned char *) m->out + m->out_len,
1482 m->priv->alloced_size - m->out_len,
1483 m->priv->alloced_size - m->out_len);
1486 len = full_read(sock, 0, m->out + m->out_len,
1487 m->priv->alloced_size - m->out_len,
1488 m->priv->alloced_size - m->out_len);
1492 if ((int) len + (int) m->out_len > (m->priv->alloced_size - 1)) {
1493 failureval = EX_TOOBIG;
1498 shutdown(sock, SHUT_RD);
1502 libspamc_timeout = 0;
1504 if (m->out_len != m->content_length) {
1505 libspamc_log(flags, LOG_ERR,
1506 "failed sanity check, %d bytes claimed, %d bytes seen",
1507 m->content_length, m->out_len);
1508 failureval = EX_PROTOCOL;
1512 if (flags & SPAMC_HEADERS) {
1513 if (_append_original_body(m, flags) != EX_OK) {
1521 _use_msg_for_out(m);
1525 libspamc_timeout = 0;
1527 if (flags & SPAMC_USE_SSL) {
1536 int message_process(struct transport *trans, char *username, int max_size,
1537 int in_fd, int out_fd, const int flags)
1542 assert(trans != NULL);
1544 m.type = MESSAGE_NONE;
1546 /* enforce max_size being unsigned, therefore >= 0 */
1551 m.max_len = (unsigned int) max_size;
1553 ret = message_read(in_fd, flags, &m);
1556 ret = message_filter(trans, username, flags, &m);
1559 if (message_write(out_fd, &m) < 0)
1561 if (m.is_spam != EX_TOOBIG) {
1562 message_cleanup(&m);
1565 message_cleanup(&m);
1569 if (flags & SPAMC_CHECK_ONLY) {
1570 full_write(out_fd, 1, "0/0\n", 4);
1571 message_cleanup(&m);
1575 message_dump(in_fd, out_fd, &m, flags);
1576 message_cleanup(&m);
1581 int message_tell(struct transport *tp, const char *username, int flags,
1582 struct message *m, int msg_class,
1583 unsigned int tellflags, unsigned int *didtellflags)
1586 size_t bufsiz = (sizeof(buf) / sizeof(*buf)) - 4; /* bit of breathing room */
1594 SSL_CTX *ctx = NULL;
1596 const SSL_METHOD *meth;
1601 if (flags & SPAMC_USE_SSL) {
1603 SSLeay_add_ssl_algorithms();
1604 meth = SSLv23_client_method();
1605 SSL_load_error_strings();
1606 ctx = SSL_CTX_new(meth);
1608 UNUSED_VARIABLE(ssl);
1609 UNUSED_VARIABLE(meth);
1610 UNUSED_VARIABLE(ctx);
1611 libspamc_log(flags, LOG_ERR, "spamc not built with SSL support");
1616 m->is_spam = EX_TOOBIG;
1618 if (m->outbuf != NULL)
1620 m->priv->alloced_size = m->max_len + EXPANSION_ALLOWANCE + 1;
1621 if ((m->outbuf = malloc(m->priv->alloced_size)) == NULL) {
1622 failureval = EX_OSERR;
1628 /* Build spamd protocol header */
1629 strcpy(buf, "TELL ");
1632 if (len + strlen(PROTOCOL_VERSION) + 2 >= bufsiz) {
1633 _use_msg_for_out(m);
1637 strcat(buf, PROTOCOL_VERSION);
1638 strcat(buf, "\r\n");
1641 if (msg_class != 0) {
1642 strcpy(buf + len, "Message-class: ");
1643 if (msg_class == SPAMC_MESSAGE_CLASS_SPAM) {
1644 strcat(buf + len, "spam\r\n");
1647 strcat(buf + len, "ham\r\n");
1649 len += strlen(buf + len);
1652 if ((tellflags & SPAMC_SET_LOCAL) || (tellflags & SPAMC_SET_REMOTE)) {
1653 int needs_comma_p = 0;
1654 strcat(buf + len, "Set: ");
1655 if (tellflags & SPAMC_SET_LOCAL) {
1656 strcat(buf + len, "local");
1659 if (tellflags & SPAMC_SET_REMOTE) {
1660 if (needs_comma_p == 1) {
1661 strcat(buf + len, ",");
1663 strcat(buf + len, "remote");
1665 strcat(buf + len, "\r\n");
1666 len += strlen(buf + len);
1669 if ((tellflags & SPAMC_REMOVE_LOCAL) || (tellflags & SPAMC_REMOVE_REMOTE)) {
1670 int needs_comma_p = 0;
1671 strcat(buf + len, "Remove: ");
1672 if (tellflags & SPAMC_REMOVE_LOCAL) {
1673 strcat(buf + len, "local");
1676 if (tellflags & SPAMC_REMOVE_REMOTE) {
1677 if (needs_comma_p == 1) {
1678 strcat(buf + len, ",");
1680 strcat(buf + len, "remote");
1682 strcat(buf + len, "\r\n");
1683 len += strlen(buf + len);
1686 if (username != NULL) {
1687 if (strlen(username) + 8 >= (bufsiz - len)) {
1688 _use_msg_for_out(m);
1691 strcpy(buf + len, "User: ");
1692 strcat(buf + len, username);
1693 strcat(buf + len, "\r\n");
1694 len += strlen(buf + len);
1696 if ((m->msg_len > SPAMC_MAX_MESSAGE_LEN) || ((len + 27) >= (bufsiz - len))) {
1697 _use_msg_for_out(m);
1700 len += sprintf(buf + len, "Content-length: %d\r\n\r\n", (int) m->msg_len);
1702 if (m->priv->spamc_header_callback != NULL) {
1704 m->priv->spamc_header_callback(m, flags, buf2, 1024);
1705 strncat(buf, buf2, bufsiz - len);
1708 libspamc_timeout = m->timeout;
1709 libspamc_connect_timeout = m->connect_timeout; /* Sep 8, 2008 mrgus: separate connect timeout */
1712 rc = _try_to_connect_unix(tp, &sock);
1714 rc = _try_to_connect_tcp(tp, &sock);
1717 _use_msg_for_out(m);
1718 return rc; /* use the error code try_to_connect_*() gave us. */
1721 if (flags & SPAMC_USE_SSL) {
1724 SSL_set_fd(ssl, sock);
1730 if (flags & SPAMC_USE_SSL) {
1732 SSL_write(ssl, buf, len);
1733 SSL_write(ssl, m->msg, m->msg_len);
1737 full_write(sock, 0, buf, len);
1738 full_write(sock, 0, m->msg, m->msg_len);
1739 shutdown(sock, SHUT_WR);
1742 /* ok, now read and parse it. SPAMD/1.2 line first... */
1744 _spamc_read_full_line(m, flags, ssl, sock, buf, &len, bufsiz);
1745 if (failureval != EX_OK) {
1749 if (sscanf(buf, "SPAMD/%18s %d %*s", versbuf, &response) != 2) {
1750 libspamc_log(flags, LOG_ERR, "spamd responded with bad string '%s'", buf);
1751 failureval = EX_PROTOCOL;
1756 version = _locale_safe_string_to_float(versbuf, 20);
1757 if (version < 1.0) {
1758 libspamc_log(flags, LOG_ERR, "spamd responded with bad version string '%s'",
1760 failureval = EX_PROTOCOL;
1766 m->is_spam = EX_TOOBIG;
1769 _spamc_read_full_line(m, flags, ssl, sock, buf, &len, bufsiz);
1770 if (failureval != EX_OK) {
1774 if (len == 0 && buf[0] == '\0') {
1775 break; /* end of headers */
1778 if (_handle_spamd_header(m, flags, buf, len, didtellflags) < 0) {
1779 failureval = EX_PROTOCOL;
1784 len = 0; /* overwrite those headers */
1786 shutdown(sock, SHUT_RD);
1790 libspamc_timeout = 0;
1795 _use_msg_for_out(m);
1799 libspamc_timeout = 0;
1801 if (flags & SPAMC_USE_SSL) {
1810 void message_cleanup(struct message *m)
1813 if (m->outbuf != NULL)
1817 if (m->priv != NULL)
1822 /* Aug 14, 2002 bj: Obsolete! */
1823 int process_message(struct transport *tp, char *username, int max_size,
1824 int in_fd, int out_fd, const int my_check_only,
1825 const int my_safe_fallback)
1829 flags = SPAMC_RAW_MODE;
1831 flags |= SPAMC_CHECK_ONLY;
1832 if (my_safe_fallback)
1833 flags |= SPAMC_SAFE_FALLBACK;
1835 return message_process(tp, username, max_size, in_fd, out_fd, flags);
1841 * Given a pointer to a transport structure, set it to "all empty".
1842 * The default is a localhost connection.
1844 void transport_init(struct transport *tp)
1848 memset(tp, 0, sizeof *tp);
1850 tp->type = TRANSPORT_LOCALHOST;
1853 tp->retry_sleep = -1;
1859 * Given the transport object that contains one or more IP addresses
1860 * in this "hosts" list, rotate it by a random number of shifts to
1861 * randomize them - this is a kind of load balancing. It's possible
1862 * that the random number will be 0, which says not to touch. We don't
1863 * do anything unless
1866 static void _randomize_hosts(struct transport *tp)
1868 #ifdef SPAMC_HAS_ADDRINFO
1869 struct addrinfo *tmp;
1878 if (tp->nhosts <= 1)
1881 rnum = rand() % tp->nhosts;
1883 while (rnum-- > 0) {
1886 for (i = 1; i < tp->nhosts; i++)
1887 tp->hosts[i - 1] = tp->hosts[i];
1889 tp->hosts[i - 1] = tmp;
1896 * Given a "transport" object that says how we're to connect to the
1897 * spam daemon, perform all the initial setup required to make the
1898 * connection process a smooth one. The main work is to do the host
1899 * name lookup and copy over all the IP addresses to make a local copy
1900 * so they're not kept in the resolver's static state.
1902 * Here we also manage quasi-load balancing and failover: if we're
1903 * doing load balancing, we randomly "rotate" the list to put it in
1904 * a different order, and then if we're not doing failover we limit
1905 * the hosts to just one. This way *all* connections are done with
1906 * the intention of failover - makes the code a bit more clear.
1908 int transport_setup(struct transport *tp, int flags)
1910 #ifdef SPAMC_HAS_ADDRINFO
1911 struct addrinfo hints, *res, *addrp;
1918 char *hostlist, *hostname;
1922 /* Start Winsock up */
1925 if ((nCode = WSAStartup(MAKEWORD(1, 1), &wsaData)) != 0) {
1926 printf("WSAStartup() returned error code %d\n", nCode);
1935 #ifdef SPAMC_HAS_ADDRINFO
1936 snprintf(port, 6, "%d", tp->port);
1938 memset(&hints, 0, sizeof(hints));
1940 hints.ai_socktype = SOCK_STREAM;
1942 if ( (flags & SPAMC_USE_INET4) && !(flags & SPAMC_USE_INET6)) {
1943 hints.ai_family = PF_INET;
1945 } else if ((flags & SPAMC_USE_INET6) && !(flags & SPAMC_USE_INET4)) {
1946 hints.ai_family = PF_INET6;
1949 hints.ai_family = PF_UNSPEC;
1955 case TRANSPORT_UNIX:
1956 assert(tp->socketpath != 0);
1959 case TRANSPORT_LOCALHOST:
1960 #ifdef SPAMC_HAS_ADDRINFO
1961 /* getaddrinfo(NULL) will look up the loopback address.
1962 * See also bug 5057, ::1 will be tried before 127.0.0.1
1963 * unless overridden (through hints) by a command line option -4
1965 if ((origerr = getaddrinfo(NULL, port, &hints, &res)) != 0) {
1966 libspamc_log(flags, LOG_ERR,
1967 "getaddrinfo for a loopback address failed: %s",
1968 gai_strerror(origerr));
1973 tp->hosts[0].s_addr = inet_addr("127.0.0.1");
1979 if ((hostlist = strdup(tp->hostname)) == NULL)
1982 /* We want to return the least permanent error, in this bitmask we
1983 * record the errors seen with:
1987 * EX_OSERR will return immediately.
1988 * Bits aren't reset so a check against nhosts is needed to determine
1989 * if something went wrong.
1993 /* Start with char offset in front of the string because we'll add
1996 hostname = hostlist - 1;
2001 hostend = strchr(hostname, ',');
2002 if (hostend != NULL) {
2005 #ifdef SPAMC_HAS_ADDRINFO
2006 if ((origerr = getaddrinfo(hostname, port, &hints, &res))) {
2007 libspamc_log(flags, LOG_DEBUG,
2008 "getaddrinfo(%s) failed: %s",
2009 hostname, gai_strerror(origerr));
2014 case EAI_FAMILY: /*address family not supported*/
2015 case EAI_SOCKTYPE: /*socket type not supported*/
2016 case EAI_BADFLAGS: /*ai_flags is invalid*/
2017 case EAI_NONAME: /*node or service unknown*/
2018 case EAI_SERVICE: /*service not available*/
2019 /* work around Cygwin IPv6 patch - err codes not defined in Windows aren't in patch */
2020 #ifdef HAVE_EAI_ADDRFAMILY
2021 case EAI_ADDRFAMILY: /*no addresses in requested family*/
2023 #ifdef HAVE_EAI_SYSTEM
2024 case EAI_SYSTEM: /*system error, check errno*/
2026 #ifdef HAVE_EAI_NODATA
2027 case EAI_NODATA: /*address exists, but no data*/
2029 case EAI_MEMORY: /*out of memory*/
2030 case EAI_FAIL: /*name server returned permanent error*/
2034 /* should not happen, all errors are checked above */
2038 goto nexthost; /* try next host in list */
2041 if ((hp = gethostbyname(hostname)) == NULL) {
2042 int origerr = h_errno; /* take a copy before syslog() */
2043 libspamc_log(flags, LOG_DEBUG, "gethostbyname(%s) failed: h_errno=%d",
2049 case HOST_NOT_FOUND:
2055 /* should not happen, all errors are checked above */
2059 goto nexthost; /* try next host in list */
2063 /* If we have no hosts at all */
2064 #ifdef SPAMC_HAS_ADDRINFO
2067 if (hp->h_addr_list[0] == NULL
2068 || hp->h_length != sizeof tp->hosts[0]
2069 || hp->h_addrtype != AF_INET)
2070 /* no hosts/bad size/wrong family */
2074 goto nexthost; /* try next host in list */
2077 /* Copy all the IP addresses into our private structure.
2078 * This gets them out of the resolver's static area and
2079 * means we won't ever walk all over the list with other
2082 #ifdef SPAMC_HAS_ADDRINFO
2083 if(tp->nhosts == TRANSPORT_MAX_HOSTS) {
2084 libspamc_log(flags, LOG_NOTICE,
2085 "hit limit of %d hosts, ignoring remainder",
2086 TRANSPORT_MAX_HOSTS);
2090 /* treat all A or AAAA records of each host as one entry */
2091 tp->hosts[tp->nhosts++] = res;
2093 /* alternatively, treat multiple A or AAAA records
2094 of one host as individual entries */
2095 /* for (addrp = res; addrp != NULL; ) {
2096 * tp->hosts[tp->nhosts] = addrp;
2097 * addrp = addrp->ai_next; /-* before NULLing ai_next *-/
2098 * tp->hosts[tp->nhosts]->ai_next = NULL;
2104 for (addrp = hp->h_addr_list; *addrp; addrp++) {
2105 if (tp->nhosts == TRANSPORT_MAX_HOSTS) {
2106 libspamc_log(flags, LOG_NOTICE, "hit limit of %d hosts, ignoring remainder",
2107 TRANSPORT_MAX_HOSTS);
2110 memcpy(&tp->hosts[tp->nhosts], *addrp, hp->h_length);
2116 } while (hostname != NULL);
2119 if (tp->nhosts == 0) {
2121 libspamc_log(flags, LOG_ERR, "could not resolve any hosts (%s): a temporary error occurred",
2126 libspamc_log(flags, LOG_ERR, "could not resolve any hosts (%s): no such host",
2132 /* QUASI-LOAD-BALANCING
2134 * If the user wants to do quasi load balancing, "rotate"
2135 * the list by a random amount based on the current time.
2136 * This may later be truncated to a single item. This is
2137 * meaningful only if we have more than one host.
2140 if ((flags & SPAMC_RANDOMIZE_HOSTS) && tp->nhosts > 1) {
2141 _randomize_hosts(tp);
2144 /* If the user wants no fallback, simply truncate the host
2145 * list to just one - this pretends that this is the extent
2146 * of our connection list - then it's not a special case.
2148 if (!(flags & SPAMC_SAFE_FALLBACK) && tp->nhosts > 1) {
2149 /* truncating list */
2156 /* oops, unknown transport type */
2161 * transport_cleanup()
2163 * Given a "transport" object that says how we're to connect to the
2164 * spam daemon, delete and free any buffers allocated so that it
2165 * can be discarded without causing a memory leak.
2167 void transport_cleanup(struct transport *tp)
2170 #ifdef SPAMC_HAS_ADDRINFO
2173 for(i=0;i<tp->nhosts;i++) {
2174 if (tp->hosts[i] != NULL) {
2175 freeaddrinfo(tp->hosts[i]);
2176 tp->hosts[i] = NULL;
2184 * register_libspamc_log_callback()
2186 * Register a callback handler for libspamc_log to replace the default behaviour.
2189 void register_libspamc_log_callback(void (*function)(int flags, int level, char *msg, va_list args)) {
2190 libspamc_log_callback = function;
2194 * register_spamc_header_callback()
2196 * Register a callback handler to generate spamc headers for a given message
2199 void register_spamc_header_callback(const struct message *m, void (*func)(struct message *m, int flags, char *buf, int len)) {
2200 m->priv->spamc_header_callback = func;
2204 * register_spamd_header_callback()
2206 * Register a callback handler to generate spamd headers for a given message
2209 void register_spamd_header_callback(const struct message *m, void (*func)(struct message *m, int flags, const char *buf, int len)) {
2210 m->priv->spamd_header_callback = func;
2213 /* --------------------------------------------------------------------------- */
2215 #define LOG_BUFSIZ 1023
2218 libspamc_log (int flags, int level, char *msg, ...)
2221 char buf[LOG_BUFSIZ+1];
2226 if ((flags & SPAMC_LOG_TO_CALLBACK) != 0 && libspamc_log_callback != NULL) {
2227 libspamc_log_callback(flags, level, msg, ap);
2229 else if ((flags & SPAMC_LOG_TO_STDERR) != 0) {
2230 /* create a log-line buffer */
2231 len = snprintf(buf, LOG_BUFSIZ, "spamc: ");
2232 len += vsnprintf(buf+len, LOG_BUFSIZ-len, msg, ap);
2234 /* avoid buffer overflow */
2235 if (len > (LOG_BUFSIZ-2)) { len = (LOG_BUFSIZ-3); }
2237 len += snprintf(buf+len, LOG_BUFSIZ-len, "\n");
2238 buf[LOG_BUFSIZ] = '\0'; /* ensure termination */
2239 (void) write (2, buf, len);
2242 vsnprintf(buf, LOG_BUFSIZ, msg, ap);
2243 buf[LOG_BUFSIZ] = '\0'; /* ensure termination */
2245 syslog (level, "%s", buf);
2247 (void) level; /* not used. suppress compiler warning */
2248 f_printerr ("%s\n", buf);
2255 /* --------------------------------------------------------------------------- */
2258 * Unit tests. Must be built externally, e.g.:
2260 * gcc -g -DLIBSPAMC_UNIT_TESTS spamd/spamc.c spamd/libspamc.c spamd/utils.c -o libspamctest
2264 #ifdef LIBSPAMC_UNIT_TESTS
2266 static void _test_locale_safe_string_to_float_val(float input)
2268 char inputstr[99], cmpbuf1[99], cmpbuf2[99];
2271 /* sprintf instead of snprintf is safe here because it is only a controlled test */
2272 sprintf(inputstr, "%f", input);
2273 output = _locale_safe_string_to_float(inputstr, 99);
2274 if (input == output) {
2278 /* could be a rounding error. print as string and compare those */
2279 sprintf(cmpbuf1, "%f", input);
2280 sprintf(cmpbuf2, "%f", output);
2281 if (!strcmp(cmpbuf1, cmpbuf2)) {
2285 printf("FAIL: input=%f != output=%f\n", input, output);
2288 static void unit_test_locale_safe_string_to_float(void)
2290 float statictestset[] = { /* will try both +ve and -ve */
2291 0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001,
2292 9.1, 9.91, 9.991, 9.9991, 9.99991, 9.999991,
2293 0.0 /* end of set constant */
2298 printf("starting unit_test_locale_safe_string_to_float\n");
2299 /* tests of precision */
2300 for (i = 0; statictestset[i] != 0.0; i++) {
2301 _test_locale_safe_string_to_float_val(statictestset[i]);
2302 _test_locale_safe_string_to_float_val(-statictestset[i]);
2303 _test_locale_safe_string_to_float_val(1 - statictestset[i]);
2304 _test_locale_safe_string_to_float_val(1 + statictestset[i]);
2306 /* now exhaustive, in steps of 0.01 */
2307 for (num = -1000.0; num < 1000.0; num += 0.01) {
2308 _test_locale_safe_string_to_float_val(num);
2310 printf("finished unit_test_locale_safe_string_to_float\n");
2313 void do_libspamc_unit_tests(void)
2315 unit_test_locale_safe_string_to_float();
2319 #endif /* LIBSPAMC_UNIT_TESTS */