From cbe845f056c7a15d97056435a9e112a87f2ad312 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Tue, 13 Aug 2024 19:36:04 +0100 Subject: [PATCH 1/1] dyn load auth/router/transport files --- src/src/drtables.c | 4 +- src/src/functions.h | 4 +- src/src/readconf.c | 121 ++++++++++++++++++++++++++++++++++++-------- src/src/route.c | 29 ++++------- src/src/structs.h | 5 ++ src/src/transport.c | 2 +- 6 files changed, 119 insertions(+), 46 deletions(-) diff --git a/src/src/drtables.c b/src/src/drtables.c index 1a3a80460..0f64728b4 100644 --- a/src/src/drtables.c +++ b/src/src/drtables.c @@ -626,8 +626,8 @@ void init_lookup_list(void) { #ifdef LOOKUP_MODULE_DIR -DIR *dd; -struct dirent *ent; +DIR * dd; +struct dirent * ent; int countmodules = 0; int moduleerrors = 0; #endif diff --git a/src/src/functions.h b/src/src/functions.h index dbfad63eb..3253043ae 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -107,6 +107,8 @@ extern uschar *acl_standalone_setvar(const uschar *, BOOL); extern tree_node *acl_var_create(uschar *); extern void acl_var_write(uschar *, uschar *, void *); +extern void add_driver_info(driver_info **, const driver_info *, size_t); + #ifdef EXPERIMENTAL_ARC # ifdef SUPPORT_DMARC @@ -423,7 +425,7 @@ extern int rda_interpret(redirect_block *, int, const uschar *, uschar **, error_block **, int *, const uschar *); extern int rda_is_filter(const uschar *); extern BOOL readconf_depends(driver_instance *, uschar *); -extern void readconf_driver_init(driver_instance **, driver_info *, int, +extern void readconf_driver_init(driver_instance **, driver_info **, int, void *, int, optionlist *, int, const uschar *); extern const uschar *readconf_find_option(void *); extern void readconf_main(BOOL); diff --git a/src/src/readconf.c b/src/src/readconf.c index f22ff700e..da94071b5 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -3699,6 +3699,18 @@ if (!nowarn && !keep_environment && environ && *environ) +/* Add a driver info struct to a list. */ + +void +add_driver_info(driver_info ** drlist_p, const driver_info * newent, + size_t size) +{ +driver_info * listent = store_get(size, newent); +memcpy(listent, newent, size); +listent->next = *drlist_p; +*drlist_p= listent; +} + /************************************************* * Initialize one driver * *************************************************/ @@ -3711,20 +3723,18 @@ set by another incarnation of the same driver). Arguments: d pointer to driver instance block, with generic options filled in - drivers_available vector of available drivers + info_anchor list of available drivers size_of_info size of each block in drivers_available - class class of driver, for error message + class class of driver Returns: pointer to the driver info block */ static driver_info * -init_driver(driver_instance * d, driver_info * drivers_available, +init_driver(driver_instance * d, driver_info ** info_anchor, int size_of_info, const uschar * class) { -/*XXX if dynamic, the _info entry will be here but code may or may not -be loaded. How to tell? What's the entry point? init call? -Currently that is IN the _info entry. +/*XXX if dynamic, the _info entry will not be here yet. For lookups it does it by pulling the info entry out of the dlopen()d file (for dynamic) or direct from the lookup .o file (for static). @@ -3753,26 +3763,93 @@ and scan from dlopen if marked dynamic. #ifdef old /*XXX walk the old array */ -for (driver_info * dd = drivers_available; dd->driver_name[0] != 0; - dd = (driver_info *)((US dd) + size_of_info)) +for (driver_info * di= *info_anchor; di->driver_name[0] != 0; + di= (driver_info *)((US di) + size_of_info)) #endif -for (driver_info * dd = drivers_available; dd; dd = dd->next) - if (Ustrcmp(d->driver_name, dd->driver_name) == 0) +driver_info * di; +int len; +DIR * dd; + +/* First scan the list of statically-built drivers. */ + +for (di = *info_anchor; di; di = di->next) + if (Ustrcmp(d->driver_name, di->driver_name) == 0) + goto found; + +#ifdef LOOKUP_MODULE_DIR +/* Potentially a loadable module. Look for a file with the right name. */ + +if (!(dd = exim_opendir(CUS LOOKUP_MODULE_DIR))) + { + log_write(0, LOG_MAIN|LOG_PANIC, + "Couldn't open %s: not loading driver modules\n", LOOKUP_MODULE_DIR); + } +else + { + uschar * fname = string_sprintf("%s_%s." DYNLIB_FN_EXT, d->driver_name, class), * sname; + const char * errormsg; + + DEBUG(D_any) debug_printf("Loading %s %s driver from %s\n", + d->driver_name, class, LOOKUP_MODULE_DIR); + + for(struct dirent * ent; ent = readdir(dd); ) if (Ustrcmp(ent->d_name, fname) == 0) { - int len = dd->options_len; - d->info = dd; - d->options_block = store_get_perm(len, GET_UNTAINTED); - memcpy(d->options_block, dd->options_block, len); - for (int i = 0; i < *dd->options_count; i++) - dd->options[i].type &= ~opt_set; - return dd; + void * dl = dlopen(CS string_sprintf(LOOKUP_MODULE_DIR "/%s", fname), RTLD_NOW); + static driver_magics dm[] = { + { ROUTER_MAGIC, US"router" }, + { TRANSPORT_MAGIC, US"transport" }, + { AUTH_MAGIC, US"auth" }, + }; + + if (!dl) + { + errormsg = dlerror(); + log_write(0, LOG_MAIN|LOG_PANIC, "Error loading %s %s driver: %s\n", + d->driver_name, class, errormsg); + break; + } + (void) dlerror(); /* cf. comment in init_lookup_list() */ + + di = (driver_info *) dlsym(dl, CS string_sprintf("_%s_info", class)); + if ((errormsg = dlerror())) + { + log_write(0, LOG_MAIN|LOG_PANIC, + "%s does not appear to be a %s module (%s)\n", fname, class, errormsg); + dlclose(dl); + break; + } + for(driver_magics * dmp = dm; dmp < dm + nelem(dm); dmp++) + if(Ustrcmp(dmp->class, class) == 0 && dmp->magic == di->dyn_magic) + { + add_driver_info(info_anchor, di, size_of_info); + DEBUG(D_any) debug_printf("Loaded %s %s\n", d->driver_name, class); + closedir(dd); + goto found; + } + + log_write(0, LOG_MAIN|LOG_PANIC, + "%s module %s is not compatible with this version of Exim\n", + class, d->driver_name); + dlclose(dl); + break; } + closedir(dd); + } +#endif /* LOOKUP_MODULE_DIR */ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "%s %s: cannot find %s driver \"%s\"", class, d->name, class, d->driver_name); -return NULL; /* never obeyed */ +found: + +len = di->options_len; +d->info = di; +d->options_block = store_get_perm(len, GET_UNTAINTED); +memcpy(d->options_block, di->options_block, len); +for (int i = 0; i < *di->options_count; i++) + di->options[i].type &= ~opt_set; +return di; } @@ -3806,7 +3883,7 @@ blocks for this shared code to work. Arguments: anchor &routers, &transports, &auths - drivers_available available drivers + info_anchor available drivers size_of_info size of each info block instance_default points to default data for an instance instance_size size of instance block @@ -3821,7 +3898,7 @@ Returns: nothing void readconf_driver_init( driver_instance ** anchor, - driver_info * drivers_available, /*XXX now list not array */ + driver_info ** info_anchor, /*XXX now list not array, static only so far */ int size_of_info, void * instance_default, int instance_size, @@ -3914,7 +3991,7 @@ while ((buffer = get_config_line())) driver_optionlist_count, d, NULL)) { if (!d->info && d->driver_name) - init_driver(d, drivers_available, size_of_info, class); + init_driver(d, info_anchor, size_of_info, class); } /* Handle private options - pass the generic block because some may @@ -4296,7 +4373,7 @@ for (auth_info * tblent = auths_available_oldarray; readconf_driver_init((driver_instance **)&auths, /* chain anchor */ - (driver_info *)auths_available_newlist, /* available drivers */ + (driver_info **)&auths_available_newlist, /* available drivers */ sizeof(auth_info), /* size of info block */ &auth_defaults, /* default values for generic options */ sizeof(auth_instance), /* size of instance block */ diff --git a/src/src/route.c b/src/src/route.c index cce120967..b733d1a52 100644 --- a/src/src/route.c +++ b/src/src/route.c @@ -220,17 +220,6 @@ if (after && !afterthis) * Initialize router list * *************************************************/ -/*XXX will likely want to rename to generic */ - -static void -add_router(driver_info ** drlist_p, const driver_info * newent, size_t size) -{ -driver_info * listent = store_get(size, newent); -memcpy(listent, newent, size); -listent->next = *drlist_p; -*drlist_p= listent; -} - /* Read the routers section of the configuration file, and set up a chain of router instances according to its contents. Each router has generic options and may also have its own private options. This function is only ever called when @@ -273,30 +262,30 @@ store_pool = POOL_PERM; extern router_info redirect_router_info; extern router_info queryprogram_router_info; - /*XXX this addsonly the statics. We can't get the dynamics as they + /*XXX this adds only the statics. We can't get the dynamics as they are not linked. Until dlopen(), when we can use dlsym(). So the discovery is by the file exitence, via the filename pattern. */ /*XXX TODO: move the info structs to individual driver files */ #if defined(ROUTER_ACCEPT) && ROUTER_ACCEPT!=2 - add_router(anchor, &accept_router_info.drinfo, sizeof(router_info)); + add_driver_info(anchor, &accept_router_info.drinfo, sizeof(router_info)); #endif #if defined(ROUTER_DNSLOOKUP) && ROUTER_DNSLOOKUP!=2 - add_router(anchor, &dnslookup_router_info.drinfo, sizeof(router_info)); + add_driver_info(anchor, &dnslookup_router_info.drinfo, sizeof(router_info)); #endif # if defined(ROUTER_IPLITERAL) && ROUTER_IPLITERAL!=2 - add_router(anchor, &ipliteral_router_info.drinfo, sizeof(router_info)); + add_driver_info(anchor, &ipliteral_router_info.drinfo, sizeof(router_info)); #endif #if defined(ROUTER_IPLOOKUP) && ROUTER_IPLOOKUP!=2 - add_router(anchor, &iplookup_router_info.drinfo, sizeof(router_info)); + add_driver_info(anchor, &iplookup_router_info.drinfo, sizeof(router_info)); #endif #if defined(ROUTER_MANUALROUTE) && ROUTER_MANUALROUTE!=2 - add_router(anchor, &manualroute_router_info.drinfo, sizeof(router_info)); + add_driver_info(anchor, &manualroute_router_info.drinfo, sizeof(router_info)); #endif #if defined(ROUTER_REDIRECT) && ROUTER_REDIRECT!=2 - add_router(anchor, &redirect_router_info.drinfo, sizeof(router_info)); + add_driver_info(anchor, &redirect_router_info.drinfo, sizeof(router_info)); #endif #if defined(ROUTER_QUERYPROGRAM) && ROUTER_QUERYPROGRAM!=2 - add_router(anchor, &queryprogram_router_info.drinfo, sizeof(router_info)); + add_driver_info(anchor, &queryprogram_router_info.drinfo, sizeof(router_info)); #endif } store_pool = old_pool; @@ -305,7 +294,7 @@ store_pool = old_pool; /*XXX this does the config file "routers" section reading */ readconf_driver_init((driver_instance **)&routers, /* chain anchor */ - (driver_info *)routers_available_newlist, /* available drivers */ + (driver_info **)&routers_available_newlist, /* available drivers */ sizeof(router_info), /* size of info blocks */ &router_defaults, /* default values for generic options */ sizeof(router_instance), /* size of instance block */ diff --git a/src/src/structs.h b/src/src/structs.h index 3446664cd..ffcd8a5df 100644 --- a/src/src/structs.h +++ b/src/src/structs.h @@ -158,6 +158,11 @@ typedef struct driver_info { #define TRANSPORT_MAGIC 0x54504d31 /* TPM1 */ #define AUTH_MAGIC 0x65554d31 /* AUM1 */ +typedef struct { + uint magic; + uschar * class; +} driver_magics; + /* Structure for holding information about the configured transports. Some of the generally accessible options are set from the configuration file; others diff --git a/src/src/transport.c b/src/src/transport.c index e92541bbf..7e47cf1da 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -159,7 +159,7 @@ for (transport_info * tblent = transports_available_oldarray; } readconf_driver_init((driver_instance **)&transports, /* chain anchor */ - (driver_info *)transports_available_newlist, /* available drivers */ + (driver_info **)&transports_available_newlist, /* available drivers */ sizeof(transport_info), /* size of info block */ &transport_defaults, /* default values for generic options */ sizeof(transport_instance), /* size of instance block */ -- 2.30.2