[Top][All Lists]
[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
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- Patch for -R -a and -B loop prevention -- against CVS,
Joe Maimon <=