1 /* <@LICENSE>
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:
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  * </@LICENSE>
17  */
18 
19 /*
20   Compile with extra warnings -- gcc only, not suitable for use as default:
21 
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
24  */
25 
26 #include "config.h"
27 #include "claws-features.h"
28 #include "libspamc.h"
29 
30 #include <stdarg.h>
31 #include <stdlib.h>
32 #include <assert.h>
33 #include <stdio.h>
34 #include <string.h>
35 #ifdef _WIN32
36 #define snprintf _snprintf
37 #define vsnprintf _vsnprintf
38 #define strcasecmp stricmp
39 #define sleep Sleep
40 #include <io.h>
41 #else
42 #include <syslog.h>
43 #include <unistd.h>
44 #include <sys/types.h>
45 #include <sys/socket.h>
46 #include <netinet/in.h>
47 #include <sys/un.h>
48 #include <netinet/tcp.h>
49 #include <arpa/inet.h>
50 #define closesocket(x) close(x)
51 #endif
52 
53 #ifdef HAVE_SYSEXITS_H
54 #include <sysexits.h>
55 #endif
56 #ifdef HAVE_ERRNO_H
57 #include <errno.h>
58 #endif
59 #ifdef HAVE_SYS_ERRNO_H
60 #include <sys/errno.h>
61 #endif
62 #ifdef HAVE_TIME_H
63 #include <time.h>
64 #endif
65 #ifdef HAVE_SYS_TIME_H
66 #include <sys/time.h>
67 #endif
68 #ifdef HAVE_ZLIB_H
69 #include <zlib.h>
70 #endif
71 
72 /* must load *after* errno.h, Bug 6697 */
73 #include "file-utils.h"
74 #include "utils.h"
75 
76 /* RedHat 5.2 doesn't define Shutdown 2nd Parameter Constants */
77 /* KAM 12-4-01 */
78 /* SJF 2003/04/25 - now test for macros directly */
79 #ifndef SHUT_RD
80 #  define SHUT_RD 0		/* no more receptions */
81 #endif
82 #ifndef SHUT_WR
83 #  define SHUT_WR 1		/* no more transmissions */
84 #endif
85 #ifndef SHUT_RDWR
86 #  define SHUT_RDWR 2		/* no more receptions or transmissions */
87 #endif
88 
89 #ifndef HAVE_H_ERRNO
90 #define h_errno errno
91 #endif
92 
93 #ifdef _WIN32
94 #define spamc_get_errno()   WSAGetLastError()
95 #else
96 #define spamc_get_errno()   errno
97 #endif
98 
99 #ifndef HAVE_OPTARG
100 extern char *optarg;
101 #endif
102 
103 #ifndef HAVE_INADDR_NONE
104 #define INADDR_NONE             ((in_addr_t) 0xffffffff)
105 #endif
106 
107 /* jm: turned off for now, it should not be necessary. */
108 #undef USE_TCP_NODELAY
109 
110 #ifndef HAVE_EX__MAX
111 /* jm: very conservative figure, should be well out of range on almost all NIXes */
112 #define EX__MAX 200
113 #endif
114 
115 #undef DO_CONNECT_DEBUG_SYSLOGS
116 /*
117 #define DO_CONNECT_DEBUG_SYSLOGS 1
118 #define CONNECT_DEBUG_LEVEL LOG_DEBUG
119 */
120 
121 /* bug 4477 comment 14 */
122 #ifdef NI_MAXHOST
123 #define SPAMC_MAXHOST NI_MAXHOST
124 #else
125 #define SPAMC_MAXHOST 256
126 #endif
127 
128 #ifdef NI_MAXSERV
129 #define SPAMC_MAXSERV NI_MAXSERV
130 #else
131 #define SPAMC_MAXSERV 256
132 #endif
133 
134 /* static const int ESC_PASSTHROUGHRAW = EX__MAX + 666;  No longer seems to be used */
135 
136 /* set EXPANSION_ALLOWANCE to something more than might be
137    added to a message in X-headers and the report template */
138 static const int EXPANSION_ALLOWANCE = 16384;
139 
140 /* set NUM_CHECK_BYTES to number of bytes that have to match at beginning and end
141    of the data streams before and after processing by spamd
142    Aug  7 2002 jm: no longer seems to be used
143    static const int NUM_CHECK_BYTES = 32;
144  */
145 
146 /* Set the protocol version that this spamc speaks */
147 static const char *PROTOCOL_VERSION = "SPAMC/1.5";
148 
149 /* "private" part of struct message.
150  * we use this instead of the struct message directly, so that we
151  * can add new members without affecting the ABI.
152  */
153 struct libspamc_private_message
154 {
155     int flags;			/* copied from "flags" arg to message_read() */
156     int alloced_size;           /* allocated space for the "out" buffer */
157 
158     void (*spamc_header_callback)(struct message *m, int flags, char *buf, int len);
159     void (*spamd_header_callback)(struct message *m, int flags, const char *buf, int len);
160 };
161 
162 void (*libspamc_log_callback)(int flags, int level, char *msg, va_list args) = NULL;
163 
164 int libspamc_timeout = 0;
165 int libspamc_connect_timeout = 0;	/* Sep 8, 2008 mrgus: separate connect timeout */
166 
167 /*
168  * translate_connect_errno()
169  *
170  *	Given a UNIX error number obtained (probably) from "connect(2)",
171  *	translate this to a failure code. This module is shared by both
172  *	transport modules - UNIX and TCP.
173  *
174  *	This should ONLY be called when there is an error.
175  */
_translate_connect_errno(int err)176 static int _translate_connect_errno(int err)
177 {
178     switch (err) {
179     case EBADF:
180     case EFAULT:
181     case ENOTSOCK:
182     case EISCONN:
183     case EADDRINUSE:
184     case EINPROGRESS:
185     case EALREADY:
186     case EAFNOSUPPORT:
187 	return EX_SOFTWARE;
188 
189     case ECONNREFUSED:
190     case ETIMEDOUT:
191     case ENETUNREACH:
192 	return EX_UNAVAILABLE;
193 
194     case EACCES:
195 	return EX_NOPERM;
196 
197     default:
198 	return EX_SOFTWARE;
199     }
200 }
201 
202 /*
203  * opensocket()
204  *
205  *	Given a socket family (PF_INET or PF_INET6 or PF_UNIX), try to
206  *	create this socket and store the FD in the pointed-to place.
207  *	If it's successful, do any other setup required to make the socket
208  *	ready to use, such as setting TCP_NODELAY mode, and in any case
209  *      we return EX_OK if all is well.
210  *
211  *	Upon failure we return one of the other EX_??? error codes.
212  */
213 #ifdef SPAMC_HAS_ADDRINFO
_opensocket(int flags,struct addrinfo * res,int * psock)214 static int _opensocket(int flags, struct addrinfo *res, int *psock)
215 {
216 #else
217 static int _opensocket(int flags, int type, int *psock)
218 {
219     int proto = 0;
220 #endif
221     const char *typename;
222     int origerr;
223 #ifdef _WIN32
224     int socktout;
225 #endif
226 
227     assert(psock != 0);
228 
229 	/*----------------------------------------------------------------
230 	 * Create a few induction variables that are implied by the socket
231 	 * type given by the user. The typename is strictly used for debug
232 	 * reporting.
233 	 */
234 #ifdef SPAMC_HAS_ADDRINFO
235     switch(res->ai_family) {
236        case PF_UNIX:
237           typename = "PF_UNIX";
238           break;
239        case PF_INET:
240           typename = "PF_INET";
241           break;
242        case PF_INET6:
243           typename = "PF_INET6";
244           break;
245        default:
246           typename = "Unknown";
247           break;
248     }
249 #else
250     if (type == PF_UNIX) {
251 	typename = "PF_UNIX";
252     }
253     else {
254 	typename = "PF_INET";
255 	proto = IPPROTO_TCP;
256     }
257 #endif
258 
259 #ifdef DO_CONNECT_DEBUG_SYSLOGS
260     libspamc_log(flags, CONNECT_DEBUG_LEVEL, "dbg: create socket(%s)", typename);
261 #endif
262 
263 #ifdef SPAMC_HAS_ADDRINFO
264     if ((*psock = socket(res->ai_family, res->ai_socktype, res->ai_protocol))
265 #else
266     if ((*psock = socket(type, SOCK_STREAM, proto))
267 #endif
268 #ifndef _WIN32
269 	< 0
270 #else
271 	== INVALID_SOCKET
272 #endif
273 	) {
274 
275 		/*--------------------------------------------------------
276 		 * At this point we had a failure creating the socket, and
277 		 * this is pretty much fatal. Translate the error reason
278 		 * into something the user can understand.
279 		 */
280 	origerr = spamc_get_errno();
281 #ifndef _WIN32
282 	libspamc_log(flags, LOG_ERR, "socket(%s) to spamd failed: %s", typename, strerror(origerr));
283 #else
284 	libspamc_log(flags, LOG_ERR, "socket(%s) to spamd failed: %d", typename, origerr);
285 #endif
286 
287 	switch (origerr) {
288 	case EPROTONOSUPPORT:
289 	case EINVAL:
290 	    return EX_SOFTWARE;
291 
292 	case EACCES:
293 	    return EX_NOPERM;
294 
295 	case ENFILE:
296 	case EMFILE:
297 	case ENOBUFS:
298 	case ENOMEM:
299 	    return EX_OSERR;
300 
301 	default:
302 	    return EX_SOFTWARE;
303 	}
304     }
305 
306 #ifdef _WIN32
307     /* bug 4344: makes timeout functional on Win32 */
308     socktout = libspamc_timeout * 1000;
309     if (type == PF_INET
310         && setsockopt(*psock, SOL_SOCKET, SO_RCVTIMEO, (char *)&socktout, sizeof(socktout)) != 0)
311     {
312 
313         origerr = spamc_get_errno();
314         switch (origerr)
315         {
316         case EBADF:
317         case ENOTSOCK:
318         case ENOPROTOOPT:
319         case EFAULT:
320             libspamc_log(flags, LOG_ERR, "setsockopt(SO_RCVTIMEO) failed: %d", origerr);
321             closesocket(*psock);
322             return EX_SOFTWARE;
323 
324         default:
325             break;		/* ignored */
326         }
327     }
328 #endif
329 
330 	/*----------------------------------------------------------------
331 	 * Do a bit of setup on the TCP socket if required. Notes above
332 	 * suggest this is probably not set
333 	 */
334 #ifdef USE_TCP_NODELAY
335     {
336 	int one = 1;
337 
338 	if ( (   type == PF_INET
339 #ifdef PF_INET6
340               || type == PF_INET6
341 #endif
342              ) && setsockopt(*psock, 0, TCP_NODELAY, &one, sizeof one) != 0) {
343 	    origerr = spamc_get_errno();
344 	    switch (origerr) {
345 	    case EBADF:
346 	    case ENOTSOCK:
347 	    case ENOPROTOOPT:
348 	    case EFAULT:
349 		libspamc_log(flags, LOG_ERR,
350 #ifndef _WIN32
351 		       "setsockopt(TCP_NODELAY) failed: %s", strerror(origerr));
352 #else
353 		       "setsockopt(TCP_NODELAY) failed: %d", origerr);
354 #endif
355 		closesocket(*psock);
356 		return EX_SOFTWARE;
357 
358 	    default:
359 		break;		/* ignored */
360 	    }
361 	}
362     }
363 #endif /* USE_TCP_NODELAY */
364 
365     return EX_OK;		/* all is well */
366 }
367 
368 /*
369  * try_to_connect_unix()
370  *
371  *	Given a transport handle that implies using a UNIX domain
372  *	socket, try to make a connection to it and store the resulting
373  *	file descriptor in *sockptr. Return is EX_OK if we did it,
374  *	and some other error code otherwise.
375  */
376 static int _try_to_connect_unix(struct transport *tp, int *sockptr)
377 {
378 #ifndef _WIN32
379     int mysock, status, origerr;
380     struct sockaddr_un addrbuf;
381 #ifdef SPAMC_HAS_ADDRINFO
382     struct addrinfo hints, *res;
383 #else
384     int res = PF_UNIX;
385 #endif
386     int ret;
387 
388     assert(tp != 0);
389     assert(sockptr != 0);
390     assert(tp->socketpath != 0);
391 
392 #ifdef SPAMC_HAS_ADDRINFO
393     memset(&hints, 0, sizeof(hints));
394     hints.ai_family = PF_UNIX;
395     hints.ai_socktype = SOCK_STREAM;
396     hints.ai_protocol = 0;
397     res = &hints;
398 #endif
399 	/*----------------------------------------------------------------
400 	 * If the socket itself can't be created, this is a fatal error.
401 	 */
402     if ((ret = _opensocket(tp->flags, res, &mysock)) != EX_OK)
403 	return ret;
404 
405     /* set up the UNIX domain socket */
406     memset(&addrbuf, 0, sizeof addrbuf);
407     addrbuf.sun_family = AF_UNIX;
408     strncpy(addrbuf.sun_path, tp->socketpath, sizeof addrbuf.sun_path - 1);
409     addrbuf.sun_path[sizeof addrbuf.sun_path - 1] = '\0';
410 
411 #ifdef DO_CONNECT_DEBUG_SYSLOGS
412     libspamc_log(tp->flags, CONNECT_DEBUG_LEVEL, "dbg: connect(AF_UNIX) to spamd at %s",
413 	   addrbuf.sun_path);
414 #endif
415 
416     status = timeout_connect(mysock, (struct sockaddr *) &addrbuf, sizeof(addrbuf));
417 
418     origerr = errno;
419 
420     if (status >= 0) {
421 #ifdef DO_CONNECT_DEBUG_SYSLOGS
422 	libspamc_log(tp->flags, CONNECT_DEBUG_LEVEL, "dbg: connect(AF_UNIX) ok");
423 #endif
424 
425 	*sockptr = mysock;
426 
427 	return EX_OK;
428     }
429 
430     libspamc_log(tp->flags, LOG_ERR, "connect(AF_UNIX) to spamd using --socket='%s' failed: %s",
431 	   addrbuf.sun_path, strerror(origerr));
432     closesocket(mysock);
433 
434     return _translate_connect_errno(origerr);
435 #else
436     (void) tp; /* not used. suppress compiler warning */
437     (void) sockptr; /* not used. suppress compiler warning */
438     return EX_OSERR;
439 #endif
440 }
441 
442 /*
443  * try_to_connect_tcp()
444  *
445  *	Given a transport that implies a TCP connection, either to
446  *	localhost or a list of IP addresses, attempt to connect. The
447  *	list of IP addresses has already been randomized (if requested)
448  *	and limited to just one if fallback has been enabled.
449  */
450 static int _try_to_connect_tcp(const struct transport *tp, int *sockptr)
451 {
452     int numloops;
453     int origerr = 0;
454     int ret;
455 #ifdef SPAMC_HAS_ADDRINFO
456     struct addrinfo *res = NULL;
457     char port[SPAMC_MAXSERV-1]; /* port, for logging */
458 #else
459     int res = PF_INET;
460 #endif
461     char host[SPAMC_MAXHOST-1]; /* hostname, for logging */
462     int connect_retries, retry_sleep;
463 
464     assert(tp != 0);
465     assert(sockptr != 0);
466     assert(tp->nhosts > 0);
467 
468     /* default values */
469     retry_sleep = tp->retry_sleep;
470     connect_retries = tp->connect_retries;
471     if (connect_retries == 0) {
472       connect_retries = 3;
473     }
474     if (retry_sleep < 0) {
475       retry_sleep = 1;
476     }
477 
478     for (numloops = 0; numloops < connect_retries; numloops++) {
479         const int hostix = numloops % tp->nhosts;
480         int status, mysock;
481         int innocent = 0;
482 
483                 /*--------------------------------------------------------
484                 * We always start by creating the socket, as we get only
485                 * one attempt to connect() on each one. If this fails,
486                 * we're done.
487                 */
488 
489 #ifdef SPAMC_HAS_ADDRINFO
490         res = tp->hosts[hostix];
491         while(res) {
492 #ifdef DO_CONNECT_DEBUG_SYSLOGS
493             char *family = NULL;
494             switch(res->ai_family) {
495             case AF_INET:
496                 family = "AF_INET";
497                 break;
498             case AF_INET6:
499                 family = "AF_INET6";
500                 break;
501             default:
502                 family = "Unknown";
503                 break;
504             }
505 #endif
506 
507             if ((ret = _opensocket(tp->flags, res, &mysock)) != EX_OK) {
508                 res = res->ai_next;
509                 continue;
510             }
511 
512             getnameinfo(res->ai_addr, res->ai_addrlen,
513                   host, sizeof(host),
514                   port, sizeof(port),
515                   NI_NUMERICHOST|NI_NUMERICSERV);
516 
517 #ifdef DO_CONNECT_DEBUG_SYSLOGS
518             libspamc_log(tp->flags, CONNECT_DEBUG_LEVEL,
519               "dbg: connect(%s) to spamd (host %s, port %s) (try #%d of %d)",
520                       family, host, port, numloops + 1, connect_retries);
521 #endif
522 
523             /* this is special-cased so that we have an address we can
524              * safely use as an "always fail" test case */
525             if (!strcmp(host, "255.255.255.255")) {
526               libspamc_log(tp->flags, LOG_ERR,
527                           "connect to spamd on %s failed, broadcast addr",
528                           host);
529               status = -1;
530             }
531             else {
532               status = timeout_connect(mysock, res->ai_addr, res->ai_addrlen);
533               if (status != 0) origerr = spamc_get_errno();
534             }
535 
536 #else
537 	    struct sockaddr_in addrbuf;
538 	    const char *ipaddr;
539 	    const char* family="AF_INET";
540 	    if ((ret = _opensocket(tp->flags, PF_INET, &mysock)) != EX_OK)
541 	      return ret;
542 
543 	    memset(&addrbuf, 0, sizeof(addrbuf));
544 
545 	    addrbuf.sin_family = AF_INET;
546 	    addrbuf.sin_port = htons(tp->port);
547 	    addrbuf.sin_addr = tp->hosts[hostix];
548 
549 	    ipaddr = inet_ntoa(addrbuf.sin_addr);
550 
551             /* make a copy in host, for logging (bug 5577) */
552             strncpy (host, ipaddr, sizeof(host) - 1);
553 
554 #ifdef DO_CONNECT_DEBUG_SYSLOGS
555 	    libspamc_log(tp->flags, LOG_DEBUG,
556 			 "dbg: connect(AF_INET) to spamd at %s (try #%d of %d)",
557 			 ipaddr, numloops + 1, connect_retries);
558 #endif
559 
560             /* this is special-cased so that we have an address we can
561              * safely use as an "always fail" test case */
562             if (!strcmp(ipaddr, "255.255.255.255")) {
563               libspamc_log(tp->flags, LOG_ERR,
564                           "connect to spamd on %s failed, broadcast addr",
565                           ipaddr);
566               status = -1;
567             }
568             else {
569               status = timeout_connect(mysock, (struct sockaddr *) &addrbuf,
570                         sizeof(addrbuf));
571               if (status != 0) origerr = spamc_get_errno();
572             }
573 
574 #endif
575 
576             if (status != 0) {
577                   closesocket(mysock);
578 
579                   innocent = origerr == ECONNREFUSED && numloops+1 < tp->nhosts;
580                   libspamc_log(tp->flags, innocent ? LOG_DEBUG : LOG_ERR,
581                       "connect to spamd on %s failed, retrying (#%d of %d): %s",
582                       host, numloops+1, connect_retries,
583 #ifdef _WIN32
584                       origerr
585 #else
586                       strerror(origerr)
587 #endif
588                   );
589 
590             } else {
591 #ifdef DO_CONNECT_DEBUG_SYSLOGS
592                   libspamc_log(tp->flags, CONNECT_DEBUG_LEVEL,
593                           "dbg: connect(%s) to spamd done",family);
594 #endif
595                   *sockptr = mysock;
596 
597                   return EX_OK;
598             }
599 #ifdef SPAMC_HAS_ADDRINFO
600             res = res->ai_next;
601         }
602 #endif
603         if (numloops+1 < connect_retries && !innocent) sleep(retry_sleep);
604     } /* for(numloops...) */
605 
606     libspamc_log(tp->flags, LOG_ERR,
607               "connection attempt to spamd aborted after %d retries",
608               connect_retries);
609 
610     return _translate_connect_errno(origerr);
611 }
612 
613 /* Aug 14, 2002 bj: Reworked things. Now we have message_read, message_write,
614  * message_dump, lookup_host, message_filter, and message_process, and a bunch
615  * of helper functions.
616  */
617 
618 static void _clear_message(struct message *m)
619 {
620     m->type = MESSAGE_NONE;
621     m->raw = NULL;
622     m->raw_len = 0;
623     m->pre = NULL;
624     m->pre_len = 0;
625     m->msg = NULL;
626     m->msg_len = 0;
627     m->post = NULL;
628     m->post_len = 0;
629     m->is_spam = EX_TOOBIG;
630     m->score = 0.0;
631     m->threshold = 0.0;
632     m->outbuf = NULL;
633     m->out = NULL;
634     m->out_len = 0;
635     m->content_length = -1;
636 }
637 
638 static void _free_zlib_buffer(unsigned char **zlib_buf, int *zlib_bufsiz)
639 {
640 	if(*zlib_buf) {
641 	free(*zlib_buf);
642 	*zlib_buf=NULL;
643 	}
644 	*zlib_bufsiz=0;
645 }
646 
647 static void _use_msg_for_out(struct message *m)
648 {
649     if (m->outbuf)
650 	free(m->outbuf);
651     m->outbuf = NULL;
652     m->out = m->msg;
653     m->out_len = m->msg_len;
654 }
655 
656 static int _message_read_raw(int fd, struct message *m)
657 {
658     _clear_message(m);
659     if ((m->raw = malloc(m->max_len + 1)) == NULL)
660 	return EX_OSERR;
661     m->raw_len = full_read(fd, 1, m->raw, m->max_len + 1, m->max_len + 1);
662     if (m->raw_len <= 0) {
663 	free(m->raw);
664 	m->raw = NULL;
665 	m->raw_len = 0;
666 	return EX_IOERR;
667     }
668     m->type = MESSAGE_ERROR;
669     if (m->raw_len > (int) m->max_len)
670     {
671         libspamc_log(m->priv->flags, LOG_NOTICE,
672                 "skipped message, greater than max message size (%d bytes)",
673                 m->max_len);
674 	return EX_TOOBIG;
675     }
676     m->type = MESSAGE_RAW;
677     m->msg = m->raw;
678     m->msg_len = m->raw_len;
679     m->out = m->msg;
680     m->out_len = m->msg_len;
681     return EX_OK;
682 }
683 
684 static int _message_read_bsmtp(int fd, struct message *m)
685 {
686     unsigned int i, j, p_len;
687     char prev;
688     char* p;
689 
690     _clear_message(m);
691     if ((m->raw = malloc(m->max_len + 1)) == NULL)
692 	return EX_OSERR;
693 
694     /* Find the DATA line */
695     m->raw_len = full_read(fd, 1, m->raw, m->max_len + 1, m->max_len + 1);
696     if (m->raw_len <= 0) {
697 	free(m->raw);
698 	m->raw = NULL;
699 	m->raw_len = 0;
700 	return EX_IOERR;
701     }
702     m->type = MESSAGE_ERROR;
703     if (m->raw_len > (int) m->max_len)
704 	return EX_TOOBIG;
705     p = m->pre = m->raw;
706     /* Search for \nDATA\n which marks start of actual message */
707     while ((p_len = (m->raw_len - (p - m->raw))) > 8) { /* leave room for at least \nDATA\n.\n */
708       char* q = memchr(p, '\n', p_len - 8);  /* find next \n then see if start of \nDATA\n */
709       if (q == NULL) break;
710       q++;
711       if (((q[0]|0x20) == 'd') && /* case-insensitive ASCII comparison */
712 	  ((q[1]|0x20) == 'a') &&
713 	  ((q[2]|0x20) == 't') &&
714 	  ((q[3]|0x20) == 'a')) {
715 	q+=4;
716 	if (q[0] == '\r') ++q;
717 	if (*(q++) == '\n') {  /* leave q at start of message if we found it */
718 	  m->msg = q;
719 	  m->pre_len = q - m->raw;
720 	  m->msg_len = m->raw_len - m->pre_len;
721 	  break;
722 	}
723       }
724       p = q; /* the above code ensures no other '\n' comes before q */
725     }
726     if (m->msg == NULL)
727 	return EX_DATAERR;
728 
729     /* ensure this is >= 0 */
730     if (m->msg_len < 0) {
731 	return EX_SOFTWARE;
732     }
733 
734     /* Find the end-of-DATA line */
735     prev = '\n';
736     for (i = j = 0; i < (unsigned int) m->msg_len; i++) {
737 	if (prev == '\n' && m->msg[i] == '.') {
738 	    /* Dot at the beginning of a line */
739             if (((int) (i+1) == m->msg_len)
740                 || ((int) (i+1) < m->msg_len && m->msg[i + 1] == '\n')
741                 || ((int) (i+2) < m->msg_len && m->msg[i + 1] == '\r' && m->msg[i + 2] == '\n')) {
742 		/* Lone dot! That's all, folks */
743 		m->post = m->msg + i;
744 		m->post_len = m->msg_len - i;
745 		m->msg_len = j;
746 		break;
747 	    }
748 	    else if ((int) (i+1) < m->msg_len && m->msg[i + 1] == '.') {
749 		/* Escaping dot, eliminate. */
750 		prev = '.';
751 		continue;
752 	    }			/* Else an ordinary dot, drop down to ordinary char handler */
753 	}
754 	prev = m->msg[i];
755 	m->msg[j++] = m->msg[i];
756     }
757 
758     /* if bad format with no end "\n.\n", error out */
759     if (m->post == NULL)
760 	return EX_DATAERR;
761     m->type = MESSAGE_BSMTP;
762     m->out = m->msg;
763     m->out_len = m->msg_len;
764     return EX_OK;
765 }
766 
767 int message_read(int fd, int flags, struct message *m)
768 {
769     assert(m != NULL);
770 
771     libspamc_timeout = 0;
772 
773     /* create the "private" part of the struct message */
774     m->priv = malloc(sizeof(struct libspamc_private_message));
775     if (m->priv == NULL) {
776 	libspamc_log(flags, LOG_ERR, "message_read: malloc failed");
777 	return EX_OSERR;
778     }
779     m->priv->flags = flags;
780     m->priv->alloced_size = 0;
781     m->priv->spamc_header_callback = 0;
782     m->priv->spamd_header_callback = 0;
783 
784     if (flags & SPAMC_PING) {
785       _clear_message(m);
786       return EX_OK;
787     }
788 
789     switch (flags & SPAMC_MODE_MASK) {
790     case SPAMC_RAW_MODE:
791 	return _message_read_raw(fd, m);
792 
793     case SPAMC_BSMTP_MODE:
794 	return _message_read_bsmtp(fd, m);
795 
796     default:
797 	libspamc_log(flags, LOG_ERR, "message_read: Unknown mode %d",
798 		flags & SPAMC_MODE_MASK);
799 	return EX_USAGE;
800     }
801 }
802 
803 long message_write(int fd, struct message *m)
804 {
805     long total = 0;
806     off_t i, j;
807     off_t jlimit;
808     char buffer[1024];
809 
810     assert(m != NULL);
811 
812     if (m->priv->flags & (SPAMC_CHECK_ONLY|SPAMC_PING)) {
813 	if (m->is_spam == EX_ISSPAM || m->is_spam == EX_NOTSPAM) {
814 	    return full_write(fd, 1, m->out, m->out_len);
815 
816 	}
817 	else {
818 	    libspamc_log(m->priv->flags, LOG_ERR, "oops! SPAMC_CHECK_ONLY is_spam: %d",
819                         m->is_spam);
820 	    return -1;
821 	}
822     }
823 
824     /* else we're not in CHECK_ONLY mode */
825     switch (m->type) {
826     case MESSAGE_NONE:
827 	libspamc_log(m->priv->flags, LOG_ERR, "Cannot write this message, it's MESSAGE_NONE!");
828 	return -1;
829 
830     case MESSAGE_ERROR:
831 	return full_write(fd, 1, m->raw, m->raw_len);
832 
833     case MESSAGE_RAW:
834 	return full_write(fd, 1, m->out, m->out_len);
835 
836     case MESSAGE_BSMTP:
837 	total = full_write(fd, 1, m->pre, m->pre_len);
838 	for (i = 0; i < m->out_len;) {
839 	    jlimit = (off_t) (sizeof(buffer) / sizeof(*buffer) - 4);
840 	    for (j = 0; i < (off_t) m->out_len && j < jlimit;) {
841 		if (i + 1 < m->out_len && m->out[i] == '\n'
842 		    && m->out[i + 1] == '.') {
843 		    if (j > jlimit - 4) {
844 			break;	/* avoid overflow */
845 		    }
846 		    buffer[j++] = m->out[i++];
847 		    buffer[j++] = m->out[i++];
848 		    buffer[j++] = '.';
849 		}
850 		else {
851 		    buffer[j++] = m->out[i++];
852 		}
853 	    }
854 	    total += full_write(fd, 1, buffer, j);
855 	}
856 	return total + full_write(fd, 1, m->post, m->post_len);
857 
858     default:
859 	libspamc_log(m->priv->flags, LOG_ERR, "Unknown message type %d", m->type);
860 	return -1;
861     }
862 }
863 
864 void message_dump(int in_fd, int out_fd, struct message *m, int flags)
865 {
866     char buf[8196];
867     int bytes;
868 
869     if (m == NULL) {
870 	libspamc_log(flags, LOG_ERR, "oops! message_dump called with NULL message");
871 	return;
872     }
873 
874     if (m->type != MESSAGE_NONE) {
875 	message_write(out_fd, m);
876     }
877 
878     while ((bytes = full_read(in_fd, 1, buf, 8192, 8192)) > 0) {
879 	if (bytes != full_write(out_fd, 1, buf, bytes)) {
880 	    libspamc_log(flags, LOG_ERR, "oops! message_dump of %d returned different",
881 		   bytes);
882 	}
883     }
884 }
885 
886 static int
887 _spamc_read_full_line(struct message *m, int flags, SSL * ssl, int sock,
888 		      char *buf, size_t *lenp, size_t bufsiz)
889 {
890     int failureval;
891     int bytesread = 0;
892     size_t len;
893 
894     UNUSED_VARIABLE(m);
895 
896     *lenp = 0;
897     /* Now, read from spamd */
898     for (len = 0; len < bufsiz - 1; len++) {
899 	if (flags & SPAMC_USE_SSL) {
900 	    bytesread = ssl_timeout_read(ssl, buf + len, 1);
901 	}
902 	else {
903 	    bytesread = fd_timeout_read(sock, 0, buf + len, 1);
904 	}
905 
906 	if (bytesread <= 0) {
907 	    failureval = EX_IOERR;
908 	    goto failure;
909 	}
910 
911 	if (buf[len] == '\n') {
912 	    buf[len] = '\0';
913 	    if (len > 0 && buf[len - 1] == '\r') {
914 		len--;
915 		buf[len] = '\0';
916 	    }
917 	    *lenp = len;
918 	    return EX_OK;
919 	}
920     }
921 
922     libspamc_log(flags, LOG_ERR, "spamd responded with line of %d bytes, dying", len);
923     failureval = EX_TOOBIG;
924 
925   failure:
926     return failureval;
927 }
928 
929 /*
930  * May  7 2003 jm: using %f is bad where LC_NUMERIC is "," in the locale.
931  * work around using our own locale-independent float-parser code.
932  */
933 static float _locale_safe_string_to_float(char *buf, int siz)
934 {
935     int is_neg;
936     char *cp, *dot;
937     int divider;
938     float ret, postdot;
939 
940     buf[siz - 1] = '\0';	/* ensure termination */
941 
942     /* ok, let's illustrate using "100.033" as an example... */
943 
944     is_neg = 0;
945     if (*buf == '-') {
946 	is_neg = 1;
947     }
948 
949     ret = (float) (strtol(buf, &dot, 10));
950     if (dot == NULL) {
951 	return 0.0;
952     }
953     if (dot != NULL && *dot != '.') {
954 	return ret;
955     }
956 
957     /* ex: ret == 100.0 */
958 
959     cp = (dot + 1);
960     postdot = (float) (strtol(cp, NULL, 10));
961     /* note: don't compare floats == 0.0, it's unsafe.  use a range */
962     if (postdot >= -0.00001 && postdot <= 0.00001) {
963 	return ret;
964     }
965 
966     /* ex: postdot == 33.0, cp="033" */
967 
968     /* now count the number of decimal places and figure out what power of 10 to use */
969     divider = 1;
970     while (*cp != '\0') {
971 	divider *= 10;
972 	cp++;
973     }
974 
975     /* ex:
976      * cp="033", divider=1
977      * cp="33", divider=10
978      * cp="3", divider=100
979      * cp="", divider=1000
980      */
981 
982     if (is_neg) {
983 	ret -= (postdot / ((float) divider));
984     }
985     else {
986 	ret += (postdot / ((float) divider));
987     }
988     /* ex: ret == 100.033, tada! ... hopefully */
989 
990     return ret;
991 }
992 
993 static int
994 _handle_spamd_header(struct message *m, int flags, char *buf, int len,
995 		     unsigned int *didtellflags)
996 {
997     char is_spam[6];
998     char s_str[21], t_str[21];
999     char didset_ret[15];
1000     char didremove_ret[15];
1001 
1002     UNUSED_VARIABLE(len);
1003 
1004     /* Feb 12 2003 jm: actually, I think sccanf is working fine here ;)
1005      * let's stick with it for this parser.
1006      * May  7 2003 jm: using %f is bad where LC_NUMERIC is "," in the locale.
1007      * work around using our own locale-independent float-parser code.
1008      */
1009     if (sscanf(buf, "Spam: %5s ; %20s / %20s", is_spam, s_str, t_str) == 3) {
1010 	m->score = _locale_safe_string_to_float(s_str, 20);
1011 	m->threshold = _locale_safe_string_to_float(t_str, 20);
1012 
1013 	/* set bounds on these to ensure no buffer overflow in the sprintf */
1014 	if (m->score > 1e10)
1015 	    m->score = 1e10;
1016 	else if (m->score < -1e10)
1017 	    m->score = -1e10;
1018 	if (m->threshold > 1e10)
1019 	    m->threshold = 1e10;
1020 	else if (m->threshold < -1e10)
1021 	    m->threshold = -1e10;
1022 
1023 	/* Format is "Spam: x; y / x" */
1024 	m->is_spam =
1025 	    strcasecmp("true", is_spam) == 0 ? EX_ISSPAM : EX_NOTSPAM;
1026 
1027 	if (flags & SPAMC_CHECK_ONLY) {
1028 	    m->out_len = sprintf(m->out,
1029 				 "%.1f/%.1f\n", m->score, m->threshold);
1030 	}
1031 	else if ((flags & SPAMC_REPORT_IFSPAM && m->is_spam == EX_ISSPAM)
1032 		 || (flags & SPAMC_REPORT)) {
1033 	    m->out_len = sprintf(m->out,
1034 				 "%.1f/%.1f\n", m->score, m->threshold);
1035 	}
1036 	return EX_OK;
1037 
1038     }
1039     else if (sscanf(buf, "Content-length: %d", &m->content_length) == 1) {
1040 	if (m->content_length < 0) {
1041 	    libspamc_log(flags, LOG_ERR, "spamd responded with bad Content-length '%s'",
1042 		   buf);
1043 	    return EX_PROTOCOL;
1044 	}
1045 	return EX_OK;
1046     }
1047     else if (sscanf(buf, "DidSet: %14s", didset_ret) == 1) {
1048       if (strstr(didset_ret, "local")) {
1049 	  *didtellflags |= SPAMC_SET_LOCAL;
1050 	}
1051 	if (strstr(didset_ret, "remote")) {
1052 	  *didtellflags |= SPAMC_SET_REMOTE;
1053 	}
1054     }
1055     else if (sscanf(buf, "DidRemove: %14s", didremove_ret) == 1) {
1056         if (strstr(didremove_ret, "local")) {
1057 	  *didtellflags |= SPAMC_REMOVE_LOCAL;
1058 	}
1059 	if (strstr(didremove_ret, "remote")) {
1060 	  *didtellflags |= SPAMC_REMOVE_REMOTE;
1061 	}
1062     }
1063     else if (m->priv->spamd_header_callback != NULL)
1064       m->priv->spamd_header_callback(m, flags, buf, len);
1065 
1066     return EX_OK;
1067 }
1068 
1069 static int
1070 _zlib_compress (char *m_msg, int m_msg_len,
1071         unsigned char **zlib_buf, int *zlib_bufsiz, int flags)
1072 {
1073     int rc;
1074     int len, totallen;
1075 
1076 #ifndef HAVE_LIBZ
1077 
1078     UNUSED_VARIABLE(m_msg);
1079     UNUSED_VARIABLE(m_msg_len);
1080     UNUSED_VARIABLE(zlib_buf);
1081     UNUSED_VARIABLE(zlib_bufsiz);
1082     UNUSED_VARIABLE(rc);
1083     UNUSED_VARIABLE(len);
1084     UNUSED_VARIABLE(totallen);
1085     libspamc_log(flags, LOG_ERR, "spamc not built with zlib support");
1086     return EX_SOFTWARE;
1087 
1088 #else
1089     z_stream strm;
1090 
1091     UNUSED_VARIABLE(flags);
1092 
1093     /* worst-case, according to http://www.zlib.org/zlib_tech.html ;
1094       * same as input, plus 5 bytes per 16k, plus 6 bytes.  this should
1095       * be plenty */
1096     *zlib_bufsiz = (int) (m_msg_len * 1.0005) + 1024;
1097     *zlib_buf = (unsigned char *) malloc (*zlib_bufsiz);
1098     if (*zlib_buf == NULL) {
1099         return EX_OSERR;
1100     }
1101 
1102     strm.zalloc = Z_NULL;
1103     strm.zfree = Z_NULL;
1104     strm.opaque = Z_NULL;
1105     rc = deflateInit(&strm, 3);
1106     if (rc != Z_OK) {
1107         return EX_OSERR;
1108     }
1109 
1110     strm.avail_in = m_msg_len;
1111     strm.next_in = (unsigned char *) m_msg;
1112     strm.avail_out = *zlib_bufsiz;
1113     strm.next_out = (unsigned char *) *zlib_buf;
1114 
1115     totallen = 0;
1116     do {
1117         rc = deflate(&strm, Z_FINISH);
1118         assert(rc != Z_STREAM_ERROR);
1119         len = (size_t) (*zlib_bufsiz - strm.avail_out);
1120         strm.next_out += len;
1121         totallen += len;
1122     } while (strm.avail_out == 0);
1123 
1124     *zlib_bufsiz = totallen;
1125     return EX_OK;
1126 
1127 #endif
1128 }
1129 
1130 int
1131 _append_original_body (struct message *m, int flags)
1132 {
1133     char *cp, *cpend, *bodystart;
1134     int bodylen, outspaceleft, towrite;
1135 
1136     /* at this stage, m->out now contains the rewritten headers.
1137      * find and append the raw message's body, up to m->priv->alloced_size
1138      * bytes.
1139      */
1140 
1141 #define CRNLCRNL        "\r\n\r\n"
1142 #define CRNLCRNL_LEN    4
1143 #define NLNL            "\n\n"
1144 #define NLNL_LEN        2
1145 
1146     cpend = m->raw + m->raw_len;
1147     bodystart = NULL;
1148 
1149     for (cp = m->raw; cp < cpend; cp++) {
1150         if (*cp == '\r' && cpend - cp >= CRNLCRNL_LEN &&
1151                             !strncmp(cp, CRNLCRNL, CRNLCRNL_LEN))
1152         {
1153             bodystart = cp + CRNLCRNL_LEN;
1154             break;
1155         }
1156         else if (*cp == '\n' && cpend - cp >= NLNL_LEN &&
1157                            !strncmp(cp, NLNL, NLNL_LEN))
1158         {
1159             bodystart = cp + NLNL_LEN;
1160             break;
1161         }
1162     }
1163 
1164     if (bodystart == NULL) {
1165         libspamc_log(flags, LOG_ERR, "failed to find end-of-headers");
1166         return EX_SOFTWARE;
1167     }
1168 
1169     bodylen = cpend - bodystart;
1170     outspaceleft = (m->priv->alloced_size-1) - m->out_len;
1171     towrite = (bodylen < outspaceleft ? bodylen : outspaceleft);
1172 
1173     /* copy in the body; careful not to overflow */
1174     strncpy (m->out + m->out_len, bodystart, towrite);
1175     m->out_len += towrite;
1176     return EX_OK;
1177 }
1178 
1179 int message_filter(struct transport *tp, const char *username,
1180                    int flags, struct message *m)
1181 {
1182     char buf[8192];
1183     size_t bufsiz = (sizeof(buf) / sizeof(*buf)) - 4; /* bit of breathing room */
1184     size_t len;
1185     int sock = -1;
1186     int rc;
1187     char versbuf[20];
1188     float version;
1189     int response;
1190     int failureval = EX_SOFTWARE;
1191     unsigned int throwaway;
1192     SSL_CTX *ctx = NULL;
1193     SSL *ssl = NULL;
1194     const SSL_METHOD *meth;
1195     char zlib_on = 0;
1196     unsigned char *zlib_buf = NULL;
1197     int zlib_bufsiz = 0;
1198     unsigned char *towrite_buf;
1199     int towrite_len;
1200     int filter_retry_count;
1201     int filter_retry_sleep;
1202     int filter_retries;
1203     #ifdef SPAMC_HAS_ADDRINFO
1204         struct addrinfo *tmphost;
1205     #else
1206         struct in_addr tmphost;
1207     #endif
1208     int nhost_counter;
1209 
1210     assert(tp != NULL);
1211     assert(m != NULL);
1212 
1213     if ((flags & SPAMC_USE_ZLIB) != 0) {
1214       zlib_on = 1;
1215     }
1216 
1217     if (flags & SPAMC_USE_SSL) {
1218 #ifdef SPAMC_SSL
1219 	SSLeay_add_ssl_algorithms();
1220 	meth = SSLv23_client_method();
1221 	SSL_load_error_strings();
1222 	ctx = SSL_CTX_new(meth);
1223 #else
1224 	UNUSED_VARIABLE(ssl);
1225 	UNUSED_VARIABLE(meth);
1226 	UNUSED_VARIABLE(ctx);
1227 	libspamc_log(flags, LOG_ERR, "spamc not built with SSL support");
1228 	return EX_SOFTWARE;
1229 #endif
1230     }
1231 
1232     m->is_spam = EX_TOOBIG;
1233 
1234     if (m->outbuf != NULL)
1235         free(m->outbuf);
1236     m->priv->alloced_size = m->max_len + EXPANSION_ALLOWANCE + 1;
1237     if ((m->outbuf = malloc(m->priv->alloced_size)) == NULL) {
1238 	failureval = EX_OSERR;
1239 	goto failure;
1240     }
1241     m->out = m->outbuf;
1242     m->out_len = 0;
1243 
1244     /* If the spamd filter takes too long and we timeout, then
1245      * retry again.  This gets us around a hung child thread
1246      * in spamd or a problem on a spamd host in a multi-host
1247      * setup.  If there is more than one destination host
1248      * we move to the next host on each attempt.
1249      */
1250 
1251     /* default values */
1252     filter_retry_sleep = tp->filter_retry_sleep;
1253     filter_retries = tp->filter_retries;
1254     if (filter_retries == 0) {
1255         filter_retries = 1;
1256     }
1257     if (filter_retry_sleep < 0) {
1258         filter_retry_sleep = 1;
1259     }
1260 
1261     /* filterloop - Ensure that we run through this at least
1262      * once, and again if there are errors
1263      */
1264     filter_retry_count = 0;
1265     while ((filter_retry_count==0) ||
1266                 ((filter_retry_count<tp->filter_retries) && (failureval == EX_IOERR)))
1267     {
1268         if (filter_retry_count != 0){
1269             /* Ensure that the old socket gets closed */
1270             if (sock != -1) {
1271                 closesocket(sock);
1272                 sock=-1;
1273             }
1274 
1275             /* Move to the next host in the list, if nhosts>1 */
1276             if (tp->nhosts > 1) {
1277                 tmphost = tp->hosts[0];
1278 
1279                 /* TODO: free using freeaddrinfo() */
1280                 for (nhost_counter = 1; nhost_counter < tp->nhosts; nhost_counter++) {
1281                     tp->hosts[nhost_counter - 1] = tp->hosts[nhost_counter];
1282                 }
1283 
1284                 tp->hosts[nhost_counter - 1] = tmphost;
1285             }
1286 
1287             /* Now sleep the requested amount */
1288             sleep(filter_retry_sleep);
1289         }
1290 
1291         filter_retry_count++;
1292 
1293         /* Build spamd protocol header */
1294         if (flags & SPAMC_CHECK_ONLY)
1295           strcpy(buf, "CHECK ");
1296         else if (flags & SPAMC_REPORT_IFSPAM)
1297           strcpy(buf, "REPORT_IFSPAM ");
1298         else if (flags & SPAMC_REPORT)
1299           strcpy(buf, "REPORT ");
1300         else if (flags & SPAMC_SYMBOLS)
1301           strcpy(buf, "SYMBOLS ");
1302         else if (flags & SPAMC_PING)
1303           strcpy(buf, "PING ");
1304         else if (flags & SPAMC_HEADERS)
1305           strcpy(buf, "HEADERS ");
1306         else
1307           strcpy(buf, "PROCESS ");
1308 
1309         len = strlen(buf);
1310         if (len + strlen(PROTOCOL_VERSION) + 2 >= bufsiz) {
1311             _use_msg_for_out(m);
1312             return EX_OSERR;
1313         }
1314 
1315         strcat(buf, PROTOCOL_VERSION);
1316         strcat(buf, "\r\n");
1317         len = strlen(buf);
1318 
1319         towrite_buf = (unsigned char *) m->msg;
1320         towrite_len = (int) m->msg_len;
1321         if (zlib_on) {
1322             if (_zlib_compress(m->msg, m->msg_len, &zlib_buf, &zlib_bufsiz, flags) != EX_OK)
1323             {
1324                 _free_zlib_buffer(&zlib_buf, &zlib_bufsiz);
1325                 return EX_OSERR;
1326             }
1327             towrite_buf = zlib_buf;
1328             towrite_len = zlib_bufsiz;
1329         }
1330 
1331         if (!(flags & SPAMC_PING)) {
1332           if (username != NULL) {
1333               if (strlen(username) + 8 >= (bufsiz - len)) {
1334                   _use_msg_for_out(m);
1335                   if (zlib_on) {
1336                       _free_zlib_buffer(&zlib_buf, &zlib_bufsiz);
1337                   }
1338                   return EX_OSERR;
1339               }
1340               strcpy(buf + len, "User: ");
1341               strcat(buf + len, username);
1342               strcat(buf + len, "\r\n");
1343               len += strlen(buf + len);
1344           }
1345           if (zlib_on) {
1346               len += snprintf(buf + len, 8192-len, "Compress: zlib\r\n");
1347           }
1348           if ((m->msg_len > SPAMC_MAX_MESSAGE_LEN) || ((len + 27) >= (bufsiz - len))) {
1349               _use_msg_for_out(m);
1350               if (zlib_on) {
1351                   _free_zlib_buffer(&zlib_buf, &zlib_bufsiz);
1352               }
1353               return EX_DATAERR;
1354           }
1355           len += snprintf(buf + len, 8192-len, "Content-length: %d\r\n", (int) towrite_len);
1356         }
1357         /* bug 6187, PING needs empty line too, bumps protocol version to 1.5 */
1358         len += snprintf(buf + len, 8192-len, "\r\n");
1359 
1360         libspamc_timeout = m->timeout;
1361         libspamc_connect_timeout = m->connect_timeout;	/* Sep 8, 2008 mrgus: separate connect timeout */
1362 
1363         if (tp->socketpath)
1364           rc = _try_to_connect_unix(tp, &sock);
1365         else
1366           rc = _try_to_connect_tcp(tp, &sock);
1367 
1368         if (rc != EX_OK) {
1369           _use_msg_for_out(m);
1370           if (zlib_on) {
1371               _free_zlib_buffer(&zlib_buf, &zlib_bufsiz);
1372           }
1373           return rc;      /* use the error code try_to_connect_*() gave us. */
1374         }
1375 
1376         if (flags & SPAMC_USE_SSL) {
1377 #ifdef SPAMC_SSL
1378             ssl = SSL_new(ctx);
1379             SSL_set_fd(ssl, sock);
1380             SSL_connect(ssl);
1381 #endif
1382         }
1383 
1384         /* Send to spamd */
1385         if (flags & SPAMC_USE_SSL) {
1386 #ifdef SPAMC_SSL
1387             SSL_write(ssl, buf, len);
1388             SSL_write(ssl, towrite_buf, towrite_len);
1389 #endif
1390         }
1391         else {
1392             full_write(sock, 0, buf, len);
1393             full_write(sock, 0, towrite_buf, towrite_len);
1394             shutdown(sock, SHUT_WR);
1395         }
1396 
1397         /* free zlib buffer
1398         * bug 6025: zlib buffer not freed if compression is used
1399         */
1400         if (zlib_on) {
1401             _free_zlib_buffer(&zlib_buf, &zlib_bufsiz);
1402         }
1403 
1404         /* ok, now read and parse it.  SPAMD/1.2 line first... */
1405         failureval =
1406                 _spamc_read_full_line(m, flags, ssl, sock, buf, &len, bufsiz);
1407     } /* end of filterloop */
1408 
1409     if (failureval != EX_OK) {
1410         goto failure;
1411     }
1412 
1413     if (sscanf(buf, "SPAMD/%18s %d %*s", versbuf, &response) != 2) {
1414 	libspamc_log(flags, LOG_ERR, "spamd responded with bad string '%s'", buf);
1415 	failureval = EX_PROTOCOL;
1416 	goto failure;
1417     }
1418 
1419     versbuf[19] = '\0';
1420     version = _locale_safe_string_to_float(versbuf, 20);
1421     if (version < 1.0) {
1422 	libspamc_log(flags, LOG_ERR, "spamd responded with bad version string '%s'",
1423 	       versbuf);
1424 	failureval = EX_PROTOCOL;
1425 	goto failure;
1426     }
1427 
1428     if (flags & SPAMC_PING) {
1429 	closesocket(sock);
1430 	sock = -1;
1431         m->out_len = sprintf(m->out, "SPAMD/%s %d\n", versbuf, response);
1432         m->is_spam = EX_NOTSPAM;
1433         return EX_OK;
1434     }
1435 
1436     m->score = 0;
1437     m->threshold = 0;
1438     m->is_spam = EX_TOOBIG;
1439     while (1) {
1440 	failureval =
1441 	    _spamc_read_full_line(m, flags, ssl, sock, buf, &len, bufsiz);
1442 	if (failureval != EX_OK) {
1443 	    goto failure;
1444 	}
1445 
1446 	if (len == 0 && buf[0] == '\0') {
1447 	    break;		/* end of headers */
1448 	}
1449 
1450 	if (_handle_spamd_header(m, flags, buf, len, &throwaway) < 0) {
1451 	    failureval = EX_PROTOCOL;
1452 	    goto failure;
1453 	}
1454     }
1455 
1456     len = 0;			/* overwrite those headers */
1457 
1458     if (flags & SPAMC_CHECK_ONLY) {
1459 	closesocket(sock);
1460 	sock = -1;
1461 	if (m->is_spam == EX_TOOBIG) {
1462 	    /* We should have gotten headers back... Damnit. */
1463 	    failureval = EX_PROTOCOL;
1464 	    goto failure;
1465 	}
1466 	return EX_OK;
1467     }
1468     else {
1469 	if (m->content_length < 0) {
1470 	    /* should have got a length too. */
1471 	    failureval = EX_PROTOCOL;
1472 	    goto failure;
1473 	}
1474 
1475 	/* have we already got something in the buffer (e.g. REPORT and
1476 	 * REPORT_IFSPAM both create a line from the "Spam:" hdr)?  If
1477 	 * so, add the size of that so our sanity check passes.
1478 	 */
1479 	if (m->out_len > 0) {
1480 	    m->content_length += m->out_len;
1481 	}
1482 
1483 	if (flags & SPAMC_USE_SSL) {
1484 	    len = full_read_ssl(ssl, (unsigned char *) m->out + m->out_len,
1485 				m->priv->alloced_size - m->out_len,
1486 				m->priv->alloced_size - m->out_len);
1487 	}
1488 	else {
1489 	    len = full_read(sock, 0, m->out + m->out_len,
1490 			    m->priv->alloced_size - m->out_len,
1491 			    m->priv->alloced_size - m->out_len);
1492 	}
1493 
1494 
1495 	if ((int) len + (int) m->out_len > (m->priv->alloced_size - 1)) {
1496 	    failureval = EX_TOOBIG;
1497 	    goto failure;
1498 	}
1499 	m->out_len += len;
1500 
1501 	shutdown(sock, SHUT_RD);
1502 	closesocket(sock);
1503 	sock = -1;
1504     }
1505     libspamc_timeout = 0;
1506 
1507     if (m->out_len != m->content_length) {
1508 	libspamc_log(flags, LOG_ERR,
1509 	       "failed sanity check, %d bytes claimed, %d bytes seen",
1510 	       m->content_length, m->out_len);
1511 	failureval = EX_PROTOCOL;
1512 	goto failure;
1513     }
1514 
1515     if (flags & SPAMC_HEADERS) {
1516         if (_append_original_body(m, flags) != EX_OK) {
1517             goto failure;
1518         }
1519     }
1520 
1521     return EX_OK;
1522 
1523   failure:
1524 	_use_msg_for_out(m);
1525     if (sock != -1) {
1526 	closesocket(sock);
1527     }
1528     libspamc_timeout = 0;
1529 
1530     if (flags & SPAMC_USE_SSL) {
1531 #ifdef SPAMC_SSL
1532 	SSL_free(ssl);
1533 	SSL_CTX_free(ctx);
1534 #endif
1535     }
1536     return failureval;
1537 }
1538 
1539 int message_process(struct transport *trans, char *username, int max_size,
1540 		    int in_fd, int out_fd, const int flags)
1541 {
1542     int ret;
1543     struct message m;
1544 
1545     assert(trans != NULL);
1546 
1547     m.type = MESSAGE_NONE;
1548 
1549     /* enforce max_size being unsigned, therefore >= 0 */
1550     if (max_size < 0) {
1551 	ret = EX_SOFTWARE;
1552         goto FAIL;
1553     }
1554     m.max_len = (unsigned int) max_size;
1555 
1556     ret = message_read(in_fd, flags, &m);
1557     if (ret != EX_OK)
1558         goto FAIL;
1559     ret = message_filter(trans, username, flags, &m);
1560     if (ret != EX_OK)
1561         goto FAIL;
1562     if (message_write(out_fd, &m) < 0)
1563         goto FAIL;
1564     if (m.is_spam != EX_TOOBIG) {
1565         message_cleanup(&m);
1566         return m.is_spam;
1567     }
1568     message_cleanup(&m);
1569     return ret;
1570 
1571   FAIL:
1572     if (flags & SPAMC_CHECK_ONLY) {
1573         full_write(out_fd, 1, "0/0\n", 4);
1574         message_cleanup(&m);
1575         return EX_NOTSPAM;
1576     }
1577     else {
1578         message_dump(in_fd, out_fd, &m, flags);
1579         message_cleanup(&m);
1580         return ret;
1581     }
1582 }
1583 
1584 int message_tell(struct transport *tp, const char *username, int flags,
1585 		 struct message *m, int msg_class,
1586 		 unsigned int tellflags, unsigned int *didtellflags)
1587 {
1588     char buf[8192];
1589     size_t bufsiz = (sizeof(buf) / sizeof(*buf)) - 4; /* bit of breathing room */
1590     size_t len;
1591     int sock = -1;
1592     int rc;
1593     char versbuf[20];
1594     float version;
1595     int response;
1596     int failureval;
1597     SSL_CTX *ctx = NULL;
1598     SSL *ssl = NULL;
1599     const SSL_METHOD *meth;
1600 
1601     assert(tp != NULL);
1602     assert(m != NULL);
1603 
1604     if (flags & SPAMC_USE_SSL) {
1605 #ifdef SPAMC_SSL
1606 	SSLeay_add_ssl_algorithms();
1607 	meth = SSLv23_client_method();
1608 	SSL_load_error_strings();
1609 	ctx = SSL_CTX_new(meth);
1610 #else
1611 	UNUSED_VARIABLE(ssl);
1612 	UNUSED_VARIABLE(meth);
1613 	UNUSED_VARIABLE(ctx);
1614 	libspamc_log(flags, LOG_ERR, "spamc not built with SSL support");
1615 	return EX_SOFTWARE;
1616 #endif
1617     }
1618 
1619     m->is_spam = EX_TOOBIG;
1620 
1621     if (m->outbuf != NULL)
1622         free(m->outbuf);
1623     m->priv->alloced_size = m->max_len + EXPANSION_ALLOWANCE + 1;
1624     if ((m->outbuf = malloc(m->priv->alloced_size)) == NULL) {
1625 	failureval = EX_OSERR;
1626 	goto failure;
1627     }
1628     m->out = m->outbuf;
1629     m->out_len = 0;
1630 
1631     /* Build spamd protocol header */
1632     strcpy(buf, "TELL ");
1633 
1634     len = strlen(buf);
1635     if (len + strlen(PROTOCOL_VERSION) + 2 >= bufsiz) {
1636 	_use_msg_for_out(m);
1637 	return EX_OSERR;
1638     }
1639 
1640     strcat(buf, PROTOCOL_VERSION);
1641     strcat(buf, "\r\n");
1642     len = strlen(buf);
1643 
1644     if (msg_class != 0) {
1645       strcpy(buf + len, "Message-class: ");
1646       if (msg_class == SPAMC_MESSAGE_CLASS_SPAM) {
1647 	strcat(buf + len, "spam\r\n");
1648       }
1649       else {
1650 	strcat(buf + len, "ham\r\n");
1651       }
1652       len += strlen(buf + len);
1653     }
1654 
1655     if ((tellflags & SPAMC_SET_LOCAL) || (tellflags & SPAMC_SET_REMOTE)) {
1656       int needs_comma_p = 0;
1657       strcat(buf + len, "Set: ");
1658       if (tellflags & SPAMC_SET_LOCAL) {
1659 	strcat(buf + len, "local");
1660 	needs_comma_p = 1;
1661       }
1662       if (tellflags & SPAMC_SET_REMOTE) {
1663 	if (needs_comma_p == 1) {
1664 	  strcat(buf + len, ",");
1665 	}
1666 	strcat(buf + len, "remote");
1667       }
1668       strcat(buf + len, "\r\n");
1669       len += strlen(buf + len);
1670     }
1671 
1672     if ((tellflags & SPAMC_REMOVE_LOCAL) || (tellflags & SPAMC_REMOVE_REMOTE)) {
1673       int needs_comma_p = 0;
1674       strcat(buf + len, "Remove: ");
1675       if (tellflags & SPAMC_REMOVE_LOCAL) {
1676 	strcat(buf + len, "local");
1677 	needs_comma_p = 1;
1678       }
1679       if (tellflags & SPAMC_REMOVE_REMOTE) {
1680 	if (needs_comma_p == 1) {
1681 	  strcat(buf + len, ",");
1682 	}
1683 	strcat(buf + len, "remote");
1684       }
1685       strcat(buf + len, "\r\n");
1686       len += strlen(buf + len);
1687     }
1688 
1689     if (username != NULL) {
1690 	if (strlen(username) + 8 >= (bufsiz - len)) {
1691 	    _use_msg_for_out(m);
1692 	    return EX_OSERR;
1693 	}
1694 	strcpy(buf + len, "User: ");
1695 	strcat(buf + len, username);
1696 	strcat(buf + len, "\r\n");
1697 	len += strlen(buf + len);
1698     }
1699     if ((m->msg_len > SPAMC_MAX_MESSAGE_LEN) || ((len + 27) >= (bufsiz - len))) {
1700 	_use_msg_for_out(m);
1701 	return EX_DATAERR;
1702     }
1703     len += sprintf(buf + len, "Content-length: %d\r\n\r\n", (int) m->msg_len);
1704 
1705     if (m->priv->spamc_header_callback != NULL) {
1706       char buf2[1024];
1707       m->priv->spamc_header_callback(m, flags, buf2, 1024);
1708       strncat(buf, buf2, bufsiz - len);
1709     }
1710 
1711     libspamc_timeout = m->timeout;
1712     libspamc_connect_timeout = m->connect_timeout;	/* Sep 8, 2008 mrgus: separate connect timeout */
1713 
1714     if (tp->socketpath)
1715 	rc = _try_to_connect_unix(tp, &sock);
1716     else
1717 	rc = _try_to_connect_tcp(tp, &sock);
1718 
1719     if (rc != EX_OK) {
1720 	_use_msg_for_out(m);
1721 	return rc;      /* use the error code try_to_connect_*() gave us. */
1722     }
1723 
1724     if (flags & SPAMC_USE_SSL) {
1725 #ifdef SPAMC_SSL
1726 	ssl = SSL_new(ctx);
1727 	SSL_set_fd(ssl, sock);
1728 	SSL_connect(ssl);
1729 #endif
1730     }
1731 
1732     /* Send to spamd */
1733     if (flags & SPAMC_USE_SSL) {
1734 #ifdef SPAMC_SSL
1735 	SSL_write(ssl, buf, len);
1736 	SSL_write(ssl, m->msg, m->msg_len);
1737 #endif
1738     }
1739     else {
1740 	full_write(sock, 0, buf, len);
1741 	full_write(sock, 0, m->msg, m->msg_len);
1742 	shutdown(sock, SHUT_WR);
1743     }
1744 
1745     /* ok, now read and parse it.  SPAMD/1.2 line first... */
1746     failureval =
1747 	_spamc_read_full_line(m, flags, ssl, sock, buf, &len, bufsiz);
1748     if (failureval != EX_OK) {
1749 	goto failure;
1750     }
1751 
1752     if (sscanf(buf, "SPAMD/%18s %d %*s", versbuf, &response) != 2) {
1753 	libspamc_log(flags, LOG_ERR, "spamd responded with bad string '%s'", buf);
1754 	failureval = EX_PROTOCOL;
1755 	goto failure;
1756     }
1757 
1758     versbuf[19] = '\0';
1759     version = _locale_safe_string_to_float(versbuf, 20);
1760     if (version < 1.0) {
1761 	libspamc_log(flags, LOG_ERR, "spamd responded with bad version string '%s'",
1762 	       versbuf);
1763 	failureval = EX_PROTOCOL;
1764 	goto failure;
1765     }
1766 
1767     m->score = 0;
1768     m->threshold = 0;
1769     m->is_spam = EX_TOOBIG;
1770     while (1) {
1771 	failureval =
1772 	    _spamc_read_full_line(m, flags, ssl, sock, buf, &len, bufsiz);
1773 	if (failureval != EX_OK) {
1774 	    goto failure;
1775 	}
1776 
1777 	if (len == 0 && buf[0] == '\0') {
1778 	    break;		/* end of headers */
1779 	}
1780 
1781 	if (_handle_spamd_header(m, flags, buf, len, didtellflags) < 0) {
1782 	    failureval = EX_PROTOCOL;
1783 	    goto failure;
1784 	}
1785     }
1786 
1787     len = 0;			/* overwrite those headers */
1788 
1789     shutdown(sock, SHUT_RD);
1790     closesocket(sock);
1791     sock = -1;
1792 
1793     libspamc_timeout = 0;
1794 
1795     return EX_OK;
1796 
1797   failure:
1798     _use_msg_for_out(m);
1799     if (sock != -1) {
1800         closesocket(sock);
1801     }
1802     libspamc_timeout = 0;
1803 
1804     if (flags & SPAMC_USE_SSL) {
1805 #ifdef SPAMC_SSL
1806 	SSL_free(ssl);
1807 	SSL_CTX_free(ctx);
1808 #endif
1809     }
1810     return failureval;
1811 }
1812 
1813 void message_cleanup(struct message *m)
1814 {
1815     assert(m != NULL);
1816     if (m->outbuf != NULL)
1817         free(m->outbuf);
1818     if (m->raw != NULL)
1819         free(m->raw);
1820     if (m->priv != NULL)
1821         free(m->priv);
1822     _clear_message(m);
1823 }
1824 
1825 /* Aug 14, 2002 bj: Obsolete! */
1826 int process_message(struct transport *tp, char *username, int max_size,
1827 		    int in_fd, int out_fd, const int my_check_only,
1828 		    const int my_safe_fallback)
1829 {
1830     int flags;
1831 
1832     flags = SPAMC_RAW_MODE;
1833     if (my_check_only)
1834         flags |= SPAMC_CHECK_ONLY;
1835     if (my_safe_fallback)
1836         flags |= SPAMC_SAFE_FALLBACK;
1837 
1838     return message_process(tp, username, max_size, in_fd, out_fd, flags);
1839 }
1840 
1841 /*
1842 * init_transport()
1843 *
1844 *	Given a pointer to a transport structure, set it to "all empty".
1845 *	The default is a localhost connection.
1846 */
1847 void transport_init(struct transport *tp)
1848 {
1849     assert(tp != 0);
1850 
1851     memset(tp, 0, sizeof *tp);
1852 
1853     tp->type = TRANSPORT_LOCALHOST;
1854     tp->port = 783;
1855     tp->flags = 0;
1856     tp->retry_sleep = -1;
1857 }
1858 
1859 /*
1860 * randomize_hosts()
1861 *
1862 *	Given the transport object that contains one or more IP addresses
1863 *	in this "hosts" list, rotate it by a random number of shifts to
1864 *	randomize them - this is a kind of load balancing. It's possible
1865 *	that the random number will be 0, which says not to touch. We don't
1866 *	do anything unless
1867 */
1868 
1869 static void _randomize_hosts(struct transport *tp)
1870 {
1871 #ifdef SPAMC_HAS_ADDRINFO
1872     struct addrinfo *tmp;
1873 #else
1874     struct in_addr tmp;
1875 #endif
1876     int i;
1877     int rnum;
1878 
1879     assert(tp != 0);
1880 
1881     if (tp->nhosts <= 1)
1882         return;
1883 
1884     rnum = rand() % tp->nhosts;
1885 
1886     while (rnum-- > 0) {
1887         tmp = tp->hosts[0];
1888 
1889         for (i = 1; i < tp->nhosts; i++)
1890             tp->hosts[i - 1] = tp->hosts[i];
1891 
1892         tp->hosts[i - 1] = tmp;
1893     }
1894 }
1895 
1896 /*
1897 * transport_setup()
1898 *
1899 *	Given a "transport" object that says how we're to connect to the
1900 *	spam daemon, perform all the initial setup required to make the
1901 *	connection process a smooth one. The main work is to do the host
1902 *	name lookup and copy over all the IP addresses to make a local copy
1903 *	so they're not kept in the resolver's static state.
1904 *
1905 *	Here we also manage quasi-load balancing and failover: if we're
1906 *	doing load balancing, we randomly "rotate" the list to put it in
1907 *	a different order, and then if we're not doing failover we limit
1908 *	the hosts to just one. This way *all* connections are done with
1909 *	the intention of failover - makes the code a bit more clear.
1910 */
1911 int transport_setup(struct transport *tp, int flags)
1912 {
1913 #ifdef SPAMC_HAS_ADDRINFO
1914     struct addrinfo hints, *res; /* , *addrp; */
1915     char port[6];
1916     int origerr;
1917 #else
1918     struct hostent *hp;
1919     char **addrp;
1920 #endif
1921     char *hostlist, *hostname;
1922     int errbits;
1923 
1924 #ifdef _WIN32
1925     /* Start Winsock up */
1926     WSADATA wsaData;
1927     int nCode;
1928     if ((nCode = WSAStartup(MAKEWORD(1, 1), &wsaData)) != 0) {
1929         printf("WSAStartup() returned error code %d\n", nCode);
1930         return EX_OSERR;
1931     }
1932 
1933 #endif
1934 
1935     assert(tp != NULL);
1936     tp->flags = flags;
1937 
1938 #ifdef SPAMC_HAS_ADDRINFO
1939     snprintf(port, 6, "%d", tp->port);
1940 
1941     memset(&hints, 0, sizeof(hints));
1942     hints.ai_flags = 0;
1943     hints.ai_socktype = SOCK_STREAM;
1944 
1945     if (       (flags & SPAMC_USE_INET4) && !(flags & SPAMC_USE_INET6)) {
1946       hints.ai_family = PF_INET;
1947 #ifdef PF_INET6
1948     } else if ((flags & SPAMC_USE_INET6) && !(flags & SPAMC_USE_INET4)) {
1949       hints.ai_family = PF_INET6;
1950 #endif
1951     } else {
1952       hints.ai_family = PF_UNSPEC;
1953     }
1954 #endif
1955 
1956     switch (tp->type) {
1957 #ifndef _WIN32
1958     case TRANSPORT_UNIX:
1959         assert(tp->socketpath != 0);
1960         return EX_OK;
1961 #endif
1962     case TRANSPORT_LOCALHOST:
1963 #ifdef SPAMC_HAS_ADDRINFO
1964         /* getaddrinfo(NULL) will look up the loopback address.
1965          * See also bug 5057,  ::1 will be tried before 127.0.0.1
1966          * unless overridden (through hints) by a command line option -4
1967          */
1968         if ((origerr = getaddrinfo(NULL, port, &hints, &res)) != 0) {
1969             libspamc_log(flags, LOG_ERR,
1970                   "getaddrinfo for a loopback address failed: %s",
1971                   gai_strerror(origerr));
1972             return EX_OSERR;
1973         }
1974         tp->hosts[0] = res;
1975 #else
1976         tp->hosts[0].s_addr = inet_addr("127.0.0.1");
1977 #endif
1978         tp->nhosts = 1;
1979         return EX_OK;
1980 
1981     case TRANSPORT_TCP:
1982         if ((hostlist = strdup(tp->hostname)) == NULL)
1983             return EX_OSERR;
1984 
1985         /* We want to return the least permanent error, in this bitmask we
1986          * record the errors seen with:
1987          *  0: no error
1988          *  1: EX_TEMPFAIL
1989          *  2: EX_NOHOST
1990          * EX_OSERR will return immediately.
1991          * Bits aren't reset so a check against nhosts is needed to determine
1992          * if something went wrong.
1993          */
1994         errbits = 0;
1995         tp->nhosts = 0;
1996         /* Start with char offset in front of the string because we'll add
1997          * one in the loop
1998          */
1999         hostname = hostlist - 1;
2000         do {
2001             char *hostend;
2002 
2003             hostname += 1;
2004             hostend = strchr(hostname, ',');
2005             if (hostend != NULL) {
2006                 *hostend = '\0';
2007             }
2008 #ifdef SPAMC_HAS_ADDRINFO
2009             if ((origerr = getaddrinfo(hostname, port, &hints, &res))) {
2010                 libspamc_log(flags, LOG_DEBUG,
2011                       "getaddrinfo(%s) failed: %s",
2012                       hostname, gai_strerror(origerr));
2013                 switch (origerr) {
2014                 case EAI_AGAIN:
2015                     errbits |= 1;
2016                     break;
2017                 case EAI_FAMILY: /*address family not supported*/
2018                 case EAI_SOCKTYPE: /*socket type not supported*/
2019                 case EAI_BADFLAGS: /*ai_flags is invalid*/
2020                 case EAI_NONAME: /*node or service unknown*/
2021                 case EAI_SERVICE: /*service not available*/
2022 /* work around Cygwin IPv6 patch - err codes not defined in Windows aren't in patch */
2023 #ifdef HAVE_EAI_ADDRFAMILY
2024                 case EAI_ADDRFAMILY: /*no addresses in requested family*/
2025 #endif
2026 #ifdef HAVE_EAI_SYSTEM
2027                 case EAI_SYSTEM: /*system error, check errno*/
2028 #endif
2029 #ifdef HAVE_EAI_NODATA
2030                 case EAI_NODATA: /*address exists, but no data*/
2031 #endif
2032                 case EAI_MEMORY: /*out of memory*/
2033                 case EAI_FAIL: /*name server returned permanent error*/
2034                     errbits |= 2;
2035                     break;
2036                 default:
2037                     /* should not happen, all errors are checked above */
2038                     free(hostlist);
2039                     return EX_OSERR;
2040                 }
2041                 goto nexthost; /* try next host in list */
2042             }
2043 #else
2044             if ((hp = gethostbyname(hostname)) == NULL) {
2045                 int origerr = h_errno; /* take a copy before syslog() */
2046                 libspamc_log(flags, LOG_DEBUG, "gethostbyname(%s) failed: h_errno=%d",
2047                     hostname, origerr);
2048                 switch (origerr) {
2049                 case TRY_AGAIN:
2050                     errbits |= 1;
2051                     break;
2052                 case HOST_NOT_FOUND:
2053                 case NO_ADDRESS:
2054                 case NO_RECOVERY:
2055                     errbits |= 2;
2056                     break;
2057                 default:
2058                     /* should not happen, all errors are checked above */
2059                     free(hostlist);
2060                     return EX_OSERR;
2061                 }
2062                 goto nexthost; /* try next host in list */
2063             }
2064 #endif
2065 
2066             /* If we have no hosts at all */
2067 #ifdef SPAMC_HAS_ADDRINFO
2068             if(res == NULL)
2069 #else
2070             if (hp->h_addr_list[0] == NULL
2071              || hp->h_length != sizeof tp->hosts[0]
2072              || hp->h_addrtype != AF_INET)
2073                 /* no hosts/bad size/wrong family */
2074 #endif
2075             {
2076                 errbits |= 1;
2077                 goto nexthost; /* try next host in list */
2078             }
2079 
2080             /* Copy all the IP addresses into our private structure.
2081              * This gets them out of the resolver's static area and
2082              * means we won't ever walk all over the list with other
2083              * calls.
2084              */
2085 #ifdef SPAMC_HAS_ADDRINFO
2086             if(tp->nhosts == TRANSPORT_MAX_HOSTS) {
2087                libspamc_log(flags, LOG_NOTICE,
2088                      "hit limit of %d hosts, ignoring remainder",
2089                      TRANSPORT_MAX_HOSTS);
2090                break;
2091             }
2092 
2093             /* treat all A or AAAA records of each host as one entry */
2094             tp->hosts[tp->nhosts++] = res;
2095 
2096             /* alternatively, treat multiple A or AAAA records
2097                of one host as individual entries */
2098 /*          for (addrp = res; addrp != NULL; ) {
2099  *              tp->hosts[tp->nhosts] = addrp;
2100  *              addrp = addrp->ai_next;     /-* before NULLing ai_next *-/
2101  *              tp->hosts[tp->nhosts]->ai_next = NULL;
2102  *              tp->nhosts++;
2103  *          }
2104  */
2105 
2106 #else
2107             for (addrp = hp->h_addr_list; *addrp; addrp++) {
2108                 if (tp->nhosts == TRANSPORT_MAX_HOSTS) {
2109                     libspamc_log(flags, LOG_NOTICE, "hit limit of %d hosts, ignoring remainder",
2110                         TRANSPORT_MAX_HOSTS);
2111                     break;
2112                 }
2113                 memcpy(&tp->hosts[tp->nhosts], *addrp, hp->h_length);
2114                 tp->nhosts++;
2115             }
2116 #endif
2117 nexthost:
2118             hostname = hostend;
2119         } while (hostname != NULL);
2120         free(hostlist);
2121 
2122         if (tp->nhosts == 0) {
2123             if (errbits & 1) {
2124                 libspamc_log(flags, LOG_ERR, "could not resolve any hosts (%s): a temporary error occurred",
2125                     tp->hostname);
2126                 return EX_TEMPFAIL;
2127             }
2128             else {
2129                 libspamc_log(flags, LOG_ERR, "could not resolve any hosts (%s): no such host",
2130                     tp->hostname);
2131                 return EX_NOHOST;
2132             }
2133         }
2134 
2135         /* QUASI-LOAD-BALANCING
2136          *
2137          * If the user wants to do quasi load balancing, "rotate"
2138          * the list by a random amount based on the current time.
2139          * This may later be truncated to a single item. This is
2140          * meaningful only if we have more than one host.
2141 	 */
2142 
2143         if ((flags & SPAMC_RANDOMIZE_HOSTS) && tp->nhosts > 1) {
2144             _randomize_hosts(tp);
2145         }
2146 
2147         /* If the user wants no fallback, simply truncate the host
2148          * list to just one - this pretends that this is the extent
2149          * of our connection list - then it's not a special case.
2150          */
2151         if (!(flags & SPAMC_SAFE_FALLBACK) && tp->nhosts > 1) {
2152             /* truncating list */
2153             tp->nhosts = 1;
2154         }
2155 
2156         return EX_OK;
2157     }
2158 
2159     /* oops, unknown transport type */
2160     return EX_OSERR;
2161 }
2162 
2163 /*
2164 * transport_cleanup()
2165 *
2166 *	Given a "transport" object that says how we're to connect to the
2167 *	spam daemon, delete and free any buffers allocated so that it
2168 *       can be discarded without causing a memory leak.
2169 */
2170 void transport_cleanup(struct transport *tp)
2171 {
2172 
2173 #ifdef SPAMC_HAS_ADDRINFO
2174   int i;
2175 
2176   for(i=0;i<tp->nhosts;i++) {
2177       if (tp->hosts[i] != NULL) {
2178           freeaddrinfo(tp->hosts[i]);
2179           tp->hosts[i] = NULL;
2180       }
2181   }
2182 #endif
2183 
2184 }
2185 
2186 /*
2187 * register_libspamc_log_callback()
2188 *
2189 * Register a callback handler for libspamc_log to replace the default behaviour.
2190 */
2191 
2192 void register_libspamc_log_callback(void (*function)(int flags, int level, char *msg, va_list args)) {
2193   libspamc_log_callback = function;
2194 }
2195 
2196 /*
2197 * register_spamc_header_callback()
2198 *
2199 * Register a callback handler to generate spamc headers for a given message
2200 */
2201 
2202 void register_spamc_header_callback(const struct message *m, void (*func)(struct message *m, int flags, char *buf, int len)) {
2203   m->priv->spamc_header_callback = func;
2204 }
2205 
2206 /*
2207 * register_spamd_header_callback()
2208 *
2209 * Register a callback handler to generate spamd headers for a given message
2210 */
2211 
2212 void register_spamd_header_callback(const struct message *m, void (*func)(struct message *m, int flags, const char *buf, int len)) {
2213   m->priv->spamd_header_callback = func;
2214 }
2215 
2216 /* --------------------------------------------------------------------------- */
2217 
2218 #define LOG_BUFSIZ      1023
2219 
2220 void
2221 libspamc_log (int flags, int level, char *msg, ...)
2222 {
2223     va_list ap;
2224     char buf[LOG_BUFSIZ+1];
2225     int len = 0;
2226 
2227     va_start(ap, msg);
2228 
2229     if ((flags & SPAMC_LOG_TO_CALLBACK) != 0 && libspamc_log_callback != NULL) {
2230       libspamc_log_callback(flags, level, msg, ap);
2231     }
2232     else if ((flags & SPAMC_LOG_TO_STDERR) != 0) {
2233         /* create a log-line buffer */
2234         len = snprintf(buf, LOG_BUFSIZ, "spamc: ");
2235         len += vsnprintf(buf+len, LOG_BUFSIZ-len, msg, ap);
2236 
2237         /* avoid buffer overflow */
2238         if (len > (LOG_BUFSIZ-2)) { len = (LOG_BUFSIZ-3); }
2239 
2240         len += snprintf(buf+len, LOG_BUFSIZ-len, "\n");
2241         buf[LOG_BUFSIZ] = '\0';     /* ensure termination */
2242         (void) write (2, buf, len);
2243 
2244     } else {
2245         vsnprintf(buf, LOG_BUFSIZ, msg, ap);
2246         buf[LOG_BUFSIZ] = '\0';     /* ensure termination */
2247 #ifndef _WIN32
2248         syslog (level, "%s", buf);
2249 #else
2250         (void) level;  /* not used. suppress compiler warning */
2251         f_printerr ("%s\n", buf);
2252 #endif
2253     }
2254 
2255     va_end(ap);
2256 }
2257 
2258 /* --------------------------------------------------------------------------- */
2259 
2260 /*
2261 * Unit tests.  Must be built externally, e.g.:
2262 *
2263 * gcc -g -DLIBSPAMC_UNIT_TESTS spamd/spamc.c spamd/libspamc.c spamd/utils.c -o libspamctest
2264 * ./libspamctest
2265 *
2266 */
2267 #ifdef LIBSPAMC_UNIT_TESTS
2268 
2269 static void _test_locale_safe_string_to_float_val(float input)
2270 {
2271     char inputstr[99], cmpbuf1[99], cmpbuf2[99];
2272     float output;
2273 
2274     /* sprintf instead of snprintf is safe here because it is only a controlled test */
2275     sprintf(inputstr, "%f", input);
2276     output = _locale_safe_string_to_float(inputstr, 99);
2277     if (input == output) {
2278         return;
2279     }
2280 
2281     /* could be a rounding error.  print as string and compare those */
2282     sprintf(cmpbuf1, "%f", input);
2283     sprintf(cmpbuf2, "%f", output);
2284     if (!strcmp(cmpbuf1, cmpbuf2)) {
2285         return;
2286     }
2287 
2288     printf("FAIL: input=%f != output=%f\n", input, output);
2289 }
2290 
2291 static void unit_test_locale_safe_string_to_float(void)
2292 {
2293     float statictestset[] = {	/* will try both +ve and -ve */
2294         0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001,
2295         9.1, 9.91, 9.991, 9.9991, 9.99991, 9.999991,
2296         0.0			/* end of set constant */
2297     };
2298     float num;
2299     int i;
2300 
2301     printf("starting unit_test_locale_safe_string_to_float\n");
2302     /* tests of precision */
2303     for (i = 0; statictestset[i] != 0.0; i++) {
2304         _test_locale_safe_string_to_float_val(statictestset[i]);
2305         _test_locale_safe_string_to_float_val(-statictestset[i]);
2306         _test_locale_safe_string_to_float_val(1 - statictestset[i]);
2307         _test_locale_safe_string_to_float_val(1 + statictestset[i]);
2308     }
2309     /* now exhaustive, in steps of 0.01 */
2310     for (num = -1000.0; num < 1000.0; num += 0.01) {
2311         _test_locale_safe_string_to_float_val(num);
2312     }
2313     printf("finished unit_test_locale_safe_string_to_float\n");
2314 }
2315 
2316 void do_libspamc_unit_tests(void)
2317 {
2318     unit_test_locale_safe_string_to_float();
2319     exit(0);
2320 }
2321 
2322 #endif /* LIBSPAMC_UNIT_TESTS */
2323