Use explicit O_CREAT for dbfn_open()
authorJeremy Harris <jgh146exb@wizmail.org>
Tue, 16 Jul 2024 20:24:48 +0000 (21:24 +0100)
committerJeremy Harris <jgh146exb@wizmail.org>
Tue, 16 Jul 2024 20:24:48 +0000 (21:24 +0100)
15 files changed:
src/src/acl.c
src/src/dbfn.c
src/src/deliver.c
src/src/enq.c
src/src/exim_dbutil.c
src/src/retry.c
src/src/tls-gnu.c
src/src/tls-openssl.c
src/src/transport.c
src/src/transports/smtp.c
src/src/verify.c
test/stderr/0388
test/stderr/0398
test/stderr/0432
test/stderr/5005

index 0aa789dbf820ef9b5a8d7781e40daa5f92326838..69777cf9c89610dd008b57565fdb73ea8513b8f1 100644 (file)
@@ -2584,7 +2584,7 @@ if ((t = tree_search(*anchor, key)))
 /* We aren't using a pre-computed rate, so get a previously recorded rate
 from the database, which will be updated and written back if required. */
 
-if (!(dbm = dbfn_open(US"ratelimit", O_RDWR, &dbblock, TRUE, TRUE)))
+if (!(dbm = dbfn_open(US"ratelimit", O_RDWR|O_CREAT, &dbblock, TRUE, TRUE)))
   {
   store_pool = old_pool;
   sender_rate = NULL;
@@ -2966,7 +2966,7 @@ while ((ele = string_nextinlist(&list, &slash, NULL, 0)))
   else
     goto badopt;
 
-if (!(dbm = dbfn_open(US"seen", O_RDWR, &dbblock, TRUE, TRUE)))
+if (!(dbm = dbfn_open(US"seen", O_RDWR|O_CREAT, &dbblock, TRUE, TRUE)))
   {
   HDEBUG(D_acl) debug_printf_indent("database for 'seen' not available\n");
   *log_msgptr = US"database for 'seen' not available";
index d64f1927b821533366446fd49c39f5fafa5e620f..c2c92cf44122f03bf6b7132c2ac02a0caadbfb4c 100644 (file)
@@ -128,7 +128,7 @@ return TRUE;
 Arguments:
   name     The single-component name of one of Exim's database files.
   flags    Either O_RDONLY or O_RDWR, indicating the type of open required;
-             O_RDWR implies "create if necessary"
+             optionally O_CREAT
   dbblock  Points to an open_db block to be filled in.
   lof      If TRUE, write to the log for actual open failures (locking failures
            are always logged).
@@ -165,16 +165,16 @@ unnecessarily, because usually the lock file will be there. If the directory
 exists, there is no error. */
 
 dlen = snprintf(CS dirname, sizeof(dirname), "%s/db", spool_directory);
-flen = Ustrlen(name);
-snprintf(CS filename, sizeof(filename), "%.*s/%.*s.lockfile",
-         (int)sizeof(filename) - dlen - flen - 11, dirname,
-         flen, name);
 
 dbblock->lockfd = -1;
 if (!exim_lockfile_needed())
   db_dir_make(panic);
 else
   {
+  flen = Ustrlen(name);
+  snprintf(CS filename, sizeof(filename), "%.*s/%.*s.lockfile",
+           (int)sizeof(filename) - dlen - flen - 11, dirname,
+           flen, name);
   if (!lockfile_take(dbblock, filename, flags == O_RDONLY, panic))
     {
     DEBUG(D_hints_lookup) acl_level--;
@@ -191,16 +191,15 @@ databases - often this is caused by non-matching db.h and the library. To make
 it easy to pin this down, there are now debug statements on either side of the
 open call. */
 
-flags &= O_RDONLY | O_RDWR;
 snprintf(CS filename, sizeof(filename), "%.*s/%s", dlen, dirname, name);
 
 priv_drop_temp(exim_uid, exim_gid);
-dbblock->dbptr = exim_dbopen(filename, dirname, flags, EXIMDB_MODE);
-if (!dbblock->dbptr && errno == ENOENT && flags == O_RDWR)
+dbblock->dbptr = exim_dbopen(filename, dirname, flags & O_ACCMODE, EXIMDB_MODE);
+if (!dbblock->dbptr && errno == ENOENT && flags & O_CREAT)
   {
   DEBUG(D_hints_lookup)
     debug_printf_indent("%s appears not to exist: trying to create\n", filename);
-  dbblock->dbptr = exim_dbopen(filename, dirname, flags|O_CREAT, EXIMDB_MODE);
+  dbblock->dbptr = exim_dbopen(filename, dirname, flags, EXIMDB_MODE);
   }
 save_errno = errno;
 priv_restore();
@@ -227,10 +226,11 @@ if (!dbblock->dbptr)
   }
 
 DEBUG(D_hints_lookup)
-  debug_printf_indent("opened hints database %s: flags=%s\n", filename,
-    flags == O_RDONLY ? "O_RDONLY"
-    : flags == O_RDWR ? "O_RDWR"
-    : "??");
+  debug_printf_indent("opened hints database %s: flags=%s%s\n", filename,
+    (flags & O_ACCMODE) == O_RDONLY ? "O_RDONLY"
+    : (flags & O_ACCMODE) == O_RDWR ? "O_RDWR"
+    : "??",
+    flags & O_CREAT ? "|O_CREAT" : "");
 
 /* Pass back the block containing the opened database handle and the open fd
 for the lock. */
@@ -260,12 +260,12 @@ dlen = snprintf(CS dirname, sizeof(dirname), "%s/db", spool_directory);
 snprintf(CS filename, sizeof(filename), "%.*s/%s", dlen, dirname, name);
 
 priv_drop_temp(exim_uid, exim_gid);
-dbblock->dbptr = exim_dbopen_multi(filename, dirname, flags, EXIMDB_MODE);
-if (!dbblock->dbptr && errno == ENOENT && flags == O_RDWR)
+dbblock->dbptr = exim_dbopen_multi(filename, dirname, flags & O_ACCMODE, EXIMDB_MODE);
+if (!dbblock->dbptr && errno == ENOENT && flags & O_CREAT)
   {
   DEBUG(D_hints_lookup)
     debug_printf_indent("%s appears not to exist: trying to create\n", filename);
-  dbblock->dbptr = exim_dbopen_multi(filename, dirname, O_RDWR|O_CREAT, EXIMDB_MODE);
+  dbblock->dbptr = exim_dbopen_multi(filename, dirname, flags, EXIMDB_MODE);
   }
 save_errno = errno;
 priv_restore();
@@ -289,8 +289,13 @@ if (!dbblock->dbptr)
   return NULL;
   }
 
-DEBUG(D_hints_lookup) debug_printf_indent(
-    "opened hints database %s for transactions: NOLOCK flags=O_RDWR\n", filename);
+DEBUG(D_hints_lookup)
+  debug_printf_indent("opened hints database %s for transactions: NOLOCK flags=%s%s\n",
+    filename,
+    (flags & O_ACCMODE) == O_RDONLY ? "O_RDONLY"
+    : (flags & O_ACCMODE) == O_RDWR ? "O_RDWR"
+    : "??",
+    flags & O_CREAT ? "|O_CREAT" : "");
 
 /* Pass back the block containing the opened database handle */
 
@@ -660,7 +665,7 @@ while (Ufgets(buffer, 256, stdin) != NULL)
       }
 
     start = clock();
-    odb = dbfn_open(s, O_RDWR, dbblock + i, TRUE, TRUE);
+    odb = dbfn_open(s, O_RDWR|O_CREAT, dbblock + i, TRUE, TRUE);
     stop = clock();
 
     if (odb)
index 7576682f411dfac886874b0e5970cb8fecdcbf9e..b04fe6f12045794a85d8bad28dbf68ec032da35e 100644 (file)
@@ -7453,7 +7453,7 @@ while (addr_new)           /* Loop until all addresses dealt with */
       }
     else if (!exim_lockfile_needed() && continue_transport)
       {
-      dbm_file = dbfn_open_multi(US"retry", O_RDONLY, &dbblock);
+      dbm_file = dbfn_open_multi(US"retry", O_RDWR, &dbblock);
       continue_retry_db = dbm_file ? dbm_file : (open_db *)-1;
       }
     else
index 43f53a5853cde85bf2f6533984528862bfc335ad..057dffae1f112b18925f2c2da2e60b82b3b4bc68 100644 (file)
@@ -44,12 +44,9 @@ open_db *dbm_file;
 
 DEBUG(D_transport) debug_printf("check serialized: %s\n", key);
 
-/* Open and lock the waiting information database. The absence of O_CREAT is
-deliberate; the dbfn_open() function - which is an Exim function - always tries
-to create if it can't open a read/write file. It expects only O_RDWR or
-O_RDONLY as its argument. */
+/* Open and lock the waiting information database. */
 
-if (!(dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE, TRUE)))
+if (!(dbm_file = dbfn_open(US"misc", O_RDWR|O_CREAT, &dbblock, TRUE, TRUE)))
   return FALSE;
 
 /* See if there is a record for this host or queue run; if there is, we cannot
index d3d854dd5520e78a48638c041010ca21640183ae..dd1444593b0f5c9d1203e3f69f6d40fc2338145c 100644 (file)
@@ -271,7 +271,7 @@ the lock file.
 
 Arguments:
   name     The single-component name of one of Exim's database files.
-  flags    O_RDONLY or O_RDWR
+  flags    O_RDONLY or O_RDWR, O_CREAT
   dbblock  Points to an open_db block to be filled in.
   lof      Unused.
   panic           Unused
@@ -340,8 +340,6 @@ if (exim_lockfile_needed())
 
 if (asprintf(CSS &filename, "%s/%s", dirname, name) < 0) return NULL;
 
-if (flags & O_RDWR) flags |= O_CREAT;
-
 if (!(dbblock->dbptr = exim_dbopen(filename, dirname, flags, 0)))
   {
   printf("** Failed to open hintsdb file %s for %s: %s%s\n", filename,
@@ -846,7 +844,7 @@ for(; (reset_point = store_mark()); store_reset(reset_point))
     {
     int verify = 1;
 
-    if (!(dbm = dbfn_open(aname, O_RDWR, &dbblock, FALSE, TRUE)))
+    if (!(dbm = dbfn_open(aname, O_RDWR|O_CREAT, &dbblock, FALSE, TRUE)))
       continue;
 
     if (Ustrcmp(field, "d") == 0)
@@ -1209,7 +1207,7 @@ oldest = time(NULL) - maxkeep;
 printf("Tidying Exim hints database %s/db/%s\n", argv[1], argv[2]);
 
 spool_directory = argv[1];
-if (!(dbm = dbfn_open(argv[2], O_RDWR, &dbblock, FALSE, TRUE)))
+if (!(dbm = dbfn_open(argv[2], O_RDWR|O_CREAT, &dbblock, FALSE, TRUE)))
   exit(EXIT_FAILURE);
 
 /* Prepare for building file names */
index 071c94cd8ae8448149b4662742ef743071cc7b85..e86b1afe8dbee4042400398e9c690511c237b351 100644 (file)
@@ -617,7 +617,7 @@ for (int i = 0; i < 3; i++)
         reached their retry next try time. */
 
         if (!dbm_file)
-          dbm_file = dbfn_open(US"retry", O_RDWR, &dbblock, TRUE, TRUE);
+          dbm_file = dbfn_open(US"retry", O_RDWR|O_CREAT, &dbblock, TRUE, TRUE);
 
         if (!dbm_file)
           {
index f443a6e274eebb1db07d11f05a757dd1ff5a91d0..25690aed2ea6e638a948bf5f657dd43af84c322f 100644 (file)
@@ -3385,7 +3385,7 @@ if (gnutls_session_get_flags(session) & GNUTLS_SFLAGS_SESSION_TICKET)
       memcpy(dt->session, tkt.data, tkt.size);
       gnutls_free(tkt.data);
 
-      if ((dbm_file = dbfn_open(US"tls", O_RDWR, &dbblock, FALSE, FALSE)))
+      if ((dbm_file = dbfn_open(US"tls", O_RDWR|O_CREAT, &dbblock, FALSE, FALSE)))
        {
        /* key for the db is the IP */
        dbfn_write(dbm_file, tlsp->resume_index, dt, dlen);
index c97106fe031674dc1fc3de014dbf6e2411995c50..0a5e2a7a0277be6bff75ed185f1e4bb7153ed219 100644 (file)
@@ -3933,7 +3933,7 @@ if (tlsp->host_resumable)
   tlsp->resumption |= RESUME_CLIENT_REQUESTED;
   DEBUG(D_tls)
     debug_printf("checking for resumable session for %s\n", tlsp->resume_index);
-  if ((dbm_file = dbfn_open(US"tls", O_RDWR, &dbblock, FALSE, FALSE)))
+  if ((dbm_file = dbfn_open(US"tls", O_RDWR|O_CREAT, &dbblock, FALSE, FALSE)))
     {
     if ((dt = dbfn_read_with_length(dbm_file, tlsp->resume_index, &len)))
       {
@@ -4016,7 +4016,7 @@ if (SSL_SESSION_is_resumable(ss))         /* 1.1.1 */
   dt->ocsp = tlsp->ocsp;
   (void) i2d_SSL_SESSION(ss, &s);              /* s gets bumped to end */
 
-  if ((dbm_file = dbfn_open(US"tls", O_RDWR, &dbblock, FALSE, FALSE)))
+  if ((dbm_file = dbfn_open(US"tls", O_RDWR|O_CREAT, &dbblock, FALSE, FALSE)))
     {
     dbfn_write(dbm_file, tlsp->resume_index, dt, dlen);
     dbfn_close(dbm_file);
index 3980223544c56cc8993ff0a1c19f1eb14a2564a9..1709971729b771cf8de88cdd0b79cffd69f5da66 100644 (file)
@@ -1516,7 +1516,7 @@ DEBUG(D_transport) debug_printf("updating wait-%s database\n", tpname);
 if ( continue_wait_db
    ? !dbfn_transaction_start(dbp = continue_wait_db)
    : !(dbp = dbfn_open(string_sprintf("wait-%.200s", tpname),
-                     O_RDWR, &dbblock, TRUE, TRUE))
+                     O_RDWR|O_CREAT, &dbblock, TRUE, TRUE))
    )
   return;
 
index 581e94f76e3cb534d3ce376f2ec44c2bb00c995a..5b54fa1ae889c4d6b863835e0b7760aac9f3ad30 100644 (file)
@@ -913,7 +913,7 @@ sx->ehlo_resp.limit_rcpt = sx->peer_limit_rcpt;
 sx->ehlo_resp.limit_rcptdom = sx->peer_limit_rcptdom;
 # endif
 
-if ((dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE, TRUE)))
+if ((dbm_file = dbfn_open(US"misc", O_RDWR|O_CREAT, &dbblock, TRUE, TRUE)))
   {
   uschar * ehlo_resp_key = ehlo_cache_key(sx);
   dbdata_ehlo_resp er = { .data = sx->ehlo_resp };
@@ -943,7 +943,7 @@ invalidate_ehlo_cache_entry(smtp_context * sx)
 open_db dbblock, * dbm_file;
 
 if (  sx->early_pipe_active
-   && (dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE, TRUE)))
+   && (dbm_file = dbfn_open(US"misc", O_RDWR|O_CREAT, &dbblock, TRUE, TRUE)))
   {
   uschar * ehlo_resp_key = ehlo_cache_key(sx);
   HDEBUG(D_transport)
@@ -981,7 +981,7 @@ else
     {
     DEBUG(D_transport) debug_printf("ehlo-resp record too old\n");
     dbfn_close(dbm_file);
-    if ((dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE, TRUE)))
+    if ((dbm_file = dbfn_open(US"misc", O_RDWR|O_CREAT, &dbblock, TRUE, TRUE)))
       dbfn_delete(dbm_file, ehlo_resp_key);
     }
   else
index ab56ed3743d97a9f6df09e1d78ab740290d12c5a..25fc700f94560f820a763455344cb648e7a8b560 100644 (file)
@@ -121,7 +121,7 @@ if (options & vopt_callout_no_cache)
   {
   HDEBUG(D_verify) debug_printf_indent("callout cache: disabled by no_cache\n");
   }
-else if (!(dbm_file = dbfn_open(US"callout", O_RDWR, &dbblock, FALSE, TRUE)))
+else if (!(dbm_file = dbfn_open(US"callout", O_RDWR|O_CREAT, &dbblock, FALSE, TRUE)))
   {
   HDEBUG(D_verify) debug_printf_indent("callout cache: not available\n");
   }
@@ -294,7 +294,7 @@ implying some kind of I/O error. We don't want to write the cache in that case.
 Otherwise the value is ccache_accept, ccache_reject, or ccache_reject_mfnull. */
 
 if (dom_rec->result != ccache_unknown)
-  if (!(dbm_file = dbfn_open(US"callout", O_RDWR, &dbblock, FALSE, TRUE)))
+  if (!(dbm_file = dbfn_open(US"callout", O_RDWR|O_CREAT, &dbblock, FALSE, TRUE)))
     {
     HDEBUG(D_verify) debug_printf_indent("callout cache: not available\n");
     }
@@ -316,7 +316,7 @@ is disabled. */
 if (done  &&  addr_rec->result != ccache_unknown)
   {
   if (!dbm_file)
-    dbm_file = dbfn_open(US"callout", O_RDWR, &dbblock, FALSE, TRUE);
+    dbm_file = dbfn_open(US"callout", O_RDWR|O_CREAT, &dbblock, FALSE, TRUE);
   if (!dbm_file)
     {
     HDEBUG(D_verify) debug_printf_indent("no callout cache available\n");
@@ -3497,7 +3497,7 @@ dbdata_callout_cache_address * cache_address_record;
 
 if (!pos_cache && !neg_cache)
   return FALSE;
-if (!(dbm_file = dbfn_open(US"callout", O_RDWR, &dbblock, FALSE, TRUE)))
+if (!(dbm_file = dbfn_open(US"callout", O_RDWR|O_CREAT, &dbblock, FALSE, TRUE)))
   {
   HDEBUG(D_verify) debug_printf_indent("quota cache: not available\n");
   return FALSE;
@@ -3525,7 +3525,7 @@ dbdata_callout_cache_address cache_address_record;
 
 if (!pos_cache && !neg_cache)
   return;
-if (!(dbm_file = dbfn_open(US"callout", O_RDWR, &dbblock, FALSE, TRUE)))
+if (!(dbm_file = dbfn_open(US"callout", O_RDWR|O_CREAT, &dbblock, FALSE, TRUE)))
   {
   HDEBUG(D_verify) debug_printf_indent("quota cache: not available\n");
   return;
index 7aa897242a602ec9ecbc0a0e4277418306e0f3c4..152c91a27b24eb6193890f890118aadda4899f36 100644 (file)
@@ -154,7 +154,7 @@ Processing retry items
   x@y
    ╎EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDWR
    ╎returned from EXIM_DBOPEN: 0xAAAAAAAA
-   ╎opened hints database TESTSUITE/spool/db/retry: flags=O_RDWR
+   ╎opened hints database TESTSUITE/spool/db/retry: flags=O_RDWR|O_CREAT
    ╎x@y in "*"?
    ╎ list element: *
    ╎ address match test: subject=x@y pattern=*
index bb31becd48d709b254683c7afe1bc61ce98653ad..169c332ad00abd645bc39968813c8c82ebf9c53c 100644 (file)
@@ -141,7 +141,7 @@ routed by r2 router
 Attempting full verification using callout
  EXIM_DBOPEN: file <TESTSUITE/spool/db/callout> dir <TESTSUITE/spool/db> flags=O_RDWR
  returned from EXIM_DBOPEN: 0xAAAAAAAA
- opened hints database TESTSUITE/spool/db/callout: flags=O_RDWR
+ opened hints database TESTSUITE/spool/db/callout: flags=O_RDWR|O_CREAT
  dbfn_read: key=remote
  dbfn_read: size nnn return
  callout cache: found domain record for remote
@@ -177,7 +177,7 @@ cmd buf flush ddd bytes
 cmdlog: '220:EHLO:250:MAIL:250:RCPT:550:QUIT:250'
  EXIM_DBOPEN: file <TESTSUITE/spool/db/callout> dir <TESTSUITE/spool/db> flags=O_RDWR
  returned from EXIM_DBOPEN: 0xAAAAAAAA
- opened hints database TESTSUITE/spool/db/callout: flags=O_RDWR
+ opened hints database TESTSUITE/spool/db/callout: flags=O_RDWR|O_CREAT
  dbfn_write: key=remote datalen nn
  wrote callout cache domain record for remote:
    result=1 postmaster=0 random=0
@@ -274,7 +274,7 @@ routed by r2 router
 Attempting full verification using callout
  EXIM_DBOPEN: file <TESTSUITE/spool/db/callout> dir <TESTSUITE/spool/db> flags=O_RDWR
  returned from EXIM_DBOPEN: 0xAAAAAAAA
- opened hints database TESTSUITE/spool/db/callout: flags=O_RDWR
+ opened hints database TESTSUITE/spool/db/callout: flags=O_RDWR|O_CREAT
  dbfn_read: key=remote
  dbfn_read: size nnn return
  callout cache: found domain record for remote
index 1f17d175df4e8e7bee45cdfa1b45f84cc94c9b34..162877b7a9c0150e4d06e18e631c02d5332e6406 100644 (file)
@@ -94,7 +94,7 @@ get[host|ipnode]byname[2] looked up these IP addresses:
 Attempting full verification using callout
  EXIM_DBOPEN: file <TESTSUITE/spool/db/callout> dir <TESTSUITE/spool/db> flags=O_RDWR
  returned from EXIM_DBOPEN: 0xAAAAAAAA
- opened hints database TESTSUITE/spool/db/callout: flags=O_RDWR
+ opened hints database TESTSUITE/spool/db/callout: flags=O_RDWR|O_CREAT
  dbfn_read: key=y
  dbfn_read: null return
  callout cache: no domain record found for y
@@ -130,7 +130,7 @@ cmd buf flush ddd bytes
 cmdlog: '220:EHLO:250:MAIL:250:RCPT:250:QUIT:220'
  EXIM_DBOPEN: file <TESTSUITE/spool/db/callout> dir <TESTSUITE/spool/db> flags=O_RDWR
  returned from EXIM_DBOPEN: 0xAAAAAAAA
- opened hints database TESTSUITE/spool/db/callout: flags=O_RDWR
+ opened hints database TESTSUITE/spool/db/callout: flags=O_RDWR|O_CREAT
  dbfn_write: key=y datalen nn
  wrote callout cache domain record for y:
    result=1 postmaster=0 random=0
@@ -216,7 +216,7 @@ get[host|ipnode]byname[2] looked up these IP addresses:
 Attempting full verification using callout
  EXIM_DBOPEN: file <TESTSUITE/spool/db/callout> dir <TESTSUITE/spool/db> flags=O_RDWR
  returned from EXIM_DBOPEN: 0xAAAAAAAA
- opened hints database TESTSUITE/spool/db/callout: flags=O_RDWR
+ opened hints database TESTSUITE/spool/db/callout: flags=O_RDWR|O_CREAT
  dbfn_read: key=y
  dbfn_read: size nnn return
  callout cache: found domain record for y
index 603ad30f6e7505964325563aa1d30aaa32bcc4f8..2e1c3b08dadb604adc5c17bd4b1a3e6c03aecde0 100644 (file)
@@ -552,7 +552,7 @@ Processing retry items
   userx@test.ex
    ╎EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDWR
    ╎returned from EXIM_DBOPEN: 0xAAAAAAAA
-   ╎opened hints database TESTSUITE/spool/db/retry: flags=O_RDWR
+   ╎opened hints database TESTSUITE/spool/db/retry: flags=O_RDWR|O_CREAT
    ╎userx@test.ex in "*"?
    ╎ list element: *
    ╎ address match test: subject=userx@test.ex pattern=*
@@ -752,7 +752,7 @@ Processing retry items
   userx@test.ex
    ╎EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDWR
    ╎returned from EXIM_DBOPEN: 0xAAAAAAAA
-   ╎opened hints database TESTSUITE/spool/db/retry: flags=O_RDWR
+   ╎opened hints database TESTSUITE/spool/db/retry: flags=O_RDWR|O_CREAT
    ╎userx@test.ex in "*"?
    ╎ list element: *
    ╎ address match test: subject=userx@test.ex pattern=*