+extern lookup_module_info readsock_lookup_module_info;
+
+
+#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)))
+ {
+ if (errstr)
+ *errstr = string_sprintf("Error loading %s: %s", name, dlerror());
+ else
+ (void) dlerror(); /* clear out error state */
+ return NULL;
+ }
+
+/* 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(). */
+
+(void) dlerror();
+return dl;
+}
+
+
+/* Try to load a lookup module with the given name.
+
+Arguments:
+ name name of the lookup
+ errstr if not NULL, place "open fail" error message here
+
+Return: boolean success
+*/
+
+static BOOL
+lookup_mod_load(const uschar * name, uschar ** errstr)
+{
+void * dl;
+struct lookup_module_info * info;
+const char * errormsg;
+
+if (!(dl = mod_open(name, US"lookup", errstr)))
+ return FALSE;
+
+info = (struct lookup_module_info *) dlsym(dl, "_lookup_module_info");
+if ((errormsg = dlerror()))
+ {
+ 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;
+ }
+
+addlookupmodule(info);
+DEBUG(D_lookup) debug_printf_indent("Loaded \"%s\" (%d lookup type%s)\n",
+ name, info->lookupcount,
+ info->lookupcount > 1 ? "s" : "");
+return TRUE;
+}
+
+
+/* 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).
+*/
+
+BOOL
+lookup_one_mod_load(const uschar * name, uschar ** errstr)
+{
+if (!lookup_mod_load(name, errstr)) return FALSE;
+/*XXX notify daemon? */
+return TRUE;
+}
+
+#endif /*LOOKUP_MODULE_DIR*/
+
+
+
+misc_module_info * misc_module_list = NULL;
+
+static void
+misc_mod_add(misc_module_info * mi)
+{
+mi->next = misc_module_list;
+misc_module_list = mi;
+
+if (mi->init && !mi->init(mi))
+ DEBUG(D_any)
+ debug_printf_indent("module init call failed for %s\n", mi->name);
+
+DEBUG(D_any) if (mi->lib_vers_report)
+ debug_printf_indent("%Y", mi->lib_vers_report(NULL));
+
+/* fprintf(stderr,"misc_mod_add: added %s\n", mi->name); */
+}
+
+
+#ifdef LOOKUP_MODULE_DIR
+
+/* Load a "misc" module, and add to list */
+
+static misc_module_info *
+misc_mod_load(const uschar * name, uschar ** errstr)
+{
+void * dl;
+struct misc_module_info * mi;
+const char * errormsg;
+
+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()))
+ {
+ 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;
+
+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;
+
+ 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);
+}
+
+
+
+
+