ACL: "seen" condition
authorJeremy Harris <jgh146exb@wizmail.org>
Sun, 1 Aug 2021 17:15:39 +0000 (18:15 +0100)
committerJeremy Harris <jgh146exb@wizmail.org>
Sun, 1 Aug 2021 17:15:39 +0000 (18:15 +0100)
doc/doc-docbook/spec.xfpt
doc/doc-txt/NewStuff
src/src/acl.c
src/src/dbstuff.h
src/src/exim_dbutil.c
test/confs/0626 [new file with mode: 0644]
test/scripts/0000-Basic/0626 [new file with mode: 0644]
test/stderr/0626 [new file with mode: 0644]
test/stdout/0626 [new file with mode: 0644]

index cea21a18f930f85ac2e8e94a2ff827e0b9fe0a98..397096dffd621bf74472df6bd124196371787cc5 100644 (file)
@@ -32315,6 +32315,14 @@ content-scanning extension, and is available only in the DATA, MIME, and
 non-SMTP ACLs. It causes the incoming message to be scanned for a match with
 any of the regular expressions. For details, see chapter &<<CHAPexiscan>>&.
 
 non-SMTP ACLs. It causes the incoming message to be scanned for a match with
 any of the regular expressions. For details, see chapter &<<CHAPexiscan>>&.
 
+.new
+.vitem &*seen&~=&~*&<&'parameters'&>
+.cindex "&%sseen%& ACL condition"
+This condition can be used to test if a situation has been previously met,
+for example for greylisting.
+Details are given in section &<<SECTseen>>&.
+.wen
+
 .vitem &*sender_domains&~=&~*&<&'domain&~list'&>
 .cindex "&%sender_domains%& ACL condition"
 .cindex "sender" "ACL checking"
 .vitem &*sender_domains&~=&~*&<&'domain&~list'&>
 .cindex "&%sender_domains%& ACL condition"
 .cindex "sender" "ACL checking"
@@ -33039,6 +33047,59 @@ address you should specify alternate list separators for both the outer
        dnslists = <; dnsbl.example.com/<|$acl_m_addrslist
 .endd
 
        dnslists = <; dnsbl.example.com/<|$acl_m_addrslist
 .endd
 
+
+.new
+.section "Previously seen user and hosts" "SECTseen"
+.cindex "&%sseen%& ACL condition"
+.cindex greylisting
+The &%seen%& ACL condition can be used to test whether a
+situation has been previously met.
+It uses a hints database to record a timestamp against a key.
+host. The syntax of the condition is:
+.display
+&`seen =`& <&'time interval'&> &`/`& <&'options'&>
+.endd
+
+For example,
+.code
+defer  seen = -5m / key=${sender_host_address}_$local_part@$domain
+.endd
+in a RCPT ACL will implement simple greylisting.
+
+The parameters for the condition
+are an interval followed, slash-separated, by a list of options.
+The interval is taken as an offset before the current time,
+and used for the test.
+If the interval is preceded by a minus sign then the condition returns
+whether a record is found which is before the test time.
+Otherwise, the condition returns whether one is found which is since the
+test time.
+
+Options are read in order with later ones overriding earlier ones.
+
+The default key is &$sender_host_address$&.
+An explicit key can be set using a &%key=value%& option.
+
+If a &%readonly%& option is given then
+no record create or update is done.
+If a &%write%& option is given then
+a record create or update is always done.
+An update is done if the test is for &"since"&.
+
+Creates and updates are marked with the current time.
+
+Finally, a &"before"& test which succeeds, and for which the record
+is old enough, will be refreshed with a timstamp of the test time.
+This can prevent tidying of the database from removing the entry.
+The interval for this is, by default, 10 days.
+An explicit interval can be set using a
+&%refresh=value%& option.
+
+Note that &"seen"& should be added to the list of hints databases
+for maintenance if this ACL condition is used.
+.wen
+
+
 .section "Rate limiting incoming messages" "SECTratelimiting"
 .cindex "rate limiting" "client sending"
 .cindex "limiting client sending rates"
 .section "Rate limiting incoming messages" "SECTratelimiting"
 .cindex "rate limiting" "client sending"
 .cindex "limiting client sending rates"
index 6f3d4b31c3380c4ef208ecc702d840f51a1ca5cc..478446b41f1c0ee859cf65af717ca6def5afb8c6 100644 (file)
@@ -6,6 +6,12 @@ Before a formal release, there may be quite a lot of detail so that people can
 test from the snapshots or the Git before the documentation is updated. Once
 the documentation is updated, this file is reduced to a short list.
 
 test from the snapshots or the Git before the documentation is updated. Once
 the documentation is updated, this file is reduced to a short list.
 
+Version 4.96
+------------
+
+ 1. A new ACL condition: seen. Records/tests a timestamp against a key.
+
+
 Version 4.95
 ------------
 
 Version 4.95
 ------------
 
index f47259ca08b6c5269f230d632af7afadd570db9e..be17b5768cf80bdddecbf4e7a0754abe613116bd 100644 (file)
@@ -103,6 +103,7 @@ enum { ACLC_ACL,
        ACLC_REGEX,
 #endif
        ACLC_REMOVE_HEADER,
        ACLC_REGEX,
 #endif
        ACLC_REMOVE_HEADER,
+       ACLC_SEEN,
        ACLC_SENDER_DOMAINS,
        ACLC_SENDERS,
        ACLC_SET,
        ACLC_SENDER_DOMAINS,
        ACLC_SENDERS,
        ACLC_SET,
@@ -288,6 +289,7 @@ static condition_def conditions[] = {
                                    ACL_BIT_MIME | ACL_BIT_NOTSMTP |
                                    ACL_BIT_NOTSMTP_START),
   },
                                    ACL_BIT_MIME | ACL_BIT_NOTSMTP |
                                    ACL_BIT_NOTSMTP_START),
   },
+  [ACLC_SEEN] =                        { US"seen",             TRUE, FALSE,    0 },
   [ACLC_SENDER_DOMAINS] =      { US"sender_domains",   FALSE, FALSE,
                                  ACL_BIT_AUTH | ACL_BIT_CONNECT |
                                    ACL_BIT_HELO |
   [ACLC_SENDER_DOMAINS] =      { US"sender_domains",   FALSE, FALSE,
                                  ACL_BIT_AUTH | ACL_BIT_CONNECT |
                                    ACL_BIT_HELO |
@@ -2815,6 +2817,143 @@ return rc;
 
 
 
 
 
 
+/*************************************************
+*      Handle a check for previously-seen        *
+*************************************************/
+
+/*
+ACL clauses like:   seen = -5m / key=$foo / readonly
+
+Return is true for condition-true - but the semantics
+depend heavily on the actual use-case.
+
+Negative times test for seen-before, positive for seen-more-recently-than
+(the given interval before current time).
+
+All are subject to history not having been cleaned from the DB.
+
+Default for seen-before is to create if not present, and to
+update if older than 10d (with the seen-test time).
+Default for seen-since is to always create or update.
+
+Options:
+  key=value.  Default key is $sender_host_address
+  readonly
+  write
+  refresh=<interval>:  update an existing DB entry older than given
+                       amount.  Default refresh lacking this option is 10d.
+                       The update sets the record timestamp to the seen-test time.
+
+XXX do we need separate nocreate, noupdate controls?
+
+Arguments:
+  arg         the option string for seen=
+  where       ACL_WHERE_xxxx indicating which ACL this is
+  log_msgptr  for error messages
+
+Returns:       OK        - Condition is true
+               FAIL      - Condition is false
+               DEFER     - Problem opening history database
+               ERROR     - Syntax error in options
+*/
+
+static int
+acl_seen(const uschar * arg, int where, uschar ** log_msgptr)
+{
+enum { SEEN_DEFAULT, SEEN_READONLY, SEEN_WRITE };
+
+const uschar * list = arg;
+int slash = '/', equal = '=', interval, mode = SEEN_DEFAULT, yield = FAIL;
+BOOL before;
+int refresh = 10 * 24 * 60 * 60;       /* 10 days */
+const uschar * ele, * key = sender_host_address;
+open_db dbblock, * dbm;
+dbdata_seen * dbd;
+time_t now;
+
+/* Parse the first element, the time-relation. */
+
+if (!(ele = string_nextinlist(&list, &slash, NULL, 0)))
+  goto badparse;
+if ((before = *ele == '-'))
+  ele++;
+if ((interval = readconf_readtime(ele, 0, FALSE)) < 0)
+  goto badparse;
+
+/* Remaining elements are options */
+
+while ((ele = string_nextinlist(&list, &slash, NULL, 0)))
+  if (Ustrncmp(ele, "key=", 4) == 0)
+    key = ele + 4;
+  else if (Ustrcmp(ele, "readonly") == 0)
+    mode = SEEN_READONLY;
+  else if (Ustrcmp(ele, "write") == 0)
+    mode = SEEN_WRITE;
+  else if (Ustrncmp(ele, "refresh=", 8) == 0)
+    {
+    if ((refresh = readconf_readtime(ele + 8, 0, FALSE)) < 0)
+      goto badparse;
+    }
+  else
+    goto badopt;
+
+if (!(dbm = dbfn_open(US"seen", O_RDWR, &dbblock, TRUE, TRUE)))
+  {
+  HDEBUG(D_acl) debug_printf_indent("database for 'seen' not available\n");
+  *log_msgptr = US"database for 'seen' not available";
+  return DEFER;
+  }
+
+dbd = dbfn_read_with_length(dbm, key, NULL);
+now = time(NULL);
+if (dbd)               /* an existing record */
+  {
+  time_t diff = now - dbd->time_stamp; /* time since the record was written */
+
+  if (before ? diff >= interval : diff < interval)
+    yield = OK;
+
+  if (mode == SEEN_READONLY)
+    { HDEBUG(D_acl) debug_printf_indent("seen db not written (readonly)\n"); }
+  else if (mode == SEEN_WRITE || !before)
+    {
+    dbd->time_stamp = now;
+    dbfn_write(dbm, key, dbd, sizeof(*dbd));
+    HDEBUG(D_acl) debug_printf_indent("seen db written (update)\n");
+    }
+  else if (diff >= refresh)
+    {
+    dbd->time_stamp = now - interval;
+    dbfn_write(dbm, key, dbd, sizeof(*dbd));
+    HDEBUG(D_acl) debug_printf_indent("seen db written (refresh)\n");
+    }
+  }
+else
+  {                    /* No record found, yield always FAIL */
+  if (mode != SEEN_READONLY)
+    {
+    dbdata_seen d = {.time_stamp = now};
+    dbfn_write(dbm, key, &d, sizeof(*dbd));
+    HDEBUG(D_acl) debug_printf_indent("seen db written (create)\n");
+    }
+  else
+    HDEBUG(D_acl) debug_printf_indent("seen db not written (readonly)\n");
+  }
+
+dbfn_close(dbm);
+return yield;
+
+
+badparse:
+  *log_msgptr = string_sprintf("failed to parse '%s'", arg);
+  return ERROR;
+badopt:
+  *log_msgptr = string_sprintf("unrecognised option '%s' in '%s'", ele, arg);
+  return ERROR;
+}
+
+
+
 /*************************************************
 *            The udpsend ACL modifier            *
 *************************************************/
 /*************************************************
 *            The udpsend ACL modifier            *
 *************************************************/
@@ -3740,6 +3879,10 @@ for (; cb; cb = cb->next)
     setup_remove_header(arg);
     break;
 
     setup_remove_header(arg);
     break;
 
+    case ACLC_SEEN:
+    rc = acl_seen(arg, where, log_msgptr);
+    break;
+
     case ACLC_SENDER_DOMAINS:
       {
       uschar *sdomain;
     case ACLC_SENDER_DOMAINS:
       {
       uschar *sdomain;
index 2f00dffb42d9c09c046b3ad8abb5818d522457ff..94db7f7fda92693f557bf685d2e795fd7e79389a 100644 (file)
@@ -788,6 +788,12 @@ typedef struct {
   uschar   bloom[40];     /* Bloom filter which may be larger than this */
 } dbdata_ratelimit_unique;
 
   uschar   bloom[40];     /* Bloom filter which may be larger than this */
 } dbdata_ratelimit_unique;
 
+
+/* For "seen" ACL condition */
+typedef struct {
+  time_t time_stamp;
+} dbdata_seen;
+
 #ifndef DISABLE_PIPE_CONNECT
 /* This structure records the EHLO responses, cleartext and crypted,
 for an IP, as bitmasks (cf. OPTION_TLS).  For LIMITS, also values
 #ifndef DISABLE_PIPE_CONNECT
 /* This structure records the EHLO responses, cleartext and crypted,
 for an IP, as bitmasks (cf. OPTION_TLS).  For LIMITS, also values
index 13f74540e3adf2c99f3d25fcee6d8005cd5173a0..45b778fc0aacc8039efa97b3ec22fcad392ff64f 100644 (file)
@@ -21,7 +21,9 @@ argument is the name of the database file. The available names are:
   misc:       miscellaneous hints data
   wait-<t>:   message waiting information; <t> is a transport name
   callout:    callout verification cache
   misc:       miscellaneous hints data
   wait-<t>:   message waiting information; <t> is a transport name
   callout:    callout verification cache
+  ratelimit:  ACL 'ratelimit' condition
   tls:       TLS session resumption cache
   tls:       TLS session resumption cache
+  seen:              ACL 'seen' condition
 
 There are a number of common subroutines, followed by three main programs,
 whose inclusion is controlled by -D on the compilation command. */
 
 There are a number of common subroutines, followed by three main programs,
 whose inclusion is controlled by -D on the compilation command. */
@@ -38,6 +40,7 @@ whose inclusion is controlled by -D on the compilation command. */
 #define type_callout   4
 #define type_ratelimit 5
 #define type_tls       6
 #define type_callout   4
 #define type_ratelimit 5
 #define type_tls       6
+#define type_seen      7
 
 
 /* This is used by our cut-down dbfn_open(). */
 
 
 /* This is used by our cut-down dbfn_open(). */
@@ -126,7 +129,7 @@ static void
 usage(uschar *name, uschar *options)
 {
 printf("Usage: exim_%s%s  <spool-directory> <database-name>\n", name, options);
 usage(uschar *name, uschar *options)
 {
 printf("Usage: exim_%s%s  <spool-directory> <database-name>\n", name, options);
-printf("  <database-name> = retry | misc | wait-<transport-name> | callout | ratelimit | tls\n");
+printf("  <database-name> = retry | misc | wait-<transport-name> | callout | ratelimit | tls | seen\n");
 exit(1);
 }
 
 exit(1);
 }
 
@@ -150,6 +153,7 @@ if (argc == 3)
   if (Ustrcmp(argv[2], "callout") == 0) return type_callout;
   if (Ustrcmp(argv[2], "ratelimit") == 0) return type_ratelimit;
   if (Ustrcmp(argv[2], "tls") == 0) return type_tls;
   if (Ustrcmp(argv[2], "callout") == 0) return type_callout;
   if (Ustrcmp(argv[2], "ratelimit") == 0) return type_ratelimit;
   if (Ustrcmp(argv[2], "tls") == 0) return type_tls;
+  if (Ustrcmp(argv[2], "seen") == 0) return type_seen;
   }
 usage(name, options);
 return -1;              /* Never obeyed */
   }
 usage(name, options);
 return -1;              /* Never obeyed */
@@ -581,6 +585,7 @@ for (uschar * key = dbfn_scan(dbm, TRUE, &cursor);
   dbdata_ratelimit *ratelimit;
   dbdata_ratelimit_unique *rate_unique;
   dbdata_tls_session *session;
   dbdata_ratelimit *ratelimit;
   dbdata_ratelimit_unique *rate_unique;
   dbdata_tls_session *session;
+  dbdata_seen *seen;
   int count_bad = 0;
   int length;
   uschar *t;
   int count_bad = 0;
   int length;
   uschar *t;
@@ -720,6 +725,11 @@ for (uschar * key = dbfn_scan(dbm, TRUE, &cursor);
        session = (dbdata_tls_session *)value;
        printf("  %s %.*s\n", keybuffer, length, session->session);
        break;
        session = (dbdata_tls_session *)value;
        printf("  %s %.*s\n", keybuffer, length, session->session);
        break;
+
+      case type_seen:
+       seen = (dbdata_seen *)value;
+       printf("%s\t%s\n", keybuffer, print_time(seen->time_stamp));
+       break;
       }
     }
   store_reset(reset_point);
       }
     }
   store_reset(reset_point);
diff --git a/test/confs/0626 b/test/confs/0626
new file mode 100644 (file)
index 0000000..872c4b2
--- /dev/null
@@ -0,0 +1,31 @@
+# Exim test configuration 0626
+# ACL seen condition
+
+.include DIR/aux-var/std_conf_prefix
+
+
+# ----- Main settings -----
+
+primary_hostname = test.ex
+queue_only
+
+acl_smtp_rcpt = chk_rcpt
+
+# ----- ACL -----
+
+begin acl
+
+chk_rcpt:
+  accept       seen = OPT
+
+# seen = never / $sender_host_addreee / per_call
+# seen = before=10s
+# seen = before=10s / write
+# seen = since / readonly
+#
+# seen = -10s
+# seen = -10s / readonly
+# seen = 2s
+# seen = 0s / update=20d
+#
+# End
diff --git a/test/scripts/0000-Basic/0626 b/test/scripts/0000-Basic/0626
new file mode 100644 (file)
index 0000000..6da58ee
--- /dev/null
@@ -0,0 +1,82 @@
+# ACL 'seen' condition
+#
+exim -DOPT='-1s' -bh 127.0.0.1
+HELO test
+MAIL FROM:<tester@test.ex>
+RCPT TO:<a1@test.ex>
+QUIT
+****
+# Check that a hints DB was created.
+# Only the key is useful thanks to munging; should match the IP used above.
+dump seen
+#
+sleep 1
+# should now see old-enough record
+exim -DOPT='-1s' -bh 127.0.0.1
+HELO test
+MAIL FROM:<tester@test.ex>
+RCPT TO:<a1@test.ex>
+QUIT
+****
+# force an update (visible via debug output in stdout for -bh)
+exim -DOPT='-1s / write' -bh 127.0.0.1
+HELO test
+MAIL FROM:<tester@test.ex>
+RCPT TO:<a1@test.ex>
+QUIT
+****
+# default key should change with ip
+exim -DOPT='-1s' -bh HOSTIPV4
+HELO test
+MAIL FROM:<tester@test.ex>
+RCPT TO:<a1@test.ex>
+QUIT
+****
+dump seen
+# explicit key (also checking expansion)
+exim -DOPT='-1s / key=${sender_host_address}_foo' -bh 127.0.0.1
+HELO test
+MAIL FROM:<tester@test.ex>
+RCPT TO:<a1@test.ex>
+QUIT
+****
+dump seen
+# check refresh
+sleep 1
+exim -DOPT='-1s / refresh=1s' -bh 127.0.0.1
+HELO test
+MAIL FROM:<tester@test.ex>
+RCPT TO:<a1@test.ex>
+QUIT
+****
+#
+#
+#
+#
+#
+# test for seen-more-recently-than
+# that previous one should be no older than 5s, so this should pass
+# do not update
+# check list-parsing spaceless while we're here
+exim -DOPT='5s/key=${sender_host_address}_foo/readonly' -bh 127.0.0.1
+HELO test
+MAIL FROM:<tester@test.ex>
+RCPT TO:<a1@test.ex>
+QUIT
+****
+# check the above no-update by waiting longer than the later-than interval; should fail
+# should update
+sleep 2
+exim -DOPT='1s / key=${sender_host_address}_foo' -bh 127.0.0.1
+HELO test
+MAIL FROM:<tester@test.ex>
+RCPT TO:<a1@test.ex>
+QUIT
+****
+# having updated, should pass
+exim -DOPT='1s / key=${sender_host_address}_foo' -bh 127.0.0.1
+HELO test
+MAIL FROM:<tester@test.ex>
+RCPT TO:<a1@test.ex>
+QUIT
+****
diff --git a/test/stderr/0626 b/test/stderr/0626
new file mode 100644 (file)
index 0000000..25e96bc
--- /dev/null
@@ -0,0 +1,142 @@
+>>> host in hosts_connection_nolog? no (option unset)
+>>> host in host_lookup? no (option unset)
+>>> host in host_reject_connection? no (option unset)
+>>> host in sender_unqualified_hosts? no (option unset)
+>>> host in recipient_unqualified_hosts? no (option unset)
+>>> host in helo_verify_hosts? no (option unset)
+>>> host in helo_try_verify_hosts? no (option unset)
+>>> host in helo_accept_junk_hosts? no (option unset)
+>>> test in helo_lookup_domains? no (end of list)
+>>> using ACL "chk_rcpt"
+>>> processing "accept" (TESTSUITE/test-config 19)
+>>> check seen = -1s
+>>> seen db written (create)
+>>> accept: condition test failed in ACL "chk_rcpt"
+>>> end of ACL "chk_rcpt": implicit DENY
+LOG: H=(test) [127.0.0.1] F=<tester@test.ex> rejected RCPT <a1@test.ex>
+>>> host in hosts_connection_nolog? no (option unset)
+>>> host in host_lookup? no (option unset)
+>>> host in host_reject_connection? no (option unset)
+>>> host in sender_unqualified_hosts? no (option unset)
+>>> host in recipient_unqualified_hosts? no (option unset)
+>>> host in helo_verify_hosts? no (option unset)
+>>> host in helo_try_verify_hosts? no (option unset)
+>>> host in helo_accept_junk_hosts? no (option unset)
+>>> test in helo_lookup_domains? no (end of list)
+>>> using ACL "chk_rcpt"
+>>> processing "accept" (TESTSUITE/test-config 19)
+>>> check seen = -1s
+>>> accept: condition test succeeded in ACL "chk_rcpt"
+>>> end of ACL "chk_rcpt": ACCEPT
+>>> host in hosts_connection_nolog? no (option unset)
+>>> host in host_lookup? no (option unset)
+>>> host in host_reject_connection? no (option unset)
+>>> host in sender_unqualified_hosts? no (option unset)
+>>> host in recipient_unqualified_hosts? no (option unset)
+>>> host in helo_verify_hosts? no (option unset)
+>>> host in helo_try_verify_hosts? no (option unset)
+>>> host in helo_accept_junk_hosts? no (option unset)
+>>> test in helo_lookup_domains? no (end of list)
+>>> using ACL "chk_rcpt"
+>>> processing "accept" (TESTSUITE/test-config 19)
+>>> check seen = -1s / write
+>>> seen db written (update)
+>>> accept: condition test succeeded in ACL "chk_rcpt"
+>>> end of ACL "chk_rcpt": ACCEPT
+>>> host in hosts_connection_nolog? no (option unset)
+>>> host in host_lookup? no (option unset)
+>>> host in host_reject_connection? no (option unset)
+>>> host in sender_unqualified_hosts? no (option unset)
+>>> host in recipient_unqualified_hosts? no (option unset)
+>>> host in helo_verify_hosts? no (option unset)
+>>> host in helo_try_verify_hosts? no (option unset)
+>>> host in helo_accept_junk_hosts? no (option unset)
+>>> test in helo_lookup_domains? no (end of list)
+>>> using ACL "chk_rcpt"
+>>> processing "accept" (TESTSUITE/test-config 19)
+>>> check seen = -1s
+>>> seen db written (create)
+>>> accept: condition test failed in ACL "chk_rcpt"
+>>> end of ACL "chk_rcpt": implicit DENY
+LOG: H=(test) [ip4.ip4.ip4.ip4] F=<tester@test.ex> rejected RCPT <a1@test.ex>
+>>> host in hosts_connection_nolog? no (option unset)
+>>> host in host_lookup? no (option unset)
+>>> host in host_reject_connection? no (option unset)
+>>> host in sender_unqualified_hosts? no (option unset)
+>>> host in recipient_unqualified_hosts? no (option unset)
+>>> host in helo_verify_hosts? no (option unset)
+>>> host in helo_try_verify_hosts? no (option unset)
+>>> host in helo_accept_junk_hosts? no (option unset)
+>>> test in helo_lookup_domains? no (end of list)
+>>> using ACL "chk_rcpt"
+>>> processing "accept" (TESTSUITE/test-config 19)
+>>> check seen = -1s / key=${sender_host_address}_foo
+>>>            = -1s / key=127.0.0.1_foo
+>>> seen db written (create)
+>>> accept: condition test failed in ACL "chk_rcpt"
+>>> end of ACL "chk_rcpt": implicit DENY
+LOG: H=(test) [127.0.0.1] F=<tester@test.ex> rejected RCPT <a1@test.ex>
+>>> host in hosts_connection_nolog? no (option unset)
+>>> host in host_lookup? no (option unset)
+>>> host in host_reject_connection? no (option unset)
+>>> host in sender_unqualified_hosts? no (option unset)
+>>> host in recipient_unqualified_hosts? no (option unset)
+>>> host in helo_verify_hosts? no (option unset)
+>>> host in helo_try_verify_hosts? no (option unset)
+>>> host in helo_accept_junk_hosts? no (option unset)
+>>> test in helo_lookup_domains? no (end of list)
+>>> using ACL "chk_rcpt"
+>>> processing "accept" (TESTSUITE/test-config 19)
+>>> check seen = -1s / refresh=1s
+>>> seen db written (refresh)
+>>> accept: condition test succeeded in ACL "chk_rcpt"
+>>> end of ACL "chk_rcpt": ACCEPT
+>>> host in hosts_connection_nolog? no (option unset)
+>>> host in host_lookup? no (option unset)
+>>> host in host_reject_connection? no (option unset)
+>>> host in sender_unqualified_hosts? no (option unset)
+>>> host in recipient_unqualified_hosts? no (option unset)
+>>> host in helo_verify_hosts? no (option unset)
+>>> host in helo_try_verify_hosts? no (option unset)
+>>> host in helo_accept_junk_hosts? no (option unset)
+>>> test in helo_lookup_domains? no (end of list)
+>>> using ACL "chk_rcpt"
+>>> processing "accept" (TESTSUITE/test-config 19)
+>>> check seen = 5s/key=${sender_host_address}_foo/readonly
+>>>            = 5s/key=127.0.0.1_foo/readonly
+>>> seen db not written (readonly)
+>>> accept: condition test succeeded in ACL "chk_rcpt"
+>>> end of ACL "chk_rcpt": ACCEPT
+>>> host in hosts_connection_nolog? no (option unset)
+>>> host in host_lookup? no (option unset)
+>>> host in host_reject_connection? no (option unset)
+>>> host in sender_unqualified_hosts? no (option unset)
+>>> host in recipient_unqualified_hosts? no (option unset)
+>>> host in helo_verify_hosts? no (option unset)
+>>> host in helo_try_verify_hosts? no (option unset)
+>>> host in helo_accept_junk_hosts? no (option unset)
+>>> test in helo_lookup_domains? no (end of list)
+>>> using ACL "chk_rcpt"
+>>> processing "accept" (TESTSUITE/test-config 19)
+>>> check seen = 1s / key=${sender_host_address}_foo
+>>>            = 1s / key=127.0.0.1_foo
+>>> seen db written (update)
+>>> accept: condition test failed in ACL "chk_rcpt"
+>>> end of ACL "chk_rcpt": implicit DENY
+LOG: H=(test) [127.0.0.1] F=<tester@test.ex> rejected RCPT <a1@test.ex>
+>>> host in hosts_connection_nolog? no (option unset)
+>>> host in host_lookup? no (option unset)
+>>> host in host_reject_connection? no (option unset)
+>>> host in sender_unqualified_hosts? no (option unset)
+>>> host in recipient_unqualified_hosts? no (option unset)
+>>> host in helo_verify_hosts? no (option unset)
+>>> host in helo_try_verify_hosts? no (option unset)
+>>> host in helo_accept_junk_hosts? no (option unset)
+>>> test in helo_lookup_domains? no (end of list)
+>>> using ACL "chk_rcpt"
+>>> processing "accept" (TESTSUITE/test-config 19)
+>>> check seen = 1s / key=${sender_host_address}_foo
+>>>            = 1s / key=127.0.0.1_foo
+>>> seen db written (update)
+>>> accept: condition test succeeded in ACL "chk_rcpt"
+>>> end of ACL "chk_rcpt": ACCEPT
diff --git a/test/stdout/0626 b/test/stdout/0626
new file mode 100644 (file)
index 0000000..44b481f
--- /dev/null
@@ -0,0 +1,99 @@
+
+**** SMTP testing session as if from host 127.0.0.1
+**** but without any ident (RFC 1413) callback.
+**** This is not for real!
+
+220 test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
+250 test.ex Hello test [127.0.0.1]\r
+250 OK\r
+550 Administrative prohibition\r
+221 test.ex closing connection\r
++++++++++++++++++++++++++++
+127.0.0.1      07-Mar-2000 12:21:52
+
+**** SMTP testing session as if from host 127.0.0.1
+**** but without any ident (RFC 1413) callback.
+**** This is not for real!
+
+220 test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
+250 test.ex Hello test [127.0.0.1]\r
+250 OK\r
+250 Accepted\r
+221 test.ex closing connection\r
+
+**** SMTP testing session as if from host 127.0.0.1
+**** but without any ident (RFC 1413) callback.
+**** This is not for real!
+
+220 test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
+250 test.ex Hello test [127.0.0.1]\r
+250 OK\r
+250 Accepted\r
+221 test.ex closing connection\r
+
+**** SMTP testing session as if from host ip4.ip4.ip4.ip4
+**** but without any ident (RFC 1413) callback.
+**** This is not for real!
+
+220 test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
+250 test.ex Hello test [ip4.ip4.ip4.ip4]\r
+250 OK\r
+550 Administrative prohibition\r
+221 test.ex closing connection\r
++++++++++++++++++++++++++++
+ip4.ip4.ip4.ip4        07-Mar-2000 12:21:52
+127.0.0.1      07-Mar-2000 12:21:52
+
+**** SMTP testing session as if from host 127.0.0.1
+**** but without any ident (RFC 1413) callback.
+**** This is not for real!
+
+220 test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
+250 test.ex Hello test [127.0.0.1]\r
+250 OK\r
+550 Administrative prohibition\r
+221 test.ex closing connection\r
++++++++++++++++++++++++++++
+127.0.0.1_foo  07-Mar-2000 12:21:52
+ip4.ip4.ip4.ip4        07-Mar-2000 12:21:52
+127.0.0.1      07-Mar-2000 12:21:52
+
+**** SMTP testing session as if from host 127.0.0.1
+**** but without any ident (RFC 1413) callback.
+**** This is not for real!
+
+220 test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
+250 test.ex Hello test [127.0.0.1]\r
+250 OK\r
+250 Accepted\r
+221 test.ex closing connection\r
+
+**** SMTP testing session as if from host 127.0.0.1
+**** but without any ident (RFC 1413) callback.
+**** This is not for real!
+
+220 test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
+250 test.ex Hello test [127.0.0.1]\r
+250 OK\r
+250 Accepted\r
+221 test.ex closing connection\r
+
+**** SMTP testing session as if from host 127.0.0.1
+**** but without any ident (RFC 1413) callback.
+**** This is not for real!
+
+220 test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
+250 test.ex Hello test [127.0.0.1]\r
+250 OK\r
+550 Administrative prohibition\r
+221 test.ex closing connection\r
+
+**** SMTP testing session as if from host 127.0.0.1
+**** but without any ident (RFC 1413) callback.
+**** This is not for real!
+
+220 test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
+250 test.ex Hello test [127.0.0.1]\r
+250 OK\r
+250 Accepted\r
+221 test.ex closing connection\r