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

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

Re: Recipient filtering...


From: Joe Maimon
Subject: Re: Recipient filtering...
Date: Tue, 21 Sep 2004 06:53:56 -0400
User-agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.7) Gecko/20040616 Mnenhy/0.6.0.101



Joe Maimon wrote:

Hello all,



This patch adds command line argument -R which accepts as an argument a program that will be called with a parameter of recpient email adrress, once per recipient.
(Yes i know, not the most resource cheap way)
When the program exits, the exit code of 0 is taken to mean "filtered successfully". If there are no un-filtered recipients, spamass-milter will return to sendmail a SMFIS_ACCEPT at the start of mlfi_header.

Any and all feedback is most welcome.

Joe


This version of the patch adds more debugging and switches from the system() function which is suspected to have introduced pthread/milter singaling issues to a fork.

This patch is in use for the past 16 hours without any apparent issues.

This will probably be the last patch against 0.2.0

Joe


Only in spamass-milter-0.2.0-orig/contrib: spamass-milter.spec
diff -ur spamass-milter-0.2.0-orig/spamass-milter.cpp 
spamass-milter-0.2.0/spamass-milter.cpp
--- spamass-milter-0.2.0-orig/spamass-milter.cpp        2003-06-26 
11:10:44.000000000 -0400
+++ spamass-milter-0.2.0/spamass-milter.cpp     2004-09-21 06:41:11.000000000 
-0400
@@ -150,13 +150,15 @@
   };
 
 const char *const debugstrings[] = {
-       "ALL", "FUNC", "POLL", "UORI", "STR", "MISC", "NET", "SPAMC",
+       "ALL", "FUNC", "POLL", "UORI", "STR", "MISC", "NET", "SPAMC", "RCPT",
        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 dontmodify = false;
 bool flag_sniffuser = false;
 char *defaultuser;
@@ -174,7 +176,7 @@
 main(int argc, char* argv[])
 {
    int c, err = 0;
-   const char *args = "p:fd:mr:u:D:i:b:B:";
+   const char *args = "p:fd:mr:R:u:D:i:b:B:";
    char *sock = NULL;
    bool dofork = false;
 
@@ -207,6 +209,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);
@@ -246,7 +252,7 @@
       cout << PACKAGE_NAME << " - Version " << PACKAGE_VERSION << endl;
       cout << "SpamAssassin Sendmail Milter Plugin" << endl;
       cout << "Usage: spamass-milter -p socket [-d nn] [-D host] [-f] [-i 
networks] [-m]" << endl;
-      cout << "                      [-r nn] [-u defaultuser] [-b|-B bucket ] 
[-- spamc args ]" << endl;
+      cout << "                      [-r nn] [-R rcpt_filter_prog] [-u 
defaultuser] [-b|-B bucket ] [-- spamc args ]" << endl;
       cout << "   -p socket: path to create socket" << endl;
       cout << "   -b bucket: redirect spam to this mail address.  The orignal" 
<< endl;
       cout << "          recipient(s) will not receive anything." << endl;
@@ -259,6 +265,7 @@
       cout << "   -m: don't modify body, Content-type: or Subject:" << 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 << "   -- spamc args: pass the remaining flags to spamc." << endl;
@@ -564,7 +571,10 @@
        debug(D_FUNC, "mlfi_connect: enter");
 
        /* allocate a structure to store the IP address (and SA object) in */
-       sctx = (struct context *)malloc(sizeof(*sctx));
+       sctx = (struct context *)malloc(sizeof(struct context));
+       if(!sctx)
+               return SMFIS_TEMPFAIL;
+       memset(sctx, '\0', sizeof(struct context)); 
        if (!hostaddr)
        {
                /* not a socket; probably a local user calling sendmail 
directly */
@@ -576,6 +586,7 @@
        }
        sctx->assassin = NULL;
        sctx->helo = NULL;
+       sctx->rcpts = 0;
        
        /* store a pointer to it with setpriv */
        smfi_setpriv(ctx, sctx);
@@ -718,9 +729,101 @@
        {
                assassin->recipients.push_back( *rcpt ); // XXX verify that 
this worked
        }
+
+       if(flag_rcpt_filter && rcpt_filter_program)
+               rcpt_filter(ctx, envrcpt);
        return SMFIS_CONTINUE;
 }
 
+
+void
+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;
+       
+       debug(D_RCPT,"%s: rcpt_filter: filtering",queue_id);
+       
+       if((tmp_rcpt = strdup(envrcpt[0])) == NULL)
+               return;
+       
+       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_RCPT,"%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_RCPT,"%s: rcpt_filter: Child here",queue_id);
+                       
+                       execvp(child_argv[0], child_argv);
+                       _exit(errno);
+                       break;;
+               case -1:
+                       // Failed
+                       debug(D_RCPT,"%s: rcpt_filter: fork failed: %d", 
queue_id, errno);
+                       break;
+               default:
+                       // Parent
+                       if(waitpid(child_pid, &child_status, 0)<0)
+                               debug(D_RCPT, "%s: rcpt_filter: waitpid failed: 
%d", queue_id, errno);
+                       if(WIFEXITED(child_status))
+                       {
+                               rcpt_filter_result = WEXITSTATUS(child_status);
+                               debug(D_RCPT,"%s: rcpt_filter: Child exit 
status %d",queue_id, rcpt_filter_result);
+                       }
+                       else
+                               debug(D_RCPT,"%s: rcpt_filter: Child exited 
abnormally",queue_id);
+                               
+                       break;
+       }
+       
+       if(!rcpt_filter_result)
+       {
+               debug(D_RCPT,"%s: rcpt_filter, removing rcpt", queue_id);
+               sctx->rcpts--;
+       } else
+               debug(D_RCPT,"%s: rcpt_filter, leaving rcpt", queue_id);
+
+
+       if(tmp_rcpt)
+       {
+               if(tmp_rcpt_borrowed)
+                       tmp_rcpt--;
+               free(tmp_rcpt);
+       }
+       
+       
+       debug(D_FUNC,"%s: rcpt_filter: leaveing normally", queue_id);   
+       return;
+}
 //
 // Gets called repeatedly for all header fields
 //
@@ -740,6 +843,21 @@
   SpamAssassin* assassin = ((struct context *)smfi_getpriv(ctx))->assassin;
   debug(D_FUNC, "mlfi_header: enter");
 
+  {
+       struct context * sctx = (struct context *) smfi_getpriv(ctx);
+       char * queue_id = smfi_getsymval(ctx,"i");
+       if(flag_rcpt_filter && !sctx->rcpts) 
+       {
+               debug(D_RCPT,"%s: rcpt_filter, accepting message without 
processing");
+               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_RCPT, "%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) )
      {
@@ -965,8 +1083,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;
 }
diff -ur spamass-milter-0.2.0-orig/spamass-milter.h 
spamass-milter-0.2.0/spamass-milter.h
--- spamass-milter-0.2.0-orig/spamass-milter.h  2003-06-14 15:17:41.000000000 
-0400
+++ spamass-milter-0.2.0/spamass-milter.h       2004-09-20 18:35:38.000000000 
-0400
@@ -63,7 +63,7 @@
 // Debug tokens.
 enum debuglevel 
 {
-       D_ALWAYS, D_FUNC, D_POLL, D_UORI, D_STR, D_MISC, D_NET, D_SPAMC,
+       D_ALWAYS, D_FUNC, D_POLL, D_UORI, D_STR, D_MISC, D_NET, D_SPAMC, D_RCPT,
        D_MAX // must be last
 };
 
@@ -153,6 +153,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! */
@@ -170,3 +171,4 @@
 int ip_in_networklist(struct in_addr ip, struct networklist *list);
 void parse_debuglevel(char* string);
 char *strlwr(char *str);
+void rcpt_filter(SMFICTX*, char**);
Only in spamass-milter-0.2.0-orig/contrib: spamass-milter.spec
diff -ur spamass-milter-0.2.0-orig/spamass-milter.cpp 
spamass-milter-0.2.0/spamass-milter.cpp
--- spamass-milter-0.2.0-orig/spamass-milter.cpp        2003-06-26 
11:10:44.000000000 -0400
+++ spamass-milter-0.2.0/spamass-milter.cpp     2004-09-21 06:41:11.000000000 
-0400
@@ -150,13 +150,15 @@
   };
 
 const char *const debugstrings[] = {
-       "ALL", "FUNC", "POLL", "UORI", "STR", "MISC", "NET", "SPAMC",
+       "ALL", "FUNC", "POLL", "UORI", "STR", "MISC", "NET", "SPAMC", "RCPT",
        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 dontmodify = false;
 bool flag_sniffuser = false;
 char *defaultuser;
@@ -174,7 +176,7 @@
 main(int argc, char* argv[])
 {
    int c, err = 0;
-   const char *args = "fd:mp:P:r:u:D:i:b:B:";
+   const char *args = "fd:mp:P:r:R:u:D:i:b:B:";
    char *sock = NULL;
    bool dofork = false;
 
@@ -207,6 +209,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);
@@ -246,7 +252,7 @@
       cout << PACKAGE_NAME << " - Version " << PACKAGE_VERSION << endl;
       cout << "SpamAssassin Sendmail Milter Plugin" << endl;
       cout << "Usage: spamass-milter -p socket [-d nn] [-D host] [-f] [-i 
networks] [-m]" << endl;
-      cout << "                      [-r nn] [-u defaultuser] [-b|-B bucket ] 
[-- spamc args ]" << endl;
+      cout << "                      [-r nn] [-R rcpt_filter_prog] [-u 
defaultuser] [-b|-B bucket ] [-- spamc args ]" << endl;
       cout << "   -p socket: path to create socket" << endl;
       cout << "   -b bucket: redirect spam to this mail address.  The orignal" 
<< endl;
       cout << "          recipient(s) will not receive anything." << endl;
@@ -259,6 +265,7 @@
       cout << "   -m: don't modify body, Content-type: or Subject:" << 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 << "   -- spamc args: pass the remaining flags to spamc." << endl;
@@ -564,7 +571,10 @@
        debug(D_FUNC, "mlfi_connect: enter");
 
        /* allocate a structure to store the IP address (and SA object) in */
-       sctx = (struct context *)malloc(sizeof(*sctx));
+       sctx = (struct context *)malloc(sizeof(struct context));
+       if(!sctx)
+               return SMFIS_TEMPFAIL;
+       memset(sctx, '\0', sizeof(struct context)); 
        if (!hostaddr)
        {
                /* not a socket; probably a local user calling sendmail 
directly */
@@ -576,6 +586,7 @@
        }
        sctx->assassin = NULL;
        sctx->helo = NULL;
+       sctx->rcpts = 0;
        
        /* store a pointer to it with setpriv */
        smfi_setpriv(ctx, sctx);
@@ -718,9 +729,101 @@
        {
                assassin->recipients.push_back( *rcpt ); // XXX verify that 
this worked
        }
+
+       if(flag_rcpt_filter && rcpt_filter_program)
+               rcpt_filter(ctx, envrcpt);
        return SMFIS_CONTINUE;
 }
 
+
+void
+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;
+       
+       debug(D_RCPT,"%s: rcpt_filter: filtering",queue_id);
+       
+       if((tmp_rcpt = strdup(envrcpt[0])) == NULL)
+               return;
+       
+       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_RCPT,"%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_RCPT,"%s: rcpt_filter: Child here",queue_id);
+                       
+                       execvp(child_argv[0], child_argv);
+                       _exit(errno);
+                       break;;
+               case -1:
+                       // Failed
+                       debug(D_RCPT,"%s: rcpt_filter: fork failed: %d", 
queue_id, errno);
+                       break;
+               default:
+                       // Parent
+                       if(waitpid(child_pid, &child_status, 0)<0)
+                               debug(D_RCPT, "%s: rcpt_filter: waitpid failed: 
%d", queue_id, errno);
+                       if(WIFEXITED(child_status))
+                       {
+                               rcpt_filter_result = WEXITSTATUS(child_status);
+                               debug(D_RCPT,"%s: rcpt_filter: Child exit 
status %d",queue_id, rcpt_filter_result);
+                       }
+                       else
+                               debug(D_RCPT,"%s: rcpt_filter: Child exited 
abnormally",queue_id);
+                               
+                       break;
+       }
+       
+       if(!rcpt_filter_result)
+       {
+               debug(D_RCPT,"%s: rcpt_filter, removing rcpt", queue_id);
+               sctx->rcpts--;
+       } else
+               debug(D_RCPT,"%s: rcpt_filter, leaving rcpt", queue_id);
+
+
+       if(tmp_rcpt)
+       {
+               if(tmp_rcpt_borrowed)
+                       tmp_rcpt--;
+               free(tmp_rcpt);
+       }
+       
+       
+       debug(D_FUNC,"%s: rcpt_filter: leaveing normally", queue_id);   
+       return;
+}
 //
 // Gets called repeatedly for all header fields
 //
@@ -740,6 +843,21 @@
   SpamAssassin* assassin = ((struct context *)smfi_getpriv(ctx))->assassin;
   debug(D_FUNC, "mlfi_header: enter");
 
+  {
+       struct context * sctx = (struct context *) smfi_getpriv(ctx);
+       char * queue_id = smfi_getsymval(ctx,"i");
+       if(flag_rcpt_filter && !sctx->rcpts) 
+       {
+               debug(D_RCPT,"%s: rcpt_filter, accepting message without 
processing");
+               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_RCPT, "%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) )
      {
@@ -965,8 +1083,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;
 }
diff -ur spamass-milter-0.2.0-orig/spamass-milter.h 
spamass-milter-0.2.0/spamass-milter.h
--- spamass-milter-0.2.0-orig/spamass-milter.h  2003-06-14 15:17:41.000000000 
-0400
+++ spamass-milter-0.2.0/spamass-milter.h       2004-09-20 18:35:38.000000000 
-0400
@@ -63,7 +63,7 @@
 // Debug tokens.
 enum debuglevel 
 {
-       D_ALWAYS, D_FUNC, D_POLL, D_UORI, D_STR, D_MISC, D_NET, D_SPAMC,
+       D_ALWAYS, D_FUNC, D_POLL, D_UORI, D_STR, D_MISC, D_NET, D_SPAMC, D_RCPT,
        D_MAX // must be last
 };
 
@@ -153,6 +153,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! */
@@ -170,3 +171,4 @@
 int ip_in_networklist(struct in_addr ip, struct networklist *list);
 void parse_debuglevel(char* string);
 char *strlwr(char *str);
+void rcpt_filter(SMFICTX*, char**);

reply via email to

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