SpamAssassin: replace "backup" option on server spec with "pri=<N>". Bug 670
authorJeremy Harris <jgh146exb@wizmail.org>
Sun, 15 Feb 2015 16:50:27 +0000 (16:50 +0000)
committerJeremy Harris <jgh146exb@wizmail.org>
Sun, 15 Feb 2015 16:50:27 +0000 (16:50 +0000)
doc/doc-docbook/spec.xfpt
doc/doc-txt/ChangeLog
src/src/spam.c
src/src/spam.h
test/confs/4009
test/log/4009
test/scripts/4000-scanning/4009
test/stdout/4009

index a112ec7e950aa56e5c05fb6a491123d6f3bff001..411eb988e8bf4c67c435c82cb02b7e3a096bb1aa 100644 (file)
@@ -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=<priority>      Selection priority
+weight=<value>      Selection bias
 time=<start>-<end>  Use only between these times of day
+retry=<timespec>    Retry on connect fail
 tmo=<timespec>      Connection time limit
-weight=<value>      Selection bias
-backup              Use only if all non-backup servers fail
-retry=<timespec>       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 <hour>.<minute>.<second>
 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
index 0548674f2552af9e7b849b245fcbd029cdc5673c..cff7f44452094e696105171a1fb10228f015a095 100644 (file)
@@ -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 <rommer@active.by>.
 
 JH/16 The spamd_address main option now supports a mixed list of local
index 4b99aca4b7e390b9091dcca88cefcfade6d0e43c..05a51f636547a7d9fcfade9996052a65b9ae4394 100644 (file)
@@ -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;
index 0e3acfc25a396fc3ce044fdf5d3844a1965aef1b..e7da49beb592278fedd19cf557ac35b68990e727 100644 (file)
@@ -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
index 573aa6a4aa8c957f06defc74dd6916aa19dd9b4d..2799575eadfad797c6d5af039bfdf275032dfad5 100644 (file)
@@ -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 -----
 
index 4522ddb431032bb891f167724bced2989396883d..612744e20b3d4fb26ded6b9258f4dfab12ec6784 100644 (file)
@@ -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: <userx@test.ex> 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: <userx@test.ex> R=r
+1999-03-02 09:44:33 10HmbA-0005vi-00 Completed
index 4c2ab81c6498bf6d74d765cddcead7db34b0f234..50f5ff990afbfff086670f6ae720f3b1e01a2df4 100644 (file)
@@ -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:<userx@test.ex>
@@ -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:<userx@test.ex>
+data
+Content-type: text/plain
+
+test
+.
+quit
+****
+#
+#
+#
+# Multiple servers, prioritised, with timeout spec; first one fails
+# List separator changed
+server 7833
+<REPORT SPAMC
+<User:
+<Content-length:
+<
+<From 
+<X-Envelope-From
+<X-Envelope-To
+<Received: 
+<      by 
+<      (envelope
+<      id 
+<      for 
+<Content-type: text/plain
+<Message-Id:
+<From:
+<Date:
+<
+<test
+>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:<userx@test.ex>
index 9220c7d0e8546ed9a08b9f4d56bdd5d132d31a66..cdc7677817705895b9e7141e3e6414433f8b0b46 100644 (file)
 354 Enter message, ending with "." on a line by itself\r
 250 OK id=10HmaZ-0005vi-00\r
 221 myhost.test.ex closing connection\r
+220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
+250-myhost.test.ex Hello CALLER at test.ex\r
+250-SIZE 52428800\r
+250-8BITMIME\r
+250-PIPELINING\r
+250 HELP\r
+250 OK\r
+250 Accepted\r
+354 Enter message, ending with "." on a line by itself\r
+250 OK id=10HmbA-0005vi-00\r
+221 myhost.test.ex closing connection\r
 
 ******** 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]
+<REPORT SPAMC/1.2
+<User: nobody
+<Content-length: 479
+<
+<From MAILER-DAEMON Tue Mar 02 09:44:33 1999
+<X-Envelope-From: <CALLER@myhost.test.ex>
+<X-Envelope-To: userx@test.ex
+<Received: from CALLER (helo=test.ex)
+<      by myhost.test.ex with local-esmtp (Exim x.yz)
+<      (envelope-from <CALLER@myhost.test.ex>)
+<      id 10HmbA-0005vi-00
+<      for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
+<Content-type: text/plain
+<Message-Id: <E10HmbA-0005vi-00@myhost.test.ex>
+<From: CALLER_NAME <CALLER@myhost.test.ex>
+<Date: Tue, 2 Mar 1999 09:44:33 +0000
+<
+<test
+>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