"Fossies" - the Fresh Open Source Software Archive

Member "spammilt-0.5.0-rc7/spammilt.c" (22 Jun 2005, 62220 Bytes) of package /linux/privat/old/spammilt-0.5.0-rc7.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "spammilt.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2 #-------------------------------------------------------------------------------
    3 #
    4 # $Id: spammilt.c,v 1.66 2005/06/22 18:12:20 mardan Exp $ 
    5 #
    6 # $Log: spammilt.c,v $
    7 # Revision 1.66  2005/06/22 18:12:20  mardan
    8 # code cleanups
    9 # update documentation
   10 # ----------------------------------------------------------------------
   11 #
   12 # Revision 1.65  2005/06/17 13:39:31  mardan
   13 # more smtp rfc compliant (rejects and discards)
   14 #
   15 # Revision 1.64  2005/06/09 18:45:32  mardan
   16 # properfy -h text
   17 # set priv = NULL when receiving more than 1 email in same connection after
   18 #   reject or discard
   19 #
   20 # Revision 1.63  2005/06/08 22:15:18  mardan
   21 # Added min and max thresholds for dnsbl, blacklist, whitelist and SA.
   22 # Re-arranged options
   23 #
   24 # Revision 1.62  2005/05/24 19:17:06  mardan
   25 # Fixed a bug which caused the shared memory part to fail under hardened
   26 # kernels.
   27 # Added the option to specify a max dnsbl and/or blakclist hits
   28 #
   29 # Revision 1.61  2004/08/27 21:38:59  mardan
   30 # bumbed to 0.5.0-rc5
   31 #
   32 # Revision 1.60  2004/08/27 21:05:48  mardan
   33 # Updated documentation
   34 # Treat mail as spam based upon 'forged' hostname of the connecting host
   35 # Updated documentation
   36 #
   37 # Revision 1.59  2004/08/27 20:03:37  mardan
   38 # Added filtering of spam based on X-Spam-Score from the SpamAssassin package
   39 #
   40 # Revision 1.58  2004/06/13 18:27:30  mardan
   41 # Made WhiteList ruling above all
   42 #
   43 # Revision 1.57  2004/06/12 21:25:09  mardan
   44 # Changed working dir to '/var/spool/spammilt'
   45 #
   46 # Revision 1.56  2004/06/12 20:55:57  mardan
   47 # updated automake scripts
   48 #
   49 # Revision 1.55  2003/12/30 22:57:55  mardan
   50 # Changed resolve function in xfunctions.c
   51 # Updated documentation
   52 # Bumbed to version 0.5.0-rc4
   53 #
   54 # Revision 1.54  2003/12/25 22:58:14  mardan
   55 # Fixed free bugs in temporary list in mlfi structure
   56 # Some code improvements..
   57 # Bumped to version 0.5.0-rc2
   58 #
   59 # Revision 1.53  2003/12/06 21:48:08  mardan
   60 # Added 'fastmode' option (-F), it skips further checks when one check is matched positive
   61 # Code cleanups
   62 # Fixed further processing of mail, if mail was rejected and got into some kind of localloop
   63 # Bumped version to 0.5.0-rc1
   64 #
   65 # Revision 1.52  2003/12/04 10:47:35  mardan
   66 # Added /etc... and /var... directories to Makefile
   67 # Hopefully fixed crash bug in dnsbl_check
   68 #
   69 # Revision 1.51  2003/11/29 18:06:09  mardan
   70 # Updated documentation
   71 # Added discard option
   72 # Re-introduced the reserved (LAN) ip range scan, to skip dnsbl queries
   73 #
   74 # Revision 1.50  2003/11/28 21:40:51  mardan
   75 # Added ACCEPT entry in statistics
   76 # Changed scan for IP in header
   77 # Changed err_log to syslog
   78 # Lots of cleanups
   79 #
   80 # Revision 1.49  2003/11/25 18:06:32  mardan
   81 # Added the actual use of the statistics counter in shm
   82 # Made spammilt daemon only, since we have actually 2 daemons running
   83 # Removed local lan range since it was far from perfect...
   84 # Needs a lot of cleaning and testing.
   85 #
   86 # Revision 1.48  2003/11/24 22:13:43  mardan
   87 # Started to convert statistics output from file to socket. Socket is now
   88 # temporary on 2002. The shm part is initialised at start and cleaned up
   89 # at stop, but not yet updated when processing mail.
   90 #
   91 # Revision 1.47  2003/11/19 09:57:22  mardan
   92 # Didn't check if there was a DNSBL match at end of milter, DNSBL match was only
   93 # visible if other detection-method was also _true_
   94 #
   95 # Revision 1.46  2003/11/18 22:13:20  mardan
   96 # Added 'configure' scripts
   97 #
   98 # Revision 1.45  2003/11/18 21:55:29  mardan
   99 # Only adds one warning to header if multiple the same occur (dnsbl & blacklist)
  100 # Added version (-v) option, includes some extra compile information
  101 # Specifying option '-T' now adds a warning to header; applys to redirected and
  102 # normally send _spam_ email, default is changed to none
  103 # Removed '-Q' option, use the file instead
  104 # Simple filter to filterout local LAN IPs (speeds up dnsbl queries)
  105 # X-Spam-Flag is now initially changed if excists, otherwise one is added
  106 #
  107 # Revision 1.44  2003/11/17 23:06:57  mardan
  108 # Added multiple dnsbl hosts, needs testing and lots of cleanups
  109 #
  110 # Revision 1.43  2003/11/16 00:51:03  mardan
  111 # Fixed small bug which caused all mail to be rejected (4.7.1)
  112 #
  113 # Revision 1.42  2003/11/16 00:26:22  mardan
  114 # Added new files (dynarray.c and .h) for allocating dynamic arrays for
  115 # mlfi_envrcpts and mlfi_bl_spam_match (blacklist match)
  116 # Changed standard-alloc routines to the better controlled alloc routines
  117 # Started improving better logging of errors in case of program errors
  118 #
  119 # Revision 1.41  2003/11/15 12:01:09  mardan
  120 # Fixed dnsblignorelist scan loop routine to use dnsblignorelistSize
  121 #
  122 # Revision 1.40  2003/11/13 20:07:12  mardan
  123 # cleanups
  124 #
  125 # Revision 1.39  2003/11/12 22:13:11  mardan
  126 # Added fucntions to read files, allocate and free memory
  127 # Added array counters for list-arrays to properly read/free to the end.
  128 #
  129 # Revision 1.38  2003/11/09 16:10:34  mardan
  130 # Now properly removing spammilt.sock if process was brutely interrupted
  131 #
  132 # Revision 1.37  2003/11/09 02:20:05  sanne
  133 # Added: _initial_ support for multiple dnsbl servers:
  134 #   - dnsbl-hosts.list configuration file
  135 # Limitation: first entry in dnsbl-hosts.list file is used as dnsbl server, other entries are loaded but not used for now..
  136 #
  137 # Revision 1.35  2003/11/08 17:05:03  mardan
  138 # Fixed bug in smfi_connect when unresolvable (forged) host was connected
  139 #
  140 # Revision 1.34  2003/10/28 14:18:37  mardan
  141 # Some cleanups, Fixed crashbug due to a not initialized subject when changing
  142 # subject when spam was received
  143 #
  144 # Revision 1.33  2003/05/01 23:10:00  mardan
  145 # Added option to archive emails, for initializing bayesian filter
  146 #
  147 # Revision 1.32  2003/04/29 23:02:08  mardan
  148 # Fixed segfault when receiving batched email when Bayesian filter was enabled.
  149 #
  150 # Revision 1.31  2003/04/27 23:04:04  mardan
  151 # Added removing the temporary email file.
  152 #
  153 # Revision 1.30  2003/04/27 14:21:39  mardan
  154 # Added the possibility to compile with Annoyance-filter, which is a nice
  155 # Bayesian based scan-filter. (see http://www.fourmilab.ch/annoyance-filter/)
  156 # (make BAYESIAN_ANNOYANCE=1)
  157 #
  158 # Revision 1.29  2003/01/25 16:47:23  mardan
  159 # Some changes in memory allocation
  160 # Some changes in Makefile (not complet yet)
  161 # Other things I forgot.. ;-o
  162 # Pretty stablew now has been running for couple of months now
  163 #
  164 # Revision 1.28  2002/11/13 21:37:45  mardan
  165 # Cleaned up a little bit
  166 #
  167 # Revision 1.27  2002/11/12 19:47:57  mardan
  168 # Added statistics counting and statistics dump through kill signal.
  169 #
  170 # Revision 1.26  2002/10/31 21:12:28  mardan
  171 # Improved stability by changing some of the memory declarations.
  172 # And did some other things ;-)
  173 #
  174 # Revision 1.25  2002/09/20 22:47:31  mardan
  175 # Tested some stuff, cleaned up a little bit. nothing special.
  176 #
  177 # Revision 1.24  2002/09/20 18:24:04  mardan
  178 # Re-coded some vars to be dynamicly allocating memory
  179 #
  180 # Revision 1.23  2002/09/19 20:38:55  mardan
  181 # Re-coded reading files to read files only once.
  182 #
  183 # Revision 1.22  2002/09/18 20:05:38  mardan
  184 # Added autodetect spammilt-socket from sendmail.cf
  185 #
  186 # Revision 1.21  2002/09/18 19:37:05  mardan
  187 # Coded free()ing properly.
  188 #
  189 # Revision 1.20  2002/09/16 20:51:39  mardan
  190 # Added signal to reload spamlists.
  191 # Fixed small bug, which added X-header 'max unknown host reached' when spam detected by DNSBL or Blacklist, even if UNKNOWN_HOSTS wasn't reached.
  192 #
  193 # Revision 1.19  2002/09/15 00:07:34  mardan
  194 # Cleaned up some of the code.
  195 # Made some small changes in check_dnsbl to be thread safe.
  196 # Added the option to define DNSBL queryhost using a commandline parameter.
  197 #
  198 # Revision 1.18  2002/09/14 20:06:07  mardan
  199 # Value of last string in whitelist wasn't properly NULLed.
  200 #
  201 # Revision 1.17  2002/09/14 17:36:21  mardan
  202 # Merged patch file provided by Josh Corbin. Which inlcuded the whitelist
  203 # function, rewrite of the memory allocation for the black and white lists and
  204 # cleaned/ modified the Makefile.
  205 # Made some small changes to memory allocation, because of crash during shutdown.
  206 # Still need to cleanup the code.
  207 #
  208 # Revision 1.16  2002/09/14 16:03:05  mardan
  209 # Fixed a important typo. DNSBL server wasn't correctly spelled.
  210 # Minor changes to README and Makefile.
  211 #
  212 # Revision 1.15  2002/09/12 17:11:02  mardan
  213 # Some cleanups.
  214 # Added commanddline option to specify max unresolveble hosts found in header
  215 # and to the option reject them.
  216 #
  217 # Revision 1.14  2002/09/11 19:53:11  mardan
  218 # Added feature which counts the number of suspecious unresolveble hostnames
  219 # found in header and treads the email as spam if the number is too high. (>4)
  220 #
  221 # Revision 1.13  2002/09/09 21:11:20  mardan
  222 # Fixed filtering out \n \t out of header from some MTAs.
  223 #
  224 # Revision 1.12  2002/09/08 00:07:19  mardan
  225 # Fixed scan whole header, skipped last part of header when detecting strange character.
  226 # Still doen't filter out strange characters.
  227 # Added extra check on private memory -> not to be used when not initilised
  228 # when milter doen't wants to end session.
  229 #
  230 # Revision 1.11  2002/09/01 20:33:16  mardan
  231 # Changed cleanup-free routines to be more freeing.
  232 #
  233 # Revision 1.10  2002/09/01 16:08:25  mardan
  234 # Complete rewrite of spammilt due to stability problem.
  235 # Reduced number of .c files to spammilt.c and xfunctions.c
  236 # Diff between last spammilt and this version will be enormous when viewing
  237 # with cvsweb.
  238 # Added daemon mode.
  239 #
  240 #
  241 #
  242 # Copyright (c) 2002 by D.Wijsman (mardan@tweegy.demon.nl). 
  243 # All rights reserved.
  244 #
  245 # This program is free software; you can redistribute it and/or modify it
  246 # under the terms of the GNU General Public License as published by the Free
  247 # Software Foundation; either version 2 of the License, or (at your option)
  248 # any later version.
  249 # 
  250 # This program is distributed in the hope that it will be useful, but WITHOUT
  251 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  252 # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  253 # more details.
  254 #
  255 # You should have received a copy of the GNU General Public License
  256 # along with this program; see the file COPYING.  If not, write to
  257 # the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  258 #
  259 #
  260 # Credits:
  261 #
  262 # Josh Corbin <jcorbin@jdweb.com>
  263 # For writing the whitelist code, rewriting memory allocation of the black/white
  264 # lists and pointing out some other issues.
  265 #
  266 #-------------------------------------------------------------------------------
  267 */
  268 
  269 static char const rcsid[] = "$Id: spammilt.c,v 1.66 2005/06/22 18:12:20 mardan Exp $";
  270 
  271 
  272 #include "spammilt.h"
  273 
  274 struct mlfiPriv
  275     {
  276     struct _Recipients      *mlfi_envrcpts;     // temp stored reciepients
  277     struct _DNSBLListMatch  *mlfi_dnsbl_spam;   // temp stored positive DNSBL hosts & codes
  278     struct _BlackListMatch  *mlfi_bl_spam_match;    // temp stored positive Blacklisted
  279     char            *mlfi_subject;      // temp stored subject
  280     char            *mlfi_spam_match;   // Match message black/white
  281     unsigned short int      mlfi_forged;        // possible forged connecting host 
  282     int             mlfi_result;        // Black/White
  283     unsigned short int      mlfi_fastmode;      // set 1 if _spam_ detected, skip rest of checks
  284     unsigned short int      mlfi_connect_action;     // set 0=ok 1=reject 2=discard if forged/dnsbl found on connect and reject
  285     char                        *mlfi_connect_action_msg;// rejected/discraded msg e.g. 550 You deliver spam
  286     float                       mlfi_assassin_score;     // spam assassin score
  287     int                         mlfi_whitelist_count;   // whitelist count
  288     int                         mlfi_blacklist_count;   // blacklist count
  289     int                         mlfi_dnsbl_count;       // dnsbl count
  290     char            *mlfi_fname;        // filename
  291     FILE            *mlfi_fp;       // filepointer
  292     };
  293 
  294 #define MLFIPRIV    ((struct mlfiPriv *) smfi_getpriv(ctx))
  295 
  296 
  297 // statistics socket vars
  298 int s_sock;
  299 int s_list;
  300 
  301 int DNSBL_CONNECT       = 1;
  302 int FORGED_CONNECT      = 1;
  303 int WL_MIN              = 1;
  304 int SA_MIN              = 5;
  305 int SA_MAX              = 999;
  306 int BL_MIN              = 1;
  307 int BL_MAX              = 999;
  308 int DNSBL_MIN           = 1;
  309 int DNSBL_MAX           = 999;
  310 int MAX_ACTION          = 0; // ACTION_REJECT // ACTION_DISCARD
  311 int REDIRECT        = 0;
  312 int BAYESIAN        = 0;
  313 int BAYESIAN_NO_DEL = 0;
  314 int TAG_SUBJECT     = 0;
  315 int FASTMODE        = 0;
  316 int ASSASSIN        = 0;
  317 char *REDIRECT_RCPT;
  318 char *TAG_SUBJECT_TXT;
  319 char SERVER[50];
  320 char dnsblhosts_file[]  = QUERYHOSTSFILE;
  321 char blacklist_file[]   = BLACKLISTFILE;
  322 char whitelist_file[]   = WHITELISTFILE;
  323 char dnsbl_ignore_file[]= DNSBLIGNORELISTFILE;
  324 char **dnsblhosts;
  325 int dnsblhostsSize = 0;
  326 char **blacklist;
  327 int blacklistSize = 0;
  328 char **whitelist;
  329 int whitelistSize = 0;
  330 char **dnsblignorelist;
  331 int dnsblignorelistSize = 0;
  332 
  333 #ifdef BAYESIAN_ANNOYANCE
  334     char bayesian_path[] = BAYESIAN_ANNOYANCE_PATH;
  335 #endif
  336 
  337 sfsistat mlfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *addr)
  338     {
  339     char *dnsblhost;
  340     char *dnsblcode;
  341     char host_ip[16];
  342     char msg[254];
  343     char dnsbl_code[20];
  344     int dnsblignore_status = 0;
  345     int i, j;
  346     
  347     struct mlfiPriv *priv;
  348     
  349 #ifdef DEBUG
  350     syslog(LOG_DEBUG, "DEBUG: smfi_connect %s",hostname);
  351 #endif    
  352 
  353     // allocate private memory
  354     priv = calloc(1, sizeof (*priv));
  355     if (priv == NULL) {
  356     syslog(LOG_CRIT, "ERROR: Calloc %d bytes for priv failed", 1*sizeof(*priv));
  357     return SMFIS_TEMPFAIL;
  358     }
  359     memset(priv, '\0', sizeof (*priv));
  360     
  361     priv->mlfi_forged = 0;
  362     priv->mlfi_whitelist_count = 0;
  363     priv->mlfi_connect_action = 0;
  364 
  365     // filter out brackets
  366     search_replace(hostname, "[", "");
  367     search_replace(hostname, "]", "");
  368     
  369     write_shm(1);
  370     
  371     if (!lookup_ip(hostname, host_ip)) {
  372     syslog(LOG_NOTICE, "NOTICE: Hostname '%s' possibly forged", hostname);
  373     if (FORGED_CONNECT == 2) { // reject 
  374             priv->mlfi_connect_action = ACTION_REJECT;
  375             priv->mlfi_connect_action_msg = strdup("Your hostname is possibly forged");
  376     }
  377     if (FORGED_CONNECT == 3) { // discard
  378             priv->mlfi_connect_action = ACTION_DISCARD;
  379             priv->mlfi_connect_action_msg = strdup("Connected hostname is possibly forged");
  380     }
  381         // FORGED_CONNECT == 1 // set forged; process later in min threshold
  382     priv->mlfi_forged = 1;
  383         
  384     if (FASTMODE) {
  385         priv->mlfi_fastmode = 1;
  386     }
  387     if (smfi_setpriv(ctx, priv) == MI_FAILURE) {
  388         syslog(LOG_CRIT, "ERROR: Unable to set smfi_setpriv");
  389         return SMFIS_TEMPFAIL;
  390         }
  391     return SMFIS_CONTINUE;
  392     }
  393 #ifdef DEBUG
  394     syslog(LOG_DEBUG, "DEBUG: Got IP %s Host:%s", host_ip, hostname);
  395 #endif
  396     
  397     if (check_iprange(host_ip)) {
  398 #ifdef DEBUG
  399     syslog(LOG_DEBUG, "DEBUG: private IP detected, moving on...");
  400 #endif
  401     return SMFIS_CONTINUE;
  402     }
  403 
  404 #ifdef DEBUG
  405     syslog(LOG_DEBUG, "DEBUG: smfi_connect %s",host_ip);
  406 #endif
  407 
  408     // check dnsbl on connected host
  409     for (j=0; j<dnsblhostsSize; j++) {// multiple dnsbl
  410     
  411     if (check_dnsbl(host_ip, dnsblhosts[j], dnsbl_code)) {
  412     // check if dnsbl is in ignore-list
  413     for (i=0; i<dnsblignorelistSize; i++) {
  414         if (match(host_ip, dnsblignorelist[i])) {
  415         dnsblignore_status = 1; 
  416 
  417 #ifdef DEBUG
  418         syslog(LOG_DEBUG, "DEBUG: Ignoring DNSBL match for IP: %s",dnsblignorelist[i]);
  419 #endif              
  420         }
  421         }       
  422     if (!dnsblignore_status) {
  423         if (DNSBL_CONNECT == 2) { // reject
  424         if (snprintf(msg, 254, "You are DNSBL listed on %s, code(%s), your IP:%s",dnsblhosts[j],dnsbl_code,host_ip) < 0) {
  425             syslog(LOG_ERR, "ERROR: Unable to set reply message");
  426             return SMFIS_TEMPFAIL;
  427                 }
  428         priv->mlfi_connect_action = ACTION_REJECT;
  429                 priv->mlfi_connect_action_msg = xstrdup(msg);
  430         }
  431         if (DNSBL_CONNECT == 3) { //discard 
  432         if (snprintf(msg, 254, "DNSBL listed on %s, code(%s), IP:%s",dnsblhosts[j],dnsbl_code,host_ip) < 0) {
  433             syslog(LOG_ERR, "ERROR: Unable to set reply message");
  434             return SMFIS_TEMPFAIL;
  435                 }
  436         priv->mlfi_connect_action = ACTION_DISCARD;
  437                 priv->mlfi_connect_action_msg = xstrdup(msg);                
  438         }
  439         
  440         dnsblhost = xcalloc(strlen(dnsblhosts[j])+1, sizeof(char *));
  441         memcpy (dnsblhost, dnsblhosts[j], strlen(dnsblhosts[j]));
  442         dnsblhost[strlen(dnsblhosts[j])] = 0;
  443         dnsblcode = xcalloc(strlen(dnsbl_code)+1, sizeof(char *));
  444         memcpy (dnsblcode, dnsbl_code, strlen(dnsbl_code));
  445         dnsblcode[strlen(dnsbl_code)] = '\0';
  446         if (DNSBLMatch_Search(priv->mlfi_dnsbl_spam, dnsblhost, dnsblcode) == 0) 
  447         priv->mlfi_dnsbl_spam = DNSBLListMatch_Add(priv->mlfi_dnsbl_spam, dnsblhost, dnsblcode);
  448         free(dnsblhost);
  449         free(dnsblcode);
  450         dnsblignore_status = 0;
  451         if (FASTMODE) {
  452         priv->mlfi_fastmode = 1;
  453         break;
  454         }
  455     } else {
  456         write_shm(10);
  457         }
  458     }
  459     }
  460     if (smfi_setpriv(ctx, priv) == MI_FAILURE) {
  461     syslog(LOG_CRIT, "ERROR: Unable to set smfi_setpriv");
  462     return SMFIS_TEMPFAIL;
  463     }
  464     return SMFIS_CONTINUE;  
  465     }   
  466 
  467 
  468 sfsistat mlfi_envfrom(SMFICTX *ctx, char **envfrom)
  469     {
  470     return SMFIS_CONTINUE;
  471     }
  472 
  473 
  474 sfsistat mlfi_header(SMFICTX *ctx, char *headerf, char *headerv)
  475     {
  476     char *dnsblhost;
  477     char *dnsblcode;
  478     char *blmatch;
  479     char *header_buf;
  480     char dnsbl_code[20];
  481     int i, j;//, size;
  482     float assassin_score;
  483     int dnsblignore_stat = 0;
  484     
  485     struct mlfiPriv *priv = MLFIPRIV;
  486 
  487 #ifdef DEBUG
  488     syslog(LOG_DEBUG, "DEBUG: smfi_header");
  489 #endif 
  490 
  491     if (priv == NULL) {
  492     syslog(LOG_CRIT, "ERROR: Priv=NULL");
  493     return SMFIS_TEMPFAIL;  
  494     }
  495 
  496 #ifdef BAYESIAN_ANNOYANCE
  497     if (BAYESIAN) {
  498     fprintf(MLFIPRIV->mlfi_fp, "%s: %s\r\n", headerf, headerv);    
  499     }
  500 #endif
  501 
  502     if (priv && !priv->mlfi_subject && !strcasecmp(headerf,"subject")) {
  503 
  504 #ifdef DEBUG
  505         syslog(LOG_DEBUG, "DEBUG: smfi_header->set subject in private memory");
  506 #endif
  507         priv->mlfi_subject = xstrdup(headerv);
  508         if (priv->mlfi_subject == NULL) {
  509         syslog(LOG_ERR, "ERROR: Unable to set priv->mlfi_subject=%s", headerv);
  510         return SMFIS_TEMPFAIL;
  511         }
  512     }
  513     
  514     // scan for whitelisted matches in header
  515     for (j=0; j<whitelistSize; j++) {
  516     if (match(headerv, whitelist[j])) {
  517             i = priv->mlfi_whitelist_count;
  518             i++;
  519             priv->mlfi_whitelist_count = i;
  520         if (priv->mlfi_whitelist_count != i) {
  521             syslog(LOG_ERR, "ERROR: Unable to set priv->mlfi_whitelist_count=%d", i);
  522             return SMFIS_TEMPFAIL;
  523         }
  524         }
  525     }
  526     if (!priv->mlfi_fastmode) {
  527     for (i=0; i<blacklistSize; i++ ) {
  528         if (match(headerv, blacklist[i]) && !priv->mlfi_spam_match) {
  529 
  530 #ifdef DEBUG
  531         syslog(LOG_DEBUG, "DEBUG: Blacklisted match! %s",blacklist[i]);
  532 #endif
  533         blmatch = xcalloc(strlen(blacklist[i])+1, sizeof(char *));
  534         memcpy (blmatch, blacklist[i], strlen(blacklist[i]));
  535         blmatch[strlen(blacklist[i])] = 0;
  536         if (BlackListMatch_Search(priv->mlfi_bl_spam_match, blmatch) == 0) {    
  537             priv->mlfi_bl_spam_match = BlackListMatch_Add(priv->mlfi_bl_spam_match, blmatch);
  538         }
  539                 free(blmatch);
  540         if (FASTMODE) {
  541             priv->mlfi_fastmode = 1;
  542             break;
  543         }
  544         }
  545     }
  546     }
  547 
  548     // 'Received' in headerf, lets scan it
  549     if (priv && match(headerf, "received") && !priv->mlfi_fastmode) {
  550 
  551 #ifdef DEBUG
  552     syslog(LOG_DEBUG, "DEBUG: smfi_header->header->%s", headerv);
  553 #endif
  554 
  555     if ((header_buf = scan_for_ip(headerv)) != NULL) {      
  556 #ifdef DEBUG
  557         syslog(LOG_DEBUG, "DEBUG: smfi_header->header_hostname->%s", header_buf);
  558 #endif  
  559         if (!check_iprange(header_buf)) {           
  560             for (j=0; j<dnsblhostsSize; j++) { //dnsbl loop
  561             if (check_dnsbl(header_buf, dnsblhosts[j], dnsbl_code)) {
  562                 // check if IP is in dnsbl-ignore list
  563                 for (i=0; i<dnsblignorelistSize; i++) {
  564                 if (match(header_buf, dnsblignorelist[i])) {
  565                     dnsblignore_stat = 1;   
  566 
  567 #ifdef DEBUG
  568                     syslog(LOG_DEBUG, "DEBUG: Ignoring DNSBL match for IP: %s",dnsblignorelist[i]);
  569 #endif
  570                     }
  571                 }       
  572                 if (!dnsblignore_stat) {
  573                 // set DNSBL code
  574                 dnsblhost = xcalloc(strlen(dnsblhosts[j])+1, sizeof(char *));
  575                 memcpy (dnsblhost, dnsblhosts[j], strlen(dnsblhosts[j]));
  576                 dnsblhost[strlen(dnsblhosts[j])] = 0;
  577                 dnsblcode = xcalloc(strlen(dnsbl_code)+1, sizeof(char *));
  578                 memcpy (dnsblcode, dnsbl_code, strlen(dnsbl_code));
  579                 dnsblcode[strlen(dnsbl_code)] = '\0';
  580                 if (DNSBLMatch_Search(priv->mlfi_dnsbl_spam, dnsblhost, dnsblcode) == 0) 
  581                     priv->mlfi_dnsbl_spam = DNSBLListMatch_Add(priv->mlfi_dnsbl_spam, dnsblhost, dnsblcode);
  582                 free(dnsblhost);
  583                 free(dnsblcode);
  584                 if (FASTMODE) {
  585                     priv->mlfi_fastmode = 1;
  586                     break;
  587                     }
  588                 } 
  589                 else { 
  590                 write_shm(10);
  591                 }
  592                 }
  593             }// end-bracket for dnsbl loop
  594         } // end-bracket check local ip-ranges
  595         free(header_buf);
  596         }
  597     }
  598     // scan for possible spam assassin score
  599     if (priv && !strcasecmp(headerf,"X-Spam-Score")) {
  600         assassin_score = get_assassin_score(headerv);
  601         if (priv->mlfi_assassin_score < assassin_score) {
  602             priv->mlfi_assassin_score = assassin_score;
  603         } 
  604     }
  605     
  606     if (smfi_setpriv(ctx, priv) == MI_FAILURE) {
  607     syslog(LOG_CRIT, "ERROR: Unable to set smfi_setpriv");
  608     return SMFIS_TEMPFAIL;
  609     }
  610     return SMFIS_CONTINUE;
  611     }
  612 
  613 
  614 sfsistat mlfi_eoh(SMFICTX *ctx)
  615     {
  616 
  617 #ifdef DEBUG
  618     syslog(LOG_DEBUG, "DEBUG: smfi_eoh");
  619 #endif 
  620 
  621 #ifdef BAYESIAN_ANNOYANCE    
  622     if (BAYESIAN) {
  623     fprintf(MLFIPRIV->mlfi_fp, "\r\n");
  624     }
  625 #endif
  626     
  627     return SMFIS_CONTINUE;
  628     }
  629 
  630 sfsistat mlfi_body(SMFICTX *ctx, u_char *bodyp, size_t bodylen)
  631     {
  632 
  633 #ifdef DEBUG
  634     syslog(LOG_DEBUG, "DEBUG: smfi_body");
  635 #endif 
  636 
  637 #ifdef BAYESIAN_ANNOYANCE    
  638     if (BAYESIAN) {
  639     if (fwrite(bodyp, bodylen, 1, MLFIPRIV->mlfi_fp) <= 0) {
  640         syslog(LOG_ERR, "ERROR: Unable to write email to file; '%s'", bodyp);
  641         (void) mlfi_cleanup(ctx, false);
  642         return SMFIS_TEMPFAIL;
  643         }    
  644     }
  645 #endif
  646 
  647     return SMFIS_CONTINUE;
  648     }
  649 
  650 
  651 sfsistat mlfi_eom(SMFICTX *ctx)
  652     {
  653 #ifdef DEBUG
  654     syslog(LOG_DEBUG, "DEBUG: smfi_eom");
  655 #endif 
  656 
  657     return mlfi_cleanup(ctx, true);
  658     }
  659 
  660 
  661 sfsistat mlfi_close(SMFICTX *ctx)
  662     {
  663     struct mlfiPriv *priv = MLFIPRIV;
  664 
  665 #ifdef DEBUG
  666     syslog(LOG_DEBUG, "DEBUG: smfi_close");
  667 #endif 
  668     
  669     if (priv) {
  670     (void) mlfi_cleanup(ctx, false);
  671     }
  672     
  673     return SMFIS_ACCEPT;
  674     }
  675 
  676 
  677 sfsistat mlfi_abort(SMFICTX *ctx)
  678     {
  679 
  680 #ifdef DEBUG
  681     syslog(LOG_DEBUG, "DEBUG: smfi_abort");
  682 #endif 
  683     
  684     return mlfi_cleanup(ctx, false);
  685     }
  686 
  687 
  688 sfsistat mlfi_envrcpt(SMFICTX *ctx, char **argv)
  689     {
  690     struct mlfiPriv *priv = MLFIPRIV;
  691     int argc = 0;
  692     int count;
  693     char *rcpts;
  694     char *rcptaddr;
  695 #ifdef BAYESIAN_ANNOYANCE
  696     int fd = -1;
  697 #endif
  698     
  699 #ifdef DEBUG
  700     syslog(LOG_DEBUG, "DEBUG: smfi_envrcpt");
  701 #endif
  702 
  703     if (priv == NULL) {
  704         syslog(LOG_NOTICE, "NOTICE: priv = NULL; trying to initialise");
  705     // allocate private memory
  706     priv = xcalloc(1,sizeof (*priv));
  707     if (priv == NULL) {
  708         syslog(LOG_CRIT, "ERROR: Priv = NULL");
  709         return SMFIS_TEMPFAIL;
  710         }
  711     memset(priv, '\0', sizeof (*priv));
  712     
  713     if ((priv->mlfi_forged = 0) != 0) {
  714         syslog(LOG_CRIT, "ERROR: Unable to set smfi_forged");
  715         return SMFIS_TEMPFAIL;    
  716     }
  717         if ((priv->mlfi_whitelist_count = 0) != 0) {
  718         syslog(LOG_CRIT, "ERROR: Unable to set smfi_whitelist_count");
  719         return SMFIS_TEMPFAIL;            
  720         }
  721         if ((priv->mlfi_connect_action = 0) != 0) {
  722         syslog(LOG_CRIT, "ERROR: Unable to set smfi_rejected");
  723         return SMFIS_TEMPFAIL;            
  724         }
  725     }
  726     
  727     rcptaddr = smfi_getsymval(ctx, "{rcpt_addr}");
  728     count = 0;
  729     while(*argv++) ++argc;
  730     {
  731 
  732 #ifdef DEBUG
  733     syslog(LOG_DEBUG, "DEBUG: smfi_envrcpt->add recipient: %s",rcptaddr);
  734 #endif  
  735     
  736     rcpts = xcalloc(strlen(rcptaddr) + 1, sizeof(char *)); 
  737     memcpy (rcpts, rcptaddr, strlen(rcptaddr));
  738     rcpts[strlen(rcptaddr)] = 0;
  739     priv->mlfi_envrcpts = Recipient_Add(priv->mlfi_envrcpts, rcpts);
  740     free(rcpts);
  741     }
  742 
  743 #ifdef BAYESIAN_ANNOYANCE
  744     // if we've Bayesian filter defined, save the email to file
  745     if (BAYESIAN && (priv->mlfi_fname == NULL) && (priv->mlfi_fp == NULL)) {
  746 #ifdef DEBUG
  747     syslog(LOG_DEBUG, "DEBUG: smfi_envrcpt->Setting temporary filename", "");
  748 #endif
  749     if ((priv->mlfi_fname = strdup("/var/spool/spammilt/msg.XXXXXXXX")) == NULL) {
  750         syslog(LOG_CRIT, "ERROR: Unable to set temporary filename in priv->mlfi_fname");
  751         free(priv);
  752         return SMFIS_TEMPFAIL;
  753         }
  754 #ifdef DEBUG
  755     syslog(LOG_DEBUG, "DEBUG: smfi_envrcpt->Creating temporary file", "");
  756 #endif
  757     if ((fd = mkstemp(priv->mlfi_fname)) < 0 || (priv->mlfi_fp = fdopen(fd, "w+")) == NULL) {
  758         if (fd >= 0)
  759         (void) close(fd);
  760         syslog(LOG_CRIT, "ERROR: Unable to create file; %s", priv->mlfi_fname);
  761         free(priv->mlfi_fname);
  762         free(priv);
  763         return SMFIS_TEMPFAIL;
  764         }
  765     }    
  766 #endif
  767     //
  768     if ((priv->mlfi_assassin_score = 0) != 0) {
  769     syslog(LOG_CRIT, "ERROR: Unable to set smfi_assassin_score");
  770     return SMFIS_TEMPFAIL;    
  771     }
  772     if (smfi_setpriv(ctx, priv) == MI_FAILURE) {
  773     syslog(LOG_CRIT, "ERROR: Unable to set smfi_setpriv");
  774     return SMFIS_TEMPFAIL;
  775     }
  776     return SMFIS_CONTINUE;
  777     }
  778 
  779 
  780 
  781 sfsistat mlfi_cleanup(SMFICTX *ctx, bool ok)
  782     {
  783     sfsistat rstat = SMFIS_CONTINUE;
  784     struct mlfiPriv *priv = MLFIPRIV;
  785     char hbuf[1024];
  786     char subject[1024];
  787     char *buffer;
  788     char *dnsbl_host;
  789     char *dnsbl_code;
  790     int bayesian_pos;
  791 #ifdef BAYESIAN_ANNOYANCE
  792     char *cmd_options[12];
  793     int pid, status; 
  794     char path2dictionary[200];
  795     char path2bin[200];
  796 #endif
  797 
  798 #ifdef DEBUG
  799     syslog(LOG_DEBUG, "DEBUG: smfi_cleanup");
  800 #endif 
  801     
  802 #ifdef BAYESIAN_ANNOYANCE
  803     snprintf(path2bin, sizeof(path2bin), "%s/annoyance-filter", bayesian_path);
  804     snprintf(path2dictionary, sizeof(path2dictionary), "%s/fdict.bin", bayesian_path);
  805     cmd_options[0] = "";
  806     cmd_options[1] = "--fread";
  807     cmd_options[2] = path2dictionary;
  808     cmd_options[3] = "--classify";
  809     cmd_options[4] = priv->mlfi_fname;
  810     cmd_options[5] = NULL;
  811 #endif
  812 
  813     bayesian_pos = 0;
  814     
  815     if (priv == NULL) {
  816         syslog(LOG_CRIT, "ERROR: Priv = NULL");
  817     rstat = SMFIS_TEMPFAIL;
  818         return rstat;
  819         }
  820     
  821     
  822 #ifdef BAYESIAN_ANNOYANCE
  823     if (BAYESIAN && ok) {
  824     if (priv->mlfi_fp != NULL && fclose(priv->mlfi_fp) == EOF) {
  825         syslog(LOG_CRIT, "ERROR: Unable to properly close file; %s", priv->mlfi_fname);
  826         rstat = SMFIS_TEMPFAIL;
  827         (void) unlink(priv->mlfi_fname);
  828         return rstat;
  829         }
  830     }
  831 #endif
  832 
  833     if (ok)
  834     {
  835     write_shm(2);// ++ processed messages       
  836         // reject and set reply when message should be rejected besed on connect
  837         if (priv->mlfi_connect_action == ACTION_REJECT) {
  838         if (smfi_setreply(ctx, "550", "5.7.1", priv->mlfi_connect_action_msg) == MI_FAILURE) {
  839             syslog(LOG_ERR, "ERROR: Unable to set smfi_setreply=%s", priv->mlfi_connect_action_msg);
  840             return SMFIS_TEMPFAIL;
  841         }
  842         syslog(LOG_NOTICE, "NOTICE: Rejected message; %s", priv->mlfi_connect_action_msg);
  843         write_shm(3);
  844             (void) mlfi_cleanup(ctx, false);
  845             return SMFIS_REJECT;
  846         }
  847         // discard message besed on connect
  848         if (priv->mlfi_connect_action == ACTION_DISCARD) {
  849         syslog(LOG_NOTICE, "NOTICE: Discarded message; %s", priv->mlfi_connect_action_msg);
  850         write_shm(4);
  851             (void) mlfi_cleanup(ctx, false);
  852             return SMFIS_DISCARD;
  853         }
  854 #ifdef BAYESIAN_ANNOYANCE
  855         if (BAYESIAN) {
  856 #ifdef DEBUG
  857         syslog(LOG_DEBUG, "DEBUG: smfi_cleanup->bayesian", "");
  858 #endif      
  859         pid = fork();
  860         if (pid == -1) {
  861         syslog(LOG_CRIT, "ERROR: Unable to fork Bayesian filter");
  862         rstat = SMFIS_TEMPFAIL;
  863         return rstat;
  864         }   
  865         if (pid == 0) {
  866         execv(path2bin, cmd_options);
  867         exit(127);
  868         }
  869         waitpid(pid, &status, 0);
  870         if (WIFEXITED(status)) {
  871         if (WEXITSTATUS(status) == 127) {
  872             syslog(LOG_CRIT, "ERROR: Execution of Bayesian filter failed");
  873             rstat = SMFIS_TEMPFAIL;
  874             return rstat;
  875             }
  876         if (WEXITSTATUS(status) != 0 && WEXITSTATUS(status) != 3) {
  877             syslog(LOG_CRIT, "ERROR: Unknown error by Bayesian filter");
  878             rstat = SMFIS_TEMPFAIL;
  879             return rstat;
  880             }
  881         if (WEXITSTATUS(status) == 0) {
  882 #ifdef DEBUG
  883             syslog(LOG_DEBUG, "DEBUG: Bayesian scan: is it mail?", "");
  884 #endif
  885             }
  886         if (WEXITSTATUS(status) == 3) {
  887 #ifdef DEBUG
  888             syslog(LOG_DEBUG, "DEBUG: Bayesian scan: is it spam?", "");
  889 #endif
  890             bayesian_pos = 1;
  891             }
  892         } else {
  893         syslog(LOG_CRIT, "ERROR: Bayesian filter returned in unknown state");
  894         rstat = SMFIS_TEMPFAIL;
  895         return rstat;
  896         }
  897         }
  898 #endif      
  899         //
  900         syslog(LOG_ERR, "NOTICE: SA[%f] BL[%d] WL[%d] DNSBL[%d]", priv->mlfi_assassin_score, 
  901             BlackListMatch_Count(priv->mlfi_bl_spam_match), priv->mlfi_whitelist_count, DNSBLListMatch_Count(priv->mlfi_dnsbl_spam));
  902     // check if threshold are over max 
  903     if ((DNSBLListMatch_Count(priv->mlfi_dnsbl_spam) >= DNSBL_MAX || 
  904                 BlackListMatch_Count(priv->mlfi_bl_spam_match) >= BL_MAX || 
  905                 priv->mlfi_assassin_score >= SA_MAX) &&
  906                 priv->mlfi_whitelist_count < WL_MIN) {
  907             
  908             syslog(LOG_ERR, "Spam max thresholds exceeded!");
  909         if (MAX_ACTION == ACTION_DISCARD) {
  910 #ifdef DEBUG
  911         syslog(LOG_DEBUG, "DEBUG: Messsage discarded");
  912 #endif      
  913         write_shm(4);
  914                 (void) mlfi_cleanup(ctx, false);
  915         return SMFIS_DISCARD;
  916         }
  917             if (MAX_ACTION == ACTION_REJECT) {
  918             if (DNSBLListMatch_Count(priv->mlfi_dnsbl_spam) >= DNSBL_MAX) {
  919             if (smfi_setreply(ctx, "550", "5.7.1", "Too many blacklisted hosts found in header, rejecting...") == MI_FAILURE) {
  920                 syslog(LOG_ERR, "ERROR: Unable to set smfi_setreply in reject");
  921                 return SMFIS_TEMPFAIL;
  922             }
  923             write_shm(3);
  924                     (void) mlfi_cleanup(ctx, false);
  925             return SMFIS_REJECT;
  926             }
  927             if (BlackListMatch_Count(priv->mlfi_bl_spam_match) >= BL_MAX) {
  928             if (smfi_setreply(ctx, "550", "5.7.1", "Too many blacklisted parts found in header, rejecting...") == MI_FAILURE) {
  929                 syslog(LOG_ERR, "ERROR: Unable to set smfi_setreply in reject");
  930                 return SMFIS_TEMPFAIL;
  931             }
  932             write_shm(3);
  933                     (void) mlfi_cleanup(ctx, false);
  934             return SMFIS_REJECT;
  935                 }
  936             if (priv->mlfi_assassin_score >= SA_MAX) {
  937             if (smfi_setreply(ctx, "550", "5.7.1", "SpamAssassin highscore threshold exceeded, rejecting...") == MI_FAILURE) {
  938                 syslog(LOG_ERR, "ERROR: Unable to set smfi_setreply in reject");
  939                 return SMFIS_TEMPFAIL;
  940             }
  941             write_shm(3);
  942                     (void) mlfi_cleanup(ctx, false);
  943             return SMFIS_REJECT;
  944                 }
  945             }
  946         }
  947         // add extra info header
  948         if (snprintf(hbuf, sizeof(hbuf), "spammilt-%s (%s)",VERSION,SERVER) < 0)
  949         return SMFIS_TEMPFAIL;
  950         if (smfi_addheader(ctx, "X-Spam-Scanner", hbuf) == MI_FAILURE) {
  951         syslog(LOG_CRIT, "ERROR: Unable to set smfi_addheader=%s", hbuf);
  952         return SMFIS_TEMPFAIL;
  953         }
  954         // process spammail if it not rejected or discarded
  955     if ((DNSBLListMatch_Count(priv->mlfi_dnsbl_spam) >= DNSBL_MIN || 
  956                 BlackListMatch_Count(priv->mlfi_bl_spam_match) >= BL_MIN || 
  957                 priv->mlfi_assassin_score >= SA_MIN || 
  958                 priv->mlfi_forged == 1) &&
  959                 priv->mlfi_whitelist_count < WL_MIN) { 
  960             // redirect 
  961         if (REDIRECT) {
  962         // remove all original recipients and redirect to spam account
  963         while (priv->mlfi_envrcpts)
  964             {
  965             buffer = Recipient_Pop(&priv->mlfi_envrcpts);
  966 #ifdef DEBUG
  967             syslog(LOG_DEBUG, "DEBUG: smfi_cleanup->remove_recipient %s", buffer);
  968 #endif
  969             if (smfi_delrcpt(ctx, buffer) == MI_FAILURE) {
  970             syslog(LOG_CRIT, "ERROR: Unable to set smfi_delrcpt=%s", buffer);
  971             free(buffer);
  972             return SMFIS_TEMPFAIL; 
  973             }
  974             free(buffer);
  975             }
  976 
  977 #ifdef DEBUG
  978         syslog(LOG_DEBUG, "DEBUG: smfi_cleanup->add_recipient %s", REDIRECT_RCPT);
  979 #endif
  980         if (smfi_addrcpt(ctx, REDIRECT_RCPT) == MI_FAILURE) {
  981             syslog(LOG_CRIT, "ERROR: Unable to set smfi_addrcpt=%s", REDIRECT_RCPT);
  982             return SMFIS_TEMPFAIL;
  983             }
  984         write_shm(5);
  985             }
  986         // mark as spam and send as normal
  987             // change subject
  988         if (TAG_SUBJECT) {
  989             if (!priv->mlfi_subject) {
  990             strncpy(hbuf, "", 1);
  991             strncat(hbuf, "", 1);
  992             } else {
  993             strncpy(hbuf, priv->mlfi_subject, 1023);
  994             strncat(hbuf, "", 1);
  995             }
  996             if (snprintf(subject, sizeof(subject), "%s %s", TAG_SUBJECT_TXT, hbuf) < 0)
  997                 return SMFIS_TEMPFAIL;
  998         if (smfi_chgheader(ctx, "Subject", 1, subject) == MI_FAILURE) {
  999             if (smfi_addheader(ctx, "Subject", subject) == MI_FAILURE) {
 1000             syslog(LOG_CRIT, "ERROR: Unable to set smfi_chgheader=%s", subject);
 1001             return SMFIS_TEMPFAIL;
 1002             }
 1003         }
 1004         }
 1005             // set X-Spam-Flag header to YES
 1006         if (smfi_chgheader(ctx, "X-Spam-Flag", 1, "YES") == MI_FAILURE) {
 1007         if (smfi_addheader(ctx, "X-Spam-Flag", "YES") == MI_FAILURE) {
 1008             syslog(LOG_CRIT, "ERROR: Unable to set smfi_addheader");
 1009             return SMFIS_TEMPFAIL;
 1010         }
 1011         }
 1012         // add DNSBL listed hosts to header
 1013         if (priv->mlfi_dnsbl_spam) {
 1014         while (priv->mlfi_dnsbl_spam)
 1015         {
 1016             buffer = DNSBLListMatch_Pop(&priv->mlfi_dnsbl_spam, &dnsbl_host, &dnsbl_code);
 1017             if (snprintf(hbuf, sizeof(hbuf), "DNSBL match at %s (%s)", dnsbl_host, dnsbl_code) < 0)
 1018             return SMFIS_TEMPFAIL;
 1019             if (smfi_addheader(ctx, "X-Spam-Result", hbuf) == MI_FAILURE) {
 1020             syslog(LOG_CRIT, "ERROR: Unable to set smfi_addheader; %s", hbuf);
 1021             return SMFIS_TEMPFAIL;
 1022             }
 1023             free(buffer);
 1024             free(dnsbl_code);
 1025             free(dnsbl_host);
 1026         }
 1027         write_shm(9);
 1028         }
 1029         
 1030         // add Blacklisted matches to header
 1031         if (priv->mlfi_bl_spam_match != NULL) {
 1032         while (priv->mlfi_bl_spam_match)    
 1033         {
 1034             buffer = BlackListMatch_Pop(&priv->mlfi_bl_spam_match);
 1035             if (snprintf(hbuf, sizeof(hbuf), "Blacklist match by %s (match: %s)",SERVER,buffer) < 0)
 1036             return SMFIS_TEMPFAIL;
 1037             if (smfi_addheader(ctx, "X-Spam-Result", hbuf) == MI_FAILURE) {
 1038             syslog(LOG_CRIT, "ERROR: Unable to set smfi_addheader; %s", hbuf);
 1039             return SMFIS_TEMPFAIL;
 1040             }
 1041                 free(buffer);
 1042         }
 1043         write_shm(7);
 1044         }
 1045             
 1046             // Spam Assassin Score detected > SA_MIN
 1047             if (priv->mlfi_assassin_score >= SA_MIN) {
 1048         if (smfi_addheader(ctx, "X-Spam-Result", "SpamAssassin highscore detected") == MI_FAILURE) {
 1049             syslog(LOG_CRIT, "ERROR: Unable to set smfi_addheader; %s", "SpamAssassin highscore detected");
 1050             return SMFIS_TEMPFAIL;
 1051         }
 1052                 write_shm(13);
 1053             }
 1054             
 1055         // no reverse lookup of connecting host
 1056         if (priv->mlfi_forged) {
 1057         if (smfi_addheader(ctx, "X-Spam-Result", "Connecting hostname possibly forged") == MI_FAILURE) {
 1058             syslog(LOG_CRIT, "ERROR: Unable to set smfi_addheader; %s", "Connecting hostname possibly forged");
 1059             return SMFIS_TEMPFAIL;
 1060         }       
 1061         write_shm(8);   
 1062         }
 1063                         
 1064 #ifdef BAYESIAN_ANNOYANCE
 1065         // Bayesian scan found to be positive
 1066         if (bayesian_pos) {
 1067         if (snprintf(hbuf, sizeof(hbuf), "Bayesian scan by 'Annoyance-filter' got a positive result") < 0)
 1068             return SMFIS_TEMPFAIL;
 1069         if (smfi_addheader(ctx, "X-Spam-Result", hbuf) == MI_FAILURE) {
 1070             syslog(LOG_CRIT, "ERROR: Unable to set smfi_addheader; %s", hbuf);
 1071             return SMFIS_TEMPFAIL;
 1072             }
 1073         write_shm(11);
 1074         }
 1075 #endif
 1076     } else {
 1077         if (smfi_chgheader(ctx, "X-Spam-Flag", 1 , "NO") == MI_FAILURE) {
 1078         if (smfi_addheader(ctx, "X-Spam-Flag", "NO") == MI_FAILURE) {
 1079             syslog(LOG_CRIT, "ERROR: Unable to set smfi_addheader; %s", hbuf);
 1080             return SMFIS_TEMPFAIL;
 1081         }
 1082         }
 1083         
 1084             // Whitelisted match
 1085         if (priv->mlfi_whitelist_count >= WL_MIN) {
 1086         // hmm the whitelist-match could be send to the world...
 1087         if (snprintf(hbuf, sizeof(hbuf), "Whitelist match by %s (match: ***)",SERVER) < 0)
 1088             return SMFIS_TEMPFAIL;
 1089         if (smfi_addheader(ctx, "X-Spam-Result", hbuf) == MI_FAILURE) {
 1090             syslog(LOG_CRIT, "ERROR: Unable to set smfi_addheader; %s", hbuf);
 1091             return SMFIS_TEMPFAIL;
 1092         }
 1093         write_shm(6);
 1094         }
 1095         write_shm(12);
 1096     }
 1097     }
 1098 
 1099 #ifdef BAYESIAN_ANNOYANCE
 1100     if (BAYESIAN) {
 1101     if (priv->mlfi_fname != NULL && !BAYESIAN_NO_DEL) {
 1102         if (unlink(priv->mlfi_fname)) {
 1103         syslog(LOG_ERR, "ERROR: Unable to remove '%s'", priv->mlfi_fname);
 1104         }
 1105         }
 1106     }
 1107 #endif
 1108 
 1109     // free private memory
 1110 #ifdef DEBUG
 1111     syslog(LOG_DEBUG, "DEBUG: smfi_cleanup->free private memory");  
 1112 #endif    
 1113 
 1114     if (priv->mlfi_dnsbl_spam) {
 1115 #ifdef DEBUG
 1116     syslog(LOG_DEBUG, "DEBUG: freeing dnsbl_spam");
 1117 #endif
 1118     while (priv->mlfi_dnsbl_spam)
 1119     {
 1120         DNSBLListMatch_Pop(&priv->mlfi_dnsbl_spam, &dnsbl_host, &dnsbl_code);
 1121         free(dnsbl_code);
 1122         free(dnsbl_host);
 1123     }
 1124     priv->mlfi_dnsbl_spam = NULL;
 1125     }
 1126     
 1127     if (priv->mlfi_bl_spam_match) {
 1128 #ifdef DEBUG
 1129     syslog(LOG_DEBUG, "DEBUG: freeing mlfi_bl_spam_match");
 1130 #endif
 1131     while (priv->mlfi_bl_spam_match)
 1132     {
 1133         BlackListMatch_Pop(&priv->mlfi_bl_spam_match);
 1134     }
 1135     priv->mlfi_bl_spam_match = NULL;
 1136     }
 1137     if (priv->mlfi_spam_match) {
 1138 #ifdef DEBUG
 1139     syslog(LOG_DEBUG, "DEBUG: freeing spam_match");
 1140 #endif
 1141     free(priv->mlfi_spam_match);
 1142     priv->mlfi_spam_match = NULL;
 1143     }
 1144     if (priv->mlfi_subject) {
 1145 #ifdef DEBUG
 1146     syslog(LOG_DEBUG, "DEBUG: freeing subject");
 1147 #endif
 1148     free(priv->mlfi_subject);
 1149     priv->mlfi_subject = NULL;
 1150     }
 1151     if (priv->mlfi_envrcpts) {
 1152 #ifdef DEBUG
 1153     syslog(LOG_DEBUG, "DEBUG: freeing envrcpt");
 1154 #endif
 1155     while (priv->mlfi_envrcpts)
 1156     {
 1157         Recipient_Pop(&priv->mlfi_envrcpts);
 1158     }
 1159     priv->mlfi_envrcpts = NULL;
 1160     }
 1161 
 1162     if (priv->mlfi_fname) {
 1163 #ifdef DEBUG
 1164     syslog(LOG_DEBUG, "DEBUG: freeing fname");
 1165 #endif
 1166     free(priv->mlfi_fname);
 1167     priv->mlfi_fname = NULL;
 1168     }
 1169     
 1170     if (priv->mlfi_connect_action_msg) {
 1171 #ifdef DEBUG
 1172     syslog(LOG_DEBUG, "DEBUG: freeing rejected_msg");
 1173 #endif
 1174     free(priv->mlfi_connect_action_msg);
 1175     priv->mlfi_fname = NULL;
 1176     }
 1177 
 1178     if (priv) {
 1179 #ifdef DEBUG
 1180     syslog(LOG_DEBUG, "DEBUG: freeing priv");
 1181 #endif
 1182     free(priv);
 1183     priv = NULL;
 1184     }
 1185     if (smfi_setpriv(ctx, NULL) == MI_FAILURE) {
 1186     rstat = SMFIS_TEMPFAIL;
 1187     syslog(LOG_CRIT, "ERROR: Unable to set smfi_setpriv = NULL");
 1188     return rstat;
 1189     }
 1190     return rstat;
 1191 }
 1192 
 1193 void dump_settings()
 1194 {
 1195     syslog(LOG_INFO, "\n");
 1196     syslog(LOG_INFO, "max threshold exceeded action: %d\n", MAX_ACTION);
 1197     syslog(LOG_INFO, "thresholds (dnsbl): %d %d\n", DNSBL_MIN, DNSBL_MAX);
 1198     syslog(LOG_INFO, "thresholds (bl): %d %d\n", BL_MIN, BL_MAX);
 1199     syslog(LOG_INFO, "threshold (wl): %d\n", WL_MIN);
 1200     syslog(LOG_INFO, "thresholds (SA): %d %d\n", SA_MIN, SA_MAX);
 1201 }
 1202 
 1203 void usage()
 1204 {
 1205     printf("options: -h :this help\n");
 1206     printf("         -v :version\n");
 1207     printf("\n         * file locations:\n");
 1208     printf("         -b <arg> :blacklistmatch-file (default: /etc/mail/spam/black.list)\n");
 1209     printf("         -w <arg> :whitelistmatch-file (default: /etc/mail/spam/white.list)\n");
 1210     printf("         -D <arg> :dnsbl queryhosts-file (default: /etc/mail/spam/dnsbl-hosts.list)\n");
 1211     printf("         -I <arg> :dnsbl postives ignore list (default: /etc/mail/spam/dnsbl-ignore.list)\n");
 1212     printf("\n         * min threshold actions:\n");
 1213     printf("         (note: always adds 'X-Spam-Flag' and 'X-Spam-Result')\n");
 1214     printf("         -s <arg> :redirect _spam_ to recipient <arg>\n");
 1215     printf("         -T <arg> :rewrite subject on _spam_ detection\n");
 1216     printf("\n         * max threshold actions:\n");
 1217     printf("         -a <arg>: takes 'discard' or 'reject' as argument (if not set min. threshold action is used)\n");
 1218     printf("\n         * thresholds:\n");
 1219     printf("         -A <min> <max> :enable SpamAssassin highscore detection with thresholds\n");
 1220     printf("         -M <min> <max> :set DNSBL thresholds (default <%d> <%d>)\n", DNSBL_MIN, DNSBL_MAX);
 1221     printf("         -m <min> <max> :set blacklist thresholds (default <%d> <%d>)\n", BL_MIN, BL_MAX);
 1222     printf("         -W <min> :set whitelist threshold (default <%d>)\n", WL_MIN);
 1223     printf("         -c <arg> :DNSBL connecting host: -cc count DNSBL for threshold (default),\n");
 1224     printf("                   -cr reject, -cd discard\n"); 
 1225     printf("         -f <arg> :Forged connecting host: -fw action as in min. threshold (default),\n");
 1226     printf("                   -fr reject, -fd discard\n");
 1227 #ifdef BAYESIAN_ANNOYANCE
 1228     printf("         -x :enable Annoyance-filter\n");
 1229     printf("         -X <arg> :set /path-to-annoyancedir  (default: /usr/local/annoyance-filter)\n");
 1230     printf("         -Y :archive bayesian scanned mail\n");
 1231 #endif
 1232     printf("\n         * generic settings:\n"); 
 1233     printf("         -H <arg> :set MTA servername, otherwise autodetect\n");
 1234     printf("         -F :fast-mode: don't scan rest of email if positive indication of _spam_ is found\n");
 1235     printf("\n         * milter settings:\n");
 1236     printf("         -t <arg> :milter socket timeout (in seconds)]\n");
 1237     printf("         -p <arg> :socket-address (default: autodetect or local:/var/spool/spammilt/spammilt.sock)\n");
 1238     printf("\n         * signals:\n");
 1239     printf("         killall -USR1 spammilt :reloads lists from file\n");
 1240     exit(0);
 1241 }
 1242 
 1243 
 1244 void version()
 1245 {
 1246     printf("spammilt v%s; ", VERSION);
 1247 #ifdef DEBUG
 1248     printf(" debug-version; ");
 1249 #endif
 1250 #ifdef BAYESIAN_ANNOYANCE
 1251     printf(" Bayesian-Annoyance support; ");
 1252 #endif    
 1253     printf("\n");
 1254 }
 1255 
 1256 
 1257 struct smfiDesc smfilter =
 1258     {
 1259     "spammilt",     /* filter name */
 1260     SMFI_VERSION,   /* version code -- do not change */
 1261     SMFIF_ADDHDRS |
 1262     SMFIF_CHGHDRS |
 1263     SMFIF_ADDRCPT |
 1264     SMFIF_DELRCPT,  /* flags */
 1265     mlfi_connect,   /* connection info filter */
 1266     NULL,       /* SMTP HELO command filter */
 1267     //mlfi_envfrom, /* envelope sender filter */
 1268     NULL,
 1269     mlfi_envrcpt,   /* envelope recipient filter */
 1270     mlfi_header,    /* header filter */
 1271     mlfi_eoh,       /* end of header */
 1272     mlfi_body,      /* body block filter */
 1273     mlfi_eom,       /* end of message */
 1274     mlfi_abort,     /* message aborted */
 1275     mlfi_close      /* connection cleanup */
 1276     };
 1277 
 1278 
 1279 
 1280 int main(int argc, char *argv[])
 1281     {
 1282     int c;
 1283     int daemon = 1;
 1284     char *filebuf;
 1285     char *tmparg;
 1286 #ifdef BAYESIAN_ANNOYANCE
 1287     char buffer[256];
 1288 #endif
 1289     char cf[] = "/etc/mail/sendmail.cf";
 1290     char socket[] = "local:/var/spool/spammilt/spammilt.sock";
 1291     const char *args = "hvp:c:f:W:F:A:s:b:w:D:I:H:M:m:t:a:xX:YT:";
 1292     FILE *f;
 1293     
 1294     struct passwd *uidgid;
 1295     struct utsname hostinfo;
 1296 #ifdef BAYESIAN_ANNOYANCE
 1297     struct stat buf;
 1298 #endif
 1299     
 1300     // set uid & gui of ssmsp user/group for security reasons
 1301     if ((uidgid = getpwnam("smmsp")) == NULL) {
 1302     printf("Unable to determine uid and gid of user 'smmsp'\n");
 1303     exit(1);
 1304     }
 1305     if ((setgid(uidgid->pw_gid) != 0) || (setuid(uidgid->pw_uid) != 0)) {
 1306     printf("Must be started as root\n");
 1307     exit(1);
 1308     }
 1309     
 1310     // open syslog
 1311     openlog ("spammiltd", LOG_CONS|LOG_PID|LOG_NDELAY, LOG_MAIL);
 1312 
 1313     // get server hostname
 1314     if (uname(&hostinfo) < 0) {
 1315     strcpy(SERVER, "smtp.mydomain.org");
 1316     syslog(LOG_ERR, "ERROR: Could not determine servername, using %s", SERVER);
 1317     } else {
 1318     strcpy(SERVER, hostinfo.nodename);
 1319     }
 1320     
 1321     // get socketline from sendmail.cf
 1322     f = fopen(cf, "r");
 1323     if (!f) {
 1324     syslog(LOG_ERR, "Warning: Unable to open %s", cf);
 1325     } else {
 1326     c = 0;
 1327     filebuf = (char *)xcalloc(1024, sizeof(char *));
 1328     while (fscanf(f, "%s", filebuf) != EOF)
 1329         {
 1330         if (match(filebuf, "Xspammilt")) {    
 1331         if (fscanf(f, "%s", filebuf) != EOF) {
 1332             strcpy(socket, filebuf);
 1333             search_replace(socket, ",", "");
 1334             search_replace(socket, "S=", "");
 1335             break;
 1336             }
 1337         }
 1338         }
 1339     fclose(f);
 1340     free(filebuf);
 1341     filebuf = NULL;
 1342     }
 1343     // command line paramters
 1344     while ((c = getopt(argc, argv, args)) != -1)
 1345     {
 1346     switch (c)
 1347         {
 1348         case 'h':
 1349         usage();
 1350         exit(0);
 1351         case 'v':
 1352         version();
 1353         exit(0);
 1354         case 'p':
 1355         if (optarg == NULL || *optarg == '\0' || strncmp(optarg, "-", 1) == 0) {
 1356             (void) fprintf(stderr, "Illegal socket specified: %s\n", optarg);
 1357             exit(EX_USAGE);
 1358             }
 1359         strcpy(socket, optarg);
 1360         break;
 1361         case 'c':
 1362         if (optarg == NULL || *optarg == '\0' || strncmp(optarg, "-", 1) == 0) {
 1363             (void) fprintf(stderr, "Illegal argument specified: -c %s\n", optarg);
 1364             (void) usage();
 1365             exit(-1);
 1366             }
 1367                 switch (*optarg)
 1368                     {
 1369                     case 'c':
 1370                         DNSBL_CONNECT = 1;
 1371                         break;
 1372                     case 'r':
 1373                         DNSBL_CONNECT = 2;
 1374                         break;
 1375                     case 'd':
 1376                         DNSBL_CONNECT = 3;
 1377                         break;
 1378                     }
 1379         break;
 1380         case 'f':
 1381         if (optarg == NULL || *optarg == '\0' || strncmp(optarg, "-", 1) == 0) {
 1382             (void) fprintf(stderr, "Illegal argument specified: -f %s\n", optarg);
 1383             (void) usage();
 1384             exit(-1);
 1385             }
 1386                 switch (*optarg)
 1387                     {
 1388                     case 'w':
 1389                         FORGED_CONNECT = 1;
 1390                         break;
 1391                     case 'r':
 1392                         FORGED_CONNECT = 2;
 1393                         break;
 1394                     case 'd':
 1395                         FORGED_CONNECT = 3;
 1396                         break;
 1397                     }
 1398         break;
 1399         case 'W':
 1400         if (optarg == NULL || *optarg == '\0' || strncmp(optarg, "-", 1) == 0) {
 1401             (void) fprintf(stderr, "Illegal argument specified: -W %s\n", optarg);
 1402             (void) usage();
 1403             exit(-1);
 1404             }
 1405             WL_MIN = atoi(optarg);
 1406         break;
 1407         case 'F':
 1408             FASTMODE = 1;
 1409         break;
 1410         case 'A':
 1411         if (optarg == NULL || *optarg == '\0' || strncmp(optarg, "-", 1) == 0) {
 1412             (void) fprintf(stderr, "Illegal argument specified: -A %s\n", optarg);
 1413             (void) usage();
 1414             exit(-1);
 1415             }
 1416                 SA_MIN = atoi(optarg);
 1417                 tmparg = argv[optind];
 1418                 if (tmparg == NULL || *optarg == '\0' || strncmp(tmparg, "-", 1) == 0) {
 1419             (void) fprintf(stderr, "Illegal argument(2) specified: -A %s\n", optarg);
 1420             (void) usage();
 1421             exit(-1);                
 1422                 }
 1423                 SA_MAX = atoi(tmparg);
 1424         break;
 1425         case 's':
 1426         if (optarg == NULL || *optarg == '\0' || strncmp(optarg, "-", 1) == 0) {
 1427             (void) fprintf(stderr, "Illegal argument specified: -s %s\n", optarg);
 1428             (void) usage();
 1429             exit(1);
 1430             }
 1431         REDIRECT_RCPT = optarg;    
 1432         REDIRECT = 1;
 1433         break;
 1434         case 'b':
 1435         if (optarg == NULL || *optarg == '\0' || strncmp(optarg, "-", 1) == 0) {
 1436             (void) fprintf(stderr, "Illegal argument specified: -b %s\n", optarg);
 1437             (void) usage();
 1438             exit(1);
 1439             }
 1440         strcpy(blacklist_file, optarg);
 1441         break;
 1442         case 'w':
 1443         if (optarg == NULL || *optarg == '\0' || strncmp(optarg, "-", 1) == 0) {
 1444             (void) fprintf(stderr, "Illegal argument specified: -w %s\n", optarg);
 1445             (void) usage();
 1446             exit(1);
 1447             }
 1448         strcpy(whitelist_file, optarg);
 1449         break;
 1450         case 'D':
 1451         if (optarg == NULL || *optarg == '\0' || strncmp(optarg, "-", 1) == 0) {
 1452             (void) fprintf(stderr, "Illegal argument specified: -D %s\n", optarg);
 1453             (void) usage();
 1454             exit(1);
 1455             }
 1456         strcpy(dnsblhosts_file, optarg);
 1457         break;
 1458         case 'I':
 1459         if (optarg == NULL || *optarg == '\0' || strncmp(optarg, "-", 1) == 0) {
 1460             (void) fprintf(stderr, "Illegal argument specified: -I %s\n", optarg);
 1461             (void) usage();
 1462             exit(1);
 1463             }
 1464         strcpy(dnsbl_ignore_file, optarg);
 1465         break;
 1466         case 'H':
 1467         if (optarg == NULL || *optarg == '\0' || strncmp(optarg, "-", 1) == 0) {
 1468             (void) fprintf(stderr, "Illegal argument specified: -H %s\n", optarg);
 1469             (void) usage();
 1470             exit(1);
 1471             }
 1472         strcpy(SERVER, optarg);
 1473         break;
 1474             case 'M':
 1475         if (optarg == NULL || *optarg == '\0' || strncmp(optarg, "-", 1) == 0) {
 1476             (void) fprintf(stderr, "Illegal argument specified: -M %s\n", optarg);
 1477             (void) usage();
 1478             exit(1);
 1479             }
 1480                 DNSBL_MIN = atoi(optarg);
 1481                 tmparg = argv[optind];
 1482                 if (tmparg == NULL || *optarg == '\0' || strncmp(tmparg, "-", 1) == 0) {
 1483             (void) fprintf(stderr, "Illegal argument(2) specified: -M %s\n", optarg);
 1484             (void) usage();
 1485             exit(-1);                
 1486                 }
 1487                 DNSBL_MAX = atoi(tmparg);
 1488                 break; 
 1489             case 'm':
 1490         if (optarg == NULL || *optarg == '\0' || strncmp(optarg, "-", 1) == 0) {
 1491             (void) fprintf(stderr, "Illegal argument specified: -m %s\n", optarg);
 1492             (void) usage();
 1493             exit(1);
 1494             }
 1495                 BL_MAX = atoi(optarg);
 1496                 tmparg = argv[optind];
 1497                 if (tmparg == NULL || *optarg == '\0' || strncmp(tmparg, "-", 1) == 0) {
 1498             (void) fprintf(stderr, "Illegal argument(2) specified: -m %s\n", optarg);
 1499             (void) usage();
 1500             exit(-1);                
 1501                 }
 1502                 BL_MAX = atoi(tmparg);
 1503                 break;
 1504         case 't':
 1505         if (optarg == NULL || *optarg == '\0' || strncmp(optarg, "-", 1) == 0) {
 1506             (void) fprintf(stderr, "Illegal argument specified: -t %s\n", optarg);
 1507             (void) usage();
 1508             exit(1);
 1509             }
 1510         if (smfi_settimeout(atoi(optarg)) == MI_FAILURE) {
 1511             (void) syslog(LOG_NOTICE, "NOTICE: smfi_settimeout failed");
 1512             exit(EX_SOFTWARE);
 1513             }
 1514         break;
 1515             case 'a':
 1516         if (optarg == NULL || *optarg == '\0' || strncmp(optarg, "-", 1) == 0) {
 1517             (void) fprintf(stderr, "Illegal argument specified: -a %s\n", optarg);
 1518             (void) usage();
 1519             exit(-1);
 1520             }
 1521                 if (!strcmp(optarg, "discard") != 0) {
 1522                     MAX_ACTION = ACTION_DISCARD;
 1523                 } 
 1524                 else if (!strcmp(optarg, "reject") != 0) {
 1525                     MAX_ACTION = ACTION_REJECT;
 1526                 }
 1527                 break;
 1528 
 1529 #ifdef BAYESIAN_ANNOYANCE
 1530         case 'x':
 1531         BAYESIAN = 1;
 1532         break;
 1533         case 'X':
 1534         if (optarg == NULL || *optarg == '\0' || strncmp(optarg, "-", 1) == 0) {
 1535             (void) fprintf(stderr, "Illegal argument specified: -X %s\n", optarg);
 1536             (void) usage();
 1537             exit(1);
 1538             }
 1539         strcpy(bayesian_path, optarg);
 1540         break;
 1541         case 'Y':
 1542         BAYESIAN_NO_DEL = 1;
 1543         break;
 1544 #endif
 1545         break;
 1546         case 'T':
 1547         if (optarg == NULL || *optarg == '\0' || strncmp(optarg, "-", 1) == 0) {
 1548             (void) fprintf(stderr, "Illegal argument specified: -T %s\n", optarg);
 1549             (void) usage();
 1550             exit(1);
 1551             }
 1552                 TAG_SUBJECT_TXT = optarg;
 1553         TAG_SUBJECT = 1;
 1554         break;
 1555         }
 1556     }
 1557 
 1558     // check for pid
 1559     check_pid();
 1560     // check if old socket was properly removed
 1561     check_socket(socket);
 1562     
 1563 #ifdef BAYESIAN_ANNOYANCE
 1564     // check if binary is in place
 1565     if (BAYESIAN) {
 1566     snprintf(buffer, sizeof(buffer), "%s%s", bayesian_path, "/annoyance-filter");
 1567     if (stat(buffer, &buf)) {
 1568         syslog(LOG_ERR, "ERROR: Unable to locate: %s", buffer);
 1569         fprintf(stderr, "Unable to locate: %s\n", buffer);
 1570         exit(1);
 1571         }
 1572     syslog(LOG_NOTICE, "NOTICE: Annoyance-filter enabled");
 1573     }
 1574 #endif
 1575     syslog(LOG_NOTICE, "NOTICE: Using socket '%s'", socket);
 1576     (void) smfi_setconn(socket);
 1577     
 1578     if (smfi_register(smfilter) == MI_FAILURE) {
 1579     syslog(LOG_ERR, "ERROR: smfi_register failed");
 1580     fprintf(stderr, "smfi_register failed\n");
 1581     exit(EX_UNAVAILABLE);
 1582     }
 1583 
 1584     (void)init_lists();
 1585     
 1586     daemon = fork();
 1587     if (daemon < 0) {
 1588         syslog(LOG_ERR, "ERROR: Unable to start main daemon");
 1589         exit(1);
 1590         } 
 1591     if (daemon > 0) exit(0); // parent exits, child continious
 1592     syslog(LOG_NOTICE, "Spammilt daemon started");
 1593     setsid();
 1594     chdir("/var/spool/spammilt");
 1595     umask(027);
 1596     write_pid();
 1597     signal(SIGTERM, SIG_DFL);
 1598     signal(SIGHUP, SIG_DFL);
 1599     signal(SIGINT, SIG_DFL);
 1600     signal(SIGUSR1, signal_handler); // reload black/white lists
 1601     signal(SIGUSR2, signal_handler); // write statistics
 1602     // SIGHUP, SIGTERM and SIGINT are handled by libmilter
 1603     // fork again for the statistics back-end daemon
 1604     daemon = fork();
 1605     if (daemon < 0) {
 1606     syslog(LOG_CRIT, "ERROR: Unable to start statistics daemon");
 1607     exit(EXIT_FAILURE);
 1608     }
 1609     if (daemon > 0) {
 1610     signal(SIGTERM, signal_handler);
 1611     signal(SIGUSR1, SIG_IGN); // ignore USR signal
 1612     signal(SIGUSR2, SIG_IGN); // ignore USR signal
 1613     await_stat_request();
 1614     }
 1615     if (get_shmid() == -1) {
 1616     exit(EXIT_FAILURE);
 1617     }
 1618     // start filter 
 1619     if (smfi_main() == MI_FAILURE) {
 1620     syslog(LOG_ERR, "ERROR: smfi_main returned ERROR");
 1621     fprintf(stderr, "smfi_main returned ERROR, look in your logs for details\n");
 1622         }
 1623           
 1624     (void) cleanup_lists();
 1625     
 1626     unlink(PID_FILE);
 1627     syslog(LOG_NOTICE, "Spammiltd stopped");
 1628     return(0);
 1629     }
 1630 
 1631 
 1632 void signal_handler(int sig)
 1633     {
 1634     switch(sig) 
 1635     {
 1636         case SIGUSR1:
 1637         (void)cleanup_lists();
 1638         (void)init_lists();
 1639         syslog(LOG_NOTICE, "NOTICE: Reloaded spamlists into memory");
 1640         break;
 1641         case SIGUSR2:
 1642             (void)dump_settings();
 1643             break;
 1644     case SIGTERM:
 1645         syslog(LOG_ERR, "Statistics-Daemon got SIGTERM, closing socket...");
 1646         close(s_sock);
 1647         cleanup_shm_stats();
 1648         exit(0);
 1649         break;
 1650     }
 1651     }
 1652 
 1653 
 1654 void cleanup_lists()
 1655     {
 1656 #ifdef DEBUG
 1657     syslog(LOG_DEBUG, "DEBUG: Freeing memory of whitelist");
 1658 #endif
 1659     whitelist = FreeList(whitelist, &whitelistSize);
 1660 
 1661 #ifdef DEBUG
 1662     syslog(LOG_DEBUG, "DEBUG: Freeing memory of blacklist");
 1663 #endif
 1664     blacklist = FreeList(blacklist, &blacklistSize);
 1665 
 1666 #ifdef DEBUG
 1667     syslog(LOG_DEBUG, "DEBUG: Freeing memory of dnsblignorelist");
 1668 #endif
 1669     dnsblignorelist = FreeList(dnsblignorelist, &dnsblignorelistSize);    
 1670 
 1671 #ifdef DEBUG
 1672     syslog(LOG_DEBUG, "DEBUG: Freeing memory of dnsblhosts");
 1673 #endif
 1674     dnsblhosts = FreeList(dnsblhosts, &dnsblhostsSize);
 1675     }
 1676 
 1677 
 1678 void init_lists()
 1679     {
 1680 #ifdef DEBUG
 1681     int msg_size = 200;
 1682     char msg[msg_size];
 1683 #endif     
 1684 
 1685 #ifdef DEBUG
 1686     syslog(LOG_DEBUG, "DEBUG: Initialise dnsblhosts");
 1687 #endif
 1688     dnsblhosts = ReadList(dnsblhosts_file, &dnsblhostsSize);
 1689 #ifdef DEBUG
 1690     snprintf(msg, msg_size, "Read %d entries from file %s", dnsblhostsSize, dnsblhosts_file);
 1691     syslog(LOG_DEBUG, "DEBUG: %s",msg);
 1692 #endif
 1693 
 1694 #ifdef DEBUG
 1695     syslog(LOG_DEBUG, "DEBUG: Initialise whitelist");
 1696 #endif
 1697     whitelist = ReadList(whitelist_file, &whitelistSize);
 1698 #ifdef DEBUG
 1699     snprintf(msg, msg_size, "Read %d entries from file %s", whitelistSize, whitelist_file);
 1700     syslog(LOG_DEBUG, "DEBUG: %s",msg);
 1701 #endif
 1702 
 1703 #ifdef DEBUG
 1704     syslog(LOG_DEBUG, "DEBUG: Initialise blacklist");
 1705 #endif  
 1706     blacklist = ReadList(blacklist_file, &blacklistSize);
 1707 #ifdef DEBUG
 1708     snprintf(msg, msg_size, "Read %d entries from file %s", blacklistSize, blacklist_file);
 1709     syslog(LOG_DEBUG, "DEBUG: %s",msg);
 1710 #endif
 1711     
 1712 #ifdef DEBUG
 1713     syslog(LOG_DEBUG, "DEBUG: Initialise dnsblignorelist");
 1714 #endif  
 1715     dnsblignorelist = ReadList(dnsbl_ignore_file, &dnsblignorelistSize);    
 1716 #ifdef DEBUG
 1717     snprintf(msg, msg_size, "Read %d entries from file %s", dnsblignorelistSize, dnsbl_ignore_file);
 1718     syslog(LOG_DEBUG, "DEBUG: %s",msg);
 1719 #endif
 1720     }
 1721 
 1722 
 1723 int check_pid(void)
 1724     {
 1725     FILE *FH;
 1726     pid_t F_PID;
 1727     char *txt;
 1728     
 1729     // read pid-file 
 1730     if (!(FH = fopen(PID_FILE ,"r"))) {
 1731         // no pid... OK
 1732     return(0);
 1733         }
 1734     if ((txt = (char *)xcalloc(100, sizeof(char *))) == NULL) {
 1735     syslog(LOG_ERR, "ERROR: allocate error for 'txt' in 'check_pid'");
 1736     exit(1);
 1737     } 
 1738     fscanf(FH, "%s", txt);
 1739     fclose(FH);
 1740     
 1741     F_PID = atoi(txt);
 1742     
 1743     // check if pid-nr is still alive, if so do exit
 1744     if (!((kill(F_PID, 0) == -1) && errno == ESRCH)) {  
 1745     syslog(LOG_NOTICE, "NOTICE: Process allready running: %d", F_PID);
 1746     exit(0);    
 1747     }
 1748     // if not alive remove pid-file
 1749     if (unlink(PID_FILE)) {
 1750     syslog(LOG_CRIT, "ERROR: Unable to remove old pidfile: %s", PID_FILE );
 1751     exit(1);
 1752     }
 1753     syslog(LOG_NOTICE, "NOTICE: Removed old pid file: %s", PID_FILE);
 1754     free(txt);
 1755     return(1);
 1756     }
 1757 
 1758 
 1759 void write_pid(void)
 1760     {
 1761     FILE *FH;
 1762     pid_t PID;
 1763     
 1764     PID = getpid();
 1765     
 1766     // write pid-nr to pid-file
 1767     if (!(FH = fopen(PID_FILE, "w+"))) {
 1768     // Unable to create pidfile 
 1769     syslog(LOG_CRIT, "ERROR: Unable to create pid-file: %s\n", PID_FILE);
 1770     exit(1);
 1771     }    
 1772     fprintf(FH, "%d", PID);
 1773     fclose(FH);
 1774     }
 1775 
 1776 
 1777 void check_socket(char *socket)
 1778     {
 1779     char f_socket[255];
 1780     struct stat info;
 1781     
 1782     strncpy(f_socket, socket, 254);
 1783     if ((strcasecmp("unix", f_socket)) || (strcasecmp("local", f_socket))) {
 1784     search_replace(f_socket, "local:", "");
 1785     search_replace(f_socket, "unix:", "");
 1786     if (stat(f_socket, &info)) {
 1787         return;
 1788             }
 1789         if (unlink(f_socket)) {
 1790         syslog(LOG_CRIT, "ERROR: Unable to remove socket: %s", f_socket);
 1791         exit(1);
 1792         }
 1793     syslog(LOG_NOTICE, "NOTICE: Removed old socket: %s", f_socket);
 1794     }
 1795     }
 1796     
 1797 
 1798 int await_stat_request(void) 
 1799     {
 1800     struct    sockaddr_in servaddr;
 1801     char      buffer[MAX_CHARS];
 1802     int shmid;
 1803     shared_stats *shm;
 1804     
 1805     // set socket paramters and bind socket
 1806     if ( (s_list = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) {
 1807     syslog(LOG_ERR, "ERROR: Error creating listening socket");
 1808     exit(EXIT_FAILURE);
 1809     }
 1810 
 1811     memset(&servaddr, 0, sizeof(servaddr));
 1812     servaddr.sin_family      = AF_INET;
 1813     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
 1814     servaddr.sin_port        = htons(PORT);
 1815 
 1816     if ( bind(s_list, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 ) {
 1817     syslog(LOG_ERR, "ERROR: Error calling bind()");
 1818     exit(EXIT_FAILURE);
 1819     }
 1820 
 1821     if ( listen(s_list, LISTENQ) < 0 ) {
 1822     syslog(LOG_ERR, "ERROR: Error calling listen()");
 1823     exit(EXIT_FAILURE);
 1824     }
 1825     // create shm segment and initialise it
 1826     shmid = get_shmid();
 1827     syslog(LOG_NOTICE, "Spammilt statistics-daemon started");
 1828     
 1829     while ( 1 ) 
 1830     {
 1831         // get shmid and attach to shared memory segment
 1832     shmid = get_shmid();
 1833     shm = get_shmat(shmid);
 1834     // wait for connection
 1835     if ( (s_sock = accept(s_list, NULL, NULL) ) < 0 ) {
 1836         syslog(LOG_ERR, "ERROR: Error calling accept()");
 1837         exit(EXIT_FAILURE);
 1838         }
 1839     snprintf(buffer, MAX_CHARS, "%d %d %d %d %d %d %d %d %d %d %d %d %d\n", 
 1840                     shm->CONNECTS, 
 1841                     shm->PROCESSED,
 1842                     shm->REJECTS,
 1843                     shm->DISCARDS,
 1844                     shm->REDIRECTS,
 1845                     shm->WHITELISTED,
 1846                     shm->BLACKLISTED,
 1847                     shm->FORGED,
 1848                     shm->DNSBL,
 1849                     shm->DNSBL_IGNORE,
 1850                     shm->BAYESIAN,
 1851                     shm->ACCEPT,
 1852                                         shm->ASSASSIN);
 1853     shmdt(shm);
 1854     write_stat_socket(s_sock, buffer, strlen(buffer));
 1855 
 1856     // close connection
 1857     if ( close(s_sock) < 0 ) {
 1858         syslog(LOG_ERR, "ERROR: Error calling close()");
 1859         exit(EXIT_FAILURE);
 1860         }
 1861     }
 1862     }
 1863 
 1864 
 1865 ssize_t write_stat_socket(int sockd, const void *vptr, size_t n) 
 1866     {
 1867     size_t      nleft;
 1868     ssize_t     nwritten;
 1869     const char *buffer;
 1870 
 1871     buffer = vptr;
 1872     nleft  = n;
 1873 
 1874     while ( nleft > 0 ) 
 1875     {
 1876     if ( (nwritten = write(sockd, buffer, nleft)) <= 0 ) {
 1877         if ( errno == EINTR )
 1878             nwritten = 0;
 1879         else
 1880             return -1;
 1881         }
 1882     nleft  -= nwritten;
 1883     buffer += nwritten;
 1884     }
 1885 
 1886     return n;
 1887     }
 1888 
 1889 
 1890 // SHM part
 1891 int get_shmid_create()
 1892     {
 1893     int shmid;
 1894     char *s;
 1895     shared_stats *shm;
 1896     
 1897     if ((shmid = shmget(SHM_KEY, sizeof(shared_stats), IPC_CREAT | 0600)) < 0) {
 1898         s = strerror(errno);
 1899         syslog(LOG_ERR, "ERROR: unable to allocate shared memory: %s\n", s);
 1900         exit(EXIT_FAILURE);
 1901     }
 1902     if ((shm = get_shmat(shmid)) == NULL) {
 1903         exit(EXIT_FAILURE);    
 1904     }
 1905     shm->CONNECTS = 0;
 1906     shm->PROCESSED = 0;
 1907     shm->REJECTS = 0;
 1908     shm->DISCARDS = 0;
 1909     shm->REDIRECTS = 0;
 1910     shm->WHITELISTED = 0;
 1911     shm->BLACKLISTED = 0;
 1912     shm->FORGED = 0;
 1913     shm->DNSBL = 0;
 1914     shm->DNSBL_IGNORE = 0;
 1915     shm->BAYESIAN = 0;
 1916     shm->ACCEPT = 0;
 1917     shm->ASSASSIN = 0;
 1918     shmdt(shm);
 1919     return shmid;
 1920     }
 1921 
 1922 int get_shmid()
 1923 {
 1924     int shmid;
 1925     char *s;
 1926     
 1927     if ((shmid = shmget(SHM_KEY, sizeof(shared_stats), 0600)) < 0) {
 1928         s = strerror(errno);
 1929         // shared memory not created, trying to allocate 
 1930         if ((shmid = get_shmid_create())) {
 1931             return shmid;
 1932         }
 1933         syslog(LOG_ERR, "ERROR: get_shmid and shmid_create failed: %s\n", s);
 1934         return -1;
 1935     }
 1936     return shmid;
 1937 }
 1938 
 1939 
 1940 shared_stats *get_shmat(int shmid)
 1941     {
 1942     char *s;
 1943     shared_stats *shm;
 1944     
 1945     if ((shm = (shared_stats *)shmat(shmid, NULL, 0)) == NULL) {
 1946         s = strerror(errno);
 1947         syslog(LOG_ERR, "ERROR: attaching to shared memory: %s\n", s);
 1948         return NULL;
 1949     }
 1950     return shm;
 1951     }
 1952 
 1953 
 1954 int write_shm(int code)
 1955     {
 1956     int shmid;
 1957     shared_stats *shm;
 1958     
 1959     if ((shmid = get_shmid())  == -1) 
 1960         return 1;
 1961 
 1962     if ((shm = get_shmat(shmid)) == NULL)
 1963         return 1;
 1964 
 1965     switch (code) 
 1966     {
 1967     case 1:
 1968         shm->CONNECTS++;
 1969         break;
 1970     case 2:
 1971         shm->PROCESSED++;
 1972         break;
 1973     case 3:
 1974         shm->REJECTS++;
 1975         break;
 1976     case 4:
 1977         shm->DISCARDS++;
 1978         break;
 1979     case 5:
 1980         shm->REDIRECTS++;
 1981         break;
 1982     case 6:
 1983         shm->WHITELISTED++;
 1984         break;
 1985     case 7:
 1986         shm->BLACKLISTED++;
 1987         break;
 1988     case 8:
 1989         shm->FORGED++;
 1990         break;
 1991     case 9:
 1992         shm->DNSBL++;
 1993         break;
 1994     case 10:
 1995         shm->DNSBL_IGNORE++;
 1996         break;
 1997     case 11:
 1998         shm->BAYESIAN++;
 1999         break;
 2000     case 12:
 2001         shm->ACCEPT++;
 2002         break;
 2003     case 13:
 2004         shm->ASSASSIN++;
 2005         break;
 2006     }
 2007 
 2008     shmdt(shm);
 2009     return 0;
 2010     }
 2011 
 2012 
 2013 int cleanup_shm_stats()
 2014     {
 2015     int shmid;
 2016     shared_stats *shm;
 2017     struct shmid_ds shm_ds;
 2018     
 2019     if ((shmid = get_shmid()) == -1)
 2020     return 1;
 2021 
 2022     if ((shm = get_shmat(shmid)) == NULL)
 2023     return 1;
 2024     shmctl(shmid, IPC_RMID, &shm_ds);
 2025     return 0;
 2026     }
 2027 
 2028 
 2029 char *scan_for_ip (char *string)    
 2030     {
 2031     char *ptr, *ip;
 2032     short int ip_size = 16;
 2033     short int ip_ok = 0;
 2034     short int p_count = 0;
 2035     short int found_start = 0;
 2036     short int last_is_int = 0;
 2037         
 2038     ip = calloc(ip_size, sizeof(char *));
 2039 
 2040     for (ptr = string; *ptr; ptr++) {
 2041     if (*ptr == '[') {
 2042         found_start = 1;
 2043         continue;
 2044         }
 2045     if (found_start && (*ptr == '.' || (*ptr >= '0' && *ptr <= '9'))) {
 2046         if (ip_size > 1) {
 2047         strncat(ip, ptr, 1);
 2048         ip_ok = 1;
 2049         last_is_int = 1;
 2050         }
 2051         if (*ptr == '.') {
 2052         last_is_int = 0;
 2053         p_count++;
 2054         }
 2055         ip_size--;
 2056         }
 2057     else if (*ptr == ']' && ip_ok && last_is_int) {
 2058         break;
 2059         }
 2060     else {
 2061         strcpy(ip, "\0");
 2062         found_start = 0;
 2063         ip_size = 16;
 2064         ip_ok = 0;
 2065         p_count = 0;
 2066         }
 2067     }
 2068 #ifdef DEBUG
 2069     syslog(LOG_DEBUG, "DEBUG: ip:%s p_count:%d ip_ok:%d last_is_int:%d\n", ip, p_count, ip_ok, last_is_int);
 2070 #endif
 2071     if (p_count == 3 && ip_ok && last_is_int)
 2072     return ip;
 2073 
 2074     return NULL;
 2075     }
 2076 
 2077     
 2078 /* eof */
 2079