spamass-milt-list
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Patch for -R -a and -B loop prevention -- against CVS


From: Joe Maimon
Subject: Patch for -R -a and -B loop prevention -- against CVS
Date: Wed, 22 Sep 2004 15:58:27 -0400
User-agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.7) Gecko/20040616 Mnenhy/0.6.0.101

As discussed earlier this is a patch I am experimenting with to

o Rcpt filtering as in earlier patches
o -a switch to govern when spambucketing happens
o -B loop prevention

Joe
diff -ur spamass-milt/spamass-milter.cpp spamass-milt.jm/spamass-milter.cpp
--- spamass-milt/spamass-milter.cpp     2004-09-21 21:56:08.000000000 -0400
+++ spamass-milt.jm/spamass-milter.cpp  2004-09-22 15:30:31.000000000 -0400
@@ -148,13 +148,15 @@
 
 const char *const debugstrings[] = {
        "ALL", "FUNC", "POLL", "UORI", "STR", "MISC", "NET", "SPAMC", "RCPT",
-       "COPY",
+       "COPY", "RFIL",
        NULL
 };
 
 int flag_debug = (1<<D_ALWAYS);
 bool flag_reject = false;
 int reject_score = -1;
+bool flag_rcpt_filter = false;
+char *rcpt_filter_program = NULL;
 bool dontmodifyspam = false;    // Don't modify/add body or spam results 
headers
 bool dontmodify = false;        // Don't add SA headers, ever.
 bool flag_sniffuser = false;
@@ -166,6 +168,8 @@
 char **spamc_argv;
 bool flag_bucket = false;
 bool flag_bucket_only = false;
+bool flag_bucket_and_reject = false;
+int bucket_and_reject_score = 0;
 char *spambucket;
 bool flag_full_email = false;          /* pass full email address to spamc */
 bool flag_expand = false;      /* alias/virtusertable expansion */
@@ -180,7 +184,7 @@
 main(int argc, char* argv[])
 {
    int c, err = 0;
-   const char *args = "fd:mMp:P:r:u:D:i:b:B:e:x";
+   const char *args = "a:fd:mMp:P:r:R:u:D:i:b:B:e:x";
    char *sock = NULL;
    bool dofork = false;
    char *pidfilename = NULL;
@@ -231,6 +235,10 @@
                                flag_reject = true;
                                reject_score = atoi(optarg);
                                break;
+                       case 'R':
+                               flag_rcpt_filter = true;
+                               rcpt_filter_program = optarg;
+                               break;
                        case 'u':
                                flag_sniffuser = true;
                                defaultuser = strdup(optarg);
@@ -256,6 +264,12 @@
                                // XXX we should probably verify that optarg is 
vaguely sane
                                spambucket = strdup( optarg );
                                break;
+                       case 'a':
+                               flag_bucket_and_reject = true;
+                               bucket_and_reject_score = atoi(optarg);
+                               if(!bucket_and_reject_score)
+                                       flag_bucket_and_reject = false;
+                               break;
                        case 'x':
                                flag_expand = true;
                                break;
@@ -278,11 +292,15 @@
    if (!sock || err) {
       cout << PACKAGE_NAME << " - Version " << PACKAGE_VERSION << endl;
       cout << "SpamAssassin Sendmail Milter Plugin" << endl;
-      cout << "Usage: spamass-milter -p socket [-b|-B bucket] [-d xx[,yy...]] 
[-D host]" << endl;
+      cout << "Usage: spamass-milter -p socket [-a -1|n|0] [-b|-B bucket] [-d 
xx[,yy...]]" <<endl; 
+      cout << "                      [-D host]" << endl;
       cout << "                      [-e defaultdomain] [-f] [-i networks] 
[-m] [-M]" << endl;
       cout << "                      [-P pidfile] [-r nn] [-u defaultuser] 
[-x]" << endl;
+      cout << "                      [-R rcpt_filter_program]" << endl; 
       cout << "                      [-- spamc args ]" << endl;
       cout << "   -p socket: path to create socket" << endl;
+      cout << "   -a -1|num|0: bucket all spam, bucket spam scoring higher 
than num" << endl;
+      cout << "                or bucket all spam that does not get rejected." 
<< endl;
       cout << "   -b bucket: redirect spam to this mail address.  The orignal" 
<< endl;
       cout << "          recipient(s) will not receive anything." << endl;
       cout << "   -B bucket: add this mail address as a BCC recipient of 
spam." << endl;
@@ -298,6 +316,7 @@
       cout << "   -P pidfile: Put processid in pidfile" << endl;
       cout << "   -r nn: reject messages with a score >= nn with an SMTP 
error.\n"
               "          use -1 to reject any messages tagged by SA." << endl;
+      cout << "   -R cmd: filter out recipients with zero exit status."<< endl;
       cout << "   -u defaultuser: pass the recipient's username to spamc.\n"
               "          Uses 'defaultuser' if there are multiple recipients." 
<< endl;
       cout << "   -x: pass email address through alias and virtusertable 
expansion." << endl;
@@ -414,6 +433,7 @@
   string::size_type eoh2 = assassin->d().find("\n\r\n");
   string::size_type eoh = (eoh1 < eoh2) ? eoh1 : eoh2;
   string::size_type bob = assassin->d().find_first_not_of("\r\n", eoh);
+  int score = 0;
 
   if (bob == string::npos)
        bob = assassin->d().size();
@@ -430,7 +450,7 @@
                do_reject = true;
        if (reject_score != -1)
        {
-               int score, rv;
+               int rv;
                const char *spam_status = assassin->spam_status().c_str();
                /* SA 3.0 uses the keyword "score" */
                rv = sscanf(spam_status,"%*s score=%d", &score);
@@ -453,8 +473,19 @@
                debug(D_MISC, "Rejecting");
                smfi_setreply(ctx, "550", "5.7.1", "Blocked by SpamAssassin");
 
-
-               if (flag_bucket)
+               /* Send spam to the bucket if -b or -B AND
+                  if -a was specified (without a param of 0) AND
+                  if the -a parameter is higher than score
+                  OR reject_score = -1 OR -a parameter is -1 */ 
+                
+
+               if (flag_bucket &&
+                    (flag_bucket_and_reject && 
+                     ( (bucket_and_reject_score <= score) ||
+                       (reject_score = -1) || (bucket_and_reject_score = -1)
+                     )
+                    )
+                  )
                {
                        /* If we also want a copy of the spam, shell out to 
sendmail and
                           send another copy.  The milter API will not let you 
send the
@@ -469,6 +500,8 @@
 #endif
                        char *fmt="%s \"%s\"";
                        FILE *p;
+                       
+                       debug(D_COPY,"spambucket copying....");
 
 #if defined(HAVE_ASPRINTF)
                        asprintf(&buf, fmt, SENDMAIL, spambucket);
@@ -511,13 +544,27 @@
 #if defined(HAVE_ASPRINTF)
                        free(buf);
 #endif 
-               }
+               } else if (flag_bucket)
+                       debug(D_COPY,"spambucket copying thwarted by -a. -a 
param: %d, score %d", 
+                                       bucket_and_reject_score,
+                                       score
+                                       );
                return SMFIS_REJECT;
        }
   }
 
-  /* Drop the message into the spam bucket if it's spam */
-  if ( flag_bucket ) {
+  /* Drop the message into the spam bucket if it's spam
+     AND either of:
+                 -a is not set or -a param is 0
+                 -a parameter is not larger than score
+                 -a parameter is -1, signifying bucket everything */
+  if ( flag_bucket && ((!flag_bucket_and_reject) ||
+                      (!bucket_and_reject_score > score) ||
+                      (bucket_and_reject_score = -1)
+                     ) 
+     )
+  {
+       debug(D_COPY,"spambucket copying....");
         if (!assassin->spam_flag().empty()) {
           // first, add the spambucket address
           if ( smfi_addrcpt( ctx, spambucket ) != MI_SUCCESS ) {
@@ -541,7 +588,11 @@
                 }
           }
         }
-  }
+  } else if (flag_bucket)
+       debug(D_COPY,"spambucket copying thwarted by -a. -a param: %d, score 
%d", 
+               bucket_and_reject_score,
+               score
+             );
 
   update_or_insert(assassin, ctx, assassin->spam_report(), 
&SpamAssassin::set_spam_report, "X-Spam-Report");
   update_or_insert(assassin, ctx, assassin->spam_prev_content_type(), 
&SpamAssassin::set_spam_prev_content_type, "X-Spam-Prev-Content-Type");
@@ -698,6 +749,9 @@
 
        /* allocate a structure to store the IP address (and SA object) in */
        sctx = (struct context *)malloc(sizeof(*sctx));
+       if(!sctx)
+               return SMFIS_TEMPFAIL;
+       memset(sctx, '\0', sizeof(*sctx)); 
        if (!hostaddr)
        {
                /* not a socket; probably a local user calling sendmail 
directly */
@@ -709,6 +763,7 @@
        }
        sctx->assassin = NULL;
        sctx->helo = NULL;
+       sctx->rcpts = 0;
        
        /* store a pointer to it with setpriv */
        smfi_setpriv(ctx, sctx);
@@ -804,6 +859,24 @@
 
        debug(D_FUNC, "mlfi_envrcpt: enter");
 
+       if(!flag_bucket_only && 
+           (!cmp_email_addr(spambucket,envrcpt[0]))
+          )
+       {
+               debug(D_FUNC, "mlfi_envrcpt: early exit");
+               // leaving now does not increase sctx->rcpts
+               // which means early milter SMFIS_ACCEPT if
+               // there are no other un-filtered rcpts
+               return SMFIS_CONTINUE;
+       }
+       
+       if(flag_rcpt_filter && rcpt_filter_program && rcpt_filter(ctx, envrcpt))
+       {
+               debug(D_FUNC, "mlfi_envrcpt: early exit");
+               return SMFIS_CONTINUE;
+       }       
+
+
        if (flag_expand)
        {
                /* open a pipe to sendmail so we can do address expansion */
@@ -851,10 +924,21 @@
                                */
                                if (strstr(buf, "... deliverable: mailer "))
                                {
-                                       char *p=strstr(buf,", user ");
+                                       char *p=strstr(buf,", user ")+7;
+                                       
                                        /* anything after ", user " is the 
email address */
-                                       debug(D_RCPT, "user: %s", p+7);
-                                       assassin->expandedrcpt.push_back(p+7);
+                                       debug(D_RCPT, "user: %s", p);
+                                       
+                                       if (flag_rcpt_filter &&
+                                           rcpt_filter_program &&
+                                           !(strcasecmp(p, envrcpt[0])) &&
+                                           rcpt_filter (ctx, &p)
+                                          )
+                                               continue;
+                                       if (!cmp_email_addr(spambucket,p))
+                                               continue;
+                                       
+                                       assassin->expandedrcpt.push_back(p);
                                }
                        }
                        pclose(p); p = NULL;
@@ -949,6 +1033,91 @@
        return SMFIS_CONTINUE;
 }
 
+bool
+rcpt_filter(SMFICTX* ctx, char** envrcpt)
+{
+       struct context *sctx = (struct context*)smfi_getpriv(ctx);
+       char * queue_id = smfi_getsymval(ctx,"i");
+       int rcpt_filter_result = 1;
+       int size = 0;
+       char *tmp_rcpt = NULL;
+       int tmp_rcpt_borrowed = 0;
+       int child_pid = 0;
+       int child_status = 0;
+       char *child_argv[3];
+       
+       debug(D_FUNC,"%s: rcpt_filter: enter", queue_id);       
+       
+       sctx->rcpts++;
+       if(!flag_rcpt_filter || !rcpt_filter_program)
+               return false;
+       
+       debug(D_RFIL,"%s: rcpt_filter: filtering",queue_id);
+       
+       if((tmp_rcpt = strdup(envrcpt[0])) == NULL)
+               return false;
+       
+       size = strlen(tmp_rcpt);
+       if(tmp_rcpt[size-1] == '>')
+       {
+               tmp_rcpt[size-1] = '\0';
+       }
+       if(tmp_rcpt[0] == '<')
+       {
+               tmp_rcpt_borrowed = true;
+               tmp_rcpt++;
+       }
+       
+       debug(D_RFIL,"%s: rcpt_filter: tmp_rcpt <%s>",queue_id, tmp_rcpt);
+
+       switch((child_pid = fork()))
+       {
+               case 0:
+                       // Child
+                       child_argv[0] = rcpt_filter_program;
+                       child_argv[1] = tmp_rcpt;
+                       child_argv[2] = NULL;   
+                       
+                       debug(D_RFIL,"%s: rcpt_filter: Child here",queue_id);
+                       
+                       execvp(child_argv[0], child_argv);
+                       _exit(errno);
+                       break;;
+               case -1:
+                       // Failed
+                       debug(D_RFIL,"%s: rcpt_filter: fork failed: %d", 
queue_id, errno);
+                       break;
+               default:
+                       // Parent
+                       if(waitpid(child_pid, &child_status, 0)<0)
+                               debug(D_RFIL, "%s: rcpt_filter: waitpid failed: 
%d", queue_id, errno);
+                       if(WIFEXITED(child_status))
+                       {
+                               rcpt_filter_result = WEXITSTATUS(child_status);
+                               debug(D_RFIL,"%s: rcpt_filter: Child exit 
status %d",queue_id, rcpt_filter_result);
+                       }
+                       else
+                               debug(D_RFIL,"%s: rcpt_filter: Child exited 
abnormally",queue_id);
+                       
+                       break;
+       }
+       
+       if(tmp_rcpt_borrowed)
+               tmp_rcpt--;
+       free(tmp_rcpt);
+       
+       if(!rcpt_filter_result)
+       {
+               debug(D_RFIL,"%s: rcpt_filter, removing rcpt", queue_id);
+               sctx->rcpts--;
+               return true;
+       } else
+               debug(D_RFIL,"%s: rcpt_filter, leaving rcpt", queue_id);
+
+       debug(D_FUNC,"%s: rcpt_filter: leaving normally", queue_id);    
+       return false;
+}
+
 //
 // Gets called repeatedly for all header fields
 //
@@ -968,6 +1137,22 @@
   SpamAssassin* assassin = ((struct context *)smfi_getpriv(ctx))->assassin;
   debug(D_FUNC, "mlfi_header: enter");
 
+  if ( !(assassin->connected))
+  { //Only once, before SPAMC connects
+       struct context * sctx = (struct context *) smfi_getpriv(ctx);
+       char * queue_id = smfi_getsymval(ctx,"i");
+       if(flag_rcpt_filter && !sctx->rcpts) 
+       {
+               debug(D_RFIL,"%s: rcpt_filter, accepting message without 
processing", queue_id);
+               mlfi_abort(ctx); // For lack of specific cleanup 
+               debug(D_FUNC, "%s: mlfi_header: rcpt_filter leave", queue_id);
+               return SMFIS_ACCEPT;
+       } else
+               debug(D_RFIL, "%s: rcpt_filter, processing message", queue_id);
+       
+       debug(D_FUNC, "%s: mlfi_header: rcpt_filter leave", queue_id);
+  }
+
   // Check if the SPAMC program has already been run, if not we run it.
   if ( !(assassin->connected) )
      {
@@ -1199,8 +1384,12 @@
   SpamAssassin* assassin = ((struct context *)smfi_getpriv(ctx))->assassin;
 
   debug(D_FUNC, "mlfi_abort");
-  ((struct context *)smfi_getpriv(ctx))->assassin=NULL;
-  delete assassin;
+  if(assassin)
+  {
+       ((struct context *)smfi_getpriv(ctx))->assassin=NULL;
+       delete assassin;
+  } else
+       debug(D_MISC,"%s: mlfi_abort: assassin is 
NULL!",smfi_getsymval(ctx,"i"));
 
   return SMFIS_ACCEPT;
 }
@@ -2071,5 +2260,56 @@
     return str;
 }
 
+int
+cmp_email_addr(char *addr1,char *addr2)
+{
+       char *s1 = strdup(addr1);
+       char *s2 = strdup(addr2);
+       int s1_borrowed = 0;
+       int s2_borrowed = 0;
+       int ret_status = 0;
+
+       debug(D_FUNC,"cmp_email_addr: enter");
+       debug(D_RCPT,"cmp_email_addr: \"%s\" and \"%s\"",addr1,addr2);
+       
+       /* XXX no way to show malloc fail*/
+       if( (!s1) && (s2) )
+           return 1;
+       if( (!s1) && (!s2))
+           return 0;
+       if( (s1) && (!s2) )
+           return -1;
+
+       /*de bracketize. XXX fails if trailing chars (shouldnt happen..)*/
+       if(s1[0] == '<') {
+          s1_borrowed = 1;
+          s1++;
+       }
+       if(s2[0] == '<') {
+          s2_borrowed = 1;
+          s2++;
+       }
+       
+       if(s1[strlen(s1)-1] == '>') 
+          s1[strlen(s1)-1] = '\0';
+       if(s2[strlen(s2)-1] == '>') 
+          s2[strlen(s2)-1] = '\0';
+
+       ret_status = strcasecmp(s1,s2);
+
+       if(s1_borrowed)
+          s1--;
+       if(s2_borrowed)
+          s2--;
+
+       free(s1);
+       free(s2);
+
+       debug(D_RCPT,"cmp_email_addr: results: %d", ret_status);
+       debug(D_FUNC,"cmp_email_addr: exit");
+
+       return ret_status;
+}
+
 // }}}
 // vim6:ai:noexpandtab
diff -ur spamass-milt/spamass-milter.h spamass-milt.jm/spamass-milter.h
--- spamass-milt/spamass-milter.h       2004-09-21 21:56:08.000000000 -0400
+++ spamass-milt.jm/spamass-milter.h    2004-09-22 11:35:03.000000000 -0400
@@ -74,7 +74,7 @@
 enum debuglevel 
 {
        D_ALWAYS, D_FUNC, D_POLL, D_UORI, D_STR, D_MISC, D_NET, D_SPAMC, D_RCPT,
-       D_COPY,
+       D_COPY, D_RFIL,
        D_MAX // must be last
 };
 
@@ -168,6 +168,7 @@
        struct in_addr connect_ip;      // remote IP address
        char *helo;
        SpamAssassin *assassin; // pointer to the SA object if we're processing 
a message
+       int rcpts; // must be positive if we are to do ANY spamassassin 
processing
 };
 
 /* This hack is the only way to call pointers to member functions! */
@@ -185,5 +186,6 @@
 int ip_in_networklist(struct in_addr ip, struct networklist *list);
 void parse_debuglevel(char* string);
 char *strlwr(char *str);
-
+bool rcpt_filter(SMFICTX*, char**);
+int cmp_email_addr(char *, char *);
 #endif

reply via email to

[Prev in Thread] Current Thread [Next in Thread]