build: use pkg-config for i18n
[exim.git] / src / src / drtables.c
index b95d4fc952a0f1ba72d13e8bad0823c82d1bcbb3..28846320c914c6ef03e277d978791d0f3f4cc2ee 100644 (file)
@@ -1,15 +1,16 @@
-/* $Cambridge: exim/src/src/drtables.c,v 1.9 2007/09/28 12:21:57 tom Exp $ */
-
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2007 */
+/* Copyright (c) The Exim Maintainers 2020 - 2024 */
+/* Copyright (c) University of Cambridge 1995 - 2018 */
 /* See the file NOTICE for conditions of use and distribution. */
 /* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
 
 
 #include "exim.h"
 
 
 
 #include "exim.h"
 
+#include <string.h>
 
 /* This module contains tables that define the lookup methods and drivers
 that are actually included in the binary. Its contents are controlled by
 
 /* This module contains tables that define the lookup methods and drivers
 that are actually included in the binary. Its contents are controlled by
@@ -17,874 +18,794 @@ various macros in config.h that ultimately come from Local/Makefile. They are
 all described in src/EDITME. */
 
 
 all described in src/EDITME. */
 
 
-/* The OSF1 (Digital Unix) linker puts out a worrying warning if any sections
-contain no executable code. It says
+//lookup_info **lookup_list;
+tree_node * lookups_tree = NULL;
+unsigned lookup_list_count = 0;
 
 
-Warning: Linking some objects which contain exception information sections
-        and some which do not. This may cause fatal runtime exception handling
-        problems.
+/* Lists of information about which drivers are included in the exim binary. */
 
 
-As this may cause people to worry needlessly, include a dummy function here
-to stop the message from appearing. Make it call itself to stop picky compilers
-compilers complaining that it is unused, and put in a dummy argument to stop
-even pickier compilers complaining about infinite loops. */
+auth_info * auths_available= NULL;
+router_info * routers_available = NULL;
+transport_info * transports_available = NULL;
 
 
-static void dummy(int x) { dummy(x-1); }
 
 
 
 
-/* Table of information about all possible lookup methods. The entries are
-always present, but the "open" and "find" functions are set to NULL for those
-that are not compiled into the binary. The "check" and "close" functions can
-be NULL for methods that don't need them. */
+#ifndef MACRO_PREDEF
 
 
-#ifdef LOOKUP_CDB
-#include "lookups/cdb.h"
+gstring *
+auth_show_supported(gstring * g)
+{
+uschar * b = US""               /* static-build authenticatornames */
+#if defined(AUTH_CRAM_MD5) && AUTH_CRAM_MD5!=2
+  " cram_md5"
 #endif
 #endif
-
-#ifdef LOOKUP_DBM
-#include "lookups/dbmdb.h"
+#if defined(AUTH_CYRUS_SASL) && AUTH_CYRUS_SASL!=2
+  " cyrus_sasl"
 #endif
 #endif
-
-#ifdef LOOKUP_DNSDB
-#include "lookups/dnsdb.h"
+#if defined(AUTH_DOVECOT) && AUTH_DOVECOT!=2
+  " dovecot"
 #endif
 #endif
-
-#ifdef LOOKUP_DSEARCH
-#include "lookups/dsearch.h"
+#if defined(AUTH_EXTERNAL) && AUTH_EXTERNAL!=2
+  " external"
 #endif
 #endif
-
-#ifdef LOOKUP_IBASE
-#include "lookups/ibase.h"
+#if defined(AUTH_GSASL) && AUTH_GSASL!=2
+  " gsasl"
 #endif
 #endif
-
-#ifdef LOOKUP_LDAP
-#include "lookups/ldap.h"
+#if defined(AUTH_HEIMDAL_GSSAPI) && AUTH_HEIMDAL_GSSAPI!=2
+  " heimdal_gssapi"
 #endif
 #endif
-
-#ifdef LOOKUP_LSEARCH
-#include "lookups/lsearch.h"
+#if defined(AUTH_PLAINTEXT) && AUTH_PLAINTEXT!=2
+  " plaintext"
 #endif
 #endif
-
-#ifdef LOOKUP_MYSQL
-#include "lookups/mysql.h"
+#if defined(AUTH_SPA) && AUTH_SPA!=2
+  " spa"
 #endif
 #endif
-
-#ifdef LOOKUP_NIS
-#include "lookups/nis.h"
+#if defined(AUTH_TLS) && AUTH_TLS!=2
+  " tls"
 #endif
 #endif
+  ;
 
 
-#ifdef LOOKUP_NISPLUS
-#include "lookups/nisplus.h"
+uschar * d = US""              /* dynamic-module authenticator names */
+#if defined(AUTH_CRAM_MD5) && AUTH_CRAM_MD5==2
+  " cram_md5"
 #endif
 #endif
-
-#ifdef LOOKUP_ORACLE
-#include "lookups/oracle.h"
+#if defined(AUTH_CYRUS_SASL) && AUTH_CYRUS_SASL==2
+  " cyrus_sasl"
 #endif
 #endif
-
-#ifdef LOOKUP_PASSWD
-#include "lookups/passwd.h"
+#if defined(AUTH_DOVECOT) && AUTH_DOVECOT==2
+  " dovecot"
 #endif
 #endif
-
-#ifdef LOOKUP_PGSQL
-#include "lookups/pgsql.h"
+#if defined(AUTH_EXTERNAL) && AUTH_EXTERNAL==2
+  " external"
 #endif
 #endif
-
-#ifdef EXPERIMENTAL_SPF
-#include "lookups/spf.h"
+#if defined(AUTH_GSASL) && AUTH_GSASL==2
+  " gsasl"
 #endif
 #endif
-
-#ifdef LOOKUP_SQLITE
-#include "lookups/sqlite.h"
+#if defined(AUTH_HEIMDAL_GSSAPI) && AUTH_HEIMDAL_GSSAPI==2
+  " heimdal_gssapi"
 #endif
 #endif
-
-#ifdef LOOKUP_TESTDB
-#include "lookups/testdb.h"
+#if defined(AUTH_PLAINTEXT) && AUTH_PLAINTEXT==2
+  " plaintext"
 #endif
 #endif
-
-#ifdef LOOKUP_WHOSON
-#include "lookups/whoson.h"
+#if defined(AUTH_SPA) && AUTH_SPA==2
+  " spa"
 #endif
 #endif
-
-#ifdef EXPERIMENTAL_DKIM
-#include "lookups/dkim.h"
+#if defined(AUTH_TLS) && AUTH_TLS==2
+  " tls"
 #endif
 #endif
+  ;
 
 
+if (*b) g = string_fmt_append(g, "Authenticators (built-in):%s\n", b);
+if (*d) g = string_fmt_append(g, "Authenticators (dynamic): %s\n", d);
+return g;
+}
 
 
-/* The second field in each item below is a set of bit flags:
-
-  lookup_querystyle     => this is a query-style lookup,
-                             else single-key (+ file) style
-  lookup_absfile        => an absolute file name is required,
-                             (for single-key style only)
-
-This list must be in alphabetical order of lookup name because it is
-searched by binary chop, having got rather large for the original linear
-searching. */
-
-lookup_info lookup_list[] = {
-
-/* cdb lookup in single file */
+gstring *
+route_show_supported(gstring * g)
+{
+uschar * b = US""              /* static-build router names */
+#if defined(ROUTER_ACCEPT) && ROUTER_ACCEPT!=2
+  " accept"
+#endif
+#if defined(ROUTER_DNSLOOKUP) && ROUTER_DNSLOOKUP!=2
+  " dnslookup"
+#endif
+# if defined(ROUTER_IPLITERAL) && ROUTER_IPLITERAL!=2
+  " ipliteral"
+#endif
+#if defined(ROUTER_IPLOOKUP) && ROUTER_IPLOOKUP!=2
+  " iplookup"
+#endif
+#if defined(ROUTER_MANUALROUTE) && ROUTER_MANUALROUTE!=2
+  " manualroute"
+#endif
+#if defined(ROUTER_REDIRECT) && ROUTER_REDIRECT!=2
+  " redirect"
+#endif
+#if defined(ROUTER_QUERYPROGRAM) && ROUTER_QUERYPROGRAM!=2
+  " queryprogram"
+#endif
+  ;
 
 
-  {
-  US"cdb",                       /* lookup name */
-  lookup_absfile,                /* uses absolute file name */
-#ifdef LOOKUP_CDB
-  cdb_open,                      /* open function */
-  cdb_check,                     /* check function */
-  cdb_find,                      /* find function */
-  cdb_close,                     /* close function */
-  NULL,                          /* no tidy function */
-  NULL                           /* no quoting function */
-#else
-  NULL, NULL, NULL, NULL, NULL, NULL /* lookup not present */
+uschar * d = US""              /* dynamic-module router names */
+#if defined(ROUTER_ACCEPT) && ROUTER_ACCEPT==2
+  " accept"
+#endif
+#if defined(ROUTER_DNSLOOKUP) && ROUTER_DNSLOOKUP==2
+  " dnslookup"
+#endif
+# if defined(ROUTER_IPLITERAL) && ROUTER_IPLITERAL==2
+  " ipliteral"
+#endif
+#if defined(ROUTER_IPLOOKUP) && ROUTER_IPLOOKUP==2
+  " iplookup"
 #endif
 #endif
-  },
+#if defined(ROUTER_MANUALROUTE) && ROUTER_MANUALROUTE==2
+  " manualroute"
+#endif
+#if defined(ROUTER_REDIRECT) && ROUTER_REDIRECT==2
+  " redirect"
+#endif
+#if defined(ROUTER_QUERYPROGRAM) && ROUTER_QUERYPROGRAM==2
+  " queryprogram"
+#endif
+  ;
 
 
-/* DBM file lookup; called "dbm" because that is the name in Exim,
-but the code is called dbmdb to avoid name clashes. */
+if (*b) g = string_fmt_append(g, "Routers (built-in):%s\n", b);
+if (*d) g = string_fmt_append(g, "Routers (dynamic): %s\n", d);
+return g;
+}
 
 
-  {
-  US"dbm",                       /* lookup name */
-  lookup_absfile,                /* uses absolute file name */
-#ifdef LOOKUP_DBM
-  dbmdb_open,                    /* open function */
-  dbmdb_check,                   /* check function */
-  dbmdb_find,                    /* find function */
-  dbmdb_close,                   /* close function */
-  NULL,                          /* no tidy function */
-  NULL                           /* no quoting function */
-#else
-  NULL, NULL, NULL, NULL, NULL, NULL /* lookup not present */
+gstring *
+transport_show_supported(gstring * g)
+{
+uschar * b = US""              /* static-build transportnames */
+#if defined(TRANSPORT_APPENDFILE) && TRANSPORT_APPENDFILE!=2
+  " appendfile"
+# ifdef SUPPORT_MAILDIR
+    "/maildir"
+# endif
+# ifdef SUPPORT_MAILSTORE
+    "/mailstore"
+# endif
+# ifdef SUPPORT_MBX
+    "/mbx"
+# endif
 #endif
 #endif
-  },
-
-/* This variant of DBM does not include the binary zero on the end
-of the key strings. */
+#if defined(TRANSPORT_AUTOREPLY) && TRANSPORT_AUTOREPLY!=2
+  " autoreply"
+#endif
+#if defined(TRANSPORT_LMTP) && TRANSPORT_LMTP!=2
+  " lmtp"
+#endif
+#if defined(TRANSPORT_PIPE) && TRANSPORT_PIPE!=2
+  " pipe"
+#endif
+#if defined(EXPERIMENTAL_QUEUEFILE) && EXPERIMENTAL_QUEUEFILE!=2
+  " queuefile"
+#endif
+#if defined(TRANSPORT_SMTP) && TRANSPORT_SMTP!=2
+  " smtp"
+#endif
+  ;
 
 
-  {
-  US"dbmnz",                     /* lookup name */
-  lookup_absfile,                /* uses absolute file name */
-#ifdef LOOKUP_DBM
-  dbmdb_open,      /* sic */     /* open function */
-  dbmdb_check,     /* sic */     /* check function */
-  dbmnz_find,                    /* find function */
-  dbmdb_close,     /* sic */     /* close function */
-  NULL,                          /* no tidy function */
-  NULL                           /* no quoting function */
-#else
-  NULL, NULL, NULL, NULL, NULL, NULL /* lookup not present */
+uschar * d = US""              /* dynamic-module transportnames */
+#if defined(TRANSPORT_APPENDFILE) && TRANSPORT_APPENDFILE==2
+  " appendfile"
+# ifdef SUPPORT_MAILDIR
+    "/maildir"
+# endif
+# ifdef SUPPORT_MAILSTORE
+    "/mailstore"
+# endif
+# ifdef SUPPORT_MBX
+    "/mbx"
+# endif
+#endif
+#if defined(TRANSPORT_AUTOREPLY) && TRANSPORT_AUTOREPLY==2
+  " autoreply"
+#endif
+#if defined(TRANSPORT_LMTP) && TRANSPORT_LMTP==2
+  " lmtp"
+#endif
+#if defined(TRANSPORT_PIPE) && TRANSPORT_PIPE==2
+  " pipe"
+#endif
+#if defined(EXPERIMENTAL_QUEUEFILE) && EXPERIMENTAL_QUEUEFILE==2
+  " queuefile"
+#endif
+#if defined(TRANSPORT_SMTP) && TRANSPORT_SMTP==2
+  " smtp"
 #endif
 #endif
-  },
+  ;
 
 
-/* DKIM lookups */
+if (*b) g = string_fmt_append(g, "Transports (built-in):%s\n", b);
+if (*d) g = string_fmt_append(g, "Transports (dynamic): %s\n", d);
+return g;
+}
 
 
-  {
-  US"dkim",                      /* lookup name */
-  lookup_querystyle,             /* query style */
-#ifdef EXPERIMENTAL_DKIM
-  dkim_open,                     /* open function */
-  NULL,                          /* check function */
-  dkim_find,                     /* find function */
-  NULL,                          /* no close function */
-  NULL,                          /* no tidy function */
-  NULL                           /* no quoting function */
-#else
-  NULL, NULL, NULL, NULL, NULL, NULL /* lookup not present */
-#endif
-  },
 
 
-/* Using DNS TXT records as a database */
 
 
-  {
-  US"dnsdb",                     /* lookup name */
-  lookup_querystyle,             /* query style */
-#ifdef LOOKUP_DNSDB
-  dnsdb_open,                    /* open function */
-  NULL,                          /* check function */
-  dnsdb_find,                    /* find function */
-  NULL,                          /* no close function */
-  NULL,                          /* no tidy function */
-  NULL                           /* no quoting function */
-#else
-  NULL, NULL, NULL, NULL, NULL, NULL /* lookup not present */
-#endif
-  },
+static void
+add_lookup_to_tree(lookup_info * li)
+{
+tree_node * new = store_get_perm(sizeof(tree_node) + Ustrlen(li->name),
+                                                       GET_UNTAINTED);
+new->data.ptr = (void *)li;
+Ustrcpy(new->name, li->name);
+if (tree_insertnode(&lookups_tree, new))
+  li->acq_num = lookup_list_count++;
+else
+  log_write(0, LOG_MAIN|LOG_PANIC, "Duplicate lookup name '%s'", li->name);
+}
 
 
-/* Search of files in a directory */
 
 
-  {
-  US"dsearch",                   /* lookup name */
-  lookup_absfile,                /* uses absolute file name */
-#ifdef LOOKUP_DSEARCH
-  dsearch_open,                  /* open function */
-  dsearch_check,                 /* check function */
-  dsearch_find,                  /* find function */
-  dsearch_close,                 /* close function */
-  NULL,                          /* no tidy function */
-  NULL                           /* no quoting function */
-#else
-  NULL, NULL, NULL, NULL, NULL, NULL /* lookup not present */
-#endif
-  },
+/* Add all the lookup types provided by the module */
+static void
+addlookupmodule(const struct lookup_module_info * lmi)
+{
+for (int j = 0; j < lmi->lookupcount; j++)
+  add_lookup_to_tree(lmi->lookups[j]);
+}
 
 
-/* Interbase lookup */
 
 
-  {
-  US"ibase",                     /* lookup name */
-  lookup_querystyle,             /* query-style lookup */
-#ifdef LOOKUP_IBASE
-  ibase_open,                    /* open function */
-  NULL,                          /* no check function */
-  ibase_find,                    /* find function */
-  NULL,                          /* no close function */
-  ibase_tidy,                    /* tidy function */
-  ibase_quote                    /* quoting function */
-#else
-  NULL, NULL, NULL, NULL, NULL, NULL /* lookup not present */
-#endif
-  },
 
 
-/* Linear search of single file with ip-addresses and networks; shares many
-functions with lsearch. */
+/* Hunt for the lookup with the given acquisition number */
 
 
-  {
-  US"iplsearch",                 /* lookup name */
-  lookup_absfile,                /* uses absolute file name */
-#ifdef LOOKUP_LSEARCH
-  lsearch_open,                  /* open function */
-  lsearch_check,                 /* check function */
-  iplsearch_find,                /* find function */
-  lsearch_close,                 /* close function */
-  NULL,                          /* no tidy function */
-  NULL                           /* no quoting function */
-#else
-  NULL, NULL, NULL, NULL, NULL, NULL /* lookup not present */
-#endif
-  },
+static unsigned hunt_acq;
 
 
-/* LDAP lookup, allowing data from only one entry to be returned */
+static void
+acq_cb(uschar * name, uschar * ptr, void * ctx)
+{
+lookup_info * li = (lookup_info *)ptr;
+if (li->acq_num == hunt_acq) *(lookup_info **)ctx = li;
+}
 
 
-  {
-  US"ldap",                      /* lookup name */
-  lookup_querystyle,             /* query-style lookup */
-#ifdef LOOKUP_LDAP
-  eldap_open,                    /* open function */
-  NULL,                          /* check function */
-  eldap_find,                    /* find function */
-  NULL,                          /* no close function */
-  eldap_tidy,                    /* tidy function */
-  eldap_quote                    /* quoting function */
-#else
-  NULL, NULL, NULL, NULL, NULL, NULL /* lookup not present */
-#endif
-  },
+const lookup_info *
+lookup_with_acq_num(unsigned k)
+{
+const lookup_info * li = NULL;
+hunt_acq = k;
+tree_walk(lookups_tree, acq_cb, &li);
+return li;
+}
 
 
-/* LDAP lookup, allowing the DN from more one entry to be returned */
 
 
-  {
-  US"ldapdn",                     /* lookup name */
-  lookup_querystyle,             /* query-style lookup */
-#ifdef LOOKUP_LDAP
-  eldap_open,       /* sic */    /* open function */
-  NULL,                          /* check function */
-  eldapdn_find,                  /* find function */
-  NULL,                          /* no close function */
-  eldap_tidy,       /* sic */    /* tidy function */
-  eldap_quote       /* sic */    /* quoting function */
-#else
-  NULL, NULL, NULL, NULL, NULL, NULL /* lookup not present */
-#endif
-  },
 
 
-/* LDAP lookup, allowing data from more than one entry to be returned */
+/* These need to be at file level for old versions of gcc (2.95.2 reported),
+which give parse errors on an extern in function scope.  Each entry needs
+to also be invoked in init_lookup_list() below  */
 
 
-  {
-  US"ldapm",                     /* lookup name */
-  lookup_querystyle,             /* query-style lookup */
-#ifdef LOOKUP_LDAP
-  eldap_open,       /* sic */    /* open function */
-  NULL,                          /* check function */
-  eldapm_find,                   /* find function */
-  NULL,                          /* no close function */
-  eldap_tidy,       /* sic */    /* tidy function */
-  eldap_quote       /* sic */    /* quoting function */
-#else
-  NULL, NULL, NULL, NULL, NULL, NULL /* lookup not present */
+#if defined(LOOKUP_CDB) && LOOKUP_CDB!=2
+extern lookup_module_info cdb_lookup_module_info;
+#endif
+#if defined(LOOKUP_DBM) && LOOKUP_DBM!=2
+extern lookup_module_info dbmdb_lookup_module_info;
+#endif
+#if defined(LOOKUP_DNSDB) && LOOKUP_DNSDB!=2
+extern lookup_module_info dnsdb_lookup_module_info;
 #endif
 #endif
-  },
+#if defined(LOOKUP_DSEARCH) && LOOKUP_DSEARCH!=2
+extern lookup_module_info dsearch_lookup_module_info;
+#endif
+#if defined(LOOKUP_IBASE) && LOOKUP_IBASE!=2
+extern lookup_module_info ibase_lookup_module_info;
+#endif
+#if defined(LOOKUP_JSON) && LOOKUP_JSON!=2
+extern lookup_module_info json_lookup_module_info;
+#endif
+#if defined(LOOKUP_LDAP) && LOOKUP_LDAP!=2
+extern lookup_module_info ldap_lookup_module_info;
+#endif
+#if defined(LOOKUP_LSEARCH) && LOOKUP_LSEARCH!=2
+extern lookup_module_info lsearch_lookup_module_info;
+#endif
+#if defined(LOOKUP_MYSQL) && LOOKUP_MYSQL!=2
+extern lookup_module_info mysql_lookup_module_info;
+#endif
+#if defined(LOOKUP_NIS) && LOOKUP_NIS!=2
+extern lookup_module_info nis_lookup_module_info;
+#endif
+#if defined(LOOKUP_NISPLUS) && LOOKUP_NISPLUS!=2
+extern lookup_module_info nisplus_lookup_module_info;
+#endif
+#if defined(LOOKUP_ORACLE) && LOOKUP_ORACLE!=2
+extern lookup_module_info oracle_lookup_module_info;
+#endif
+#if defined(LOOKUP_PASSWD) && LOOKUP_PASSWD!=2
+extern lookup_module_info passwd_lookup_module_info;
+#endif
+#if defined(LOOKUP_PGSQL) && LOOKUP_PGSQL!=2
+extern lookup_module_info pgsql_lookup_module_info;
+#endif
+#if defined(LOOKUP_REDIS) && LOOKUP_REDIS!=2
+extern lookup_module_info redis_lookup_module_info;
+#endif
+#if defined(LOOKUP_LMDB) && LOOKUP_LMDB!=2
+extern lookup_module_info lmdb_lookup_module_info;
+#endif
+#if defined(SUPPORT_SPF)
+extern lookup_module_info spf_lookup_module_info;      /* see below */
+#endif
+#if defined(LOOKUP_SQLITE) && LOOKUP_SQLITE!=2
+extern lookup_module_info sqlite_lookup_module_info;
+#endif
+#if defined(LOOKUP_TESTDB) && LOOKUP_TESTDB!=2
+extern lookup_module_info testdb_lookup_module_info;
+#endif
+#if defined(LOOKUP_WHOSON) && LOOKUP_WHOSON!=2
+extern lookup_module_info whoson_lookup_module_info;
+#endif
+
+extern lookup_module_info readsock_lookup_module_info;
 
 
-/* Linear search of single file */
 
 
+#ifdef LOOKUP_MODULE_DIR
+static void *
+mod_open(const uschar * name, const uschar * class, uschar ** errstr)
+{
+const uschar * path = string_sprintf(
+  LOOKUP_MODULE_DIR "/%s_%s." DYNLIB_FN_EXT, name, class);
+void * dl;
+if (!(dl = dlopen(CS path, RTLD_NOW)))
   {
   {
-  US"lsearch",                   /* lookup name */
-  lookup_absfile,                /* uses absolute file name */
-#ifdef LOOKUP_LSEARCH
-  lsearch_open,                  /* open function */
-  lsearch_check,                 /* check function */
-  lsearch_find,                  /* find function */
-  lsearch_close,                 /* close function */
-  NULL,                          /* no tidy function */
-  NULL                           /* no quoting function */
-#else
-  NULL, NULL, NULL, NULL, NULL, NULL /* lookup not present */
-#endif
-  },
+  if (errstr)
+    *errstr = string_sprintf("Error loading %s: %s", name, dlerror());
+  else
+    (void) dlerror();          /* clear out error state */
+  return NULL;
+  }
 
 
-/* MYSQL lookup */
+/* FreeBSD nsdispatch() can trigger dlerror() errors about
+_nss_cache_cycle_prevention_function; we need to clear the dlerror()
+state before calling dlsym(), so that any error afterwards only comes
+from dlsym().  */
 
 
-  {
-  US"mysql",                     /* lookup name */
-  lookup_querystyle,             /* query-style lookup */
-#ifdef LOOKUP_MYSQL
-  mysql_open,                    /* open function */
-  NULL,                          /* no check function */
-  mysql_find,                    /* find function */
-  NULL,                          /* no close function */
-  mysql_tidy,                    /* tidy function */
-  mysql_quote                    /* quoting function */
-#else
-  NULL, NULL, NULL, NULL, NULL, NULL /* lookup not present */
-#endif
-  },
+(void) dlerror();
+return dl;
+}
 
 
-/* NIS lookup, excluding trailing 0 from key */
 
 
-  {
-  US"nis",                       /* lookup name */
-  0,                             /* not abs file, not query style*/
-#ifdef LOOKUP_NIS
-  nis_open,                      /* open function */
-  NULL,                          /* check function */
-  nis_find,                      /* find function */
-  NULL,                          /* no close function */
-  NULL,                          /* no tidy function */
-  NULL                           /* no quoting function */
-#else
-  NULL, NULL, NULL, NULL, NULL, NULL /* lookup not present */
-#endif
-  },
+/* Try to load a lookup module with the given name.
 
 
-/* NIS lookup, including trailing 0 in key */
+Arguments:
+    name               name of the lookup
+    errstr             if not NULL, place "open fail" error message here
 
 
-  {
-  US"nis0",                      /* lookup name */
-  0,                             /* not absfile, not query style */
-#ifdef LOOKUP_NIS
-  nis_open,    /* sic */         /* open function */
-  NULL,                          /* check function */
-  nis0_find,                     /* find function */
-  NULL,                          /* no close function */
-  NULL,                          /* no tidy function */
-  NULL                           /* no quoting function */
-#else
-  NULL, NULL, NULL, NULL, NULL, NULL /* lookup not present */
-#endif
-  },
+Return: boolean success
+*/
+
+static BOOL
+lookup_mod_load(const uschar * name, uschar ** errstr)
+{
+void * dl;
+struct lookup_module_info * info;
+const char * errormsg;
 
 
-/* NIS+ lookup */
+if (!(dl = mod_open(name, US"lookup", errstr)))
+  return FALSE;
 
 
+info = (struct lookup_module_info *) dlsym(dl, "_lookup_module_info");
+if ((errormsg = dlerror()))
   {
   {
-  US"nisplus",                   /* lookup name */
-  lookup_querystyle,             /* query-style lookup */
-#ifdef LOOKUP_NISPLUS
-  nisplus_open,                  /* open function */
-  NULL,                          /* check function */
-  nisplus_find,                  /* find function */
-  NULL,                          /* no close function */
-  NULL,                          /* no tidy function */
-  nisplus_quote                  /* quoting function */
-#else
-  NULL, NULL, NULL, NULL, NULL, NULL /* lookup not present */
-#endif
-  },
+  fprintf(stderr, "%s does not appear to be a lookup module (%s)\n", name, errormsg);
+  log_write(0, LOG_MAIN|LOG_PANIC, "%s does not appear to be a lookup module (%s)", name, errormsg);
+  dlclose(dl);
+  return FALSE;
+  }
+if (info->magic != LOOKUP_MODULE_INFO_MAGIC)
+  {
+  fprintf(stderr, "Lookup module %s is not compatible with this version of Exim\n", name);
+  log_write(0, LOG_MAIN|LOG_PANIC, "Lookup module %s is not compatible with this version of Exim", name);
+  dlclose(dl);
+  return FALSE;
+  }
 
 
-/* Linear search of single file, with wildcarding but no pattern expansion.
-Shares many functions with lsearch. */
+addlookupmodule(info);
+DEBUG(D_lookup) debug_printf_indent("Loaded \"%s\" (%d lookup type%s)\n",
+                                   name, info->lookupcount,
+                                   info->lookupcount > 1 ? "s" : "");
+return TRUE;
+}
 
 
-  {
-  US"nwildlsearch",              /* lookup name */
-  lookup_absfile,                /* uses absolute file name */
-#ifdef LOOKUP_LSEARCH
-  lsearch_open,                  /* open function */
-  lsearch_check,                 /* check function */
-  nwildlsearch_find,             /* find function */
-  lsearch_close,                 /* close function */
-  NULL,                          /* no tidy function */
-  NULL                           /* no quoting function */
-#else
-  NULL, NULL, NULL, NULL, NULL, NULL /* lookup not present */
-#endif
-  },
 
 
-/* Oracle lookup */
+/* Try to load a lookup module, assuming the module name is the same
+as the lookup type name.  This will only work for single-method modules.
+Other have to be always-load (see the RE in init_lookup_list() below).
+*/
 
 
-  {
-  US"oracle",                    /* lookup name */
-  lookup_querystyle,             /* query-style lookup */
-#ifdef LOOKUP_ORACLE
-  oracle_open,                   /* open function */
-  NULL,                          /* check function */
-  oracle_find,                   /* find function */
-  NULL,                          /* no close function */
-  oracle_tidy,                   /* tidy function */
-  oracle_quote                   /* quoting function */
-#else
-  NULL, NULL, NULL, NULL, NULL, NULL /* lookup not present */
-#endif
-  },
+BOOL
+lookup_one_mod_load(const uschar * name, uschar ** errstr)
+{
+if (!lookup_mod_load(name, errstr)) return FALSE;
+/*XXX notify daemon? */
+return TRUE;
+}
 
 
-/* passwd lookup */
+#endif /*LOOKUP_MODULE_DIR*/
 
 
-  {
-  US"passwd",                    /* lookup name */
-  lookup_querystyle,             /* query-style lookup */
-#ifdef LOOKUP_PASSWD
-  passwd_open,                   /* open function */
-  NULL,                          /* no check function */
-  passwd_find,                   /* find function */
-  NULL,                          /* no close function */
-  NULL,                          /* no tidy function */
-  NULL                           /* no quoting function */
-#else
-  NULL, NULL, NULL, NULL, NULL, NULL   /* lookup not present */
-#endif
-  },
 
 
-/* PGSQL lookup */
 
 
-  {
-  US"pgsql",                     /* lookup name */
-  lookup_querystyle,             /* query-style lookup */
-#ifdef LOOKUP_PGSQL
-  pgsql_open,                    /* open function */
-  NULL,                          /* no check function */
-  pgsql_find,                    /* find function */
-  NULL,                          /* no close function */
-  pgsql_tidy,                    /* tidy function */
-  pgsql_quote                    /* quoting function */
-#else
-  NULL, NULL, NULL, NULL, NULL, NULL   /* lookup not present */
-#endif
-  },
+misc_module_info * misc_module_list = NULL;
 
 
-/* SPF lookup */
+static void
+misc_mod_add(misc_module_info * mi)
+{
+mi->next = misc_module_list;
+misc_module_list = mi;
 
 
-  {
-  US"spf",                       /* lookup name */
-  0,                             /* not absfile, not query style */
-#ifdef EXPERIMENTAL_SPF
-  spf_open,                      /* open function */
-  NULL,                          /* no check function */
-  spf_find,                      /* find function */
-  spf_close,                     /* close function */
-  NULL,                          /* no tidy function */
-  NULL                           /* no quoting function */
-#else
-  NULL, NULL, NULL, NULL, NULL, NULL /* lookup not present */
-#endif
-  },
+if (mi->init && !mi->init(mi))
+  DEBUG(D_any)
+    debug_printf_indent("module init call failed for %s\n", mi->name);
 
 
-/* sqlite lookup */
+DEBUG(D_any) if (mi->lib_vers_report)
+  debug_printf_indent("%Y", mi->lib_vers_report(NULL));
 
 
-  {
-  US"sqlite",                    /* lookup name */
-  lookup_absfilequery,           /* query-style lookup, starts with file name */
-#ifdef LOOKUP_SQLITE
-  sqlite_open,                   /* open function */
-  NULL,                          /* no check function */
-  sqlite_find,                   /* find function */
-  sqlite_close,                  /* close function */
-  NULL,                          /* no tidy function */
-  sqlite_quote                   /* quoting function */
-#else
-  NULL, NULL, NULL, NULL, NULL, NULL /* lookup not present */
-#endif
-  },
+/* fprintf(stderr,"misc_mod_add: added %s\n", mi->name); */
+}
 
 
-/* Testdb lookup is for testing Exim, not useful for normal running.
-For that reason, we omit the entry entirely when not building it into
-the binary, so that attempts to use it give "unknown lookup type" instead
-of "lookup type not available". */
 
 
-#ifdef LOOKUP_TESTDB
-  {
-  US"testdb",                    /* lookup name */
-  lookup_querystyle,             /* query-style lookup */
-  testdb_open,                   /* open function */
-  NULL,                          /* check function */
-  testdb_find,                   /* find function */
-  NULL,                          /* no close function */
-  NULL,                          /* no tidy function */
-  NULL                           /* no quoting function */
-  },
-#endif
+#ifdef LOOKUP_MODULE_DIR
 
 
-/* "Whoson" lookup */
+/* Load a "misc" module, and add to list */
 
 
-  {
-  US"whoson",                    /* lookup name */
-  lookup_querystyle,             /* query-style lookup */
-#ifdef LOOKUP_WHOSON
-  whoson_open,                   /* open function */
-  NULL,                          /* check function */
-  whoson_find,                   /* find function */
-  NULL,                          /* no close function */
-  NULL,                          /* no tidy function */
-  NULL                           /* no quoting function */
-#else
-  NULL, NULL, NULL, NULL, NULL, NULL   /* lookup not present */
-#endif
-  },
+static misc_module_info *
+misc_mod_load(const uschar * name, uschar ** errstr)
+{
+void * dl;
+struct misc_module_info * mi;
+const char * errormsg;
 
 
-/* Linear search of single file, with wildcarding and pattern expansion. Shares
-many functions with lsearch. */
+DEBUG(D_any) debug_printf_indent("loading module '%s'\n", name);
+if (!(dl = mod_open(name, US"miscmod", errstr)))
+  {
+  DEBUG(D_any) if (errstr) debug_printf_indent(" mod_open: %s\n", *errstr);
+  return NULL;
+  }
 
 
+mi = (struct misc_module_info *) dlsym(dl,
+                                   CS string_sprintf("%s_module_info", name));
+if ((errormsg = dlerror()))
   {
   {
-  US"wildlsearch",               /* lookup name */
-  lookup_absfile,                /* uses absolute file name */
-#ifdef LOOKUP_LSEARCH
-  lsearch_open,                  /* open function */
-  lsearch_check,                 /* check function */
-  wildlsearch_find,              /* find function */
-  lsearch_close,                 /* close function */
-  NULL,                          /* no tidy function */
-  NULL                           /* no quoting function */
-#else
-  NULL, NULL, NULL, NULL, NULL, NULL /* lookup not present */
-#endif
+  fprintf(stderr, "%s does not appear to be a '%s' module (%s)\n",
+         name, name, errormsg);
+  log_write(0, LOG_MAIN|LOG_PANIC,
+    "%s does not contain the expected module info symbol (%s)", name, errormsg);
+  dlclose(dl);
+  return NULL;
   }
   }
+if (mi->dyn_magic != MISC_MODULE_MAGIC)
+  {
+  fprintf(stderr, "Module %s is not compatible with this version of Exim\n", name);
+  log_write(0, LOG_MAIN|LOG_PANIC, "Module %s is not compatible with this version of Exim", name);
+  dlclose(dl);
+  return FALSE;
+  }
+
+DEBUG(D_lookup) debug_printf_indent("Loaded \"%s\"\n", name);
+misc_mod_add(mi);
+return mi;
+}
+
+#endif /*LOOKUP_MODULE_DIR*/
+
+
+/* Find a "misc" module by name, if loaded.
+For now use a linear search down a linked list.  If the number of
+modules gets large, we might consider a tree.
+*/
+
+misc_module_info *
+misc_mod_findonly(const uschar * name)
+{
+for (misc_module_info * mi = misc_module_list; mi; mi = mi->next)
+  if (Ustrcmp(name, mi->name) == 0)
+    return mi;
+return NULL;
+}
+
+/* Find a "misc" module, possibly already loaded, by name. */
+
+misc_module_info *
+misc_mod_find(const uschar * name, uschar ** errstr)
+{
+misc_module_info * mi;
+if ((mi = misc_mod_findonly(name))) return mi;
+#ifdef LOOKUP_MODULE_DIR
+return misc_mod_load(name, errstr);
+#else
+return NULL;
+#endif /*LOOKUP_MODULE_DIR*/
+}
+
+
+/* For any "misc" module having a connection-init routine, call it. */
+
+int
+misc_mod_conn_init(const uschar * sender_helo_name,
+  const uschar * sender_host_address)
+{
+for (const misc_module_info * mi = misc_module_list; mi; mi = mi->next)
+  if (mi->conn_init)
+    if ((mi->conn_init) (sender_helo_name, sender_host_address) != OK)
+      return FAIL;
+return OK;
+}
+
+/* Ditto, smtp-reset */
+
+void
+misc_mod_smtp_reset(void)
+{
+for (const misc_module_info * mi = misc_module_list; mi; mi = mi->next)
+  if (mi->smtp_reset)
+    (mi->smtp_reset)();
+}
+
+/* Ditto, msg-init */
+
+int
+misc_mod_msg_init(void)
+{
+for (const misc_module_info * mi = misc_module_list; mi; mi = mi->next)
+  if (mi->msg_init)
+    if ((mi->msg_init)() != OK)
+      return FAIL;
+return OK;
+}
+
+/* Ditto, authres.  Having to sort the responses (mainly for the testsuite)
+is pretty painful - maybe we should sort the modules on insertion to
+the list? */
+
+gstring *
+misc_mod_authres(gstring * g)
+{
+typedef struct {
+  const uschar * name;
+  gstring *     res;
+} pref;
+pref prefs[] = {
+  {US"spf", NULL}, {US"dkim", NULL}, {US"dmarc", NULL}, {US"arc", NULL}
 };
 };
+gstring * others = NULL;
 
 
-/* Number of entries in the list */
+for (const misc_module_info * mi = misc_module_list; mi; mi = mi->next)
+  if (mi->authres)
+    {
+    pref * p;
+    for (p = prefs; p < prefs + nelem(prefs); p++)
+      if (Ustrcmp(p->name, mi->name) == 0) break;
 
 
-int lookup_list_count = sizeof(lookup_list)/sizeof(lookup_info);
+    if (p) p->res = (mi->authres)(NULL);
+    else   others = (mi->authres)(others);
+    }
 
 
+for (pref * p = prefs; p < prefs + nelem(prefs); p++)
+  g = gstring_append(g, p->res);
+return gstring_append(g, others);
+}
 
 
 
 
-/* Table of information about all possible authentication mechamisms. All
-entries are always present if any mechanism is declared, but the functions are
-set to NULL for those that are not compiled into the binary. */
 
 
-#ifdef AUTH_CRAM_MD5
-#include "auths/cram_md5.h"
-#endif
 
 
-#ifdef AUTH_CYRUS_SASL
-#include "auths/cyrus_sasl.h"
-#endif
 
 
-#ifdef AUTH_DOVECOT
-#include "auths/dovecot.h"
-#endif
 
 
-#ifdef AUTH_PLAINTEXT
-#include "auths/plaintext.h"
+void
+init_lookup_list(void)
+{
+#ifdef LOOKUP_MODULE_DIR
+DIR * dd;
+int countmodules = 0;
 #endif
 #endif
+static BOOL lookup_list_init_done = FALSE;
+
+if (lookup_list_init_done)
+  return;
+lookup_list_init_done = TRUE;
 
 
-#ifdef AUTH_SPA
-#include "auths/spa.h"
+#if defined(LOOKUP_CDB) && LOOKUP_CDB!=2
+addlookupmodule(&cdb_lookup_module_info);
 #endif
 
 #endif
 
-auth_info auths_available[] = {
+#if defined(LOOKUP_DBM) && LOOKUP_DBM!=2
+addlookupmodule(&dbmdb_lookup_module_info);
+#endif
 
 
-/* Checking by an expansion condition on plain text */
+#if defined(LOOKUP_DNSDB) && LOOKUP_DNSDB!=2
+addlookupmodule(&dnsdb_lookup_module_info);
+#endif
 
 
-#ifdef AUTH_CRAM_MD5
-  {
-  US"cram_md5",                              /* lookup name */
-  auth_cram_md5_options,
-  &auth_cram_md5_options_count,
-  &auth_cram_md5_option_defaults,
-  sizeof(auth_cram_md5_options_block),
-  auth_cram_md5_init,                        /* init function */
-  auth_cram_md5_server,                      /* server function */
-  auth_cram_md5_client                       /* client function */
-  },
-#endif
-
-#ifdef AUTH_CYRUS_SASL
-  {
-  US"cyrus_sasl",           /* lookup name */
-  auth_cyrus_sasl_options,
-  &auth_cyrus_sasl_options_count,
-  &auth_cyrus_sasl_option_defaults,
-  sizeof(auth_cyrus_sasl_options_block),
-  auth_cyrus_sasl_init,                      /* init function */
-  auth_cyrus_sasl_server,                    /* server function */
-  NULL                                       /* client function */
-  },
-#endif
-
-#ifdef AUTH_DOVECOT
-  {
-  US"dovecot",                                /* lookup name */
-  auth_dovecot_options,
-  &auth_dovecot_options_count,
-  &auth_dovecot_option_defaults,
-  sizeof(auth_dovecot_options_block),
-  auth_dovecot_init,                          /* init function */
-  auth_dovecot_server,                        /* server function */
-  NULL                                        /* client function */
-  },
-#endif
-
-#ifdef AUTH_PLAINTEXT
-  {
-  US"plaintext",                             /* lookup name */
-  auth_plaintext_options,
-  &auth_plaintext_options_count,
-  &auth_plaintext_option_defaults,
-  sizeof(auth_plaintext_options_block),
-  auth_plaintext_init,                       /* init function */
-  auth_plaintext_server,                     /* server function */
-  auth_plaintext_client                      /* client function */
-  },
-#endif
-
-#ifdef AUTH_SPA
-  {
-  US"spa",                                   /* lookup name */
-  auth_spa_options,
-  &auth_spa_options_count,
-  &auth_spa_option_defaults,
-  sizeof(auth_spa_options_block),
-  auth_spa_init,                             /* init function */
-  auth_spa_server,                           /* server function */
-  auth_spa_client                            /* client function */
-  },
-#endif
-
-{ US"", NULL, NULL, NULL, 0, NULL, NULL, NULL  }
-};
+#if defined(LOOKUP_DSEARCH) && LOOKUP_DSEARCH!=2
+addlookupmodule(&dsearch_lookup_module_info);
+#endif
 
 
+#if defined(LOOKUP_IBASE) && LOOKUP_IBASE!=2
+addlookupmodule(&ibase_lookup_module_info);
+#endif
 
 
-/* Tables of information about which routers and transports are included in the
-exim binary. */
+#if defined(LOOKUP_LDAP) && LOOKUP_LDAP!=2
+addlookupmodule(&ldap_lookup_module_info);
+#endif
 
 
-/* Pull in the necessary header files */
+#if defined(LOOKUP_JSON) && LOOKUP_JSON!=2
+addlookupmodule(&json_lookup_module_info);
+#endif
 
 
-#include "routers/rf_functions.h"
+#if defined(LOOKUP_LSEARCH) && LOOKUP_LSEARCH!=2
+addlookupmodule(&lsearch_lookup_module_info);
+#endif
 
 
-#ifdef ROUTER_ACCEPT
-#include "routers/accept.h"
+#if defined(LOOKUP_MYSQL) && LOOKUP_MYSQL!=2
+addlookupmodule(&mysql_lookup_module_info);
 #endif
 
 #endif
 
-#ifdef ROUTER_DNSLOOKUP
-#include "routers/dnslookup.h"
+#if defined(LOOKUP_NIS) && LOOKUP_NIS!=2
+addlookupmodule(&nis_lookup_module_info);
 #endif
 
 #endif
 
-#ifdef ROUTER_MANUALROUTE
-#include "routers/manualroute.h"
+#if defined(LOOKUP_NISPLUS) && LOOKUP_NISPLUS!=2
+addlookupmodule(&nisplus_lookup_module_info);
 #endif
 
 #endif
 
-#ifdef ROUTER_IPLITERAL
-#include "routers/ipliteral.h"
+#if defined(LOOKUP_ORACLE) && LOOKUP_ORACLE!=2
+addlookupmodule(&oracle_lookup_module_info);
 #endif
 
 #endif
 
-#ifdef ROUTER_IPLOOKUP
-#include "routers/iplookup.h"
+#if defined(LOOKUP_PASSWD) && LOOKUP_PASSWD!=2
+addlookupmodule(&passwd_lookup_module_info);
 #endif
 
 #endif
 
-#ifdef ROUTER_QUERYPROGRAM
-#include "routers/queryprogram.h"
+#if defined(LOOKUP_PGSQL) && LOOKUP_PGSQL!=2
+addlookupmodule(&pgsql_lookup_module_info);
 #endif
 
 #endif
 
-#ifdef ROUTER_REDIRECT
-#include "routers/redirect.h"
+#if defined(LOOKUP_REDIS) && LOOKUP_REDIS!=2
+addlookupmodule(&redis_lookup_module_info);
 #endif
 
 #endif
 
-#ifdef TRANSPORT_APPENDFILE
-#include "transports/appendfile.h"
+#if defined(LOOKUP_LMDB) && LOOKUP_LMDB!=2
+addlookupmodule(&lmdb_lookup_module_info);
 #endif
 
 #endif
 
-#ifdef TRANSPORT_AUTOREPLY
-#include "transports/autoreply.h"
+#if defined(LOOKUP_SQLITE) && LOOKUP_SQLITE!=2
+addlookupmodule(&sqlite_lookup_module_info);
 #endif
 
 #endif
 
-#ifdef TRANSPORT_LMTP
-#include "transports/lmtp.h"
+#if defined(LOOKUP_TESTDB) && LOOKUP_TESTDB!=2
+addlookupmodule(&testdb_lookup_module_info);
 #endif
 
 #endif
 
-#ifdef TRANSPORT_PIPE
-#include "transports/pipe.h"
+#if defined(LOOKUP_WHOSON) && LOOKUP_WHOSON!=2
+addlookupmodule(&whoson_lookup_module_info);
 #endif
 
 #endif
 
-#ifdef TRANSPORT_SMTP
-#include "transports/smtp.h"
+/* This is provided by the spf "misc" module, and the lookup aspect is always
+linked statically whether or not the "misc" module (and hence libspf2) is
+dynamic-load. */
+
+#if defined(SUPPORT_SPF)
+addlookupmodule(&spf_lookup_module_info);
 #endif
 
 #endif
 
+/* This is a custom expansion, and not available as either
+a list-syntax lookup or a lookup expansion. However, it is
+implemented by a lookup module. */
 
 
-/* Now set up the structures, terminated by an entry with a null name. */
+addlookupmodule(&readsock_lookup_module_info);
 
 
-router_info routers_available[] = {
-#ifdef ROUTER_ACCEPT
-  {
-  US"accept",
-  accept_router_options,
-  &accept_router_options_count,
-  &accept_router_option_defaults,
-  sizeof(accept_router_options_block),
-  accept_router_init,
-  accept_router_entry,
-  NULL,     /* no tidyup entry */
-  ri_yestransport
-  },
-#endif
-#ifdef ROUTER_DNSLOOKUP
-  {
-  US"dnslookup",
-  dnslookup_router_options,
-  &dnslookup_router_options_count,
-  &dnslookup_router_option_defaults,
-  sizeof(dnslookup_router_options_block),
-  dnslookup_router_init,
-  dnslookup_router_entry,
-  NULL,     /* no tidyup entry */
-  ri_yestransport
-  },
-#endif
-#ifdef ROUTER_IPLITERAL
-  {
-  US"ipliteral",
-  ipliteral_router_options,
-  &ipliteral_router_options_count,
-  &ipliteral_router_option_defaults,
-  sizeof(ipliteral_router_options_block),
-  ipliteral_router_init,
-  ipliteral_router_entry,
-  NULL,     /* no tidyup entry */
-  ri_yestransport
-  },
-#endif
-#ifdef ROUTER_IPLOOKUP
-  {
-  US"iplookup",
-  iplookup_router_options,
-  &iplookup_router_options_count,
-  &iplookup_router_option_defaults,
-  sizeof(iplookup_router_options_block),
-  iplookup_router_init,
-  iplookup_router_entry,
-  NULL,     /* no tidyup entry */
-  ri_notransport
-  },
-#endif
-#ifdef ROUTER_MANUALROUTE
-  {
-  US"manualroute",
-  manualroute_router_options,
-  &manualroute_router_options_count,
-  &manualroute_router_option_defaults,
-  sizeof(manualroute_router_options_block),
-  manualroute_router_init,
-  manualroute_router_entry,
-  NULL,     /* no tidyup entry */
-  0
-  },
-#endif
-#ifdef ROUTER_QUERYPROGRAM
-  {
-  US"queryprogram",
-  queryprogram_router_options,
-  &queryprogram_router_options_count,
-  &queryprogram_router_option_defaults,
-  sizeof(queryprogram_router_options_block),
-  queryprogram_router_init,
-  queryprogram_router_entry,
-  NULL,     /* no tidyup entry */
-  0
-  },
-#endif
-#ifdef ROUTER_REDIRECT
+DEBUG(D_lookup) debug_printf("Total %d built-in lookups\n", lookup_list_count);
+
+
+#ifdef LOOKUP_MODULE_DIR
+if (!(dd = exim_opendir(CUS LOOKUP_MODULE_DIR)))
   {
   {
-  US"redirect",
-  redirect_router_options,
-  &redirect_router_options_count,
-  &redirect_router_option_defaults,
-  sizeof(redirect_router_options_block),
-  redirect_router_init,
-  redirect_router_entry,
-  NULL,     /* no tidyup entry */
-  ri_notransport
-  },
-#endif
-{ US"", NULL, NULL, NULL, 0, NULL, NULL, NULL, 0 }
-};
+  DEBUG(D_lookup) debug_printf("Couldn't open %s: not loading lookup modules\n", LOOKUP_MODULE_DIR);
+  log_write(0, LOG_MAIN|LOG_PANIC,
+         "Couldn't open %s: not loading lookup modules\n", LOOKUP_MODULE_DIR);
+  }
+else
+  {
+  /* Look specifically for modules we know offer several lookup types and
+  load them now, since we cannot load-on-first-use. */
+
+  struct dirent * ent;
+  const pcre2_code * regex_islookupmod = regex_must_compile(
+    US"(lsearch|ldap|nis)_lookup\\." DYNLIB_FN_EXT "$", MCS_NOFLAGS, TRUE);
+
+  DEBUG(D_lookup) debug_printf("Loading lookup modules from %s\n", LOOKUP_MODULE_DIR);
+  while ((ent = readdir(dd)))
+    {
+    char * name = ent->d_name;
+    int len = (int)strlen(name);
+    if (regex_match_and_setup(regex_islookupmod, US name, 0, 0))
+      {
+      uschar * errstr;
+      if (lookup_mod_load(expand_nstring[1], &errstr))
+       countmodules++;
+      else
+       {
+       fprintf(stderr, "%s\n", errstr);
+       log_write(0, LOG_MAIN|LOG_PANIC, "%s", errstr);
+       }
+      }
+    }
+  closedir(dd);
+  }
 
 
+DEBUG(D_lookup) debug_printf("Loaded %d lookup modules\n", countmodules);
+#endif
+}
 
 
 
 
-transport_info transports_available[] = {
-#ifdef TRANSPORT_APPENDFILE
-  {
-  US"appendfile",                              /* driver name */
-  appendfile_transport_options,                /* local options table */
-  &appendfile_transport_options_count,         /* number of entries */
-  &appendfile_transport_option_defaults,       /* private options defaults */
-  sizeof(appendfile_transport_options_block),  /* size of private block */
-  appendfile_transport_init,                   /* init entry point */
-  appendfile_transport_entry,                  /* main entry point */
-  NULL,                                        /* no tidyup entry */
-  NULL,                                        /* no closedown entry */
-  TRUE,                                        /* local flag */
-  },
-#endif
-#ifdef TRANSPORT_AUTOREPLY
-  {
-  US"autoreply",                               /* driver name */
-  autoreply_transport_options,                 /* local options table */
-  &autoreply_transport_options_count,          /* number of entries */
-  &autoreply_transport_option_defaults,        /* private options defaults */
-  sizeof(autoreply_transport_options_block),   /* size of private block */
-  autoreply_transport_init,                    /* init entry point */
-  autoreply_transport_entry,                   /* main entry point */
-  NULL,                                        /* no tidyup entry */
-  NULL,                                        /* no closedown entry */
-  TRUE                                         /* local flag */
-  },
-#endif
-#ifdef TRANSPORT_LMTP
-  {
-  US"lmtp",                                    /* driver name */
-  lmtp_transport_options,                      /* local options table */
-  &lmtp_transport_options_count,               /* number of entries */
-  &lmtp_transport_option_defaults,             /* private options defaults */
-  sizeof(lmtp_transport_options_block),        /* size of private block */
-  lmtp_transport_init,                         /* init entry point */
-  lmtp_transport_entry,                        /* main entry point */
-  NULL,                                        /* no tidyup entry */
-  NULL,                                        /* no closedown entry */
-  TRUE                                         /* local flag */
-  },
-#endif
-#ifdef TRANSPORT_PIPE
-  {
-  US"pipe",                                    /* driver name */
-  pipe_transport_options,                      /* local options table */
-  &pipe_transport_options_count,               /* number of entries */
-  &pipe_transport_option_defaults,             /* private options defaults */
-  sizeof(pipe_transport_options_block),        /* size of private block */
-  pipe_transport_init,                         /* init entry point */
-  pipe_transport_entry,                        /* main entry point */
-  NULL,                                        /* no tidyup entry */
-  NULL,                                        /* no closedown entry */
-  TRUE                                         /* local flag */
-  },
-#endif
-#ifdef TRANSPORT_SMTP
-  {
-  US"smtp",                                    /* driver name */
-  smtp_transport_options,                      /* local options table */
-  &smtp_transport_options_count,               /* number of entries */
-  &smtp_transport_option_defaults,             /* private options defaults */
-  sizeof(smtp_transport_options_block),        /* size of private block */
-  smtp_transport_init,                         /* init entry point */
-  smtp_transport_entry,                        /* main entry point */
-  NULL,                                        /* no tidyup entry */
-  smtp_transport_closedown,                    /* close down passed channel */
-  FALSE                                        /* local flag */
-  },
-#endif
-{ US"", NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, FALSE }
-};
+/* Add module info struct to the modules list for those that are
+built as static */
+
+#if !defined(DISABLE_DKIM) && (!defined(SUPPORT_DKIM) || SUPPORT_DKIM!=2)
+extern misc_module_info dkim_module_info;
+#endif
+#if defined(SUPPORT_DMARC) && SUPPORT_DMARC!=2
+extern misc_module_info dmarc_module_info;
+#endif
+#if defined(SUPPORT_SPF) && SUPPORT_SPF!=2
+extern misc_module_info spf_module_info;
+#endif
+#if defined(EXPERIMENTAL_ARC) && (!defined(SUPPORT_ARC) || SUPPORT_ARC!=2)
+extern misc_module_info arc_module_info;
+#endif
+#if defined(RADIUS_CONFIG_FILE) && (!defined(SUPPORT_RADIUS) || SUPPORT_RADIUS!=2)
+extern misc_module_info radius_module_info;
+#endif
+#if defined(SUPPORT_PAM) && SUPPORT_PAM!=2
+extern misc_module_info pam_module_info;
+#endif
+#if defined(EXIM_PERL) && (!defined(SUPPORT_PERL) || SUPPORT_PERL!=2)
+extern misc_module_info perl_module_info;
+#endif
+#if !defined(DISABLE_EXIM_FILTER) && (!defined(SUPPORT_EXIM_FILTER) || SUPPORT_EXIM_FILTER!=2)
+extern misc_module_info exim_filter_module_info;
+#endif
+#if !defined(DISABLE_SIEVE_FILTER) && (!defined(SUPPORT_SIEVE_FILTER) || SUPPORT_SIEVE_FILTER!=2)
+extern misc_module_info sieve_filter_module_info;
+#endif
+
+void
+init_misc_mod_list(void)
+{
+static BOOL onetime = FALSE;
+if (onetime) return;
+onetime = TRUE;
+
+#if !defined(DISABLE_DKIM) && (!defined(SUPPORT_DKIM) || SUPPORT_DKIM!=2)
+  misc_mod_add(&dkim_module_info);
+#endif
+#if defined(SUPPORT_SPF) && SUPPORT_SPF!=2
+  misc_mod_add(&spf_module_info);
+#endif
+#if defined(SUPPORT_DMARC) && SUPPORT_DMARC!=2
+/* dmarc depends on spf so this add must go after, for the both-static case */
+  misc_mod_add(&dmarc_module_info);
+#endif
+#if defined(EXPERIMENTAL_ARC) && (!defined(SUPPORT_ARC) || SUPPORT_ARC!=2)
+  misc_mod_add(&arc_module_info);
+#endif
+#if defined(RADIUS_CONFIG_FILE) && (!defined(SUPPORT_RADIUS) || SUPPORT_RADIUS!=2)
+  misc_mod_add(&radius_module_info);
+#endif
+#if defined(SUPPORT_PAM) && SUPPORT_PAM!=2
+  misc_mod_add(&pam_module_info);
+#endif
+#if defined(EXIM_PERL) && (!defined(SUPPORT_PERL) || SUPPORT_PERL!=2)
+  misc_mod_add(&perl_module_info);
+#endif
+#if !defined(DISABLE_EXIM_FILTER) && (!defined(SUPPORT_EXIM_FILTER) || SUPPORT_EXIM_FILTER!=2)
+  misc_mod_add(&exim_filter_module_info);
+#endif
+#if !defined(DISABLE_SIEVE_FILTER) && (!defined(SUPPORT_SIEVE_FILTER) || SUPPORT_SIEVE_FILTER!=2)
+  misc_mod_add(&sieve_filter_module_info);
+#endif
+}
+
 
 
+#endif /*!MACRO_PREDEF*/
 /* End of drtables.c */
 /* End of drtables.c */