DNS: time-limit cached returns, using TTL. Bug 1395
authorJeremy Harris <jgh146exb@wizmail.org>
Thu, 17 Sep 2015 12:35:16 +0000 (13:35 +0100)
committerJeremy Harris <jgh146exb@wizmail.org>
Thu, 17 Sep 2015 12:35:16 +0000 (13:35 +0100)
This can matter for fast-changing data such as DNSBLs.

39 files changed:
doc/doc-docbook/spec.xfpt
doc/doc-txt/ChangeLog
src/src/dns.c
src/src/lookupapi.h
src/src/lookups/README
src/src/lookups/cdb.c
src/src/lookups/dbmdb.c
src/src/lookups/dnsdb.c
src/src/lookups/dsearch.c
src/src/lookups/ibase.c
src/src/lookups/ldap.c
src/src/lookups/lf_functions.h
src/src/lookups/lf_sqlperform.c
src/src/lookups/lsearch.c
src/src/lookups/mysql.c
src/src/lookups/nis.c
src/src/lookups/nisplus.c
src/src/lookups/oracle.c
src/src/lookups/passwd.c
src/src/lookups/pgsql.c
src/src/lookups/redis.c
src/src/lookups/spf.c
src/src/lookups/sqlite.c
src/src/lookups/testdb.c
src/src/lookups/whoson.c
src/src/search.c
src/src/structs.h
src/src/verify.c
test/confs/2200
test/confs/2201
test/dnszones-src/db.test.ex
test/log/2200 [new file with mode: 0644]
test/log/2201 [new file with mode: 0644]
test/scripts/2200-dnsdb/2200
test/scripts/2200-dnsdb/2201
test/src/fakens.c
test/stderr/2200 [new file with mode: 0644]
test/stderr/2201
test/stdout/2200

index 34fbed88f87b8ed823c160e5971d11973ca45f74..ed3533a0c8bfd05007cc3e753c774217c146f9e4 100644 (file)
@@ -6993,6 +6993,15 @@ Retries for the dnsdb lookup can be controlled by a retry modifier.
 The form if &"retry_VAL"& where VAL is an integer.
 The default count is set by the main configuration option &%dns_retry%&.
 
 The form if &"retry_VAL"& where VAL is an integer.
 The default count is set by the main configuration option &%dns_retry%&.
 
+.new
+.cindex cacheing "of dns lookup"
+.cindex TTL "of dns lookup"
+.cindex DNS TTL
+Dnsdb lookup results are cached within a single process (and its children).
+The cache entry lifetime is limited to the smallest time-to-live (TTL)
+value of the set of returned DNS records.
+.wen
+
 
 .section "Pseudo dnsdb record types" "SECID66"
 .cindex "MX record" "in &(dnsdb)& lookup"
 
 .section "Pseudo dnsdb record types" "SECID66"
 .cindex "MX record" "in &(dnsdb)& lookup"
@@ -29208,9 +29217,15 @@ deny  dnslists = blackholes.mail-abuse.org
 warn  message  = X-Warn: sending host is on dialups list
       dnslists = dialups.mail-abuse.org
 .endd
 warn  message  = X-Warn: sending host is on dialups list
       dnslists = dialups.mail-abuse.org
 .endd
-DNS list lookups are cached by Exim for the duration of the SMTP session,
+.cindex cacheing "of dns lookup"
+.cindex DNS TTL
+DNS list lookups are cached by Exim for the duration of the SMTP session
+.new
+(but limited by the DNS return TTL value),
+.wen
 so a lookup based on the IP address is done at most once for any incoming
 so a lookup based on the IP address is done at most once for any incoming
-connection. Exim does not share information between multiple incoming
+connection (assuming long-enough TTL).
+Exim does not share information between multiple incoming
 connections (but your local name server cache should be active).
 
 
 connections (but your local name server cache should be active).
 
 
index 969f1e3a73f7dc3e970ff7c8cb3387339b51c829..37d66617e0520e8fdbe42fa0f98ba6f576864444 100644 (file)
@@ -46,6 +46,9 @@ JH/05 Downgrade message for a TLS-certificate-based authentication fail from
 HS/02 Add the Exim version string to the process info.  This way exiwhat
       gives some more detail about the running daemon.
 
 HS/02 Add the Exim version string to the process info.  This way exiwhat
       gives some more detail about the running daemon.
 
+JH/06 Bug 1395: time-limit cacheing of DNS lookups, to the TTL value.  This may
+      matter for fast-change records such as DNSBLs.
+
 
 Exim version 4.86
 -----------------
 
 Exim version 4.86
 -----------------
index a239bec3063a46a2f29c5d13507467e3a8d1faee..f99b470b21f76644de89a5b5f42731372d09b31a 100644 (file)
@@ -388,7 +388,8 @@ from the following bytes. */
 
 dnss->aptr += namelen;
 GETSHORT(dnss->srr.type, dnss->aptr); /* Record type */
 
 dnss->aptr += namelen;
 GETSHORT(dnss->srr.type, dnss->aptr); /* Record type */
-dnss->aptr += 6;                      /* Don't want class or TTL */
+dnss->aptr += 2;                      /* Don't want class */
+GETLONG(dnss->srr.ttl, dnss->aptr);   /* TTL */
 GETSHORT(dnss->srr.size, dnss->aptr); /* Size of data portion */
 dnss->srr.data = dnss->aptr;          /* The record's data follows */
 dnss->aptr += dnss->srr.size;         /* Advance to next RR */
 GETSHORT(dnss->srr.size, dnss->aptr); /* Size of data portion */
 dnss->srr.data = dnss->aptr;          /* The record's data follows */
 dnss->aptr += dnss->srr.size;         /* Advance to next RR */
index cdd1c85bfd1e7d5c8433041f88845f3821a92ded..03de8f6756ffaf071dc0a07ba7436c82c922c54a 100644 (file)
@@ -34,7 +34,7 @@ typedef struct lookup_info {
     int,                          /* length of key or query */
     uschar **,                    /* for returning answer */
     uschar **,                    /* for error message */
     int,                          /* length of key or query */
     uschar **,                    /* for returning answer */
     uschar **,                    /* for error message */
-    BOOL *);                      /* to request cache cleanup */
+    uint *);                      /* cache TTL, sconds */
   void (*close)(                  /* close function */
     void *);                      /* handle */
   void (*tidy)(void);             /* tidy function */
   void (*close)(                  /* close function */
     void *);                      /* handle */
   void (*tidy)(void);             /* tidy function */
@@ -46,9 +46,10 @@ typedef struct lookup_info {
 } lookup_info;
 
 /* This magic number is used by the following lookup_module_info structure
 } lookup_info;
 
 /* This magic number is used by the following lookup_module_info structure
-   for checking API compatibility. It's equivalent to the string"LMM2" */
-#define LOOKUP_MODULE_INFO_MAGIC 0x4c4d4d32
+   for checking API compatibility. It used to be equivalent to the string"LMM3" */
+#define LOOKUP_MODULE_INFO_MAGIC 0x4c4d4933
 /* Version 2 adds: version_report */
 /* Version 2 adds: version_report */
+/* Version 3 change: non/cache becomes TTL in seconds */
 
 typedef struct lookup_module_info {
   uint magic;
 
 typedef struct lookup_module_info {
   uint magic;
index 98905dc5c3d290f074b31d1d98f01609108623d6..31fea64489398d6428da741b5220413cd4f7810f 100644 (file)
@@ -122,12 +122,15 @@ DEFER. The arguments are:
   uschar **errmsg     where to put an error message on failure;
                       this is initially set to "", and should be left
                       as that for a standard "entry not found" error
   uschar **errmsg     where to put an error message on failure;
                       this is initially set to "", and should be left
                       as that for a standard "entry not found" error
-  BOOL *do_cache      the lookup should set this to FALSE when it changes data.
-                      This is TRUE by default. When set to FALSE the cache tree
+  uint *do_cache      the lookup should set this to 0 when it changes data.
+                      This is MAXINT by default. When set to 0 the cache tree
                       of the current search handle will be cleaned and the
                       current result will NOT be cached. Currently the mysql
                       and pgsql lookups use this when UPDATE/INSERT queries are
                       executed.
                       of the current search handle will be cleaned and the
                       current result will NOT be cached. Currently the mysql
                       and pgsql lookups use this when UPDATE/INSERT queries are
                       executed.
+                      If set to a nonzero number of seconds, the cached value
+                      becomes unusable after this time. Currently the dnsdb
+                      lookup uses this to support the TTL value.
 
 Even though the key is zero-terminated, the length is passed because in the
 common case it has been computed already and is often needed.
 
 Even though the key is zero-terminated, the length is passed because in the
 common case it has been computed already and is often needed.
index ea017def127996522505e822082b9e6eac6fca21..ba925dc12cb7cda6ca2bba60ac0609a3d4d1ed3c 100644 (file)
@@ -279,7 +279,7 @@ cdb_find(void *handle,
         int  key_len,
         uschar **result,
         uschar **errmsg,
         int  key_len,
         uschar **result,
         uschar **errmsg,
-        BOOL *do_cache)
+        uint *do_cache)
 {
   struct cdb_state * cdbp = handle;
   uint32 item_key_len,
 {
   struct cdb_state * cdbp = handle;
   uint32 item_key_len,
index 03248e490b3c8ef65cc7bc05a646807384145865..b8c42d59665f1ea77e718a962b5bdeda3404a847 100644 (file)
@@ -87,7 +87,7 @@ the keylength in order to include the terminating zero. */
 
 static int
 dbmdb_find(void *handle, uschar *filename, const uschar *keystring, int length,
 
 static int
 dbmdb_find(void *handle, uschar *filename, const uschar *keystring, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 EXIM_DB *d = (EXIM_DB *)handle;
 EXIM_DATUM key, data;
 {
 EXIM_DB *d = (EXIM_DB *)handle;
 EXIM_DATUM key, data;
@@ -120,7 +120,7 @@ return FAIL;
 
 int
 static dbmnz_find(void *handle, uschar *filename, const uschar *keystring, int length,
 
 int
 static dbmnz_find(void *handle, uschar *filename, const uschar *keystring, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 return dbmdb_find(handle, filename, keystring, length-1, result, errmsg,
   do_cache);
 {
 return dbmdb_find(handle, filename, keystring, length-1, result, errmsg,
   do_cache);
@@ -140,7 +140,7 @@ return dbmdb_find(handle, filename, keystring, length-1, result, errmsg,
 
 static int
 dbmjz_find(void *handle, uschar *filename, const uschar *keystring, int length,
 
 static int
 dbmjz_find(void *handle, uschar *filename, const uschar *keystring, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 uschar *key_item, *key_buffer, *key_p;
 const uschar *key_elems = keystring;
 {
 uschar *key_item, *key_buffer, *key_p;
 const uschar *key_elems = keystring;
index e3de279e25477ffadd73ea7d2f96074668b3609d..70e6c8c637586a3bafea0815104be05ad63be60c 100644 (file)
@@ -131,7 +131,7 @@ separator, as always, is colon. */
 
 static int
 dnsdb_find(void *handle, uschar *filename, const uschar *keystring, int length,
 
 static int
 dnsdb_find(void *handle, uschar *filename, const uschar *keystring, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 int rc;
 int size = 256;
 {
 int rc;
 int size = 256;
@@ -388,6 +388,9 @@ while ((domain = string_nextinlist(&keystring, &sep, NULL, 0)))
       {
       if (rr->type != searchtype) continue;
 
       {
       if (rr->type != searchtype) continue;
 
+      if (*do_cache > rr->ttl)
+       *do_cache = rr->ttl;
+
       if (type == T_A || type == T_AAAA || type == T_ADDRESSES)
         {
         dns_address *da;
       if (type == T_A || type == T_AAAA || type == T_ADDRESSES)
         {
         dns_address *da;
index f8c592adb9158b0e803cec019e251d4b369b24b6..9f7dd8da0c0319050bfb8586a7eaf0e099d25207 100644 (file)
@@ -67,7 +67,7 @@ for us. */
 
 int
 static dsearch_find(void *handle, uschar *dirname, const uschar *keystring, int length,
 
 int
 static dsearch_find(void *handle, uschar *dirname, const uschar *keystring, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 struct stat statbuf;
 int save_errno;
 {
 struct stat statbuf;
 int save_errno;
index 23e1dea6083ef46b95cec48921d59a770583760d..7fd53d01117d8fe3d4736857a81fa5e46c3de144 100644 (file)
@@ -451,7 +451,7 @@ deferred with a retryable error. */
 
 static int
 ibase_find(void *handle, uschar * filename, uschar * query, int length,
 
 static int
 ibase_find(void *handle, uschar * filename, uschar * query, int length,
-           uschar ** result, uschar ** errmsg, BOOL *do_cache)
+           uschar ** result, uschar ** errmsg, uint *do_cache)
 {
     int sep = 0;
     uschar *server;
 {
     int sep = 0;
     uschar *server;
index a56eff326fac792c2433ebfd3f6bd4710c384565..b870df14776a18a7450ebedac034496ba8b3cc0b 100644 (file)
@@ -1339,7 +1339,7 @@ The handle and filename arguments are not used. */
 
 static int
 eldap_find(void *handle, uschar *filename, const uschar *ldap_url, int length,
 
 static int
 eldap_find(void *handle, uschar *filename, const uschar *ldap_url, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 /* Keep picky compilers happy */
 do_cache = do_cache;
 {
 /* Keep picky compilers happy */
 do_cache = do_cache;
@@ -1348,7 +1348,7 @@ return(control_ldap_search(ldap_url, SEARCH_LDAP_SINGLE, result, errmsg));
 
 static int
 eldapm_find(void *handle, uschar *filename, const uschar *ldap_url, int length,
 
 static int
 eldapm_find(void *handle, uschar *filename, const uschar *ldap_url, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 /* Keep picky compilers happy */
 do_cache = do_cache;
 {
 /* Keep picky compilers happy */
 do_cache = do_cache;
@@ -1357,7 +1357,7 @@ return(control_ldap_search(ldap_url, SEARCH_LDAP_MULTIPLE, result, errmsg));
 
 static int
 eldapdn_find(void *handle, uschar *filename, const uschar *ldap_url, int length,
 
 static int
 eldapdn_find(void *handle, uschar *filename, const uschar *ldap_url, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 /* Keep picky compilers happy */
 do_cache = do_cache;
 {
 /* Keep picky compilers happy */
 do_cache = do_cache;
@@ -1366,7 +1366,7 @@ return(control_ldap_search(ldap_url, SEARCH_LDAP_DN, result, errmsg));
 
 int
 eldapauth_find(void *handle, uschar *filename, const uschar *ldap_url, int length,
 
 int
 eldapauth_find(void *handle, uschar *filename, const uschar *ldap_url, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 /* Keep picky compilers happy */
 do_cache = do_cache;
 {
 /* Keep picky compilers happy */
 do_cache = do_cache;
index 73e93037e3186e09ccc01d2b9b73f7e6b4ef5455..d2487d3621cd5a51be4b9344a8627ba9e06dd565 100644 (file)
@@ -12,7 +12,7 @@ extern int     lf_check_file(int, uschar *, int, int, uid_t *, gid_t *,
 extern uschar *lf_quote(uschar *, uschar *, int, uschar *, int *, int *);
 extern int     lf_sqlperform(const uschar *, const uschar *, const uschar *,
                 const uschar *, uschar **,
 extern uschar *lf_quote(uschar *, uschar *, int, uschar *, int *, int *);
 extern int     lf_sqlperform(const uschar *, const uschar *, const uschar *,
                 const uschar *, uschar **,
-                 uschar **, BOOL *, int(*)(const uschar *, uschar *, uschar **,
-                 uschar **, BOOL *, BOOL *));
+                 uschar **, uint *, int(*)(const uschar *, uschar *, uschar **,
+                 uschar **, BOOL *, uint *));
 
 /* End of lf_functions.h */
 
 /* End of lf_functions.h */
index 2d7f326840972cf124b4136e6fbe50f55b8501f1..6d4f7a798b52233dfcc3bee254c28c722812b71c 100644 (file)
@@ -27,7 +27,7 @@ Arguments:
   query          the query
   result         where to pass back the result
   errmsg         where to pass back an error message
   query          the query
   result         where to pass back the result
   errmsg         where to pass back an error message
-  do_cache       to be set FALSE if data is changed
+  do_cache       to be set zero if data is changed
   func           the lookup function to call
 
 Returns:         the return from the lookup function, or DEFER
   func           the lookup function to call
 
 Returns:         the return from the lookup function, or DEFER
@@ -36,8 +36,8 @@ Returns:         the return from the lookup function, or DEFER
 int
 lf_sqlperform(const uschar *name, const uschar *optionname,
   const uschar *optserverlist, const uschar *query,
 int
 lf_sqlperform(const uschar *name, const uschar *optionname,
   const uschar *optserverlist, const uschar *query,
-  uschar **result, uschar **errmsg, BOOL *do_cache,
-  int(*fn)(const uschar *, uschar *, uschar **, uschar **, BOOL *, BOOL *))
+  uschar **result, uschar **errmsg, uint *do_cache,
+  int(*fn)(const uschar *, uschar *, uschar **, uschar **, BOOL *, uint *))
 {
 int sep, rc;
 uschar *server;
 {
 int sep, rc;
 uschar *server;
index 3883d4ba0e4e9a8083b06aa010e5ac5247374a26..eb70a45fab4691bb1fd2bff74a054f61377adfca 100644 (file)
@@ -323,7 +323,7 @@ return FAIL;
 
 static int
 lsearch_find(void *handle, uschar *filename, const uschar *keystring, int length,
 
 static int
 lsearch_find(void *handle, uschar *filename, const uschar *keystring, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 do_cache = do_cache;  /* Keep picky compilers happy */
 return internal_lsearch_find(handle, filename, keystring, length, result,
 {
 do_cache = do_cache;  /* Keep picky compilers happy */
 return internal_lsearch_find(handle, filename, keystring, length, result,
@@ -340,7 +340,7 @@ return internal_lsearch_find(handle, filename, keystring, length, result,
 
 static int
 wildlsearch_find(void *handle, uschar *filename, const uschar *keystring, int length,
 
 static int
 wildlsearch_find(void *handle, uschar *filename, const uschar *keystring, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 do_cache = do_cache;  /* Keep picky compilers happy */
 return internal_lsearch_find(handle, filename, keystring, length, result,
 {
 do_cache = do_cache;  /* Keep picky compilers happy */
 return internal_lsearch_find(handle, filename, keystring, length, result,
@@ -357,7 +357,7 @@ return internal_lsearch_find(handle, filename, keystring, length, result,
 
 static int
 nwildlsearch_find(void *handle, uschar *filename, const uschar *keystring, int length,
 
 static int
 nwildlsearch_find(void *handle, uschar *filename, const uschar *keystring, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 do_cache = do_cache;  /* Keep picky compilers happy */
 return internal_lsearch_find(handle, filename, keystring, length, result,
 {
 do_cache = do_cache;  /* Keep picky compilers happy */
 return internal_lsearch_find(handle, filename, keystring, length, result,
@@ -375,7 +375,7 @@ return internal_lsearch_find(handle, filename, keystring, length, result,
 
 static int
 iplsearch_find(void *handle, uschar *filename, const uschar *keystring, int length,
 
 static int
 iplsearch_find(void *handle, uschar *filename, const uschar *keystring, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 do_cache = do_cache;  /* Keep picky compilers happy */
 if ((length == 1 && keystring[0] == '*') ||
 {
 do_cache = do_cache;  /* Keep picky compilers happy */
 if ((length == 1 && keystring[0] == '*') ||
index 8dff86ad18b10d926ff0b1c7037a2213776265c3..1ce8831e8950094638f4c8a16913b7cb160b7fce 100644 (file)
@@ -74,7 +74,7 @@ Arguments:
   resultptr    where to store the result
   errmsg       where to point an error message
   defer_break  TRUE if no more servers are to be tried after DEFER
   resultptr    where to store the result
   errmsg       where to point an error message
   defer_break  TRUE if no more servers are to be tried after DEFER
-  do_cache     set false if data is changed
+  do_cache     set zero if data is changed
 
 The server string is of the form "host/dbname/user/password". The host can be
 host:port. This string is in a nextinlist temporary buffer, so can be
 
 The server string is of the form "host/dbname/user/password". The host can be
 host:port. This string is in a nextinlist temporary buffer, so can be
@@ -85,7 +85,7 @@ Returns:       OK, FAIL, or DEFER
 
 static int
 perform_mysql_search(const uschar *query, uschar *server, uschar **resultptr,
 
 static int
 perform_mysql_search(const uschar *query, uschar *server, uschar **resultptr,
-  uschar **errmsg, BOOL *defer_break, BOOL *do_cache)
+  uschar **errmsg, BOOL *defer_break, uint *do_cache)
 {
 MYSQL *mysql_handle = NULL;        /* Keep compilers happy */
 MYSQL_RES *mysql_result = NULL;
 {
 MYSQL *mysql_handle = NULL;        /* Keep compilers happy */
 MYSQL_RES *mysql_result = NULL;
@@ -225,7 +225,7 @@ can be detected by calling mysql_field_count(). If its result is zero, no data
 was expected (this is all explained clearly in the MySQL manual). In this case,
 we return the number of rows affected by the command. In this event, we do NOT
 want to cache the result; also the whole cache for the handle must be cleaned
 was expected (this is all explained clearly in the MySQL manual). In this case,
 we return the number of rows affected by the command. In this event, we do NOT
 want to cache the result; also the whole cache for the handle must be cleaned
-up. Setting do_cache FALSE requests this. */
+up. Setting do_cache zero requests this. */
 
 if ((mysql_result = mysql_use_result(mysql_handle)) == NULL)
   {
 
 if ((mysql_result = mysql_use_result(mysql_handle)) == NULL)
   {
@@ -233,7 +233,7 @@ if ((mysql_result = mysql_use_result(mysql_handle)) == NULL)
     {
     DEBUG(D_lookup) debug_printf("MYSQL: query was not one that returns data\n");
     result = string_sprintf("%d", mysql_affected_rows(mysql_handle));
     {
     DEBUG(D_lookup) debug_printf("MYSQL: query was not one that returns data\n");
     result = string_sprintf("%d", mysql_affected_rows(mysql_handle));
-    *do_cache = FALSE;
+    *do_cache = 0;
     goto MYSQL_EXIT;
     }
   *errmsg = string_sprintf("MYSQL: lookup result failed: %s\n",
     goto MYSQL_EXIT;
     }
   *errmsg = string_sprintf("MYSQL: lookup result failed: %s\n",
@@ -341,7 +341,7 @@ shared with other SQL lookups. */
 
 static int
 mysql_find(void *handle, uschar *filename, const uschar *query, int length,
 
 static int
 mysql_find(void *handle, uschar *filename, const uschar *query, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 return lf_sqlperform(US"MySQL", US"mysql_servers", mysql_servers, query,
   result, errmsg, do_cache, perform_mysql_search);
 {
 return lf_sqlperform(US"MySQL", US"mysql_servers", mysql_servers, query,
   result, errmsg, do_cache, perform_mysql_search);
index 7b012b14cbb74bbf2e300f080fc7e4063b16f33c..1faa884a166667309e1ebcd14327ab2039bc76e7 100644 (file)
@@ -42,7 +42,7 @@ code. */
 
 static int
 nis_find(void *handle, uschar *filename, uschar *keystring, int length,
 
 static int
 nis_find(void *handle, uschar *filename, uschar *keystring, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 int rc;
 uschar *nis_data;
 {
 int rc;
 uschar *nis_data;
@@ -68,7 +68,7 @@ return (rc == YPERR_KEY || rc == YPERR_MAP)? FAIL : DEFER;
 
 static int
 nis0_find(void *handle, uschar *filename, uschar *keystring, int length,
 
 static int
 nis0_find(void *handle, uschar *filename, uschar *keystring, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 int rc;
 uschar *nis_data;
 {
 int rc;
 uschar *nis_data;
index 8895ceeecc6a29fc7cf2569061cd5d20293fdcab..a4a7a2d5b7fc4966297e810d5e977c344d4e2d18 100644 (file)
@@ -43,7 +43,7 @@ equals sign. */
 
 static int
 nisplus_find(void *handle, uschar *filename, uschar *query, int length,
 
 static int
 nisplus_find(void *handle, uschar *filename, uschar *query, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 int i;
 int ssize = 0;
 {
 int i;
 int ssize = 0;
index 1f2520ac004adae1622c419ee5a8e0cec9947f7b..adb17b4da410cd8599c774b6e3b530a13dd72631 100644 (file)
@@ -517,7 +517,7 @@ deferred with a retryable error. */
 
 static int
 oracle_find(void *handle, uschar *filename, uschar *query, int length,
 
 static int
 oracle_find(void *handle, uschar *filename, uschar *query, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 int sep = 0;
 uschar *server;
 {
 int sep = 0;
 uschar *server;
index e726f3e8ee74aac8b0341b0c17a973e9ecfae191..315677ffa7fdf090e85ee9669c456a18a30f9b4b 100644 (file)
@@ -34,7 +34,7 @@ return (void *)(-1);     /* Just return something non-null */
 
 static int
 passwd_find(void *handle, uschar *filename, const uschar *keystring, int length,
 
 static int
 passwd_find(void *handle, uschar *filename, const uschar *keystring, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 struct passwd *pw;
 
 {
 struct passwd *pw;
 
index c86ac23ed8a4a778038d571babcc5a79f2884bf8..4be3d98f1546159154f4d74f2f1a605adead9767 100644 (file)
@@ -119,7 +119,7 @@ Returns:       OK, FAIL, or DEFER
 
 static int
 perform_pgsql_search(const uschar *query, uschar *server, uschar **resultptr,
 
 static int
 perform_pgsql_search(const uschar *query, uschar *server, uschar **resultptr,
-  uschar **errmsg, BOOL *defer_break, BOOL *do_cache)
+  uschar **errmsg, BOOL *defer_break, uint *do_cache)
 {
 PGconn *pg_conn = NULL;
 PGresult *pg_result = NULL;
 {
 PGconn *pg_conn = NULL;
 PGresult *pg_result = NULL;
@@ -290,10 +290,10 @@ else
     /* The command was successful but did not return any data since it was
      * not SELECT but either an INSERT, UPDATE or DELETE statement. Tell the
      * high level code to not cache this query, and clean the current cache for
     /* The command was successful but did not return any data since it was
      * not SELECT but either an INSERT, UPDATE or DELETE statement. Tell the
      * high level code to not cache this query, and clean the current cache for
-     * this handle by setting *do_cache FALSE. */
+     * this handle by setting *do_cache zero. */
     result = string_copy(US PQcmdTuples(pg_result));
     offset = Ustrlen(result);
     result = string_copy(US PQcmdTuples(pg_result));
     offset = Ustrlen(result);
-    *do_cache = FALSE;
+    *do_cache = 0;
     DEBUG(D_lookup) debug_printf("PGSQL: command does not return any data "
       "but was successful. Rows affected: %s\n", result);
 
     DEBUG(D_lookup) debug_printf("PGSQL: command does not return any data "
       "but was successful. Rows affected: %s\n", result);
 
@@ -399,7 +399,7 @@ shared with other SQL lookups. */
 
 static int
 pgsql_find(void *handle, uschar *filename, const uschar *query, int length,
 
 static int
 pgsql_find(void *handle, uschar *filename, const uschar *query, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 return lf_sqlperform(US"PostgreSQL", US"pgsql_servers", pgsql_servers, query,
   result, errmsg, do_cache, perform_pgsql_search);
 {
 return lf_sqlperform(US"PostgreSQL", US"pgsql_servers", pgsql_servers, query,
   result, errmsg, do_cache, perform_pgsql_search);
index ac4d0ec301063a92a367c25e9fa6a70777e95e32..18cd3a0afe69adf74ef28b4bdfda2ee3ca24843e 100644 (file)
@@ -65,7 +65,7 @@ redis_tidy(void)
  */
 static int
 perform_redis_search(uschar *command, uschar *server, uschar **resultptr,
  */
 static int
 perform_redis_search(uschar *command, uschar *server, uschar **resultptr,
-  uschar **errmsg, BOOL *defer_break, BOOL *do_cache)
+  uschar **errmsg, BOOL *defer_break, uint *do_cache)
 {
        redisContext *redis_handle = NULL;        /* Keep compilers happy */
        redisReply *redis_reply = NULL;
 {
        redisContext *redis_handle = NULL;        /* Keep compilers happy */
        redisReply *redis_reply = NULL;
@@ -197,7 +197,7 @@ perform_redis_search(uschar *command, uschar *server, uschar **resultptr,
        case REDIS_REPLY_ERROR:
                *errmsg = string_sprintf("REDIS: lookup result failed: %s\n", redis_reply->str);
                *defer_break = FALSE;
        case REDIS_REPLY_ERROR:
                *errmsg = string_sprintf("REDIS: lookup result failed: %s\n", redis_reply->str);
                *defer_break = FALSE;
-               *do_cache = FALSE;
+               *do_cache = 0;
                goto REDIS_EXIT;
                /* NOTREACHED */
 
                goto REDIS_EXIT;
                /* NOTREACHED */
 
@@ -205,7 +205,7 @@ perform_redis_search(uschar *command, uschar *server, uschar **resultptr,
        case REDIS_REPLY_NIL:
                DEBUG(D_lookup) debug_printf("REDIS: query was not one that returned any data\n");
                result = string_sprintf("");
        case REDIS_REPLY_NIL:
                DEBUG(D_lookup) debug_printf("REDIS: query was not one that returned any data\n");
                result = string_sprintf("");
-               *do_cache = FALSE;
+               *do_cache = 0;
                goto REDIS_EXIT;
                /* NOTREACHED */
 
                goto REDIS_EXIT;
                /* NOTREACHED */
 
@@ -304,7 +304,7 @@ perform_redis_search(uschar *command, uschar *server, uschar **resultptr,
 
 static int
 redis_find(void *handle __attribute__((unused)), uschar *filename __attribute__((unused)),
 
 static int
 redis_find(void *handle __attribute__((unused)), uschar *filename __attribute__((unused)),
-           uschar *command, int length, uschar **result, uschar **errmsg, BOOL *do_cache)
+           uschar *command, int length, uschar **result, uschar **errmsg, uint *do_cache)
 {
        return lf_sqlperform(US"Redis", US"redis_servers", redis_servers, command,
          result, errmsg, do_cache, perform_redis_search);
 {
        return lf_sqlperform(US"Redis", US"redis_servers", redis_servers, command,
          result, errmsg, do_cache, perform_redis_search);
index 23ad2adddeac90dfd2ed8bf5dae86ba8e4d79ffd..2671fc9c4719b28c47bdbe6d1749f5d6ca6e2034 100644 (file)
@@ -31,7 +31,9 @@ static void dummy(int x) { dummy2(x-1); }
 #include <spf2/spf_dns_resolv.h>
 #include <spf2/spf_dns_cache.h>
 
 #include <spf2/spf_dns_resolv.h>
 #include <spf2/spf_dns_cache.h>
 
-static void *spf_open(uschar *filename, uschar **errmsg) {
+static void *
+spf_open(uschar *filename, uschar **errmsg)
+{
   SPF_server_t *spf_server = NULL;
   spf_server = SPF_server_new(SPF_DNS_CACHE, 0);
   if (spf_server == NULL) {
   SPF_server_t *spf_server = NULL;
   spf_server = SPF_server_new(SPF_DNS_CACHE, 0);
   if (spf_server == NULL) {
@@ -41,13 +43,17 @@ static void *spf_open(uschar *filename, uschar **errmsg) {
   return (void *) spf_server;
 }
 
   return (void *) spf_server;
 }
 
-static void spf_close(void *handle) {
+static void
+spf_close(void *handle)
+{
   SPF_server_t *spf_server = handle;
   if (spf_server) SPF_server_free(spf_server);
 }
 
   SPF_server_t *spf_server = handle;
   if (spf_server) SPF_server_free(spf_server);
 }
 
-static int spf_find(void *handle, uschar *filename, uschar *keystring, int key_len,
-             uschar **result, uschar **errmsg, BOOL *do_cache) {
+static int
+spf_find(void *handle, uschar *filename, uschar *keystring, int key_len,
+             uschar **result, uschar **errmsg, uint *do_cache)
+{
   SPF_server_t *spf_server = handle;
   SPF_request_t *spf_request = NULL;
   SPF_response_t *spf_response = NULL;
   SPF_server_t *spf_server = handle;
   SPF_request_t *spf_request = NULL;
   SPF_response_t *spf_response = NULL;
index bb92c8c1843bed82e73a1a9d00b3d64ad03ba541..e2330f920240c49ba5279268367c7a2ffd2c6401 100644 (file)
@@ -81,7 +81,7 @@ return 0;
 
 static int
 sqlite_find(void *handle, uschar *filename, const uschar *query, int length,
 
 static int
 sqlite_find(void *handle, uschar *filename, const uschar *query, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 int ret;
 struct strbuf res = { NULL, 0, 0 };
 {
 int ret;
 struct strbuf res = { NULL, 0, 0 };
@@ -93,7 +93,7 @@ if (ret != SQLITE_OK)
   return FAIL;
   }
 
   return FAIL;
   }
 
-if (res.string == NULL) *do_cache = FALSE;
+if (res.string == NULL) *do_cache = 0;
 
 *result = res.string;
 return OK;
 
 *result = res.string;
 return OK;
index c82fa7f3eb8ba8a969a97a608209c7cf53e354b9..401f7c8bf232d4f30aa5761ff58e95da9a74e3fb 100644 (file)
@@ -38,7 +38,7 @@ return (void *)(1);    /* Just return something non-null */
 
 static int
 testdb_find(void *handle, uschar *filename, const uschar *query, int length,
 
 static int
 testdb_find(void *handle, uschar *filename, const uschar *query, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 handle = handle;          /* Keep picky compilers happy */
 filename = filename;
 {
 handle = handle;          /* Keep picky compilers happy */
 filename = filename;
@@ -57,7 +57,7 @@ if (Ustrcmp(query, "defer") == 0)
   return DEFER;
   }
 
   return DEFER;
   }
 
-if (Ustrcmp(query, "nocache") == 0) *do_cache = FALSE;
+if (Ustrcmp(query, "nocache") == 0) *do_cache = 0;
 
 *result = string_copy(query);
 return OK;
 
 *result = string_copy(query);
 return OK;
index 4166089bd700775004c1ede8da4bc7812660ff63..9ac5a3a4341625e84d23b70e25bf0e4f14bfea73 100644 (file)
@@ -36,7 +36,7 @@ return (void *)(1);    /* Just return something non-null */
 
 static int
 whoson_find(void *handle, uschar *filename, uschar *query, int length,
 
 static int
 whoson_find(void *handle, uschar *filename, uschar *query, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 uschar buffer[80];
 handle = handle;          /* Keep picky compilers happy */
 {
 uschar buffer[80];
 handle = handle;          /* Keep picky compilers happy */
index a055291961aa5418ae001a0ed76aa1c35962f244..cd522dae82344e36e44c96aa0e3d0b138dd567e2 100644 (file)
@@ -466,6 +466,7 @@ internal_search_find(void *handle, uschar *filename, uschar *keystring)
 {
 tree_node *t = (tree_node *)handle;
 search_cache *c = (search_cache *)(t->data.ptr);
 {
 tree_node *t = (tree_node *)handle;
 search_cache *c = (search_cache *)(t->data.ptr);
+expiring_data *e;
 uschar *data = NULL;
 int search_type = t->name[0] - '0';
 int old_pool = store_pool;
 uschar *data = NULL;
 int search_type = t->name[0] - '0';
 int old_pool = store_pool;
@@ -491,18 +492,27 @@ store_pool = POOL_SEARCH;
 /* Look up the data for the key, unless it is already in the cache for this
 file. No need to check c->item_cache for NULL, tree_search will do so. */
 
 /* Look up the data for the key, unless it is already in the cache for this
 file. No need to check c->item_cache for NULL, tree_search will do so. */
 
-if ((t = tree_search(c->item_cache, keystring)) == NULL)
+if (  (t = tree_search(c->item_cache, keystring))
+   && (!(e = t->data.ptr)->expiry || e->expiry > time(NULL))
+   )
+  { /* Data was in the cache already; set the pointer from the tree node */
+  data = e->ptr;
+  DEBUG(D_lookup) debug_printf("cached data used for lookup of %s%s%s\n",
+    keystring,
+    filename ? US"\n  in " : US"", filename ? filename : US"");
+  }
+else
   {
   {
-  BOOL do_cache = TRUE;
+  uint do_cache = UINT_MAX;
   int keylength = Ustrlen(keystring);
 
   DEBUG(D_lookup)
     {
   int keylength = Ustrlen(keystring);
 
   DEBUG(D_lookup)
     {
-    if (filename != NULL)
-      debug_printf("file lookup required for %s\n  in %s\n",
-        keystring, filename);
-    else
-      debug_printf("database lookup required for %s\n", keystring);
+    if (t) debug_printf("cached data found but past valid time; ");
+    debug_printf("%s lookup required for %s%s%s\n",
+      filename ? US"file" : US"database",
+      keystring,
+      filename ? US"\n  in " : US"", filename ? filename : US"");
     }
 
   /* Call the code for the different kinds of search. DEFER is handled
     }
 
   /* Call the code for the different kinds of search. DEFER is handled
@@ -511,9 +521,7 @@ if ((t = tree_search(c->item_cache, keystring)) == NULL)
 
   if (lookup_list[search_type]->find(c->handle, filename, keystring, keylength,
       &data, &search_error_message, &do_cache) == DEFER)
 
   if (lookup_list[search_type]->find(c->handle, filename, keystring, keylength,
       &data, &search_error_message, &do_cache) == DEFER)
-    {
     search_find_defer = TRUE;
     search_find_defer = TRUE;
-    }
 
   /* A record that has been found is now in data, which is either NULL
   or points to a bit of dynamic store. Cache the result of the lookup if
 
   /* A record that has been found is now in data, which is either NULL
   or points to a bit of dynamic store. Cache the result of the lookup if
@@ -524,10 +532,22 @@ if ((t = tree_search(c->item_cache, keystring)) == NULL)
   else if (do_cache)
     {
     int len = keylength + 1;
   else if (do_cache)
     {
     int len = keylength + 1;
-    t = store_get(sizeof(tree_node) + len);
-    memcpy(t->name, keystring, len);
-    t->data.ptr = data;
-    tree_insertnode(&c->item_cache, t);
+
+    if (t)     /* Previous, out-of-date cache entry.  Update with the */
+      {        /* new result and forget the old one */
+      e->expiry = do_cache == UINT_MAX ? 0 : time(NULL)+do_cache;
+      e->ptr = data;
+      }
+    else
+      {
+      t = store_get(sizeof(tree_node) + len + sizeof(expiring_data));
+      e = (expiring_data *)((char *)t + sizeof(tree_node) + len);
+      e->expiry = do_cache == UINT_MAX ? 0 : time(NULL)+do_cache;
+      e->ptr = data;
+      memcpy(t->name, keystring, len);
+      t->data.ptr = e;
+      tree_insertnode(&c->item_cache, t);
+      }
     }
 
   /* If caching was disabled, empty the cache tree. We just set the cache
     }
 
   /* If caching was disabled, empty the cache tree. We just set the cache
@@ -540,34 +560,19 @@ if ((t = tree_search(c->item_cache, keystring)) == NULL)
     }
   }
 
     }
   }
 
-/* Data was in the cache already; set the pointer from the tree node */
-
-else
-  {
-  data = US t->data.ptr;
-  DEBUG(D_lookup) debug_printf("cached data used for lookup of %s%s%s\n",
-    keystring,
-    (filename == NULL)? US"" : US"\n  in ",
-    (filename == NULL)? US"" : filename);
-  }
-
-/* Debug: output the answer */
-
 DEBUG(D_lookup)
   {
 DEBUG(D_lookup)
   {
-  if (data == NULL)
-    {
-    if (search_find_defer) debug_printf("lookup deferred: %s\n",
-      search_error_message);
-    else debug_printf("lookup failed\n");
-    }
-  else debug_printf("lookup yielded: %s\n", data);
+  if (data)
+    debug_printf("lookup yielded: %s\n", data);
+  else if (search_find_defer)
+    debug_printf("lookup deferred: %s\n", search_error_message);
+  else debug_printf("lookup failed\n");
   }
 
 /* Return it in new dynamic store in the regular pool */
 
 store_pool = old_pool;
   }
 
 /* Return it in new dynamic store in the regular pool */
 
 store_pool = old_pool;
-return (data == NULL)? NULL : string_copy(data);
+return data ? string_copy(data) : NULL;
 }
 
 
 }
 
 
index db9e843ac380a46d3127e712189780c6ef1da240..c36d08ca78e8ead143bf269b4aa51c5cbaeed5a0 100644 (file)
@@ -662,6 +662,16 @@ typedef struct tree_node {
   uschar  name[1];                /* node name - variable length */
 } tree_node;
 
   uschar  name[1];                /* node name - variable length */
 } tree_node;
 
+/* Structure for holding time-limited data such as DNS returns.
+We use this rather than extending tree_node to avoid wasting
+space for most tree use (variables...) at the cost of complexity
+for the lookups cache */
+
+typedef struct expiring_data {
+  time_t expiry;                 /* if nonzero, data invalid after this time */
+  void   *ptr;                   /* pointer to data */
+} expiring_data;
+
 /* Structure for holding the handle and the cached last lookup for searches.
 This block is pointed to by the tree entry for the file. The file can get
 closed if too many are opened at once. There is a LRU chain for deciding which
 /* Structure for holding the handle and the cached last lookup for searches.
 This block is pointed to by the tree entry for the file. The file can get
 closed if too many are opened at once. There is a LRU chain for deciding which
@@ -681,6 +691,7 @@ uncompressed, but the data pointer is into the raw data. */
 typedef struct {
   uschar  name[DNS_MAXNAME];      /* domain name */
   int     type;                   /* record type */
 typedef struct {
   uschar  name[DNS_MAXNAME];      /* domain name */
   int     type;                   /* record type */
+  unsigned short ttl;            /* time-to-live, seconds */
   int     size;                   /* size of data */
   uschar *data;                   /* pointer to data */
 } dns_record;
   int     size;                   /* size of data */
   uschar *data;                   /* pointer to data */
 } dns_record;
index 7992d58fc381da3fe7860d32c69e30214b8dffbe..dc9c58224134a64385a0e934b8d658c29b4384f6 100644 (file)
@@ -21,6 +21,7 @@ uschar ctbuffer[8192];
 /* Structure for caching DNSBL lookups */
 
 typedef struct dnsbl_cache_block {
 /* Structure for caching DNSBL lookups */
 
 typedef struct dnsbl_cache_block {
+  time_t expiry;
   dns_address *rhs;
   uschar *text;
   int rc;
   dns_address *rhs;
   uschar *text;
   int rc;
@@ -3584,21 +3585,37 @@ if (!string_format(query, sizeof(query), "%s.%s", prepend, domain))
 
 /* Look for this query in the cache. */
 
 
 /* Look for this query in the cache. */
 
-t = tree_search(dnsbl_cache, query);
+if (  (t = tree_search(dnsbl_cache, query))
+   && (cb = t->data.ptr)->expiry > time(NULL)
+   )
+
+/* Previous lookup was cached */
+
+  {
+  HDEBUG(D_dnsbl) debug_printf("using result of previous DNS lookup\n");
+  }
 
 /* If not cached from a previous lookup, we must do a DNS lookup, and
 cache the result in permanent memory. */
 
 
 /* If not cached from a previous lookup, we must do a DNS lookup, and
 cache the result in permanent memory. */
 
-if (t == NULL)
+else
   {
   {
+  uint ttl = UINT_MAX;
+
   store_pool = POOL_PERM;
 
   store_pool = POOL_PERM;
 
-  /* Set up a tree entry to cache the lookup */
+  if (t)
+    {
+    HDEBUG(D_dnsbl) debug_printf("cached data found but past valid time; ");
+    }
 
 
-  t = store_get(sizeof(tree_node) + Ustrlen(query));
-  Ustrcpy(t->name, query);
-  t->data.ptr = cb = store_get(sizeof(dnsbl_cache_block));
-  (void)tree_insertnode(&dnsbl_cache, t);
+  else
+    {  /* Set up a tree entry to cache the lookup */
+    t = store_get(sizeof(tree_node) + Ustrlen(query));
+    Ustrcpy(t->name, query);
+    t->data.ptr = cb = store_get(sizeof(dnsbl_cache_block));
+    (void)tree_insertnode(&dnsbl_cache, t);
+    }
 
   /* Do the DNS loopup . */
 
 
   /* Do the DNS loopup . */
 
@@ -3634,6 +3651,7 @@ if (t == NULL)
           *addrp = da;
           while (da->next != NULL) da = da->next;
           addrp = &(da->next);
           *addrp = da;
           while (da->next != NULL) da = da->next;
           addrp = &(da->next);
+         if (ttl > rr->ttl) ttl = rr->ttl;
           }
         }
       }
           }
         }
       }
@@ -3645,17 +3663,10 @@ if (t == NULL)
     if (cb->rhs == NULL) cb->rc = DNS_NODATA;
     }
 
     if (cb->rhs == NULL) cb->rc = DNS_NODATA;
     }
 
+  cb->expiry = time(NULL)+ttl;
   store_pool = old_pool;
   }
 
   store_pool = old_pool;
   }
 
-/* Previous lookup was cached */
-
-else
-  {
-  HDEBUG(D_dnsbl) debug_printf("using result of previous DNS lookup\n");
-  cb = t->data.ptr;
-  }
-
 /* We now have the result of the DNS lookup, either newly done, or cached
 from a previous call. If the lookup succeeded, check against the address
 list if there is one. This may be a positive equality list (introduced by
 /* We now have the result of the DNS lookup, either newly done, or cached
 from a previous call. If the lookup succeeded, check against the address
 list if there is one. This may be a positive equality list (introduced by
index 3bef13363455a0ef1d5eae823889852815f94167..ae5988cee09f050794b2a3f3d4eb853680ff7508 100644 (file)
@@ -1,4 +1,5 @@
 # Exim test configuration 2200
 # Exim test configuration 2200
+# Check for dnsdb cache TTL handling
 
 exim_path = EXIM_PATH
 host_lookup_order = bydns
 
 exim_path = EXIM_PATH
 host_lookup_order = bydns
@@ -8,4 +9,20 @@ log_file_path = DIR/spool/log/%slog
 gecos_pattern = ""
 gecos_name = CALLER_NAME
 
 gecos_pattern = ""
 gecos_name = CALLER_NAME
 
+# ----- Main settings -----
+
+acl_not_smtp = check_rcpt
+queue_only
+
+begin acl
+
+check_rcpt:
+  warn
+       set acl_m1 =    ${map {<,$recipients} \
+                               {${lookup dnsdb{a=${domain:$item}}{$value}fail}}}
+       delay =         4s
+       set acl_m1 =    ${map {<,$recipients} \
+                               {${lookup dnsdb{a=${domain:$item}}{$value}fail}}}
+  accept
+
 # End
 # End
index ae17b7cb49acf96755a493075aa0562e9cec6e16..7b257cce3dd5d96569a67343274a4cc81b58920d 100644 (file)
@@ -12,11 +12,43 @@ gecos_name = CALLER_NAME
 
 trusted_users = CALLER
 
 
 trusted_users = CALLER
 
+acl_smtp_rcpt = check_rcpt
+acl_not_smtp = check_sndr
+queue_only
+
+# - ACL --
+begin acl
+
+check_rcpt:
+       # Do not care about result, looking at debug output
+       # expect an original lookup, a cached lookup avoidance
+       # then a TTL-required repeat lookup
+  warn dnslists =      rbl.test.ex/V4NET.11.12.14
+       dnslists =      rbl.test.ex/V4NET.11.12.14
+       delay =         4s
+       dnslists =      rbl.test.ex/V4NET.11.12.14
+  accept
+
+check_sndr:
+       # Do not care about result, looking at debug output
+       # expect an original lookup, a cached lookup avoidance
+       # then a TTL-required repeat lookup
+  warn sender_domains = dnsdb;a=$sender_address_domain
+       sender_domains = dnsdb;a=$sender_address_domain
+       delay =         4s
+       sender_domains = dnsdb;a=$sender_address_domain
+  accept
+
 
 # ----- Routers -----
 
 begin routers
 
 
 # ----- Routers -----
 
 begin routers
 
+r0:
+  driver = accept
+  senders = a@shorthost.test.ex
+  transport = remote_delivery
+
 r1:
   driver = accept
   domains = dnsdb;$domain
 r1:
   driver = accept
   domains = dnsdb;$domain
@@ -41,6 +73,11 @@ local_delivery:
   file = DIR/test-mail/$local_part
   user = CALLER
 
   file = DIR/test-mail/$local_part
   user = CALLER
 
+remote_delivery:
+  driver = smtp
+  hosts = 127.0.0.1
+  allow_localhost
+  port = PORT_D
 
 # ----- Retry -----
 
 
 # ----- Retry -----
 
index 4cbf0f2517be0fc924aaab92e9158aa7ea2eef63..61f274eb0b60896af35b212f6cb24a5e0cb184f5 100644 (file)
@@ -51,6 +51,10 @@ mx.xn--1xa         A       V4NET.255.255.255
 thishost     A       127.0.0.1
 localhost4   A       127.0.0.1
 
 thishost     A       127.0.0.1
 localhost4   A       127.0.0.1
 
+; A localhost with short TTL
+
+TTL=2 shorthost A    127.0.0.1
+
 
 ; Something that gives both the IP and the loopback
 
 
 ; Something that gives both the IP and the loopback
 
@@ -170,7 +174,7 @@ cname4       CNAME   thishost
 
 13.12.11.V4NET.rbl    A   127.0.0.2
                       TXT "This is a test blacklisting message"
 
 13.12.11.V4NET.rbl    A   127.0.0.2
                       TXT "This is a test blacklisting message"
-14.12.11.V4NET.rbl    A   127.0.0.2
+TTL=2 14.12.11.V4NET.rbl A 127.0.0.2
                       TXT "This is a test blacklisting message"
 15.12.11.V4NET.rbl    A   127.0.0.2
                       TXT "This is a very long blacklisting message, continuing for ages and ages and certainly being longer than 128 characters which was a previous limit on the length that Exim was prepared to handle."
                       TXT "This is a test blacklisting message"
 15.12.11.V4NET.rbl    A   127.0.0.2
                       TXT "This is a very long blacklisting message, continuing for ages and ages and certainly being longer than 128 characters which was a previous limit on the length that Exim was prepared to handle."
diff --git a/test/log/2200 b/test/log/2200
new file mode 100644 (file)
index 0000000..f59faf8
--- /dev/null
@@ -0,0 +1 @@
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss
diff --git a/test/log/2201 b/test/log/2201
new file mode 100644 (file)
index 0000000..d413dda
--- /dev/null
@@ -0,0 +1,7 @@
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= a@shorthost.test.ex U=CALLER P=local S=sss
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= a@shorthost.test.ex H=localhost (myhost.test.ex) [127.0.0.1] P=esmtp S=sss id=E10HmaX-0005vi-00@myhost.test.ex
+1999-03-02 09:44:33 10HmaX-0005vi-00 => t@test.ex R=r0 T=remote_delivery H=127.0.0.1 [127.0.0.1] C="250 OK id=10HmaY-0005vi-00"
+1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
index 40837dbd0eafc96d8592e947962d056181d15f24..9f6262524a9da559eeae5bd61a7818c6b3a34df1 100644 (file)
@@ -49,3 +49,16 @@ defer_strict:ten-1         ${lookup dnsdb{defer_strict,a=test.again.dns:ten-1.te
 
 delay1500                  ${lookup dnsdb{retrans_1s,retry_2,a=delay1500.test.ex}}
 ****
 
 delay1500                  ${lookup dnsdb{retrans_1s,retry_2,a=delay1500.test.ex}}
 ****
+#
+# Cacheability
+exim -d-all+lookup -be
+a=localhost.test.ex        ${lookup dnsdb{a=localhost.test.ex}{$value}fail}
+a=localhost.test.ex        ${lookup dnsdb{a=localhost.test.ex}{$value}fail}
+****
+#
+# TTL-limited noncacheability
+exim -d-all+lookup -odq user@shorthost.test.ex
+****
+#
+no_msglog_check
+#
index 6002987931ff94f1b06307ab4ae9a66905c5a604..d432ca1870ce625d217a97e38fba02ba0a599cc7 100644 (file)
@@ -1,6 +1,24 @@
 # query-style lookup in domains, local_parts, senders
 # query-style lookup in domains, local_parts, senders
+munge debug_pid
 exim -d -bt test.ex@test.ex unknown@test.ex
 ****
 2
 exim -f a@b.c -bt test.ex@test.ex unknown@test.ex
 ****
 exim -d -bt test.ex@test.ex unknown@test.ex
 ****
 2
 exim -f a@b.c -bt test.ex@test.ex unknown@test.ex
 ****
+#
+#
+# lookup non/cacheability, lookup done as a list item
+exim -d-all+lookup -odq -f a@shorthost.test.ex t@test.ex
+****
+#
+#
+# lookup non/cacheability, lookup done for a dnslists= ACL condition
+exim -DSERVER=server -d-all+dnsbl -bd -oX PORT_D
+****
+exim -q
+****
+#
+killdaemon
+millisleep 500
+no_msglog_check
+#
index a03f94a07414615bacdde0e5e7c85b75aa80a1e5..7e93979ecfb529267dcde21dbe386286162f7d47 100644 (file)
@@ -61,6 +61,9 @@ Any DNS record line in a zone file can be prefixed with "AA "
 if all the records found by a lookup are marked
 as such then the response will have the "AA" bit set.
 
 if all the records found by a lookup are marked
 as such then the response will have the "AA" bit set.
 
+Any DNS record line in a zone file can be prefixed with "TTL=" and
+a number of seconds (followed by one space).
+
 */
 
 #include <ctype.h>
 */
 
 #include <ctype.h>
@@ -105,6 +108,8 @@ typedef struct tlist {
   int value;
 } tlist;
 
   int value;
 } tlist;
 
+#define DEFAULT_TTL 3600U
+
 /* On some (older?) operating systems, the standard ns_t_xxx definitions are
 not available, and only the older T_xxx ones exist in nameser.h. If ns_t_a is
 not defined, assume we are in this state. A really old system might not even
 /* On some (older?) operating systems, the standard ns_t_xxx definitions are
 not available, and only the older T_xxx ones exist in nameser.h. If ns_t_a is
 not defined, assume we are in this state. A really old system might not even
@@ -347,6 +352,7 @@ while (fgets(CS buffer, sizeof(buffer), f) != NULL)
   BOOL rr_sec = FALSE;
   BOOL rr_aa = FALSE;
   int delay = 0;
   BOOL rr_sec = FALSE;
   BOOL rr_aa = FALSE;
   int delay = 0;
+  uint ttl = DEFAULT_TTL;
 
   p = buffer;
   while (isspace(*p)) p++;
 
   p = buffer;
   while (isspace(*p)) p++;
@@ -380,6 +386,12 @@ while (fgets(CS buffer, sizeof(buffer), f) != NULL)
       for (p += 6; *p >= '0' && *p <= '9'; p++) delay = delay*10 + *p - '0';
       if (isspace(*p)) p++;
       }
       for (p += 6; *p >= '0' && *p <= '9'; p++) delay = delay*10 + *p - '0';
       if (isspace(*p)) p++;
       }
+    else if (Ustrncmp(p, US"TTL=", 4) == 0)     /* TTL for record */
+      {
+      ttl = 0;
+      for (p += 4; *p >= '0' && *p <= '9'; p++) ttl = ttl*10 + *p - '0';
+      if (isspace(*p)) p++;
+      }
     else
       break;
     }
     else
       break;
     }
@@ -459,7 +471,10 @@ while (fgets(CS buffer, sizeof(buffer), f) != NULL)
   *pk++ = 0;
   *pk++ = 1;     /* class = IN */
 
   *pk++ = 0;
   *pk++ = 1;     /* class = IN */
 
-  pk += 4;       /* TTL field; don't care */
+  *pk++ = (ttl >>24) & 255;
+  *pk++ = (ttl >>16) & 255;
+  *pk++ = (ttl >> 8) & 255;
+  *pk++ = ttl & 255;
 
   rdlptr = pk;   /* remember rdlength field */
   pk += 2;
 
   rdlptr = pk;   /* remember rdlength field */
   pk += 2;
diff --git a/test/stderr/2200 b/test/stderr/2200
new file mode 100644 (file)
index 0000000..4fec895
--- /dev/null
@@ -0,0 +1,52 @@
+Exim version x.yz ....
+configuration file is TESTSUITE/test-config
+admin user
+search_open: dnsdb "NULL"
+search_find: file="NULL"
+  key="a=localhost.test.ex" partial=-1 affix=NULL starflags=0
+LRU list:
+internal_search_find: file="NULL"
+  type=dnsdb key="a=localhost.test.ex"
+database lookup required for a=localhost.test.ex
+dnsdb key: localhost.test.ex
+lookup yielded: 127.0.0.1
+search_open: dnsdb "NULL"
+  cached open
+search_find: file="NULL"
+  key="a=localhost.test.ex" partial=-1 affix=NULL starflags=0
+LRU list:
+internal_search_find: file="NULL"
+  type=dnsdb key="a=localhost.test.ex"
+cached data used for lookup of a=localhost.test.ex
+lookup yielded: 127.0.0.1
+search_tidyup called
+>>>>>>>>>>>>>>>> Exim pid=pppp terminating with rc=0 >>>>>>>>>>>>>>>>
+Exim version x.yz ....
+configuration file is TESTSUITE/test-config
+admin user
+search_tidyup called
+search_tidyup called
+search_open: dnsdb "NULL"
+search_find: file="NULL"
+  key="a=shorthost.test.ex" partial=-1 affix=NULL starflags=0
+LRU list:
+internal_search_find: file="NULL"
+  type=dnsdb key="a=shorthost.test.ex"
+database lookup required for a=shorthost.test.ex
+dnsdb key: shorthost.test.ex
+lookup yielded: 127.0.0.1
+search_open: dnsdb "NULL"
+  cached open
+search_find: file="NULL"
+  key="a=shorthost.test.ex" partial=-1 affix=NULL starflags=0
+LRU list:
+internal_search_find: file="NULL"
+  type=dnsdb key="a=shorthost.test.ex"
+cached data found but past valid time; database lookup required for a=shorthost.test.ex
+dnsdb key: shorthost.test.ex
+lookup yielded: 127.0.0.1
+LOG: MAIN
+  <= CALLER@myhost.test.ex U=CALLER P=local S=sss
+created log directory TESTSUITE/spool/log
+search_tidyup called
+>>>>>>>>>>>>>>>> Exim pid=pppp terminating with rc=0 >>>>>>>>>>>>>>>>
index 925d3c6f761507380158c6026682a054056c37f1..523ae4395b0a1feab3da50239fbb28167d30b700 100644 (file)
@@ -3,9 +3,11 @@ changed uid/gid: forcing real = effective
   uid=uuuu gid=CALLER_GID pid=pppp
 seeking password data for user "CALLER": cache not available
 getpwnam() succeeded uid=CALLER_UID gid=CALLER_GID
   uid=uuuu gid=CALLER_GID pid=pppp
 seeking password data for user "CALLER": cache not available
 getpwnam() succeeded uid=CALLER_UID gid=CALLER_GID
+tls_validate_require_cipher child ppppp ended: status=0x0
 configuration file is TESTSUITE/test-config
 trusted user
 admin user
 configuration file is TESTSUITE/test-config
 trusted user
 admin user
+DSN: r0 propagating DSN
 DSN: r1 propagating DSN
 DSN: r2 propagating DSN
 seeking password data for user "CALLER": using cached result
 DSN: r1 propagating DSN
 DSN: r2 propagating DSN
 seeking password data for user "CALLER": using cached result
@@ -19,6 +21,12 @@ Testing test.ex@test.ex
 Considering test.ex@test.ex
 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
 routing test.ex@test.ex
 Considering test.ex@test.ex
 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
 routing test.ex@test.ex
+--------> r0 router <--------
+local_part=test.ex domain=test.ex
+checking senders
+address match test: subject=CALLER@myhost.test.ex pattern=a@shorthost.test.ex
+CALLER@myhost.test.ex in "a@shorthost.test.ex"? no (end of list)
+r0 router skipped: senders mismatch
 --------> r1 router <--------
 local_part=test.ex domain=test.ex
 checking domains
 --------> r1 router <--------
 local_part=test.ex domain=test.ex
 checking domains
@@ -62,6 +70,12 @@ Testing unknown@test.ex
 Considering unknown@test.ex
 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
 routing unknown@test.ex
 Considering unknown@test.ex
 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
 routing unknown@test.ex
+--------> r0 router <--------
+local_part=unknown domain=test.ex
+checking senders
+address match test: subject=CALLER@myhost.test.ex pattern=a@shorthost.test.ex
+CALLER@myhost.test.ex in "a@shorthost.test.ex"? no (end of list)
+r0 router skipped: senders mismatch
 --------> r1 router <--------
 local_part=unknown domain=test.ex
 checking domains
 --------> r1 router <--------
 local_part=unknown domain=test.ex
 checking domains
@@ -132,3 +146,84 @@ routed by r2 router
   transport: local_delivery
 search_tidyup called
 >>>>>>>>>>>>>>>> Exim pid=pppp terminating with rc=0 >>>>>>>>>>>>>>>>
   transport: local_delivery
 search_tidyup called
 >>>>>>>>>>>>>>>> Exim pid=pppp terminating with rc=0 >>>>>>>>>>>>>>>>
+Exim version x.yz ....
+configuration file is TESTSUITE/test-config
+trusted user
+admin user
+search_tidyup called
+search_tidyup called
+search_open: dnsdb "NULL"
+search_find: file="NULL"
+  key="a=shorthost.test.ex" partial=-1 affix=NULL starflags=0
+LRU list:
+internal_search_find: file="NULL"
+  type=dnsdb key="a=shorthost.test.ex"
+database lookup required for a=shorthost.test.ex
+dnsdb key: shorthost.test.ex
+lookup yielded: 127.0.0.1
+search_open: dnsdb "NULL"
+  cached open
+search_find: file="NULL"
+  key="a=shorthost.test.ex" partial=-1 affix=NULL starflags=0
+LRU list:
+internal_search_find: file="NULL"
+  type=dnsdb key="a=shorthost.test.ex"
+cached data used for lookup of a=shorthost.test.ex
+lookup yielded: 127.0.0.1
+search_open: dnsdb "NULL"
+  cached open
+search_find: file="NULL"
+  key="a=shorthost.test.ex" partial=-1 affix=NULL starflags=0
+LRU list:
+internal_search_find: file="NULL"
+  type=dnsdb key="a=shorthost.test.ex"
+cached data found but past valid time; database lookup required for a=shorthost.test.ex
+dnsdb key: shorthost.test.ex
+lookup yielded: 127.0.0.1
+LOG: MAIN
+  <= a@shorthost.test.ex U=CALLER P=local S=sss
+created log directory TESTSUITE/spool/log
+search_tidyup called
+>>>>>>>>>>>>>>>> Exim pid=pppp terminating with rc=0 >>>>>>>>>>>>>>>>
+
+******** SERVER ********
+Exim version x.yz ....
+configuration file is TESTSUITE/test-config
+trusted user
+admin user
+ppppp daemon_smtp_port overridden by -oX:
+ppppp   <: 1225
+ppppp listening on all interfaces (IPv6) port 1225
+ppppp listening on all interfaces (IPv4) port 1225
+ppppp pid written to TESTSUITE/spool/exim-daemon.pid
+ppppp LOG: MAIN
+ppppp   exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+ppppp daemon running with uid=EXIM_UID gid=EXIM_GID euid=EXIM_UID egid=EXIM_GID
+ppppp Listening...
+ppppp Connection request from 127.0.0.1 port sssss
+ppppp 1 SMTP accept process running
+ppppp Listening...
+ppppp Process ppppp is handling incoming connection from [127.0.0.1]
+ppppp Process ppppp is ready for new message
+ppppp DNS list check: rbl.test.ex/V4NET.11.12.14
+ppppp new DNS lookup for 14.12.11.V4NET.rbl.test.ex
+ppppp DNS lookup for 14.12.11.V4NET.rbl.test.ex succeeded (yielding 127.0.0.2)
+ppppp => that means V4NET.11.12.14 is listed at rbl.test.ex
+ppppp DNS list check: rbl.test.ex/V4NET.11.12.14
+ppppp using result of previous DNS lookup
+ppppp DNS lookup for 14.12.11.V4NET.rbl.test.ex succeeded (yielding 127.0.0.2)
+ppppp => that means V4NET.11.12.14 is listed at rbl.test.ex
+ppppp DNS list check: rbl.test.ex/V4NET.11.12.14
+ppppp cached data found but past valid time; new DNS lookup for 14.12.11.V4NET.rbl.test.ex
+ppppp DNS lookup for 14.12.11.V4NET.rbl.test.ex succeeded (yielding 127.0.0.2)
+ppppp => that means V4NET.11.12.14 is listed at rbl.test.ex
+PDKIM >> Hashed body data, canonicalized >>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+ppppp LOG: MAIN
+ppppp   <= a@shorthost.test.ex H=localhost (myhost.test.ex) [127.0.0.1] P=esmtp S=sss id=E10HmaX-0005vi-00@myhost.test.ex
+ppppp Process ppppp is ready for new message
+ppppp LOG: smtp_connection MAIN
+ppppp   SMTP connection from localhost (myhost.test.ex) [127.0.0.1] closed by QUIT
+ppppp child ppppp ended: status=0x0
+ppppp   normal exit, 0
+ppppp 0 SMTP accept processes now running
+ppppp Listening...
index b775948037eb035cb2fd8940f8dfb437662b519d..71ff120177c31ed9ec0ca08e5c7e5eb41899d6ad 100644 (file)
@@ -51,3 +51,6 @@ ten-2.test.ex
 > 
 > delay1500                  ip4.ip4.ip4.ip4
 > 
 > 
 > delay1500                  ip4.ip4.ip4.ip4
 > 
+> a=localhost.test.ex        127.0.0.1
+> a=localhost.test.ex        127.0.0.1
+>