3 // $Id: spamass-milter.cpp,v 1.94 2011/02/14 21:50:53 dnelson Exp $
6 // - a rather trivial SpamAssassin Sendmail Milter plugin
8 // for information about SpamAssassin please see
9 // http://www.spamassassin.org
11 // for information about Sendmail please see
12 // http://www.sendmail.org
14 // Copyright (c) 2002 Georg C. F. Greve <greve@gnu.org>,
15 // all rights maintained by FSF Europe e.V.,
16 // Villa Vogelsang, Antonienallee 1, 45279 Essen, Germany
19 // {{{ License, Contact, Notes & Includes
21 // This program is free software; you can redistribute it and/or modify
22 // it under the terms of the GNU General Public License as published by
23 // the Free Software Foundation; either version 2 of the License, or
24 // (at your option) any later version.
26 // This program is distributed in the hope that it will be useful,
27 // but WITHOUT ANY WARRANTY; without even the implied warranty of
28 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 // GNU General Public License for more details.
31 // You should have received a copy of the GNU General Public License
32 // along with this program; if not, write to the Free Software
33 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
36 // Michael Brown <michaelb@opentext.com>
41 // The libmilter for sendmail works callback-oriented, so if you have no
42 // experience with event-driven programming, the following may be hard for
45 // The code should be reasonably thread-safe. No guarantees, though.
47 // This program roughly does the following steps:
49 // 1. register filter with libmilter & set up socket
50 // 2. register the callback functions defined in this file
51 // -- wait for mail to show up --
52 // 3. start spamc client
53 // 4. assemble mail since libmilter passes it in pieces and put
54 // these parts in the output pipe to spamc.
55 // 5. when the mail is complete, close the pipe.
56 // 6. read output from spamc, close input pipe and clean up PID
57 // 7. check for the flags affected by SpamAssassin and set/change
59 // 8. replace the body with the one provided by SpamAssassin if the
60 // mail was rated spam, unless -m is specified
61 // 9. free all temporary data
62 // 10. tell sendmail to let the mail to go on (default) or be discarded
63 // -- wait for mail to show up -- (restart at 3)
69 #include <arpa/inet.h>
70 #include <sys/types.h>
73 #include <netinet/in.h>
88 #include "subst_poll.h"
103 #include "libmilter/mfapi.h"
104 //#include "libmilter/mfdef.h"
106 #if !HAVE_DECL_STRSEP
107 char *strsep(char **stringp, const char *delim);
110 #if !HAVE_DECL_DAEMON
111 int daemon(int nochdir, int noclose);
118 #include "spamass-milter.h"
124 #ifndef INADDR_LOOPBACK
125 #define INADDR_LOOPBACK 0x7F000001
130 static const char Id[] = "$Id: spamass-milter.cpp,v 1.94 2011/02/14 21:50:53 dnelson Exp $";
132 struct smfiDesc smfilter =
134 "SpamAssassin", // filter name
135 SMFI_VERSION, // version code -- leave untouched
136 SMFIF_ADDHDRS|SMFIF_CHGHDRS|SMFIF_CHGBODY, // flags
137 mlfi_connect, // info filter callback
138 mlfi_helo, // HELO filter callback
139 mlfi_envfrom, // envelope sender filter callback
140 mlfi_envrcpt, // envelope recipient filter callback
141 mlfi_header, // header filter callback
142 mlfi_eoh, // end of header callback
143 mlfi_body, // body filter callback
144 mlfi_eom, // end of message callback
145 mlfi_abort, // message aborted callback
146 mlfi_close, // connection cleanup callback
149 const char *const debugstrings[] = {
150 "ALL", "FUNC", "POLL", "UORI", "STR", "MISC", "NET", "SPAMC", "RCPT",
155 int flag_debug = (1<<D_ALWAYS);
156 bool flag_reject = false;
157 int reject_score = -1;
158 bool dontmodifyspam = false; // Don't modify/add body or spam results headers
159 bool dontmodify = false; // Don't add SA headers, ever.
160 bool flag_sniffuser = false;
161 char *defaultuser; /* Username to send to spamc if there are multiple recipients */
162 char *defaultdomain; /* Domain to append if incoming address has none */
164 struct networklist ignorenets;
167 bool flag_bucket = false;
168 bool flag_bucket_only = false;
170 bool flag_full_email = false; /* pass full email address to spamc */
171 bool flag_expand = false; /* alias/virtusertable expansion */
172 bool warnedmacro = false; /* have we logged that we couldn't fetch a macro? */
177 main(int argc, char* argv[])
180 const char *args = "fd:mMp:P:r:u:D:i:b:B:e:x";
183 char *pidfilename = NULL;
184 FILE *pidfile = NULL;
186 #ifdef HAVE_VERBOSE_TERMINATE_HANDLER
187 std::set_terminate (__gnu_cxx::__verbose_terminate_handler);
190 openlog("spamass-milter", LOG_PID, LOG_MAIL);
192 /* Process command line options */
193 while ((c = getopt(argc, argv, args)) != -1) {
199 parse_debuglevel(optarg);
202 spamdhost = strdup(optarg);
205 flag_full_email = true;
206 defaultdomain = strdup(optarg);
209 debug(D_MISC, "Parsing ignore list");
210 parse_networklist(optarg, &ignorenets);
213 dontmodifyspam = true;
214 smfilter.xxfi_flags &= ~SMFIF_CHGBODY;
218 dontmodifyspam = true;
219 smfilter.xxfi_flags &= ~(SMFIF_CHGBODY|SMFIF_CHGHDRS);
222 sock = strdup(optarg);
225 pidfilename = strdup(optarg);
229 reject_score = atoi(optarg);
232 flag_sniffuser = true;
233 defaultuser = strdup(optarg);
239 fprintf(stderr, "Can only have one -b or -B flag\n");
246 flag_bucket_only = true;
247 smfilter.xxfi_flags |= SMFIF_DELRCPT; // May delete recipients
249 // we will modify the recipient list; if spamc returns
250 // indicating that this mail is spam, the message will be
251 // sent to <optarg>@localhost
252 smfilter.xxfi_flags |= SMFIF_ADDRCPT; // May add recipients
253 // XXX we should probably verify that optarg is vaguely sane
254 spambucket = strdup( optarg );
265 if (flag_full_email && !flag_sniffuser)
267 fprintf(stderr, "-e flag requires -u\n");
271 /* remember the remainer of the arguments so we can pass them to spamc */
272 spamc_argc = argc - optind;
273 spamc_argv = argv + optind;
276 cout << PACKAGE_NAME << " - Version " << PACKAGE_VERSION << endl;
277 cout << "SpamAssassin Sendmail Milter Plugin" << endl;
278 cout << "Usage: spamass-milter -p socket [-b|-B bucket] [-d xx[,yy...]] [-D host]" << endl;
279 cout << " [-e defaultdomain] [-f] [-i networks] [-m] [-M]" << endl;
280 cout << " [-P pidfile] [-r nn] [-u defaultuser] [-x]" << endl;
281 cout << " [-- spamc args ]" << endl;
282 cout << " -p socket: path to create socket" << endl;
283 cout << " -b bucket: redirect spam to this mail address. The orignal" << endl;
284 cout << " recipient(s) will not receive anything." << endl;
285 cout << " -B bucket: add this mail address as a BCC recipient of spam." << endl;
286 cout << " -d xx[,yy ...]: set debug flags. Logs to syslog" << endl;
287 cout << " -D host: connect to spamd at remote host (deprecated)" << endl;
288 cout << " -e defaultdomain: pass full email address to spamc instead of just\n"
289 " username. Uses 'defaultdomain' if there was none" << endl;
290 cout << " -f: fork into background" << endl;
291 cout << " -i: skip (ignore) checks from these IPs or netblocks" << endl;
292 cout << " example: -i 192.168.12.5,10.0.0.0/8,172.16.0.0/255.255.0.0" << endl;
293 cout << " -m: don't modify body, Content-type: or Subject:" << endl;
294 cout << " -M: don't modify the message at all" << endl;
295 cout << " -P pidfile: Put processid in pidfile" << endl;
296 cout << " -r nn: reject messages with a score >= nn with an SMTP error.\n"
297 " use -1 to reject any messages tagged by SA." << endl;
298 cout << " -u defaultuser: pass the recipient's username to spamc.\n"
299 " Uses 'defaultuser' if there are multiple recipients." << endl;
300 cout << " -x: pass email address through alias and virtusertable expansion." << endl;
301 cout << " -- spamc args: pass the remaining flags to spamc." << endl;
309 pidfile = fopen(pidfilename,"w");
312 fprintf(stderr, "Could not open pidfile: %s\n", strerror(errno));
315 /* leave the file open through the fork, since we don't know our pid
323 if (daemon(0, 0) == -1)
325 fprintf(stderr, "daemon() failed: %s\n", strerror(errno));
332 fprintf(pidfile, "%ld\n", (long)getpid());
339 if (stat(sock,&junk) == 0) unlink(sock);
342 (void) smfi_setconn(sock);
343 if (smfi_register(smfilter) == MI_FAILURE) {
344 fprintf(stderr, "smfi_register failed\n");
345 exit(EX_UNAVAILABLE);
347 debug(D_MISC, "smfi_register succeeded");
349 debug(D_ALWAYS, "spamass-milter %s starting", PACKAGE_VERSION);
351 debug(D_ALWAYS, "spamass-milter %s exiting", PACKAGE_VERSION);
359 /* Update a header if SA changes it, or add it if it is new. */
360 void update_or_insert(SpamAssassin* assassin, SMFICTX* ctx, string oldstring, t_setter setter, char *header )
362 string::size_type eoh1 = assassin->d().find("\n\n");
363 string::size_type eoh2 = assassin->d().find("\n\r\n");
364 string::size_type eoh = ( eoh1 < eoh2 ? eoh1 : eoh2 );
367 string::size_type oldsize;
369 debug(D_UORI, "u_or_i: looking at <%s>", header);
370 debug(D_UORI, "u_or_i: oldstring: <%s>", oldstring.c_str());
372 newstring = retrieve_field(assassin->d().substr(0, eoh), header);
373 debug(D_UORI, "u_or_i: newstring: <%s>", newstring.c_str());
375 oldsize = callsetter(*assassin,setter)(newstring);
379 if (newstring != oldstring)
381 /* change if old one was present, append if non-null */
382 char* cstr = const_cast<char*>(newstring.c_str());
385 debug(D_UORI, "u_or_i: changing");
386 smfi_chgheader(ctx, header, 1, newstring.size() > 0 ?
388 } else if (newstring.size() > 0)
390 debug(D_UORI, "u_or_i: inserting");
391 smfi_addheader(ctx, header, cstr);
395 debug(D_UORI, "u_or_i: no change");
403 // implement the changes suggested by SpamAssassin for the mail. Returns
404 // the milter error code.
406 assassinate(SMFICTX* ctx, SpamAssassin* assassin)
408 // find end of header (eol in last line of header)
409 // and beginning of body
410 string::size_type eoh1 = assassin->d().find("\n\n");
411 string::size_type eoh2 = assassin->d().find("\n\r\n");
412 string::size_type eoh = (eoh1 < eoh2) ? eoh1 : eoh2;
413 string::size_type bob = assassin->d().find_first_not_of("\r\n", eoh);
415 if (bob == string::npos)
416 bob = assassin->d().size();
418 update_or_insert(assassin, ctx, assassin->spam_flag(), &SpamAssassin::set_spam_flag, "X-Spam-Flag");
419 update_or_insert(assassin, ctx, assassin->spam_status(), &SpamAssassin::set_spam_status, "X-Spam-Status");
421 /* Summarily reject the message if SA tagged it, or if we have a minimum
422 score, reject if it exceeds that score. */
425 bool do_reject = false;
426 if (reject_score == -1 && !assassin->spam_flag().empty())
428 if (reject_score != -1)
431 const char *spam_status = assassin->spam_status().c_str();
432 /* SA 3.0 uses the keyword "score" */
433 rv = sscanf(spam_status,"%*s score=%d", &score);
436 /* SA 2.x uses the keyword "hits" */
437 rv = sscanf(spam_status,"%*s hits=%d", &score);
440 debug(D_ALWAYS, "Could not extract score from <%s>", spam_status);
443 debug(D_MISC, "SA score: %d", score);
444 if (score >= reject_score)
450 debug(D_MISC, "Rejecting");
451 smfi_setreply(ctx, "550", "5.7.1", "Blocked by SpamAssassin");
456 /* If we also want a copy of the spam, shell out to sendmail and
457 send another copy. The milter API will not let you send the
458 message AND return a failure code to the sender, so this is
459 the only way to do it. */
464 popen_argv[0] = SENDMAIL;
465 popen_argv[1] = spambucket;
466 popen_argv[2] = NULL;
468 debug(D_COPY, "calling %s %s", SENDMAIL, spambucket);
469 p = popenv(popen_argv, "w", &pid);
472 debug(D_COPY, "popenv failed(%s). Will not send a copy to spambucket", strerror(errno));
475 // Send message provided by SpamAssassin
476 fwrite(assassin->d().c_str(), assassin->d().size(), 1, p);
478 waitpid(pid, NULL, 0);
485 /* Drop the message into the spam bucket if it's spam */
487 if (!assassin->spam_flag().empty()) {
488 // first, add the spambucket address
489 if ( smfi_addrcpt( ctx, spambucket ) != MI_SUCCESS ) {
490 throw string( "Failed to add spambucket to recipients" );
492 if (flag_bucket_only) {
493 // Move recipients to a non-active header, one at a
494 // time. Note, this may generate multiple X-Spam-Orig-To
495 // headers, but that's okay.
496 while( !assassin->recipients.empty()) {
497 if ( smfi_addheader( ctx, "X-Spam-Orig-To", (char *)assassin->recipients.front().c_str()) != MI_SUCCESS ) {
498 throw string( "Failed to save recipient" );
501 // It's not 100% important that this succeeds, so we'll just warn on failure rather than bailing out.
502 if ( smfi_delrcpt( ctx, (char *)assassin->recipients.front().c_str()) != MI_SUCCESS ) {
503 // throw_error really just logs a warning as opposed to actually throw()ing
504 debug(D_ALWAYS, "Failed to remove recipient %s from the envelope", assassin->recipients.front().c_str() );
506 assassin->recipients.pop_front();
512 update_or_insert(assassin, ctx, assassin->spam_report(), &SpamAssassin::set_spam_report, "X-Spam-Report");
513 update_or_insert(assassin, ctx, assassin->spam_prev_content_type(), &SpamAssassin::set_spam_prev_content_type, "X-Spam-Prev-Content-Type");
514 update_or_insert(assassin, ctx, assassin->spam_level(), &SpamAssassin::set_spam_level, "X-Spam-Level");
515 update_or_insert(assassin, ctx, assassin->spam_checker_version(), &SpamAssassin::set_spam_checker_version, "X-Spam-Checker-Version");
518 // If SpamAssassin thinks it is spam, replace
523 // However, only issue the header replacement calls if the content has
524 // actually changed. If SA didn't change subject or content-type, don't
525 // replace here unnecessarily.
526 if (!dontmodifyspam && assassin->spam_flag().size()>0)
528 update_or_insert(assassin, ctx, assassin->subject(), &SpamAssassin::set_subject, "Subject");
529 update_or_insert(assassin, ctx, assassin->content_type(), &SpamAssassin::set_content_type, "Content-Type");
531 // Replace body with the one SpamAssassin provided
532 string::size_type body_size = assassin->d().size() - bob;
533 string body=assassin->d().substr(bob, string::npos);
534 if ( smfi_replacebody(ctx, (unsigned char *)body.c_str(), body_size) == MI_FAILURE )
535 throw string("error. could not replace body.");
539 return SMFIS_CONTINUE;
542 // retrieve the content of a specific field in the header
545 old_retrieve_field(const string& header, const string& field)
547 // look for beginning of content
548 string::size_type pos = find_nocase(header, "\n" + field + ": ");
550 // return empty string if not found
551 if (pos == string::npos)
553 debug(D_STR, "r_f: failed");
557 // look for end of field name
558 pos = find_nocase(header, " ", pos) + 1;
560 string::size_type pos2(pos);
563 if (pos2 == find_nocase(header, "\n", pos2))
566 // look for end of content
569 pos2 = find_nocase(header, "\n", pos2+1);
572 while ( pos2 < string::npos &&
573 isspace(header[pos2+1]) );
575 return header.substr(pos, pos2-pos);
579 // retrieve the content of a specific field in the header
582 retrieve_field(const string& header, const string& field)
585 string::size_type field_start = string::npos;
586 string::size_type field_end = string::npos;
587 string::size_type idx = 0;
589 while( field_start == string::npos ) {
590 idx = find_nocase( header, field + ":", idx );
593 if ( idx == string::npos ) {
597 // The string we've found needs to be either at the start of the
598 // headers string, or immediately following a "\n"
600 if ( header[ idx - 1 ] != '\n' ) {
601 idx++; // so we don't get stuck in an infinite loop
602 continue; // loop around again
609 // A mail field starts just after the header. Ideally, there's a
610 // space, but it's possible that there isn't.
611 field_start += field.length() + 1;
612 if ( field_start < ( header.length() - 1 ) && header[ field_start ] == ' ' ) {
616 // See if there's anything left, to shortcut the rest of the
618 if ( field_start == header.length() - 1 ) {
622 // The field continues to the end of line. If the start of the next
623 // line is whitespace, then the field continues.
625 while( field_end == string::npos ) {
626 idx = header.find( "\n", idx );
628 // if we don't find a "\n", gobble everything to the end of the headers
629 if ( idx == string::npos ) {
630 field_end = header.length();
632 // check the next character
633 if (( idx + 1 ) < header.length() && ( isspace( header[ idx + 1 ] ))) {
634 idx ++; // whitespace found, so wrap to the next line
641 /* if the header line ends in \r\n, don't return the \r */
642 if (header[field_end-1] == '\r')
645 string data = header.substr( field_start, field_end - field_start );
647 /* Replace all CRLF pairs with LF */
649 while ( (idx = data.find("\r\n", idx)) != string::npos )
651 data.replace(idx,2,"\n");
660 // {{{ MLFI callbacks
663 // Gets called once when a client connects to sendmail
665 // gets the originating IP address and checks it against the ignore list
666 // if it isn't in the list, store the IP in a structure and store a
667 // pointer to it in the private data area.
670 mlfi_connect(SMFICTX * ctx, char *hostname, _SOCK_ADDR * hostaddr)
672 struct context *sctx;
675 debug(D_FUNC, "mlfi_connect: enter");
677 /* allocate a structure to store the IP address (and SA object) in */
678 sctx = (struct context *)malloc(sizeof(*sctx));
681 /* not a socket; probably a local user calling sendmail directly */
682 /* set to 127.0.0.1 */
683 sctx->connect_ip.s_addr = htonl(INADDR_LOOPBACK);
686 sctx->connect_ip = ((struct sockaddr_in *) hostaddr)->sin_addr;
688 sctx->assassin = NULL;
691 /* store a pointer to it with setpriv */
692 rv = smfi_setpriv(ctx, sctx);
693 if (rv != MI_SUCCESS)
695 debug(D_ALWAYS, "smfi_setpriv failed!");
696 return SMFIS_TEMPFAIL;
698 /* debug(D_ALWAYS, "ZZZ set private context to %p", sctx); */
700 if (ip_in_networklist(sctx->connect_ip, &ignorenets))
702 debug(D_NET, "%s is in our ignore list - accepting message",
703 inet_ntoa(sctx->connect_ip));
704 debug(D_FUNC, "mlfi_connect: exit ignore");
708 // Tell Milter to continue
709 debug(D_FUNC, "mlfi_connect: exit");
711 return SMFIS_CONTINUE;
715 // Gets called on every "HELO"
717 // stores the result in the context structure
719 sfsistat mlfi_helo(SMFICTX * ctx, char * helohost)
721 struct context *sctx = (struct context*)smfi_getpriv(ctx);
724 sctx->helo = strdup(helohost);
726 return SMFIS_CONTINUE;
730 // Gets called first for all messages
732 // creates SpamAssassin object and makes pointer to it
733 // private data of this filter process
736 mlfi_envfrom(SMFICTX* ctx, char** envfrom)
738 SpamAssassin* assassin;
739 struct context *sctx = (struct context *)smfi_getpriv(ctx);
744 debug(D_ALWAYS, "smfi_getpriv failed!");
745 return SMFIS_TEMPFAIL;
747 /* debug(D_ALWAYS, "ZZZ got private context %p", sctx); */
749 debug(D_FUNC, "mlfi_envfrom: enter");
751 // launch new SpamAssassin
752 assassin=new SpamAssassin;
753 } catch (string& problem)
755 throw_error(problem);
756 return SMFIS_TEMPFAIL;
759 assassin->set_connectip(string(inet_ntoa(sctx->connect_ip)));
761 // Store a pointer to the assassin object in our context struct
762 sctx->assassin = assassin;
764 // remember the MAIL FROM address
765 assassin->set_from(string(envfrom[0]));
767 queueid=smfi_getsymval(ctx,"i");
771 warnmacro("i", "ENVFROM");
773 assassin->queueid = queueid;
775 debug(D_MISC, "queueid=%s", queueid);
777 // tell Milter to continue
778 debug(D_FUNC, "mlfi_envfrom: exit");
780 return SMFIS_CONTINUE;
784 // Gets called once for each recipient
786 // stores the first recipient in the spamassassin object and
787 // stores all addresses and the number thereof (some redundancy)
792 mlfi_envrcpt(SMFICTX* ctx, char** envrcpt)
794 struct context *sctx = (struct context*)smfi_getpriv(ctx);
795 SpamAssassin* assassin = sctx->assassin;
798 debug(D_FUNC, "mlfi_envrcpt: enter");
802 /* open a pipe to sendmail so we can do address expansion */
808 popen_argv[0] = SENDMAIL;
809 popen_argv[1] = "-bv";
810 popen_argv[2] = envrcpt[0];
811 popen_argv[3] = NULL;
813 debug(D_RCPT, "calling %s -bv %s", SENDMAIL, envrcpt[0]);
815 p = popenv(popen_argv, "r", &pid);
818 debug(D_RCPT, "popenv failed(%s). Will not expand aliases", strerror(errno));
819 assassin->expandedrcpt.push_back(envrcpt[0]);
822 while (fgets(buf, sizeof(buf), p) != NULL)
825 /* strip trailing EOLs */
826 while (i > 0 && buf[i - 1] <= ' ')
829 debug(D_RCPT, "sendmail output: %s", buf);
830 /* From a quick scan of the sendmail source, a valid email
831 address gets printed via either
832 "deliverable: mailer %s, host %s, user %s"
833 or "deliverable: mailer %s, user %s"
835 if (strstr(buf, "... deliverable: mailer "))
837 char *p=strstr(buf,", user ");
838 /* anything after ", user " is the email address */
839 debug(D_RCPT, "user: %s", p+7);
840 assassin->expandedrcpt.push_back(p+7);
844 waitpid(pid, NULL, 0);
848 assassin->expandedrcpt.push_back(envrcpt[0]);
850 debug(D_RCPT, "Total of %d actual recipients", (int)assassin->expandedrcpt.size());
852 if (assassin->numrcpt() == 0)
854 /* Send the envelope headers as X-Envelope-From: and
855 X-Envelope-To: so that SpamAssassin can use them in its
856 whitelist checks. Also forge as complete a dummy
857 Received: header as possible because SA gets a lot of
860 HReceived: $?sfrom $s $.$?_($?s$|from $.$_)
861 $.$?{auth_type}(authenticated$?{auth_ssf} bits=${auth_ssf}$.)
862 $.by $j ($v/$Z)$?r with $r$. id $i$?{tls_version}
863 (version=${tls_version} cipher=${cipher} bits=${cipher_bits} verify=${verify})$.$?u
869 const char *macro_b, *macro_i, *macro_j, *macro_r,
870 *macro_s, *macro_v, *macro_Z, *macro__;
874 macro_b = smfi_getsymval(ctx, "b");
879 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", localtime(&tval));
881 warnmacro("b", "ENVRCPT");
885 macro_i = smfi_getsymval(ctx, "i");
889 warnmacro("i", "ENVRCPT");
892 /* FQDN of this site */
893 macro_j = smfi_getsymval(ctx, "j");
896 macro_j = "localhost";
897 warnmacro("j", "ENVRCPT");
900 /* Protocol used to receive the message */
901 macro_r = smfi_getsymval(ctx, "r");
905 warnmacro("r", "ENVRCPT");
908 /* Sendmail currently cannot pass us the {s} macro, but
909 I do not know why. Leave this in for the day sendmail is
910 fixed. Until that day, use the value remembered by
913 macro_s = smfi_getsymval(ctx, "s");
915 macro_s = sctx->helo;
919 /* Sendmail binary version */
920 macro_v = smfi_getsymval(ctx, "v");
924 warnmacro("v", "ENVRCPT");
927 /* Sendmail .cf version */
928 macro_Z = smfi_getsymval(ctx, "Z");
932 warnmacro("Z", "ENVRCPT");
935 /* Validated sending site's address */
936 macro__ = smfi_getsymval(ctx, "_");
940 warnmacro("_", "ENVRCPT");
943 assassin->output((string)"X-Envelope-From: "+assassin->from()+"\r\n");
944 assassin->output((string)"X-Envelope-To: "+envrcpt[0]+"\r\n");
946 assassin->output((string)
947 "Received: from "+macro_s+" ("+macro__+")\r\n\t"+
948 "by "+macro_j+"("+macro_v+"/"+macro_Z+") with "+macro_r+" id "+macro_i+";\r\n\t"+
950 "(envelope-from "+assassin->from()+")\r\n");
953 assassin->output((string)"X-Envelope-To: "+envrcpt[0]+"\r\n");
955 /* increment RCPT TO: count */
956 assassin->set_numrcpt();
958 /* If we expanded to at least one user and we haven't recorded one yet,
959 record the first one */
960 if (!assassin->expandedrcpt.empty() && (assassin->rcpt().size() == 0))
962 debug(D_RCPT, "remembering %s for spamc", assassin->expandedrcpt.front().c_str());
963 assassin->set_rcpt(assassin->expandedrcpt.front());
966 debug(D_RCPT, "remembering recipient %s", envrcpt[0]);
967 assassin->recipients.push_back( envrcpt[0] ); // XXX verify that this worked
969 debug(D_FUNC, "mlfi_envrcpt: exit");
971 return SMFIS_CONTINUE;
975 // Gets called repeatedly for all header fields
977 // assembles the headers and passes them on to the SpamAssassin client
980 // only exception: SpamAssassin header fields (X-Spam-*) get suppressed
981 // but are being stored in the SpamAssassin element.
983 // this function also starts the connection with the SPAMC program the
984 // first time it is called.
988 mlfi_header(SMFICTX* ctx, char* headerf, char* headerv)
990 SpamAssassin* assassin = ((struct context *)smfi_getpriv(ctx))->assassin;
991 debug(D_FUNC, "mlfi_header: enter");
993 // Check if the SPAMC program has already been run, if not we run it.
994 if ( !(assassin->connected) )
997 assassin->connected = 1; // SPAMC is getting ready to run
1000 catch (string& problem) {
1001 throw_error(problem);
1002 ((struct context *)smfi_getpriv(ctx))->assassin=NULL;
1004 debug(D_FUNC, "mlfi_header: exit error connect");
1005 return SMFIS_TEMPFAIL;
1009 // Is it a "X-Spam-" header field?
1010 if ( cmp_nocase_partial("X-Spam-", headerf) == 0 )
1013 // memorize content of old fields
1015 if ( cmp_nocase_partial("X-Spam-Status", headerf) == 0 )
1016 assassin->set_spam_status(headerv);
1017 else if ( cmp_nocase_partial("X-Spam-Flag", headerf) == 0 )
1018 assassin->set_spam_flag(headerv);
1019 else if ( cmp_nocase_partial("X-Spam-Report", headerf) == 0 )
1020 assassin->set_spam_report(headerv);
1021 else if ( cmp_nocase_partial("X-Spam-Prev-Content-Type", headerf) == 0 )
1022 assassin->set_spam_prev_content_type(headerv);
1023 else if ( cmp_nocase_partial("X-Spam-Level", headerf) == 0 )
1024 assassin->set_spam_level(headerv);
1025 else if ( cmp_nocase_partial("X-Spam-Checker-Version", headerf) == 0 )
1026 assassin->set_spam_checker_version(headerv);
1029 /* Hm. X-Spam header, but not one we recognize. Pass it through. */
1035 debug(D_FUNC, "mlfi_header: suppress");
1036 return SMFIS_CONTINUE;
1040 // Content-Type: will be stored if present
1041 if ( cmp_nocase_partial("Content-Type", headerf) == 0 )
1042 assassin->set_content_type(headerv);
1044 // Subject: should be stored
1045 if ( cmp_nocase_partial("Subject", headerf) == 0 )
1046 assassin->set_subject(headerv);
1048 // assemble header to be written to SpamAssassin
1049 string header = string(headerf) + ": " + headerv + "\r\n";
1052 // write to SpamAssassin client
1053 assassin->output(header.c_str(),header.size());
1054 } catch (string& problem)
1056 throw_error(problem);
1057 ((struct context *)smfi_getpriv(ctx))->assassin=NULL;
1059 debug(D_FUNC, "mlfi_header: exit error output");
1060 return SMFIS_TEMPFAIL;
1064 debug(D_FUNC, "mlfi_header: exit");
1066 return SMFIS_CONTINUE;
1070 // Gets called once when the header is finished.
1072 // writes empty line to SpamAssassin client to separate
1073 // headers from body.
1076 mlfi_eoh(SMFICTX* ctx)
1078 SpamAssassin* assassin = ((struct context *)smfi_getpriv(ctx))->assassin;
1080 debug(D_FUNC, "mlfi_eoh: enter");
1082 // Check if the SPAMC program has already been run, if not we run it.
1083 if ( !(assassin->connected) )
1086 assassin->connected = 1; // SPAMC is getting ready to run
1087 assassin->Connect();
1089 catch (string& problem) {
1090 throw_error(problem);
1091 ((struct context *)smfi_getpriv(ctx))->assassin=NULL;
1094 debug(D_FUNC, "mlfi_eoh: exit error connect");
1095 return SMFIS_TEMPFAIL;
1100 // add blank line between header and body
1101 assassin->output("\r\n",2);
1102 } catch (string& problem)
1104 throw_error(problem);
1105 ((struct context *)smfi_getpriv(ctx))->assassin=NULL;
1108 debug(D_FUNC, "mlfi_eoh: exit error output");
1109 return SMFIS_TEMPFAIL;
1114 debug(D_FUNC, "mlfi_eoh: exit");
1115 return SMFIS_CONTINUE;
1119 // Gets called repeatedly to transmit the body
1121 // writes everything directly to SpamAssassin client
1124 mlfi_body(SMFICTX* ctx, u_char *bodyp, size_t bodylen)
1126 debug(D_FUNC, "mlfi_body: enter");
1127 SpamAssassin* assassin = ((struct context *)smfi_getpriv(ctx))->assassin;
1131 assassin->output(bodyp, bodylen);
1132 } catch (string& problem)
1134 throw_error(problem);
1135 ((struct context *)smfi_getpriv(ctx))->assassin=NULL;
1137 debug(D_FUNC, "mlfi_body: exit error");
1138 return SMFIS_TEMPFAIL;
1142 debug(D_FUNC, "mlfi_body: exit");
1143 return SMFIS_CONTINUE;
1147 // Gets called once at the end of mail processing
1149 // tells SpamAssassin client that the mail is complete
1150 // through EOF and then modifies the mail accordingly by
1151 // calling the "assassinate" function
1154 mlfi_eom(SMFICTX* ctx)
1156 SpamAssassin* assassin = ((struct context *)smfi_getpriv(ctx))->assassin;
1159 debug(D_FUNC, "mlfi_eom: enter");
1162 // close output pipe to signal EOF to SpamAssassin
1163 assassin->close_output();
1165 // read what the Assassin is telling us
1168 milter_status = assassinate(ctx, assassin);
1170 // now cleanup the element.
1171 ((struct context *)smfi_getpriv(ctx))->assassin=NULL;
1174 } catch (string& problem)
1176 throw_error(problem);
1177 ((struct context *)smfi_getpriv(ctx))->assassin=NULL;
1179 debug(D_FUNC, "mlfi_eom: exit error");
1180 return SMFIS_TEMPFAIL;
1184 debug(D_FUNC, "mlfi_eom: exit");
1185 return milter_status;
1189 // Gets called on session-basis. This keeps things nice & quiet.
1192 mlfi_close(SMFICTX* ctx)
1194 struct context *sctx;
1195 debug(D_FUNC, "mlfi_close");
1197 sctx = (struct context*)smfi_getpriv(ctx);
1199 return SMFIS_ACCEPT;
1204 smfi_setpriv(ctx, NULL);
1206 return SMFIS_ACCEPT;
1210 // Gets called when things are being aborted.
1212 // kills the SpamAssassin object, its destructor should
1213 // take care of everything.
1216 mlfi_abort(SMFICTX* ctx)
1218 SpamAssassin* assassin = ((struct context *)smfi_getpriv(ctx))->assassin;
1220 debug(D_FUNC, "mlfi_abort");
1221 ((struct context *)smfi_getpriv(ctx))->assassin=NULL;
1224 return SMFIS_ACCEPT;
1229 // {{{ SpamAssassin Class
1232 // This is a new constructor for the SpamAssassin object. It simply
1233 // initializes two variables. The original constructor has been
1234 // renamed to Connect().
1236 SpamAssassin::SpamAssassin():
1245 SpamAssassin::~SpamAssassin()
1249 // close all pipes that are still open
1250 if (pipe_io[0][0] > -1) close(pipe_io[0][0]);
1251 if (pipe_io[0][1] > -1) close(pipe_io[0][1]);
1252 if (pipe_io[1][0] > -1) close(pipe_io[1][0]);
1253 if (pipe_io[1][1] > -1) close(pipe_io[1][1]);
1255 // child still running?
1258 // make sure the pid is valid
1263 // wait for child to terminate
1265 waitpid(pid, &status, 0);
1270 // Clean up the recip list. Might be overkill, but it's good housekeeping.
1271 while( !recipients.empty())
1273 recipients.pop_front();
1275 // Clean up the recip list. Might be overkill, but it's good housekeeping.
1276 while( !expandedrcpt.empty())
1278 expandedrcpt.pop_front();
1283 // This is the old SpamAssassin constructor. It has been renamed Connect(),
1284 // and is now called at the beginning of the mlfi_header() function.
1287 void SpamAssassin::Connect()
1289 // set up pipes for in- and output
1290 if (pipe(pipe_io[0]))
1291 throw string(string("pipe error: ")+string(strerror(errno)));
1292 if (pipe(pipe_io[1]))
1293 throw string(string("pipe error: ")+string(strerror(errno)));
1295 // now execute SpamAssassin client for contact with SpamAssassin spamd
1297 // start child process
1298 switch(pid = fork())
1301 // forking trouble. throw error.
1302 throw string(string("fork error: ")+string(strerror(errno)));
1307 // close unused pipes
1308 close(pipe_io[1][0]);
1309 close(pipe_io[0][1]);
1311 // redirect stdin(0), stdout(1) and stderr(2)
1312 dup2(pipe_io[0][0],0);
1313 dup2(pipe_io[1][1],1);
1314 dup2(pipe_io[1][1],2);
1319 // absolute path (determined in autoconf)
1320 // should be a little more secure
1321 // XXX arbitrary 100-argument max
1323 char** argv = (char**) malloc(100*sizeof(char*));
1324 argv[argc++] = SPAMC;
1327 argv[argc++] = "-u";
1328 if ( expandedrcpt.size() != 1 )
1330 // More (or less?) than one recipient, so we pass the default
1331 // username to SPAMC. This way special rules can be defined for
1332 // multi recipient messages.
1333 debug(D_RCPT, "%d recipients; spamc gets default username %s", (int)expandedrcpt.size(), defaultuser);
1334 argv[argc++] = defaultuser;
1337 // There is only 1 recipient so we pass the username
1338 // (converted to lowercase) to SPAMC. Don't worry about
1339 // freeing this memory as we're exec()ing anyhow.
1340 if (flag_full_email)
1341 argv[argc] = strlwr(strdup(full_user().c_str()));
1343 argv[argc] = strlwr(strdup(local_user().c_str()));
1345 debug(D_RCPT, "spamc gets %s", argv[argc]);
1352 argv[argc++] = "-d";
1353 argv[argc++] = spamdhost;
1357 memcpy(argv+argc, spamc_argv, spamc_argc * sizeof(char *));
1362 execvp(argv[0] , argv); // does not return!
1365 throw_error(string("execution error: ")+string(strerror(errno)));
1372 // close unused pipes
1373 close(pipe_io[0][0]);
1374 close(pipe_io[1][1]);
1378 // mark the pipes non-blocking
1379 if(fcntl(pipe_io[0][1], F_SETFL, O_NONBLOCK) == -1)
1380 throw string(string("Cannot set pipe01 nonblocking: ")+string(strerror(errno)));
1381 #if 0 /* don't really need to make the sink pipe nonblocking */
1382 if(fcntl(pipe_io[1][0], F_SETFL, O_NONBLOCK) == -1)
1383 throw string(string("Cannot set pipe10 nonblocking: ")+string(strerror(errno)));
1386 // we have to assume the client is running now.
1389 /* If we have any buffered output, write it now. */
1390 if (outputbuffer.size())
1392 output(outputbuffer);
1397 // write to SpamAssassin
1399 SpamAssassin::output(const void* buffer, long size)
1401 debug(D_FUNC, "::output enter");
1403 debug(D_SPAMC, "output \"%*.*s\"", (int)size, (int)size, (char *)buffer);
1405 // if there are problems, fail.
1407 throw string("tried output despite problems. failed.");
1409 /* If we haven't launched spamc yet, just store the data */
1412 /* Silly C++ can't tell the difference between
1413 (const char*, string::size_type) and
1414 (string::size_type, char), so we have to cast the parameters.
1416 outputbuffer.append((const char *)buffer,(string::size_type)size);
1417 debug(D_FUNC, "::output exit1");
1421 // send to SpamAssassin
1427 struct pollfd fds[2];
1428 int nfds = 2, nready;
1429 fds[0].fd = pipe_io[0][1];
1430 fds[0].events = POLLOUT;
1431 fds[1].fd = pipe_io[1][0];
1432 fds[1].events = POLLIN;
1434 debug(D_POLL, "polling fds %d and %d", pipe_io[0][1], pipe_io[1][0]);
1435 nready = poll(fds, nfds, 1000);
1437 throw("poll failed");
1439 debug(D_POLL, "poll returned %d, fd0=%d, fd1=%d", nready, fds[0].revents, fds[1].revents);
1441 if (fds[1].revents & (POLLERR|POLLNVAL|POLLHUP))
1443 throw string("poll says my read pipe is busted");
1446 if (fds[0].revents & (POLLERR|POLLNVAL|POLLHUP))
1448 throw string("poll says my write pipe is busted");
1451 if (fds[1].revents & POLLIN)
1453 debug(D_POLL, "poll says I can read");
1457 if (fds[0].revents & POLLOUT)
1459 debug(D_POLL, "poll says I can write");
1460 switch(wsize = write(pipe_io[0][1], (char *)buffer + total, size - total))
1463 if (errno == EAGAIN)
1465 reason = string(strerror(errno));
1468 close(pipe_io[0][1]);
1469 close(pipe_io[1][0]);
1480 // wait until child is dead
1481 waitpid(pid, &status, 0);
1483 throw string(string("write error: ")+reason);
1487 debug(D_POLL, "wrote %ld bytes", wsize);
1491 } while ( total < size );
1493 debug(D_FUNC, "::output exit2");
1496 void SpamAssassin::output(const void* buffer)
1498 output(buffer, strlen((const char *)buffer));
1501 void SpamAssassin::output(string buffer)
1503 output(buffer.c_str(), buffer.size());
1506 // close output pipe
1508 SpamAssassin::close_output()
1510 if(close(pipe_io[0][1]))
1511 throw string(string("close error: ")+string(strerror(errno)));
1516 SpamAssassin::input()
1518 debug(D_FUNC, "::input enter");
1519 // if the child has exited or we experienced an error, return
1521 if (!running || error)
1523 debug(D_FUNC, "::input exit1");
1527 // keep reading from input pipe until it is empty
1528 empty_and_close_pipe();
1530 // that's it, we're through
1533 // wait until child is dead
1535 if(waitpid(pid, &status, 0)<0)
1538 throw string(string("waitpid error: ")+string(strerror(errno)));
1540 debug(D_FUNC, "::input exit2");
1544 // return reference to mail
1553 // get values of the different SpamAssassin fields
1556 SpamAssassin::spam_status()
1558 return x_spam_status;
1562 SpamAssassin::spam_flag()
1568 SpamAssassin::spam_report()
1570 return x_spam_report;
1574 SpamAssassin::spam_prev_content_type()
1576 return x_spam_prev_content_type;
1580 SpamAssassin::spam_checker_version()
1582 return x_spam_checker_version;
1586 SpamAssassin::spam_level()
1588 return x_spam_level;
1592 SpamAssassin::content_type()
1594 return _content_type;
1598 SpamAssassin::subject()
1604 SpamAssassin::rcpt()
1610 SpamAssassin::from()
1616 SpamAssassin::connectip()
1623 SpamAssassin::local_user()
1625 // assuming we have a recipient in the form: <username@somehost.somedomain>
1626 // (angle brackets optional) we return 'username'
1627 if (_rcpt[0] == '<')
1628 return _rcpt.substr(1, _rcpt.find_first_of("@+")-1);
1630 return _rcpt.substr(0, _rcpt.find_first_of("@+"));
1634 SpamAssassin::full_user()
1637 // assuming we have a recipient in the form: <username@somehost.somedomain>
1638 // (angle brackets optional) we return 'username@somehost.somedomain'
1639 if (_rcpt[0] == '<')
1640 name = _rcpt.substr(1, _rcpt.find('>')-1);
1643 if(name.find('@') == string::npos)
1645 /* if the name had no domain part (local delivery), append the default one */
1646 name = name + "@" + defaultdomain;
1652 SpamAssassin::numrcpt()
1658 SpamAssassin::set_numrcpt()
1665 SpamAssassin::set_numrcpt(const int val)
1672 // set the values of the different SpamAssassin
1673 // fields in our element. Returns former size of field
1676 SpamAssassin::set_spam_status(const string& val)
1678 string::size_type old = x_spam_status.size();
1679 x_spam_status = val;
1684 SpamAssassin::set_spam_flag(const string& val)
1686 string::size_type old = x_spam_flag.size();
1692 SpamAssassin::set_spam_report(const string& val)
1694 string::size_type old = x_spam_report.size();
1695 x_spam_report = val;
1700 SpamAssassin::set_spam_prev_content_type(const string& val)
1702 string::size_type old = x_spam_prev_content_type.size();
1703 x_spam_prev_content_type = val;
1708 SpamAssassin::set_spam_checker_version(const string& val)
1710 string::size_type old = x_spam_checker_version.size();
1711 x_spam_checker_version = val;
1716 SpamAssassin::set_spam_level(const string& val)
1718 string::size_type old = x_spam_level.size();
1724 SpamAssassin::set_content_type(const string& val)
1726 string::size_type old = _content_type.size();
1727 _content_type = val;
1732 SpamAssassin::set_subject(const string& val)
1734 string::size_type old = _subject.size();
1740 SpamAssassin::set_rcpt(const string& val)
1742 string::size_type old = _rcpt.size();
1748 SpamAssassin::set_from(const string& val)
1750 string::size_type old = _from.size();
1756 SpamAssassin::set_connectip(const string& val)
1758 string::size_type old = _connectip.size();
1764 // Read available output from SpamAssassin client
1767 SpamAssassin::read_pipe()
1774 debug(D_FUNC, "::read_pipe enter");
1776 if (pipe_io[1][0] == -1)
1778 debug(D_FUNC, "::read_pipe exit - shouldn't have been called?");
1782 size = read(pipe_io[1][0], iobuff, 1024);
1787 reason = string(strerror(errno));
1789 // Close remaining pipe.
1790 close(pipe_io[1][0]);
1800 // wait until child is dead
1801 waitpid(pid, &status, 0);
1803 // throw the error message that caused this trouble
1804 throw string(string("read error: ")+reason);
1805 } else if ( size == 0 )
1808 // EOF. Close the pipe
1809 if(close(pipe_io[1][0]))
1810 throw string(string("close error: ")+string(strerror(errno)));
1815 // append to mail buffer
1816 mail.append(iobuff, size);
1817 debug(D_POLL, "read %ld bytes", size);
1818 debug(D_SPAMC, "input \"%*.*s\"", (int)size, (int)size, iobuff);
1820 debug(D_FUNC, "::read_pipe exit");
1825 // Read all output from SpamAssassin client
1826 // and close the pipe
1829 SpamAssassin::empty_and_close_pipe()
1831 debug(D_FUNC, "::empty_and_close_pipe enter");
1834 debug(D_FUNC, "::empty_and_close_pipe exit");
1839 // {{{ Some small subroutines without much relation to functionality
1841 // output error message to syslog facility
1843 throw_error(const string& errmsg)
1846 syslog(LOG_ERR, "Thrown error: %s", errmsg.c_str());
1848 syslog(LOG_ERR, "Unknown error");
1851 /* Takes a comma or space-delimited string of debug tokens and sets the
1852 appropriate bits in flag_debug. "all" sets all the bits.
1854 void parse_debuglevel(char* string)
1858 /* make a copy so we don't overwrite argv[] */
1859 string = strdup(string);
1861 /* handle the old numeric values too */
1862 switch(atoi(string))
1865 flag_debug |= (1<<D_UORI) | (1<<D_STR);
1867 flag_debug |= (1<<D_POLL);
1869 flag_debug |= (1<<D_MISC) | (1<<D_FUNC);
1870 debug(D_ALWAYS, "Setting debug level to 0x%0x", flag_debug);
1877 while ((token = strsep(&string, ", ")))
1880 for (i=0; debugstrings[i]; i++)
1882 if(strcasecmp(token, "ALL")==0)
1884 flag_debug = (1<<D_MAX)-1;
1887 if(strcasecmp(token, debugstrings[i])==0)
1889 flag_debug |= (1<<i);
1894 if (!debugstrings[i])
1896 fprintf(stderr, "Invalid debug token \"%s\"\n", token);
1900 debug(D_ALWAYS, "Setting debug level to 0x%0x", flag_debug);
1905 Output a line to syslog using print format, but only if the appropriate
1906 debug level is set. The D_ALWAYS level is always enabled.
1908 void debug(enum debuglevel level, const char* fmt, ...)
1910 if ((1<<level) & flag_debug)
1912 #if defined(HAVE_VSYSLOG)
1915 vsyslog(LOG_ERR, fmt, vl);
1918 #if defined(HAVE_VASPRINTF)
1925 #if defined(HAVE_VASPRINTF)
1926 vasprintf(&buf, fmt, vl);
1928 #if defined(HAVE_VSNPRINTF)
1929 vsnprintf(buf, sizeof(buf)-1, fmt, vl);
1931 /* XXX possible buffer overflow here; be careful what you pass to debug() */
1932 vsprintf(buf, fmt, vl);
1936 syslog(LOG_ERR, "%s", buf);
1937 #if defined(HAVE_VASPRINTF)
1940 #endif /* vsyslog */
1944 // case-insensitive search
1946 find_nocase(const string& array, const string& pattern, string::size_type start)
1948 string::size_type pos(start);
1950 while (pos < array.size())
1952 string::size_type ctr = 0;
1954 while( (pos+ctr) < array.size() &&
1955 toupper(array[pos+ctr]) == toupper(pattern[ctr]) )
1958 if (ctr == pattern.size())
1960 debug(D_STR, "f_nc: <%s><%s>: hit", array.c_str(), pattern.c_str());
1968 debug(D_STR, "f_nc: <%s><%s>: nohit", array.c_str(), pattern.c_str());
1969 return string::npos;
1972 // compare case-insensitive
1974 cmp_nocase_partial(const string& s, const string& s2)
1976 string::const_iterator p=s.begin();
1977 string::const_iterator p2=s2.begin();
1979 while ( p != s.end() && p2 <= s2.end() ) {
1980 if (toupper(*p) != toupper(*p2))
1982 debug(D_STR, "c_nc_p: <%s><%s> : miss", s.c_str(), s2.c_str());
1983 return (toupper(*p) < toupper(*p2)) ? -1 : 1;
1989 debug(D_STR, "c_nc_p: <%s><%s> : hit", s.c_str(), s2.c_str());
1994 /* closeall() - close all FDs >= a specified value */
1995 void closeall(int fd)
1997 int fdlimit = sysconf(_SC_OPEN_MAX);
1998 while (fd < fdlimit)
2002 void parse_networklist(char *string, struct networklist *list)
2006 /* make a copy so we don't overwrite argv[] */
2007 string = strdup(string);
2009 while ((token = strsep(&string, ", ")))
2011 char *tnet = strsep(&token, "/");
2012 char *tmask = token;
2013 struct in_addr net, mask;
2015 if (list->num_nets % 10 == 0)
2016 list->nets = (struct net*)realloc(list->nets, sizeof(*list->nets) * (list->num_nets + 10));
2018 if (!inet_aton(tnet, &net))
2020 fprintf(stderr, "Could not parse \"%s\" as a network\n", tnet);
2026 if (strchr(tmask, '.') == NULL)
2031 ret = sscanf(tmask, "%u", &bits);
2032 if (ret != 1 || bits > 32)
2034 fprintf(stderr,"%s: bad CIDR value", tmask);
2037 mask.s_addr = htonl(~((1L << (32 - bits)) - 1) & 0xffffffff);
2038 } else if (!inet_aton(tmask, &mask))
2040 fprintf(stderr, "Could not parse \"%s\" as a netmask\n", tmask);
2044 mask.s_addr = 0xffffffff;
2047 char *snet = strdup(inet_ntoa(net));
2048 debug(D_MISC, "Adding %s/%s to network list", snet, inet_ntoa(mask));
2052 net.s_addr = net.s_addr & mask.s_addr;
2053 list->nets[list->num_nets].network = net;
2054 list->nets[list->num_nets].netmask = mask;
2060 int ip_in_networklist(struct in_addr ip, struct networklist *list)
2064 if (list->num_nets == 0)
2067 debug(D_NET, "Checking %s against:", inet_ntoa(ip));
2068 for (i = 0; i < list->num_nets; i++)
2070 debug(D_NET, "%s", inet_ntoa(list->nets[i].network));
2071 debug(D_NET, "/%s", inet_ntoa(list->nets[i].netmask));
2072 if ((ip.s_addr & list->nets[i].netmask.s_addr) == list->nets[i].network.s_addr)
2074 debug(D_NET, "Hit!");
2082 char *strlwr(char *str)
2093 /* Log a message about missing milter macros, but only the first time */
2094 void warnmacro(char *macro, char *scope)
2098 debug(D_ALWAYS, "Could not retrieve sendmail macro \"%s\"!. Please add it to confMILTER_MACROS_%s for better spamassassin results",
2104 untrusted-argument-safe popen function - only supports "r" and "w" modes
2105 for simplicity, and always reads stdout and stderr in "r" mode. Call
2106 fclose to close the FILE, and waitpid to reap the child process (pid).
2108 FILE *popenv(char *const argv[], const char *type, pid_t *pid)
2114 if ((*type != 'r' && *type != 'w') || type[1])
2121 switch (*pid = fork()) {
2123 case -1: /* Error. */
2125 (void)close(pdes[0]);
2126 (void)close(pdes[1]);
2130 case 0: /* Child. */
2133 * The dup2() to STDIN_FILENO is repeated to avoid
2134 * writing to pdes[1], which might corrupt the
2135 * parent's copy. This isn't good enough in
2136 * general, since the exit() is no return, so
2137 * the compiler is free to corrupt all the local
2140 (void)close(pdes[0]);
2141 (void)dup2(pdes[1], STDOUT_FILENO);
2142 (void)dup2(pdes[1], STDERR_FILENO);
2143 if (pdes[1] != STDOUT_FILENO && pdes[1] != STDERR_FILENO) {
2144 (void)close(pdes[1]);
2147 if (pdes[0] != STDIN_FILENO) {
2148 (void)dup2(pdes[0], STDIN_FILENO);
2149 (void)close(pdes[0]);
2151 (void)close(pdes[1]);
2153 execv(argv[0], argv);
2158 /* Parent; assume fdopen can't fail. */
2160 iop = fdopen(pdes[0], type);
2161 (void)close(pdes[1]);
2163 iop = fdopen(pdes[1], type);
2164 (void)close(pdes[0]);
2171 // vim6:ai:noexpandtab