2 * This code is copyright 2001 by Craig Hughes
3 * Portions copyright 2002 by Brad Jorsch
4 * It is licensed under the same license as Perl itself. The text of this
5 * license is included in the SpamAssassin distribution in the file named
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <netinet/in.h>
21 #include <netinet/tcp.h>
22 #include <arpa/inet.h>
24 #ifdef HAVE_SYSEXITS_H
30 #ifdef HAVE_SYS_ERRNO_H
31 #include <sys/errno.h>
36 #ifdef HAVE_SYS_TIME_H
40 #define MAX_CONNECT_RETRIES 3
41 #define CONNECT_RETRY_SLEEP 1
43 /* RedHat 5.2 doesn't define Shutdown 2nd Parameter Constants */
46 #define SHUT_RD (0) /* No more receptions. */
47 #define SHUT_WR (1) /* No more transmissions. */
48 #define SHUT_RDWR (2) /* No more receptions or transmissions. */
59 #ifndef HAVE_INADDR_NONE
60 #define INADDR_NONE ((in_addr_t) 0xffffffff)
63 /* jm: turned off for now, it should not be necessary. */
64 #undef USE_TCP_NODELAY
67 /* jm: very conservative figure, should be well out of range on almost all NIXes */
71 #undef DO_CONNECT_DEBUG_SYSLOGS
72 /* or #define DO_CONNECT_DEBUG_SYSLOGS 1 */
74 static const int ESC_PASSTHROUGHRAW
= EX__MAX
+666;
76 /* set EXPANSION_ALLOWANCE to something more than might be
77 added to a message in X-headers and the report template */
78 static const int EXPANSION_ALLOWANCE
= 16384;
80 /* set NUM_CHECK_BYTES to number of bytes that have to match at beginning and end
81 of the data streams before and after processing by spamd
82 Aug 7 2002 jm: no longer seems to be used
83 static const int NUM_CHECK_BYTES = 32;
86 /* Set the protocol version that this spamc speaks */
87 static const char *PROTOCOL_VERSION
="SPAMC/1.3";
89 int libspamc_timeout
= 0;
92 try_to_connect (const struct sockaddr
*argaddr
, struct hostent
*hent
,
93 int hent_port
, int *sockptr
)
95 #ifdef USE_TCP_NODELAY
103 struct sockaddr_in addrbuf
, *addr
;
104 struct in_addr inaddrlist
[256];
106 #ifdef DO_CONNECT_DEBUG_SYSLOGS
107 int dbgiter
; char dbgbuf
[2048]; int dbgbuflen
= 0;
110 /* NOTE: do not call syslog() (unless you are about to return) before
111 * we take a copy of the h_addr_list.
114 /* only one set of connection targets can be used. assert this */
115 if (argaddr
== NULL
&& hent
== NULL
) {
116 syslog (LOG_ERR
, "oops! both NULL in try_to_connect");
118 } else if (argaddr
!= NULL
&& hent
!= NULL
) {
119 syslog (LOG_ERR
, "oops! both non-NULL in try_to_connect");
123 /* take a copy of the h_addr_list part of the struct hostent */
125 memset (inaddrlist
, 0, sizeof(inaddrlist
));
127 for (hostnum
=0; hent
->h_addr_list
[hostnum
] != 0; hostnum
++) {
129 #ifdef DO_CONNECT_DEBUG_SYSLOGS
130 dbgbuflen
+= snprintf (dbgbuf
+dbgbuflen
, 2047-dbgbuflen
,
131 "[%d %lx: %d.%d.%d.%d]",
132 hostnum
, hent
->h_addr_list
[hostnum
],
133 hent
->h_addr_list
[hostnum
][0],
134 hent
->h_addr_list
[hostnum
][1],
135 hent
->h_addr_list
[hostnum
][2],
136 hent
->h_addr_list
[hostnum
][3]);
140 syslog (LOG_ERR
, "too many address in hostent (%d), ignoring others",
145 if (hent
->h_addr_list
[hostnum
] == NULL
) {
146 /* shouldn't happen */
147 syslog (LOG_ERR
, "hent->h_addr_list[hostnum] == NULL! foo!");
151 #ifdef DO_CONNECT_DEBUG_SYSLOGS
152 dbgbuflen
+= snprintf (dbgbuf
+dbgbuflen
, 2047-dbgbuflen
,
153 "[%d: %d.%d.%d.%d] ", sizeof (struct in_addr
),
154 hent
->h_addr_list
[hostnum
][0],
155 hent
->h_addr_list
[hostnum
][1],
156 hent
->h_addr_list
[hostnum
][2],
157 hent
->h_addr_list
[hostnum
][3]);
160 memcpy ((void *) &(inaddrlist
[hostnum
]),
161 (void *) hent
->h_addr_list
[hostnum
],
162 sizeof (struct in_addr
));
165 #ifdef DO_CONNECT_DEBUG_SYSLOGS
166 syslog (LOG_DEBUG
, "dbg: %d %s", hostnum
, dbgbuf
); dbgbuflen
= 0;
171 #ifdef DO_CONNECT_DEBUG_SYSLOGS
172 for (dbgiter
= 0; dbgiter
< hostnum
; dbgiter
++) {
173 syslog (LOG_DEBUG
, "dbg: host addr %d/%d = %lx at %lx", dbgiter
, hostnum
,
174 inaddrlist
[dbgiter
].s_addr
, &(inaddrlist
[dbgiter
]));
178 hent
= NULL
; /* cannot use hent after this point, syslog() may overwrite it */
180 #ifdef DO_CONNECT_DEBUG_SYSLOGS
181 syslog (LOG_DEBUG
, "dbg: socket");
184 if(-1 == (mysock
= socket(PF_INET
,SOCK_STREAM
,0)))
186 origerr
= errno
; /* take a copy before syslog() */
187 syslog (LOG_ERR
, "socket() to spamd failed: %m");
190 case EPROTONOSUPPORT
:
205 #ifdef USE_TCP_NODELAY
206 /* TODO: should this be up above the connect()? */
207 value
= 1; /* make this explicit! */
208 if(-1 == setsockopt(mysock
,0,TCP_NODELAY
,&value
,sizeof(value
)))
216 syslog (LOG_ERR
, "setsockopt() to spamd failed: %m");
226 for (numloops
=0; numloops
< MAX_CONNECT_RETRIES
; numloops
++) {
228 #ifdef DO_CONNECT_DEBUG_SYSLOGS
229 syslog (LOG_DEBUG
, "dbg: connect() to spamd %d", numloops
);
232 if (argaddr
!= NULL
) {
233 addr
= (struct sockaddr_in
*) argaddr
; /* use the one provided */
235 #ifdef DO_CONNECT_DEBUG_SYSLOGS
236 syslog (LOG_DEBUG
, "dbg: using argaddr");
240 /* cycle through the addrs in hent */
241 memset(&addrbuf
, 0, sizeof(addrbuf
));
242 addrbuf
.sin_family
=AF_INET
;
243 addrbuf
.sin_port
=htons(hent_port
);
245 if (sizeof(addrbuf
.sin_addr
) != sizeof(struct in_addr
)) { /* shouldn't happen */
247 "foo! sizeof(sockaddr.sin_addr) != sizeof(struct in_addr)");
251 #ifdef DO_CONNECT_DEBUG_SYSLOGS
252 syslog (LOG_DEBUG
, "dbg: cpy addr %d/%d at %lx",
253 numloops
%hostnum
, hostnum
, &(inaddrlist
[numloops
% hostnum
]));
256 memcpy (&addrbuf
.sin_addr
, &(inaddrlist
[numloops
% hostnum
]),
257 sizeof(addrbuf
.sin_addr
));
260 #ifdef DO_CONNECT_DEBUG_SYSLOGS
261 syslog (LOG_DEBUG
, "dbg: conn addr %d/%d = %lx",
262 numloops
%hostnum
, hostnum
, addrbuf
.sin_addr
.s_addr
);
267 #ifdef DO_CONNECT_DEBUG_SYSLOGS
268 syslog (LOG_DEBUG
, "dbg: connect() to spamd at %s",
269 inet_ntoa(((struct sockaddr_in
*)addr
)->sin_addr
));
271 status
= connect(mysock
,(const struct sockaddr
*) addr
, sizeof(*addr
));
273 #ifdef DO_CONNECT_DEBUG_SYSLOGS
274 syslog (LOG_DEBUG
, "dbg: connect() to spamd at %s done",
275 inet_ntoa(((struct sockaddr_in
*)addr
)->sin_addr
));
280 origerr
= errno
; /* take a copy before syslog() */
281 syslog (LOG_ERR
, "connect() to spamd at %s failed, retrying (%d/%d): %m",
282 inet_ntoa(((struct sockaddr_in
*)addr
)->sin_addr
),
283 numloops
+1, MAX_CONNECT_RETRIES
);
284 sleep(CONNECT_RETRY_SLEEP
);
292 /* failed, even with a few retries */
294 syslog (LOG_ERR
, "connection attempt to spamd aborted after %d retries",
295 MAX_CONNECT_RETRIES
);
311 return EX_UNAVAILABLE
;
319 /* Aug 14, 2002 bj: Reworked things. Now we have message_read, message_write,
320 * message_dump, lookup_host, message_filter, and message_process, and a bunch
321 * of helper functions.
324 static void clear_message(struct message
*m
){
325 m
->type
=MESSAGE_NONE
;
326 m
->raw
=NULL
; m
->raw_len
=0;
327 m
->pre
=NULL
; m
->pre_len
=0;
328 m
->msg
=NULL
; m
->msg_len
=0;
329 m
->post
=NULL
; m
->post_len
=0;
330 m
->is_spam
=EX_TOOBIG
;
331 m
->score
=0.0; m
->threshold
=0.0;
332 m
->out
=NULL
; m
->out_len
=0;
333 m
->content_length
=-1;
337 message_read_raw(int fd
, struct message
*m
){
339 if((m
->raw
=malloc(m
->max_len
+1))==NULL
) return EX_OSERR
;
340 m
->raw_len
=full_read(fd
, (unsigned char *) m
->raw
, m
->max_len
+1, m
->max_len
+1);
342 free(m
->raw
); m
->raw
=NULL
; m
->raw_len
=0;
345 m
->type
=MESSAGE_ERROR
;
346 if(m
->raw_len
>m
->max_len
) return EX_TOOBIG
;
349 m
->msg_len
=m
->raw_len
;
351 m
->out_len
=m
->msg_len
;
355 static int message_read_bsmtp(int fd
, struct message
*m
){
360 if((m
->raw
=malloc(m
->max_len
+1))==NULL
) return EX_OSERR
;
362 /* Find the DATA line */
363 m
->raw_len
=full_read(fd
, (unsigned char *) m
->raw
, m
->max_len
+1, m
->max_len
+1);
365 free(m
->raw
); m
->raw
=NULL
; m
->raw_len
=0;
368 m
->type
=MESSAGE_ERROR
;
369 if(m
->raw_len
>m
->max_len
) return EX_TOOBIG
;
371 for(i
=0; i
<m
->raw_len
-6; i
++){
372 if((m
->raw
[i
]=='\n') &&
373 (m
->raw
[i
+1]=='D' || m
->raw
[i
+1]=='d') &&
374 (m
->raw
[i
+2]=='A' || m
->raw
[i
+2]=='a') &&
375 (m
->raw
[i
+3]=='T' || m
->raw
[i
+3]=='t') &&
376 (m
->raw
[i
+4]=='A' || m
->raw
[i
+4]=='a') &&
377 ((m
->raw
[i
+5]=='\r' && m
->raw
[i
+6]=='\n') || m
->raw
[i
+5]=='\n')){
380 if(m
->raw
[i
-1]=='\r') i
++;
383 m
->msg_len
=m
->raw_len
-i
;
387 if(m
->msg
==NULL
) return EX_DATAERR
;
389 /* Find the end-of-DATA line */
391 for(i
=j
=0; i
<m
->msg_len
; i
++){
392 if(prev
=='\n' && m
->msg
[i
]=='.'){
393 /* Dot at the beginning of a line */
394 if((m
->msg
[i
+1]=='\r' && m
->msg
[i
+2]=='\n') || m
->msg
[i
+1]=='\n'){
395 /* Lone dot! That's all, folks */
397 m
->post_len
=m
->msg_len
-i
;
400 } else if(m
->msg
[i
+1]=='.'){
401 /* Escaping dot, eliminate. */
404 } /* Else an ordinary dot, drop down to ordinary char handler */
407 m
->msg
[j
++]=m
->msg
[i
];
410 m
->type
=MESSAGE_BSMTP
;
412 m
->out_len
=m
->msg_len
;
416 int message_read(int fd
, int flags
, struct message
*m
){
417 libspamc_timeout
= 0;
419 switch(flags
&SPAMC_MODE_MASK
){
421 return message_read_raw(fd
, m
);
423 case SPAMC_BSMTP_MODE
:
424 return message_read_bsmtp(fd
, m
);
427 syslog(LOG_ERR
, "message_read: Unknown mode %d\n", flags
&SPAMC_MODE_MASK
);
432 long message_write(int fd
, struct message
*m
){
438 /* if we're to output a message, m->is_spam will be EX_OUTPUTMESSAGE */
439 if(m
->is_spam
==EX_ISSPAM
|| m
->is_spam
==EX_NOTSPAM
){
440 return full_write(fd
, (unsigned char *) m
->out
, m
->out_len
);
443 if (m
->is_spam
!= EX_OUTPUTMESSAGE
&& m
->is_spam
!= EX_TOOBIG
) {
445 "Cannot write this message, is_spam = %d!\n", m
->is_spam
);
451 syslog(LOG_ERR
, "Cannot write this message, it's MESSAGE_NONE!\n");
455 return full_write(fd
, (unsigned char *) m
->raw
, m
->raw_len
);
458 return full_write(fd
, (unsigned char *) m
->out
, m
->out_len
);
461 total
=full_write(fd
, (unsigned char *) m
->pre
, m
->pre_len
);
462 for(i
=0; i
<m
->out_len
; ){
463 jlimit
= (off_t
) (sizeof(buffer
)/sizeof(*buffer
)-4);
464 for(j
=0; i
< (off_t
) m
->out_len
&&
467 if(i
+1<m
->out_len
&& m
->out
[i
]=='\n' && m
->out
[i
+1]=='.'){
468 if (j
> jlimit
- 4) {
469 break; /* avoid overflow */
471 buffer
[j
++]=m
->out
[i
++];
472 buffer
[j
++]=m
->out
[i
++];
475 buffer
[j
++]=m
->out
[i
++];
478 total
+=full_write(fd
, (unsigned char *) buffer
, j
);
480 return total
+full_write(fd
, (unsigned char *) m
->post
, m
->post_len
);
483 syslog(LOG_ERR
, "Unknown message type %d\n", m
->type
);
488 void message_dump(int in_fd
, int out_fd
, struct message
*m
){
492 if(m
!=NULL
&& m
->type
!=MESSAGE_NONE
) {
493 message_write(out_fd
, m
);
495 while((bytes
=full_read(in_fd
, (unsigned char *) buf
, 8192, 8192))>0){
496 if (bytes
!=full_write(out_fd
, (unsigned char *) buf
, bytes
)) {
497 syslog(LOG_ERR
, "oops! message_dump of %d returned different", bytes
);
503 _spamc_read_full_line (struct message
*m
, int flags
, SSL
*ssl
, int sock
,
504 char *buf
, int *lenp
, int bufsiz
)
510 /* Now, read from spamd */
511 for(len
=0; len
<bufsiz
-1; len
++) {
512 if(flags
&SPAMC_USE_SSL
) {
513 bytesread
= ssl_timeout_read (ssl
, buf
+len
, 1);
515 bytesread
= fd_timeout_read (sock
, buf
+len
, 1);
520 if (len
> 0 && buf
[len
-1] == '\r') {
529 failureval
= EX_IOERR
; goto failure
;
533 syslog(LOG_ERR
, "spamd responded with line of %d bytes, dying", len
);
534 failureval
= EX_TOOBIG
;
541 * May 7 2003 jm: using %f is bad where LC_NUMERIC is "," in the locale.
542 * work around using our own locale-independent float-parser code.
545 _locale_safe_string_to_float (char *buf
, int siz
)
552 buf
[siz
-1] = '\0'; /* ensure termination */
554 /* ok, let's illustrate using "100.033" as an example... */
557 if (*buf
== '-') { is_neg
= 1; }
559 ret
= (float) (strtol (buf
, &dot
, 10));
560 if (dot
== NULL
) { return 0.0; }
561 if (dot
!= NULL
&& *dot
!= '.') { return ret
; }
563 /* ex: ret == 100.0 */
566 postdot
= (float) (strtol (cp
, NULL
, 10));
567 if (postdot
== 0.0) { return ret
; }
569 /* ex: postdot == 33.0, cp="033" */
571 /* now count the number of decimal places and figure out what power of 10 to use */
573 while (*cp
!= '\0') {
578 * cp="033", divider=1
579 * cp="33", divider=10
580 * cp="3", divider=100
581 * cp="", divider=1000
585 ret
-= (postdot
/ ((float) divider
));
587 ret
+= (postdot
/ ((float) divider
));
589 /* ex: ret == 100.033, tada! ... hopefully */
595 _handle_spamd_header (struct message
*m
, int flags
, char *buf
, int len
)
598 char s_str
[20], t_str
[20];
600 /* Feb 12 2003 jm: actually, I think sccanf is working fine here ;)
601 * let's stick with it for this parser.
602 * May 7 2003 jm: using %f is bad where LC_NUMERIC is "," in the locale.
603 * work around using our own locale-independent float-parser code.
605 if (sscanf(buf
, "Spam: %5s ; %20s / %20s", is_spam
, s_str
, t_str
) == 3)
607 m
->score
= _locale_safe_string_to_float (s_str
, 20);
608 m
->threshold
= _locale_safe_string_to_float (t_str
, 20);
610 /* Format is "Spam: x; y / x" */
611 m
->is_spam
=strcasecmp("true", is_spam
) == 0 ? EX_ISSPAM
: EX_NOTSPAM
;
613 if(flags
&SPAMC_CHECK_ONLY
) {
614 m
->out_len
=snprintf (m
->out
, m
->max_len
+EXPANSION_ALLOWANCE
,
615 "%.1f/%.1f\n", m
->score
, m
->threshold
);
619 } else if(sscanf(buf
, "Content-length: %d", &m
->content_length
) == 1) {
620 if (m
->content_length
< 0) {
621 syslog(LOG_ERR
, "spamd responded with bad Content-length '%s'", buf
);
627 syslog(LOG_ERR
, "spamd responded with bad header '%s'", buf
);
631 static int _message_filter(const struct sockaddr
*addr
,
632 const struct hostent
*hent
, int hent_port
, char *username
,
633 int flags
, struct message
*m
)
636 int bufsiz
= (sizeof(buf
) / sizeof(*buf
)) - 4; /* bit of breathing room */
647 if (flags
&SPAMC_USE_SSL
) {
649 SSLeay_add_ssl_algorithms();
650 meth
= SSLv2_client_method();
651 SSL_load_error_strings();
652 ctx
= SSL_CTX_new(meth
);
654 (void) ssl
; (void) meth
; (void) ctx
; /* avoid "unused" warnings */
655 syslog(LOG_ERR
, "spamc not built with SSL support");
660 m
->is_spam
=EX_TOOBIG
;
661 if((m
->out
=malloc(m
->max_len
+EXPANSION_ALLOWANCE
+1))==NULL
){
662 failureval
= EX_OSERR
; goto failure
;
667 /* Build spamd protocol header */
668 if(flags
& SPAMC_CHECK_ONLY
)
669 len
=snprintf(buf
, bufsiz
, "CHECK %s\r\n", PROTOCOL_VERSION
);
670 else if(flags
& SPAMC_REPORT_IFSPAM
)
671 len
=snprintf(buf
, bufsiz
, "REPORT_IFSPAM %s\r\n", PROTOCOL_VERSION
);
672 else if(flags
& SPAMC_REPORT
)
673 len
=snprintf(buf
, bufsiz
, "REPORT %s\r\n", PROTOCOL_VERSION
);
674 else if(flags
& SPAMC_SYMBOLS
)
675 len
=snprintf(buf
, bufsiz
, "SYMBOLS %s\r\n", PROTOCOL_VERSION
);
677 len
=snprintf(buf
, bufsiz
, "PROCESS %s\r\n", PROTOCOL_VERSION
);
679 if(len
<0 || len
>= bufsiz
){ free(m
->out
); m
->out
=m
->msg
; m
->out_len
=m
->msg_len
; return EX_OSERR
; }
681 len
+=i
=snprintf(buf
+len
, bufsiz
-len
, "User: %s\r\n", username
);
682 if(i
<0 || len
>= bufsiz
){ free(m
->out
); m
->out
=m
->msg
; m
->out_len
=m
->msg_len
; return EX_OSERR
; }
684 len
+=i
=snprintf(buf
+len
, bufsiz
-len
, "Content-length: %d\r\n", m
->msg_len
);
685 if(i
<0 || len
>= bufsiz
){ free(m
->out
); m
->out
=m
->msg
; m
->out_len
=m
->msg_len
; return EX_OSERR
; }
686 len
+=i
=snprintf(buf
+len
, bufsiz
-len
, "\r\n");
687 if(i
<0 || len
>= bufsiz
){ free(m
->out
); m
->out
=m
->msg
; m
->out_len
=m
->msg_len
; return EX_OSERR
; }
689 libspamc_timeout
= m
->timeout
;
691 if((i
=try_to_connect(addr
, (struct hostent
*) hent
,
692 hent_port
, &sock
)) != EX_OK
)
694 free(m
->out
); m
->out
=m
->msg
; m
->out_len
=m
->msg_len
;
698 if(flags
&SPAMC_USE_SSL
) {
701 SSL_set_fd(ssl
, sock
);
707 if(flags
&SPAMC_USE_SSL
) {
709 SSL_write(ssl
, buf
, len
);
710 SSL_write(ssl
, m
->msg
, m
->msg_len
);
713 full_write(sock
, (unsigned char *) buf
, len
);
714 full_write(sock
, (unsigned char *) m
->msg
, m
->msg_len
);
715 shutdown(sock
, SHUT_WR
);
718 /* ok, now read and parse it. SPAMD/1.2 line first... */
719 failureval
= _spamc_read_full_line (m
, flags
, ssl
, sock
, buf
, &len
, bufsiz
);
720 if (failureval
!= EX_OK
) { goto failure
; }
722 if(sscanf(buf
, "SPAMD/%s %d %*s", versbuf
, &response
)!=2) {
723 syslog(LOG_ERR
, "spamd responded with bad string '%s'", buf
);
724 failureval
= EX_PROTOCOL
; goto failure
;
727 version
= _locale_safe_string_to_float (versbuf
, 20);
729 syslog(LOG_ERR
, "spamd responded with bad version string '%s'", versbuf
);
730 failureval
= EX_PROTOCOL
; goto failure
;
735 m
->is_spam
= EX_TOOBIG
;
737 failureval
= _spamc_read_full_line (m
, flags
, ssl
, sock
, buf
, &len
, bufsiz
);
738 if (failureval
!= EX_OK
) { goto failure
; }
740 if (len
== 0 && buf
[0] == '\0') {
741 break; /* end of headers */
744 if (_handle_spamd_header(m
, flags
, buf
, len
) < 0) {
745 failureval
= EX_PROTOCOL
; goto failure
;
749 len
= 0; /* overwrite those headers */
751 if (flags
&SPAMC_CHECK_ONLY
) {
752 close(sock
); sock
= -1;
753 if (m
->is_spam
== EX_TOOBIG
) {
754 /* We should have gotten headers back... Damnit. */
755 failureval
= EX_PROTOCOL
; goto failure
;
760 m
->is_spam
=EX_OUTPUTMESSAGE
;
761 if (m
->content_length
< 0) {
762 /* should have got a length too. */
763 failureval
= EX_PROTOCOL
; goto failure
;
766 if (flags
&SPAMC_USE_SSL
) {
767 len
= ssl_timeout_read (ssl
, m
->out
+m
->out_len
,
768 m
->max_len
+EXPANSION_ALLOWANCE
+1-m
->out_len
);
770 len
= full_read (sock
, (unsigned char *) m
->out
+m
->out_len
,
771 m
->max_len
+EXPANSION_ALLOWANCE
+1-m
->out_len
,
772 m
->max_len
+EXPANSION_ALLOWANCE
+1-m
->out_len
);
776 if(len
+m
->out_len
>m
->max_len
+EXPANSION_ALLOWANCE
){
777 failureval
= EX_TOOBIG
; goto failure
;
781 shutdown(sock
, SHUT_RD
);
782 close(sock
); sock
= -1;
784 libspamc_timeout
= 0;
786 if(m
->out_len
!=m
->content_length
) {
787 syslog(LOG_ERR
, "failed sanity check, %d bytes claimed, %d bytes seen",
788 m
->content_length
, m
->out_len
);
789 failureval
= EX_PROTOCOL
; goto failure
;
795 free(m
->out
); m
->out
=m
->msg
; m
->out_len
=m
->msg_len
;
799 libspamc_timeout
= 0;
801 if(flags
&SPAMC_USE_SSL
) {
810 static int _lookup_host(const char *hostname
, struct hostent
*out_hent
)
812 struct hostent
*hent
= NULL
;
815 /* no need to try using inet_addr(), gethostbyname() will do that */
817 if (NULL
== (hent
= gethostbyname(hostname
))) {
818 origherr
= h_errno
; /* take a copy before syslog() */
819 syslog (LOG_ERR
, "gethostbyname(%s) failed: h_errno=%d",
834 memcpy (out_hent
, hent
, sizeof(struct hostent
));
839 int message_process(const char *hostname
, int port
, char *username
, int max_size
, int in_fd
, int out_fd
, const int flags
){
846 ret
=lookup_host_for_failover(hostname
, &hent
);
847 if(ret
!=EX_OK
) goto FAIL
;
850 ret
=message_read(in_fd
, flags
, &m
);
851 if(ret
!=EX_OK
) goto FAIL
;
852 ret
=message_filter_with_failover(&hent
, port
, username
, flags
, &m
);
853 if(ret
!=EX_OK
) goto FAIL
;
854 if(message_write(out_fd
, &m
)<0) goto FAIL
;
855 if(m
.is_spam
!=EX_TOOBIG
) {
863 if(flags
&SPAMC_CHECK_ONLY
){
864 full_write(out_fd
, (unsigned char *) "0/0\n", 4);
868 message_dump(in_fd
, out_fd
, &m
);
874 void message_cleanup(struct message
*m
) {
875 if (m
->out
!= NULL
&& m
->out
!= m
->raw
) free(m
->out
);
876 if (m
->raw
!= NULL
) free(m
->raw
);
880 /* Aug 14, 2002 bj: Obsolete! */
881 int process_message(const char *hostname
, int port
, char *username
, int max_size
, int in_fd
, int out_fd
, const int my_check_only
, const int my_safe_fallback
){
884 flags
=SPAMC_RAW_MODE
;
885 if(my_check_only
) flags
|=SPAMC_CHECK_ONLY
;
886 if(my_safe_fallback
) flags
|=SPAMC_SAFE_FALLBACK
;
888 return message_process(hostname
, port
, username
, max_size
, in_fd
, out_fd
, flags
);
891 /* public APIs, which call into the static code and enforce sockaddr-OR-hostent
894 int lookup_host(const char *hostname
, int port
, struct sockaddr
*out_addr
)
896 struct sockaddr_in
*addr
= (struct sockaddr_in
*)out_addr
;
900 memset(&out_addr
, 0, sizeof(out_addr
));
901 addr
->sin_family
=AF_INET
;
902 addr
->sin_port
=htons(port
);
903 ret
= _lookup_host(hostname
, &hent
);
904 memcpy (&(addr
->sin_addr
), hent
.h_addr
, sizeof(addr
->sin_addr
));
908 int lookup_host_for_failover(const char *hostname
, struct hostent
*hent
) {
909 return _lookup_host(hostname
, hent
);
912 int message_filter(const struct sockaddr
*addr
, char *username
, int flags
,
914 { return _message_filter (addr
, NULL
, 0, username
, flags
, m
); }
916 int message_filter_with_failover (const struct hostent
*hent
, int port
,
917 char *username
, int flags
, struct message
*m
)
918 { return _message_filter (NULL
, hent
, port
, username
, flags
, m
); }