* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) The Exim Maintainers 2020 - 2022 */
+/* Copyright (c) The Exim Maintainers 2020 - 2024 */
/* Copyright (c) University of Cambridge 1995 - 2018 */
/* See the file NOTICE for conditions of use and distribution. */
/* SPDX-License-Identifier: GPL-2.0-or-later */
{ "domains", opt_stringptr|opt_public,
LOFF(domains) },
{ "driver", opt_stringptr|opt_public,
- LOFF(driver_name) },
+ LOFF(drinst.driver_name) },
{ "dsn_lasthop", opt_bool|opt_public,
LOFF(dsn_lasthop) },
{ "errors_to", opt_stringptr|opt_public,
options_from_list(optionlist_routers, nelem(optionlist_routers), US"ROUTERS", NULL);
-for (router_info * ri = routers_available; ri->driver_name[0]; ri++)
+for (driver_info * di = (driver_info *)routers_available; di; di = di->next)
{
- spf(buf, sizeof(buf), US"_DRIVER_ROUTER_%T", ri->driver_name);
+ spf(buf, sizeof(buf), US"_DRIVER_ROUTER_%T", di->driver_name);
builtin_macro_create(buf);
- options_from_list(ri->options, (unsigned)*ri->options_count, US"ROUTER", ri->driver_name);
+ options_from_list(di->options, (unsigned)*di->options_count,
+ US"ROUTER", di->driver_name);
}
}
BOOL afterthis = FALSE;
router_instance *rr;
-for (rr = routers; rr; rr = rr->next)
+for (rr = routers; rr; rr = rr->drinst.next)
{
- if (Ustrcmp(name, rr->name) == 0)
+ if (Ustrcmp(name, rr->drinst.name) == 0)
{
*ptr = rr;
break;
if (!rr)
log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
- "new_router \"%s\" not found for \"%s\" router", name, r->name);
+ "new_router \"%s\" not found for \"%s\" router", name, r->drinst.name);
if (after && !afterthis)
log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
- "new_router \"%s\" does not follow \"%s\" router", name, r->name);
+ "new_router \"%s\" does not follow \"%s\" router", name, r->drinst.name);
}
void
route_init(void)
{
-readconf_driver_init(US"router",
- (driver_instance **)(&routers), /* chain anchor */
- (driver_info *)routers_available, /* available drivers */
+int old_pool = store_pool;
+store_pool = POOL_PERM;
+ {
+ driver_info ** anchor = (driver_info **) &routers_available;
+
+ /* Add the router drivers that are built for static linkage to the
+ list of availables. */
+
+#if defined(ROUTER_ACCEPT) && ROUTER_ACCEPT!=2
+ extern router_info accept_router_info;
+ add_driver_info(anchor, &accept_router_info.drinfo, sizeof(router_info));
+#endif
+#if defined(ROUTER_DNSLOOKUP) && ROUTER_DNSLOOKUP!=2
+ extern router_info dnslookup_router_info;
+ add_driver_info(anchor, &dnslookup_router_info.drinfo, sizeof(router_info));
+#endif
+# if defined(ROUTER_IPLITERAL) && ROUTER_IPLITERAL!=2
+ extern router_info ipliteral_router_info;
+ add_driver_info(anchor, &ipliteral_router_info.drinfo, sizeof(router_info));
+#endif
+#if defined(ROUTER_IPLOOKUP) && ROUTER_IPLOOKUP!=2
+ extern router_info iplookup_router_info;
+ add_driver_info(anchor, &iplookup_router_info.drinfo, sizeof(router_info));
+#endif
+#if defined(ROUTER_MANUALROUTE) && ROUTER_MANUALROUTE!=2
+ extern router_info manualroute_router_info;
+ add_driver_info(anchor, &manualroute_router_info.drinfo, sizeof(router_info));
+#endif
+#if defined(ROUTER_REDIRECT) && ROUTER_REDIRECT!=2
+ extern router_info redirect_router_info;
+ add_driver_info(anchor, &redirect_router_info.drinfo, sizeof(router_info));
+#endif
+#if defined(ROUTER_QUERYPROGRAM) && ROUTER_QUERYPROGRAM!=2
+ extern router_info queryprogram_router_info;
+ add_driver_info(anchor, &queryprogram_router_info.drinfo, sizeof(router_info));
+#endif
+ }
+store_pool = old_pool;
+
+
+/* Read the config file "routers" section, creating a routers instance list.
+For any yet-undiscovered driver, check for a loadable module and add it to
+those available. */
+
+readconf_driver_init((driver_instance **)&routers, /* chain anchor */
+ (driver_info **)&routers_available, /* available drivers */
sizeof(router_info), /* size of info blocks */
&router_defaults, /* default values for generic options */
sizeof(router_instance), /* size of instance block */
optionlist_routers, /* generic options */
- optionlist_routers_size);
+ optionlist_routers_size,
+ US"router");
-for (router_instance * r = routers; r; r = r->next)
+for (router_instance * r = routers; r; r = r->drinst.next)
{
- uschar *s = r->self;
+ uschar * s = r->self;
+ router_info * ri = r->drinst.info;
/* If log_as_local is unset, its overall default is FALSE. (The accept
router defaults it to TRUE.) */
/* Check for transport or no transport on certain routers */
- if ( (r->info->ri_flags & ri_yestransport)
- && !r->transport_name && !r->verify_only)
+ if (ri->ri_flags & ri_yestransport && !r->transport_name && !r->verify_only)
log_write(0, LOG_PANIC_DIE|LOG_CONFIG, "%s router:\n "
- "a transport is required for this router", r->name);
+ "a transport is required for this router", r->drinst.name);
- if ((r->info->ri_flags & ri_notransport) && r->transport_name)
+ if (ri->ri_flags & ri_notransport && r->transport_name)
log_write(0, LOG_PANIC_DIE|LOG_CONFIG, "%s router:\n "
- "a transport must not be defined for this router", r->name);
+ "a transport must not be defined for this router", r->drinst.name);
/* The "self" option needs to be decoded into a code value and possibly a
new domain string and a rewrite boolean. */
else if (Ustrncmp(s, "reroute:", 8) == 0)
{
s += 8;
- while (isspace(*s)) s++;
+ Uskip_whitespace(&s);
if (Ustrncmp(s, "rewrite:", 8) == 0)
{
r->self_rewrite = TRUE;
s += 8;
- while (isspace(*s)) s++;
+ Uskip_whitespace(&s);
}
r->self = s;
r->self_code = self_reroute;
}
else log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n "
- "%s is not valid for the self option", r->name, s);
+ "%s is not valid for the self option", r->drinst.name, s);
/* If any router has check_local_user set, default retry_use_local_part
TRUE; otherwise its default is FALSE. */
void
route_tidyup(void)
{
-for (router_instance * r = routers; r; r = r->next)
- if (r->info->tidyup) (r->info->tidyup)(r);
+for (router_instance * r = routers; r; r = r->drinst.next)
+ {
+ router_info * ri = r->drinst.info;
+ if (ri->tidyup) (ri->tidyup)(r);
+ }
}
*/
static int
-route_check_dls(uschar *rname, uschar *type, const uschar *list,
- tree_node **anchorptr, unsigned int *cache_bits, int listtype,
- const uschar *domloc, const uschar **ldata, BOOL caseless, uschar **perror)
+route_check_dls(const uschar * rname, const uschar * type, const uschar * list,
+ tree_node ** anchorptr, unsigned int * cache_bits, int listtype,
+ const uschar * domloc, const uschar ** ldata, BOOL caseless, uschar ** perror)
{
if (!list) return OK; /* Empty list always succeeds */
if (*ss == '+')
{
eacces_code = 1;
- while (isspace((*(++ss))));
+ while (isspace(*++ss));
}
if (*ss == '!')
{
invert = TRUE;
- while (isspace((*(++ss))));
+ while (isspace(*++ss));
}
if (*ss != '/')
int rc;
uschar * check_local_part;
unsigned int * localpart_cache;
+const uschar * rname = r->drinst.name;
/* Reset variables to hold a home directory and data from lookup of a domain or
local part, and ensure search_find_defer is unset, in case there aren't any
if ((verify == v_none || verify == v_expn) && r->verify_only)
{
- DEBUG(D_route) debug_printf("%s router skipped: verify_only set\n", r->name);
+ DEBUG(D_route) debug_printf("%s router skipped: verify_only set\n", rname);
return SKIP;
}
if (f.address_test_mode && !r->address_test)
{
DEBUG(D_route) debug_printf("%s router skipped: address_test is unset\n",
- r->name);
+ rname);
return SKIP;
}
(verify == v_recipient && !r->verify_recipient))
{
DEBUG(D_route) debug_printf("%s router skipped: verify %d %d %d\n",
- r->name, verify, r->verify_sender, r->verify_recipient);
+ rname, verify, r->verify_sender, r->verify_recipient);
return SKIP;
}
if (verify == v_expn && !r->expn)
{
- DEBUG(D_route) debug_printf("%s router skipped: no_expn set\n", r->name);
+ DEBUG(D_route) debug_printf("%s router skipped: no_expn set\n", rname);
return SKIP;
}
/* Skip this router if there's a domain mismatch. */
-if ((rc = route_check_dls(r->name, US"domains", r->domains, &domainlist_anchor,
+if ((rc = route_check_dls(rname, US"domains", r->domains, &domainlist_anchor,
addr->domain_cache, TRUE, addr->domain, CUSS &deliver_domain_data,
MCL_DOMAIN, perror)) != OK)
return rc;
check_local_part[Ustrlen(check_local_part) - Ustrlen(addr->suffix)] = 0;
}
-if ((rc = route_check_dls(r->name, US"local_parts", r->local_parts,
+if ((rc = route_check_dls(rname, US"local_parts", r->local_parts,
&localpartlist_anchor, localpart_cache, MCL_LOCALPART,
check_local_part, CUSS &deliver_localpart_data,
!r->caseful_local_part, perror)) != OK)
if (!route_finduser(addr->local_part, pw, NULL))
{
DEBUG(D_route) debug_printf("%s router skipped: %s is not a local user\n",
- r->name, addr->local_part);
+ rname, addr->local_part);
return SKIP;
}
addr->prop.localpart_data =
local user check so that $home is set - enabling the possibility of letting
individual recipients specify lists of acceptable/unacceptable senders. */
-if ((rc = route_check_dls(r->name, US"senders", r->senders, NULL,
+if ((rc = route_check_dls(rname, US"senders", r->senders, NULL,
sender_address_cache, MCL_ADDRESS, NULL, NULL, FALSE, perror)) != OK)
return rc;
if ((rc = check_files(r->require_files, perror)) != OK)
{
- DEBUG(D_route) debug_printf("%s router %s: file check\n", r->name,
+ DEBUG(D_route) debug_printf("%s router %s: file check\n", rname,
(rc == SKIP)? "skipped" : "deferred");
return rc;
}
{
DEBUG(D_route|D_expand)
debug_printf("checking \"condition\" \"%.80s\"...\n", r->condition);
- if (!expand_check_condition(r->condition, r->name, US"router"))
+ if (!expand_check_condition(r->condition, rname, US"router"))
{
if (f.search_find_defer)
{
return DEFER;
}
DEBUG(D_route)
- debug_printf("%s router skipped: condition failure\n", r->name);
+ debug_printf("%s router skipped: condition failure\n", rname);
return SKIP;
}
}
if (bmi_check_rule(bmi_base64_verdict, r->bmi_rule) == 0)
{ /* none of the rules fired */
DEBUG(D_route)
- debug_printf("%s router skipped: none of bmi_rule rules fired\n", r->name);
+ debug_printf("%s router skipped: none of bmi_rule rules fired\n", rname);
return SKIP;
}
}
if (r->bmi_dont_deliver && bmi_deliver == 1)
{
DEBUG(D_route)
- debug_printf("%s router skipped: bmi_dont_deliver is FALSE\n", r->name);
+ debug_printf("%s router skipped: bmi_dont_deliver is FALSE\n", rname);
return SKIP;
}
)
{
DEBUG(D_route)
- debug_printf("%s router skipped: bmi_deliver_alternate is FALSE\n", r->name);
+ debug_printf("%s router skipped: bmi_deliver_alternate is FALSE\n", rname);
return SKIP;
}
)
{
DEBUG(D_route)
- debug_printf("%s router skipped: bmi_deliver_default is FALSE\n", r->name);
+ debug_printf("%s router skipped: bmi_deliver_default is FALSE\n", rname);
return SKIP;
}
#endif
*/
static void
-route_unseen(uschar *name, address_item *addr, address_item **paddr_local,
- address_item **paddr_remote, address_item **addr_new)
+route_unseen(const uschar * name, address_item * addr,
+ address_item **paddr_local, address_item ** paddr_remote,
+ address_item ** addr_new)
{
address_item *parent = deliver_make_addr(addr->address, TRUE);
address_item *new = deliver_make_addr(addr->address, TRUE);
/* As it has turned out, we haven't set headers_add or headers_remove for the
- * clone. Thinking about it, it isn't entirely clear whether they should be
- * copied from the original parent, like errors_address, or taken from the
- * unseen router, like address_data and the flags. Until somebody brings this
- * up, I propose to leave the code as it is.
- */
+clone. Thinking about it, it isn't entirely clear whether they should be
+copied from the original parent, like errors_address, or taken from the
+unseen router, like address_data and the flags. Until somebody brings this
+up, I propose to leave the code as it is. */
/* Set the cloned address to start at the next router, and put it onto the
chain of new addresses. */
-new->start_router = addr->router->next;
+new->start_router = addr->router->drinst.next;
new->next = *addr_new;
*addr_new = new;
const uschar * varlist = r->set;
tree_node ** root = (tree_node **) &addr->prop.variables;
int sep = ';';
+const uschar * drname = r->drinst.name;
GET_OPTION("set");
if (!varlist) return OK;
if (!name || name[0] != 'r' || name[1] != '_' || !name[2])
{
log_write(0, LOG_MAIN|LOG_PANIC,
- "bad router variable name '%s' in router '%s'\n", name, r->name);
+ "bad router variable name '%s' in router '%s'\n", name, drname);
return FAIL;
}
name += 2;
- while (isspace(*assignment)) assignment++;
+ Uskip_whitespace(&assignment);
if (!(val = expand_string(US assignment)))
if (f.expand_string_forcedfail)
/* Expand "more" if necessary; DEFER => an expansion failed */
GET_OPTION("more");
- yield = exp_bool(addr, US"router", r->name, D_route,
+ yield = exp_bool(addr, US"router", drname, D_route,
US"more", r->more, r->expand_more, &more);
if (yield != OK) return yield;
else
{
addr->message = string_sprintf("expansion of \"%s\" failed "
- "in %s router: %s", ele, r->name, expand_string_message);
+ "in %s router: %s", ele, drname, expand_string_message);
/* Caller will replace that for logging, if a DB lookup, to avoid exposing
passwords */
DEBUG(D_route) debug_printf("%s\n", addr->message);
BOOL unseen;
router_instance * r, * nextr;
const uschar * old_domain = addr->domain;
+const uschar * rname_l;
HDEBUG(D_route)
{
uschar * error;
struct passwd * pw = NULL;
struct passwd pwcopy;
- BOOL loop_detected = FALSE;
- BOOL more;
- int loopcount = 0;
- int rc;
+ BOOL loop_detected = FALSE, more;
+ int loopcount = 0, rc;
- DEBUG(D_route) debug_printf("--------> %s router <--------\n", r->name);
+ rname_l = r->drinst.name;
+ DEBUG(D_route) debug_printf("--------> %s router <--------\n", rname_l);
/* Reset any search error message from the previous router. */
next router. */
addr->router = r;
- nextr = r->next;
+ nextr = r->drinst.next;
/* Loop protection: If this address has an ancestor with the same address,
and that ancestor was routed by this router, we skip this router. This
if (break_loop)
{
DEBUG(D_route) debug_printf("%s router skipped: previously routed %s\n",
- r->name, parent->address);
+ rname_l, parent->address);
loop_detected = TRUE;
break;
}
}
else if (!r->prefix_optional)
{
- DEBUG(D_route) debug_printf("%s router skipped: prefix mismatch\n",
- r->name);
+ DEBUG(D_route)
+ debug_printf("%s router skipped: prefix mismatch\n", rname_l);
continue;
}
}
}
else if (!r->suffix_optional)
{
- DEBUG(D_route) debug_printf("%s router skipped: suffix mismatch\n",
- r->name);
+ DEBUG(D_route)
+ debug_printf("%s router skipped: suffix mismatch\n", rname_l);
continue;
}
}
/* Set the expansion variables now that we have the affixes and the case of
the local part sorted. */
- router_name = r->name;
- driver_srcfile = r->srcfile;
- driver_srcline = r->srcline;
+ router_name = rname_l;
+ driver_srcfile = r->drinst.srcfile;
+ driver_srcline = r->drinst.srcline;
deliver_set_expansions(addr);
/* For convenience, the pre-router checks are in a separate function, which
/* Expand "more" if necessary; DEFER => an expansion failed */
GET_OPTION("more");
- yield = exp_bool(addr, US"router", r->name, D_route,
+ yield = exp_bool(addr, US"router", rname_l, D_route,
US"more", r->more, r->expand_more, &more);
if (yield != OK) goto ROUTE_EXIT;
else
{
addr->message = string_sprintf("expansion of \"%s\" failed "
- "in %s router: %s", r->address_data, r->name, expand_string_message);
+ "in %s router: %s", r->address_data, rname_l, expand_string_message);
yield = DEFER;
goto ROUTE_EXIT;
}
/* Run the router, and handle the consequences. */
- HDEBUG(D_route) debug_printf("calling %s router\n", r->name);
+ HDEBUG(D_route) debug_printf("calling %s router\n", rname_l);
- yield = (r->info->code)(r, addr, pw, verify, paddr_local, paddr_remote,
- addr_new, addr_succeed);
+ {
+ router_info * ri = r->drinst.info;
+ yield = (ri->code)(r, addr, pw, verify, paddr_local, paddr_remote,
+ addr_new, addr_succeed);
+ }
driver_srcfile = router_name = NULL; driver_srcline = 0;
if (yield == FAIL)
{
- HDEBUG(D_route) debug_printf("%s router forced address failure\n", r->name);
+ HDEBUG(D_route) debug_printf("%s router forced address failure\n", rname_l);
goto ROUTE_EXIT;
}
)
&& (yield == OK || yield == PASS))
{
- addr->message = string_sprintf("%s router forced verify failure", r->name);
+ addr->message = string_sprintf("%s router forced verify failure", rname_l);
if (*paddr_remote == addr) *paddr_remote = addr->next;
if (*paddr_local == addr) *paddr_local = addr->next;
yield = FAIL;
HDEBUG(D_route)
{
- debug_printf("%s router %s for %s\n", r->name,
+ debug_printf("%s router %s for %s\n", rname_l,
yield == PASS ? "passed" : "declined", addr->address);
if (Ustrcmp(old_domain, addr->domain) != 0)
debug_printf("domain %s rewritten\n", old_domain);
if (yield == PASS)
{
- if (r->pass_router != NULL) nextr = r->pass_router;
+ if (r->pass_router) nextr = r->pass_router;
}
else
{
/* Expand "more" if necessary */
GET_OPTION("more");
- yield = exp_bool(addr, US"router", r->name, D_route,
+ yield = exp_bool(addr, US"router", rname_l, D_route,
US"more", r->more, r->expand_more, &more);
if (yield != OK) goto ROUTE_EXIT;
else
if (!f.expand_string_forcedfail)
log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
- "cannot_route_message in %s router: %s", addr->router->name,
+ "cannot_route_message in %s router: %s",
+ addr->router->drinst.name,
expand_string_message);
}
}
if (yield == DEFER)
{
HDEBUG(D_route) debug_printf("%s router: defer for %s\n message: %s\n",
- r->name, addr->address, addr->message ? addr->message : US"<none>");
+ rname_l, addr->address, addr->message ? addr->message : US"<none>");
goto ROUTE_EXIT;
}
if (yield != OK && yield != REROUTED)
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s router returned unknown value %d",
- r->name, yield);
+ rname_l, yield);
/* If the yield was REROUTED, the router put a child address on the new chain
as a result of a domain change of some sort (widening, typically). */
/* See if this is an unseen routing; first expand the option if necessary.
DEFER can be given if the expansion fails */
-GET_OPTION("unseen");
-yield = exp_bool(addr, US"router", r->name, D_route,
+yield = exp_bool(addr, US"router", rname_l, D_route,
US"unseen", r->unseen, r->expand_unseen, &unseen);
if (yield != OK) goto ROUTE_EXIT;
/* Debugging output recording a successful routing */
-HDEBUG(D_route) debug_printf("routed by %s router%s\n", r->name,
- unseen? " (unseen)" : "");
+HDEBUG(D_route) debug_printf("routed by %s router%s\n", rname_l,
+ unseen ? " (unseen)" : "");
DEBUG(D_route)
{
debug_printf(" envelope to: %s\n", addr->address);
debug_printf(" transport: %s\n", addr->transport
- ? addr->transport->name : US"<none>");
+ ? addr->transport->drinst.name : US"<none>");
if (addr->prop.errors_address)
debug_printf(" errors to %s\n", addr->prop.errors_address);
the "unseen" option (ignore if there are no further routers). */
addr->message = NULL;
-if (unseen && r->next)
- route_unseen(r->name, addr, paddr_local, paddr_remote, addr_new);
+if (unseen && r->drinst.next)
+ route_unseen(rname_l, addr, paddr_local, paddr_remote, addr_new);
/* Unset the address expansions, and return the final result. */