From dc7b3d368017727e9438fc7463b0841df1e4aa41 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Sun, 15 Feb 2015 16:50:27 +0000 Subject: [PATCH] SpamAssassin: replace "backup" option on server spec with "pri=". Bug 670 --- doc/doc-docbook/spec.xfpt | 22 +++++--- doc/doc-txt/ChangeLog | 2 +- src/src/spam.c | 93 ++++++++++++++++----------------- src/src/spam.h | 2 +- test/confs/4009 | 4 +- test/log/4009 | 5 ++ test/scripts/4000-scanning/4009 | 63 +++++++++++++++++++++- test/stdout/4009 | 55 +++++++++++++++++++ 8 files changed, 185 insertions(+), 61 deletions(-) diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index a112ec7e9..411eb988e 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -30829,20 +30829,29 @@ Elements after the first for Unix sockets, or second for TCP socket, are options. The supported option are: .code -variant=rspamd Use Rspamd rather than SpamAssassin protocol +pri= Selection priority +weight= Selection bias time=- Use only between these times of day +retry= Retry on connect fail tmo= Connection time limit -weight= Selection bias -backup Use only if all non-backup servers fail -retry= Retry on connect fail +variant=rspamd Use Rspamd rather than SpamAssassin protocol .endd +The &`pri`& option specifies a priority for the server within the list, +higher values being tried first. +The deafult priority is 1. + +The &`weight`& option specifies a selection bias. +Within a priority set +servers are queried in a random fashion, weighted by this value. +The default value for selection bias is 1. + Time specifications for the &`time`& option are .. in the local time zone; each element being one or more digits. Either the seconds or both minutes and seconds, plus the leading &`.`& characters, may be omitted and will be taken as zero. -Timeout specifications for the &`tmo`& and &`retry`& options +Timeout specifications for the &`retry`& and &`tmo`& options are the usual Exim time interval standard, eg. &`20s`& or &`1m`&. The &`tmo`& option specifies an overall timeout for communication. @@ -30851,9 +30860,6 @@ The default value is two minutes. The &`retry`& option specifies a time after which a single retry for a failed connect is made. The default is to not retry. - -Servers are queried in a random fashion, weighted by the selection bias. -The default value for selection bias is 1. .wen The &%spamd_address%& variable is expanded before use if it starts with diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 0548674f2..cff7f4445 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -54,7 +54,7 @@ JH/14 Bug 1573: The spam= ACL condition now additionally supports Rspamd. Patch from Andrew Lewis. JH/15 Bug 670: The spamd_address main option (for the spam= ACL condition) - now supports optional time-restrictions, weighting, and backup-only + now supports optional time-restrictions, weighting, and priority modifiers per server. Patch originally by . JH/16 The spamd_address main option now supports a mixed list of local diff --git a/src/src/spam.c b/src/src/spam.c index 4b99aca4b..05a51f636 100644 --- a/src/src/spam.c +++ b/src/src/spam.c @@ -27,12 +27,12 @@ static const uschar * loglabel = US"spam acl condition:"; static int spamd_param_init(spamd_address_container *spamd) { -/* default spamd server weight, time and backup value */ +/* default spamd server weight, time and priority value */ spamd->is_failed = FALSE; -spamd->is_backup = FALSE; spamd->weight = SPAMD_WEIGHT; spamd->timeout = SPAMD_TIMEOUT; spamd->retry = 0; +spamd->priority = 1; return 0; } @@ -44,16 +44,11 @@ static int timesinceday = -1; const uschar * s; const uschar * name; -/* check backup parameter */ -if (Ustrcmp(param, "backup") == 0) - { - spamd->is_backup = TRUE; - return 0; /* OK */ - } - /*XXX more clever parsing could discard embedded spaces? */ -/* check weight parameter */ +if (sscanf(param, "pri=%u", &spamd->priority)) + return 0; /* OK */ + if (sscanf(param, "weight=%u", &spamd->weight)) { if (spamd->weight == 0) /* this server disabled: skip it */ @@ -61,7 +56,6 @@ if (sscanf(param, "weight=%u", &spamd->weight)) return 0; /* OK */ } -/* check time parameter */ if (Ustrncmp(param, "time=", 5) == 0) { unsigned int start_h = 0, start_m = 0, start_s = 0; @@ -136,46 +130,50 @@ badval: static int -spamd_get_server(spamd_address_container **spamds, int num_servers) +spamd_get_server(spamd_address_container ** spamds, int num_servers) { unsigned int i; -long rnd, weights = 0; -static BOOL srandomed = 0; -BOOL usebackup = FALSE; - -for (;;) - { - /* seedup, if we have only 1 server */ - if (num_servers == 1) - return (spamds[0]->is_failed ? -1 : 0); +spamd_address_container * sd; +long rnd, weights; +unsigned pri; +static BOOL srandomed = FALSE; - /* init ranmod */ - if (!srandomed) - { - struct timeval tv; - gettimeofday(&tv, NULL); - srandom((unsigned int)(tv.tv_usec/1000)); - srandomed = TRUE; - } +/* seedup, if we have only 1 server */ +if (num_servers == 1) + return (spamds[0]->is_failed ? -1 : 0); - /* get sum of all weights */ - for (i = 0; i < num_servers; i++) - if (!spamds[i]->is_failed && spamds[i]->is_backup == usebackup) - weights += spamds[i]->weight; +/* init ranmod */ +if (!srandomed) + { + struct timeval tv; + gettimeofday(&tv, NULL); + srandom((unsigned int)(tv.tv_usec/1000)); + srandomed = TRUE; + } - if (weights != 0) - break; - if (usebackup) /* all servers failed (backups too) */ - return -1; - usebackup = TRUE; +/* scan for highest pri */ +for (pri = 0, i = 0; i < num_servers; i++) + { + sd = spamds[i]; + if (!sd->is_failed && sd->priority > pri) pri = sd->priority; } -rnd = random() % weights; +/* get sum of weights */ +for (weights = 0, i = 0; i < num_servers; i++) + { + sd = spamds[i]; + if (!sd->is_failed && sd->priority == pri) weights += sd->weight; + } +if (weights == 0) /* all servers failed */ + return -1; -for (i = 0; i < num_servers; i++) - if (!spamds[i]->is_failed && spamds[i]->is_backup == usebackup) - if ((rnd -= spamds[i]->weight) < 0) +for (rnd = random() % weights, i = 0; i < num_servers; i++) + { + sd = spamds[i]; + if (!sd->is_failed && sd->priority == pri) + if ((rnd -= sd->weight) <= 0) return i; + } log_write(0, LOG_MAIN|LOG_PANIC, "%s unknown error (memory/cpu corruption?)", loglabel); @@ -251,7 +249,7 @@ if (*spamd_address == '$') else spamd_address_work = spamd_address; -HDEBUG(D_acl) debug_printf("spamd: addrlist '%s'\n", spamd_address_work); +DEBUG(D_acl) debug_printf("spamd: addrlist '%s'\n", spamd_address_work); /* check if previous spamd_address was expanded and has changed. dump cached results if so */ if ( spam_ok @@ -280,12 +278,13 @@ start = time(NULL); { int num_servers = 0; int current_server; - uschar *address; - const uschar *spamd_address_list_ptr = spamd_address_work; + uschar * address; + const uschar * spamd_address_list_ptr = spamd_address_work; spamd_address_container * spamd_address_vector[32]; /* Check how many spamd servers we have and register their addresses */ + sep = 0; /* default colon-sep */ while ((address = string_nextinlist(&spamd_address_list_ptr, &sep, NULL, 0)) != NULL) { @@ -294,7 +293,7 @@ start = time(NULL); unsigned args; uschar * s; - HDEBUG(D_acl) debug_printf("spamd: addr entry '%s'\n", address); + DEBUG(D_acl) debug_printf("spamd: addr entry '%s'\n", address); sd = (spamd_address_container *)store_get(sizeof(spamd_address_container)); for (sublist = address, args = 0, spamd_param_init(sd); @@ -302,7 +301,7 @@ start = time(NULL); args++ ) { - HDEBUG(D_acl) debug_printf("spamd: addr parm '%s'\n", s); + DEBUG(D_acl) debug_printf("spamd: addr parm '%s'\n", s); switch (args) { case 0: sd->hostspec = s; diff --git a/src/src/spam.h b/src/src/spam.h index 0e3acfc25..e7da49beb 100644 --- a/src/src/spam.h +++ b/src/src/spam.h @@ -28,10 +28,10 @@ typedef struct spamd_address_container uschar * hostspec; int is_rspamd:1; int is_failed:1; - int is_backup:1; unsigned int weight; unsigned int timeout; unsigned int retry; + unsigned int priority; } spamd_address_container; #endif diff --git a/test/confs/4009 b/test/confs/4009 index 573aa6a4a..2799575ea 100644 --- a/test/confs/4009 +++ b/test/confs/4009 @@ -1,7 +1,7 @@ # Exim test configuration 4009 # Content-scan: spamassassin interface -OPT= +OPT= 127.0.0.1 7833 exim_path = EXIM_PATH host_lookup_order = bydns @@ -12,7 +12,7 @@ gecos_pattern = "" gecos_name = CALLER_NAME log_selector = +subject -spamd_address = 127.0.0.1 7833 OPT +spamd_address = OPT # ----- Main settings ----- diff --git a/test/log/4009 b/test/log/4009 index 4522ddb43..612744e20 100644 --- a/test/log/4009 +++ b/test/log/4009 @@ -10,3 +10,8 @@ 1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss 1999-03-02 09:44:33 10HmaZ-0005vi-00 => :blackhole: R=r 1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed +1999-03-02 09:44:33 10HmbA-0005vi-00 spam acl condition: spamd: failed to connect to any address for ::1: Connection refused +1999-03-02 09:44:33 10HmbA-0005vi-00 U=CALLER Warning: no action Spam detection software, running on the system "demo",\n has NOT identified this incoming email as spam. The original\n message has been attached to this so you can view it or label\n similar future email. If you have any questions, see\n @@CONTACT_ADDRESS@@ for details.\n \n Content preview: test [...]\n \n Content analysis details: (4.5 points, 5.0 required)\n \n pts rule name description\n ---- ---------------------- --------------------------------------------------\n -1.0 ALL_TRUSTED Passed through trusted hosts only via SMTP\n 1.2 MISSING_HEADERS Missing To: header\n 1.0 MISSING_FROM Missing From: header\n 1.8 MISSING_SUBJECT Missing Subject: header\n 1.4 MISSING_DATE Missing Date: header\n 0.1 MISSING_MID Missing Message-Id: header +1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss +1999-03-02 09:44:33 10HmbA-0005vi-00 => :blackhole: R=r +1999-03-02 09:44:33 10HmbA-0005vi-00 Completed diff --git a/test/scripts/4000-scanning/4009 b/test/scripts/4000-scanning/4009 index 4c2ab81c6..50f5ff990 100644 --- a/test/scripts/4000-scanning/4009 +++ b/test/scripts/4000-scanning/4009 @@ -105,7 +105,7 @@ server 7833 > *eof **** -exim -odi -bs -DOPT='retry=10s' +exim -odi -bs -DOPT='127.0.0.1 7833 retry=10s' ehlo test.ex mail from:<> rcpt to: @@ -163,7 +163,66 @@ server -i 2 7833 > *eof **** -exim -odi -bs -DOPT='retry=4s' +exim -odi -bs -DOPT='127.0.0.1 7833 retry=4s' +ehlo test.ex +mail from:<> +rcpt to: +data +Content-type: text/plain + +test +. +quit +**** +# +# +# +# Multiple servers, prioritised, with timeout spec; first one fails +# List separator changed +server 7833 +SPAMD/1.1 0 EX_OK +>Spam: False ; 4.5 / 5.0 +> +>Spam detection software, running on the system "demo", +>has NOT identified this incoming email as spam. The original +>message has been attached to this so you can view it or label +>similar future email. If you have any questions, see +>@@CONTACT_ADDRESS@@ for details. +> +>Content preview: test [...] +> +>Content analysis details: (4.5 points, 5.0 required) +> +> pts rule name description +>---- ---------------------- -------------------------------------------------- +>-1.0 ALL_TRUSTED Passed through trusted hosts only via SMTP +> 1.2 MISSING_HEADERS Missing To: header +> 1.0 MISSING_FROM Missing From: header +> 1.8 MISSING_SUBJECT Missing Subject: header +> 1.4 MISSING_DATE Missing Date: header +> 0.1 MISSING_MID Missing Message-Id: header +> +*eof +**** +exim -odi -bs -DOPT='<; 127.0.0.1 7833 ; ::1 7834 pri=2 tmo=2s' ehlo test.ex mail from:<> rcpt to: diff --git a/test/stdout/4009 b/test/stdout/4009 index 9220c7d0e..cdc767781 100644 --- a/test/stdout/4009 +++ b/test/stdout/4009 @@ -31,6 +31,17 @@ 354 Enter message, ending with "." on a line by itself 250 OK id=10HmaZ-0005vi-00 221 myhost.test.ex closing connection +220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000 +250-myhost.test.ex Hello CALLER at test.ex +250-SIZE 52428800 +250-8BITMIME +250-PIPELINING +250 HELP +250 OK +250 Accepted +354 Enter message, ending with "." on a line by itself +250 OK id=10HmbA-0005vi-00 +221 myhost.test.ex closing connection ******** SERVER ******** Listening on port 7833 ... @@ -166,3 +177,47 @@ Connection request from [127.0.0.1] > Expected EOF read from client End of script +Listening on port 7833 ... +Connection request from [127.0.0.1] + +) +< id 10HmbA-0005vi-00 +< for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 + + +SPAMD/1.1 0 EX_OK +>Spam: False ; 4.5 / 5.0 +> +>Spam detection software, running on the system "demo", +>has NOT identified this incoming email as spam. The original +>message has been attached to this so you can view it or label +>similar future email. If you have any questions, see +>@@CONTACT_ADDRESS@@ for details. +> +>Content preview: test [...] +> +>Content analysis details: (4.5 points, 5.0 required) +> +> pts rule name description +>---- ---------------------- -------------------------------------------------- +>-1.0 ALL_TRUSTED Passed through trusted hosts only via SMTP +> 1.2 MISSING_HEADERS Missing To: header +> 1.0 MISSING_FROM Missing From: header +> 1.8 MISSING_SUBJECT Missing Subject: header +> 1.4 MISSING_DATE Missing Date: header +> 0.1 MISSING_MID Missing Message-Id: header +> +Expected EOF read from client +End of script -- 2.30.2