c99 / non-gcc compatible inlineable functions
[exim.git] / src / src / route.c
index b289b0f3419aa4cf531aec6f40ebce71720c3d22..74a7922587a510f7b83038b62862cfa7a708afde 100644 (file)
@@ -2,7 +2,8 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2015 */
+/* Copyright (c) University of Cambridge 1995 - 2018 */
+/* Copyright (c) The Exim Maintainers 2020 - 2021 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Functions concerned with routing, and the list of generic router options. */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Functions concerned with routing, and the list of generic router options. */
 
 /* Generic options for routers, all of which live inside router_instance
 data blocks and which therefore have the opt_public flag set. */
 
 /* Generic options for routers, all of which live inside router_instance
 data blocks and which therefore have the opt_public flag set. */
+#define LOFF(field) OPT_OFF(router_instance, field)
 
 optionlist optionlist_routers[] = {
   { "*expand_group",      opt_stringptr | opt_hidden | opt_public,
 
 optionlist optionlist_routers[] = {
   { "*expand_group",      opt_stringptr | opt_hidden | opt_public,
-                 (void *)(offsetof(router_instance, expand_gid)) },
+                 LOFF(expand_gid) },
   { "*expand_more",       opt_stringptr | opt_hidden | opt_public,
   { "*expand_more",       opt_stringptr | opt_hidden | opt_public,
-                 (void *)(offsetof(router_instance, expand_more)) },
+                 LOFF(expand_more) },
   { "*expand_unseen",     opt_stringptr | opt_hidden | opt_public,
   { "*expand_unseen",     opt_stringptr | opt_hidden | opt_public,
-                 (void *)(offsetof(router_instance, expand_unseen)) },
+                 LOFF(expand_unseen) },
   { "*expand_user",       opt_stringptr | opt_hidden | opt_public,
   { "*expand_user",       opt_stringptr | opt_hidden | opt_public,
-                 (void *)(offsetof(router_instance, expand_uid)) },
+                 LOFF(expand_uid) },
   { "*set_group",         opt_bool | opt_hidden | opt_public,
   { "*set_group",         opt_bool | opt_hidden | opt_public,
-                 (void *)(offsetof(router_instance, gid_set)) },
+                 LOFF(gid_set) },
   { "*set_user",          opt_bool | opt_hidden | opt_public,
   { "*set_user",          opt_bool | opt_hidden | opt_public,
-                 (void *)(offsetof(router_instance, uid_set)) },
+                 LOFF(uid_set) },
   { "address_data",       opt_stringptr|opt_public,
   { "address_data",       opt_stringptr|opt_public,
-                 (void *)(offsetof(router_instance, address_data)) },
+                 LOFF(address_data) },
   { "address_test",       opt_bool|opt_public,
   { "address_test",       opt_bool|opt_public,
-                 (void *)(offsetof(router_instance, address_test)) },
+                 LOFF(address_test) },
 #ifdef EXPERIMENTAL_BRIGHTMAIL
   { "bmi_deliver_alternate",   opt_bool | opt_public,
 #ifdef EXPERIMENTAL_BRIGHTMAIL
   { "bmi_deliver_alternate",   opt_bool | opt_public,
-                 (void *)(offsetof(router_instance, bmi_deliver_alternate)) },
+                 LOFF(bmi_deliver_alternate) },
   { "bmi_deliver_default",   opt_bool | opt_public,
   { "bmi_deliver_default",   opt_bool | opt_public,
-                 (void *)(offsetof(router_instance, bmi_deliver_default)) },
+                 LOFF(bmi_deliver_default) },
   { "bmi_dont_deliver",   opt_bool | opt_public,
   { "bmi_dont_deliver",   opt_bool | opt_public,
-                 (void *)(offsetof(router_instance, bmi_dont_deliver)) },
+                 LOFF(bmi_dont_deliver) },
   { "bmi_rule",           opt_stringptr|opt_public,
   { "bmi_rule",           opt_stringptr|opt_public,
-                 (void *)(offsetof(router_instance, bmi_rule)) },
+                 LOFF(bmi_rule) },
 #endif
   { "cannot_route_message", opt_stringptr | opt_public,
 #endif
   { "cannot_route_message", opt_stringptr | opt_public,
-                 (void *)(offsetof(router_instance, cannot_route_message)) },
+                 LOFF(cannot_route_message) },
   { "caseful_local_part", opt_bool | opt_public,
   { "caseful_local_part", opt_bool | opt_public,
-                 (void *)(offsetof(router_instance, caseful_local_part)) },
+                 LOFF(caseful_local_part) },
   { "check_local_user",   opt_bool | opt_public,
   { "check_local_user",   opt_bool | opt_public,
-                 (void *)(offsetof(router_instance, check_local_user)) },
+                 LOFF(check_local_user) },
   { "condition",          opt_stringptr|opt_public|opt_rep_con,
   { "condition",          opt_stringptr|opt_public|opt_rep_con,
-                 (void *)offsetof(router_instance, condition) },
+                 LOFF(condition) },
   { "debug_print",        opt_stringptr | opt_public,
   { "debug_print",        opt_stringptr | opt_public,
-                 (void *)offsetof(router_instance, debug_string) },
+                 LOFF(debug_string) },
   { "disable_logging",    opt_bool | opt_public,
   { "disable_logging",    opt_bool | opt_public,
-                 (void *)offsetof(router_instance, disable_logging) },
+                 LOFF(disable_logging) },
   { "dnssec_request_domains",            opt_stringptr|opt_public,
   { "dnssec_request_domains",            opt_stringptr|opt_public,
-                 (void *)offsetof(router_instance, dnssec.request) },
+                 LOFF(dnssec.request) },
   { "dnssec_require_domains",            opt_stringptr|opt_public,
   { "dnssec_require_domains",            opt_stringptr|opt_public,
-                 (void *)offsetof(router_instance, dnssec.require) },
+                 LOFF(dnssec.require) },
   { "domains",            opt_stringptr|opt_public,
   { "domains",            opt_stringptr|opt_public,
-                 (void *)offsetof(router_instance, domains) },
+                 LOFF(domains) },
   { "driver",             opt_stringptr|opt_public,
   { "driver",             opt_stringptr|opt_public,
-                 (void *)offsetof(router_instance, driver_name) },
+                 LOFF(driver_name) },
   { "dsn_lasthop",        opt_bool|opt_public,
   { "dsn_lasthop",        opt_bool|opt_public,
-                 (void *)offsetof(router_instance, dsn_lasthop) },
+                 LOFF(dsn_lasthop) },
   { "errors_to",          opt_stringptr|opt_public,
   { "errors_to",          opt_stringptr|opt_public,
-                 (void *)(offsetof(router_instance, errors_to)) },
+                 LOFF(errors_to) },
   { "expn",               opt_bool|opt_public,
   { "expn",               opt_bool|opt_public,
-                 (void *)offsetof(router_instance, expn) },
+                 LOFF(expn) },
   { "fail_verify",        opt_bool_verify|opt_hidden|opt_public,
   { "fail_verify",        opt_bool_verify|opt_hidden|opt_public,
-                 (void *)offsetof(router_instance, fail_verify_sender) },
+                 LOFF(fail_verify_sender) },
   { "fail_verify_recipient", opt_bool|opt_public,
   { "fail_verify_recipient", opt_bool|opt_public,
-                 (void *)offsetof(router_instance, fail_verify_recipient) },
+                 LOFF(fail_verify_recipient) },
   { "fail_verify_sender", opt_bool|opt_public,
   { "fail_verify_sender", opt_bool|opt_public,
-                 (void *)offsetof(router_instance, fail_verify_sender) },
+                 LOFF(fail_verify_sender) },
   { "fallback_hosts",     opt_stringptr|opt_public,
   { "fallback_hosts",     opt_stringptr|opt_public,
-                 (void *)offsetof(router_instance, fallback_hosts) },
+                 LOFF(fallback_hosts) },
   { "group",              opt_expand_gid | opt_public,
   { "group",              opt_expand_gid | opt_public,
-                 (void *)(offsetof(router_instance, gid)) },
+                 LOFF(gid) },
   { "headers_add",        opt_stringptr|opt_public|opt_rep_str,
   { "headers_add",        opt_stringptr|opt_public|opt_rep_str,
-                 (void *)offsetof(router_instance, extra_headers) },
+                 LOFF(extra_headers) },
   { "headers_remove",     opt_stringptr|opt_public|opt_rep_str,
   { "headers_remove",     opt_stringptr|opt_public|opt_rep_str,
-                 (void *)offsetof(router_instance, remove_headers) },
+                 LOFF(remove_headers) },
   { "ignore_target_hosts",opt_stringptr|opt_public,
   { "ignore_target_hosts",opt_stringptr|opt_public,
-                 (void *)offsetof(router_instance, ignore_target_hosts) },
+                 LOFF(ignore_target_hosts) },
   { "initgroups",         opt_bool | opt_public,
   { "initgroups",         opt_bool | opt_public,
-                 (void *)(offsetof(router_instance, initgroups)) },
+                 LOFF(initgroups) },
   { "local_part_prefix",  opt_stringptr|opt_public,
   { "local_part_prefix",  opt_stringptr|opt_public,
-                 (void *)offsetof(router_instance, prefix) },
+                 LOFF(prefix) },
   { "local_part_prefix_optional",opt_bool|opt_public,
   { "local_part_prefix_optional",opt_bool|opt_public,
-                 (void *)offsetof(router_instance, prefix_optional) },
+                 LOFF(prefix_optional) },
   { "local_part_suffix",  opt_stringptr|opt_public,
   { "local_part_suffix",  opt_stringptr|opt_public,
-                 (void *)offsetof(router_instance, suffix) },
+                 LOFF(suffix) },
   { "local_part_suffix_optional",opt_bool|opt_public,
   { "local_part_suffix_optional",opt_bool|opt_public,
-                 (void *)offsetof(router_instance, suffix_optional) },
+                 LOFF(suffix_optional) },
   { "local_parts",        opt_stringptr|opt_public,
   { "local_parts",        opt_stringptr|opt_public,
-                 (void *)offsetof(router_instance, local_parts) },
+                 LOFF(local_parts) },
   { "log_as_local",       opt_bool|opt_public,
   { "log_as_local",       opt_bool|opt_public,
-                 (void *)offsetof(router_instance, log_as_local) },
+                 LOFF(log_as_local) },
   { "more",               opt_expand_bool|opt_public,
   { "more",               opt_expand_bool|opt_public,
-                 (void *)offsetof(router_instance, more) },
+                 LOFF(more) },
   { "pass_on_timeout",    opt_bool|opt_public,
   { "pass_on_timeout",    opt_bool|opt_public,
-                 (void *)offsetof(router_instance, pass_on_timeout) },
+                 LOFF(pass_on_timeout) },
   { "pass_router",       opt_stringptr|opt_public,
   { "pass_router",       opt_stringptr|opt_public,
-                 (void *)offsetof(router_instance, pass_router_name) },
+                 LOFF(pass_router_name) },
   { "redirect_router",    opt_stringptr|opt_public,
   { "redirect_router",    opt_stringptr|opt_public,
-                 (void *)offsetof(router_instance, redirect_router_name) },
+                 LOFF(redirect_router_name) },
   { "require_files",      opt_stringptr|opt_public,
   { "require_files",      opt_stringptr|opt_public,
-                 (void *)offsetof(router_instance, require_files) },
+                 LOFF(require_files) },
   { "retry_use_local_part", opt_bool|opt_public,
   { "retry_use_local_part", opt_bool|opt_public,
-                 (void *)offsetof(router_instance, retry_use_local_part) },
+                 LOFF(retry_use_local_part) },
   { "router_home_directory", opt_stringptr|opt_public,
   { "router_home_directory", opt_stringptr|opt_public,
-                 (void *)offsetof(router_instance, router_home_directory) },
+                 LOFF(router_home_directory) },
   { "self",               opt_stringptr|opt_public,
   { "self",               opt_stringptr|opt_public,
-                 (void *)(offsetof(router_instance, self)) },
+                 LOFF(self) },
   { "senders",            opt_stringptr|opt_public,
   { "senders",            opt_stringptr|opt_public,
-                 (void *)offsetof(router_instance, senders) },
+                 LOFF(senders) },
+  { "set",                opt_stringptr|opt_public|opt_rep_str,
+                 LOFF(set) },
   #ifdef SUPPORT_TRANSLATE_IP_ADDRESS
   { "translate_ip_address", opt_stringptr|opt_public,
   #ifdef SUPPORT_TRANSLATE_IP_ADDRESS
   { "translate_ip_address", opt_stringptr|opt_public,
-                 (void *)offsetof(router_instance, translate_ip_address) },
+                 LOFF(translate_ip_address) },
   #endif
   { "transport",          opt_stringptr|opt_public,
   #endif
   { "transport",          opt_stringptr|opt_public,
-                 (void *)offsetof(router_instance, transport_name) },
+                 LOFF(transport_name) },
   { "transport_current_directory", opt_stringptr|opt_public,
   { "transport_current_directory", opt_stringptr|opt_public,
-                 (void *)offsetof(router_instance, current_directory) },
+                 LOFF(current_directory) },
   { "transport_home_directory", opt_stringptr|opt_public,
   { "transport_home_directory", opt_stringptr|opt_public,
-                 (void *)offsetof(router_instance, home_directory) },
+                 LOFF(home_directory) },
   { "unseen",             opt_expand_bool|opt_public,
   { "unseen",             opt_expand_bool|opt_public,
-                 (void *)offsetof(router_instance, unseen) },
+                 LOFF(unseen) },
   { "user",               opt_expand_uid | opt_public,
   { "user",               opt_expand_uid | opt_public,
-                 (void *)(offsetof(router_instance, uid)) },
+                 LOFF(uid) },
   { "verify",             opt_bool_verify|opt_hidden|opt_public,
   { "verify",             opt_bool_verify|opt_hidden|opt_public,
-                 (void *)offsetof(router_instance, verify_sender) },
+                 LOFF(verify_sender) },
   { "verify_only",        opt_bool|opt_public,
   { "verify_only",        opt_bool|opt_public,
-                 (void *)offsetof(router_instance, verify_only) },
+                 LOFF(verify_only) },
   { "verify_recipient",   opt_bool|opt_public,
   { "verify_recipient",   opt_bool|opt_public,
-                 (void *)offsetof(router_instance, verify_recipient) },
+                 LOFF(verify_recipient) },
   { "verify_sender",      opt_bool|opt_public,
   { "verify_sender",      opt_bool|opt_public,
-                 (void *)offsetof(router_instance, verify_sender) }
+                 LOFF(verify_sender) }
 };
 
 };
 
-int optionlist_routers_size = sizeof(optionlist_routers)/sizeof(optionlist);
+int optionlist_routers_size = nelem(optionlist_routers);
 
 
 
 
+#ifdef MACRO_PREDEF
+
+# include "macro_predef.h"
+
+void
+options_routers(void)
+{
+uschar buf[64];
+
+options_from_list(optionlist_routers, nelem(optionlist_routers), US"ROUTERS", NULL);
+
+for (router_info * ri = routers_available; ri->driver_name[0]; ri++)
+  {
+  spf(buf, sizeof(buf), US"_DRIVER_ROUTER_%T", ri->driver_name);
+  builtin_macro_create(buf);
+  options_from_list(ri->options, (unsigned)*ri->options_count, US"ROUTER", ri->driver_name);
+  }
+}
+
+#else  /*!MACRO_PREDEF*/
 
 /*************************************************
 *          Set router pointer from name          *
 
 /*************************************************
 *          Set router pointer from name          *
@@ -201,8 +225,6 @@ function. */
 void
 route_init(void)
 {
 void
 route_init(void)
 {
-router_instance *r;
-
 readconf_driver_init(US"router",
   (driver_instance **)(&routers),     /* chain anchor */
   (driver_info *)routers_available,   /* available drivers */
 readconf_driver_init(US"router",
   (driver_instance **)(&routers),     /* chain anchor */
   (driver_info *)routers_available,   /* available drivers */
@@ -212,7 +234,7 @@ readconf_driver_init(US"router",
   optionlist_routers,                 /* generic options */
   optionlist_routers_size);
 
   optionlist_routers,                 /* generic options */
   optionlist_routers_size);
 
-for (r = routers; r; r = r->next)
+for (router_instance * r = routers; r; r = r->next)
   {
   uschar *s = r->self;
 
   {
   uschar *s = r->self;
 
@@ -223,14 +245,12 @@ for (r = routers; r; r = r->next)
 
   /* Check for transport or no transport on certain routers */
 
 
   /* Check for transport or no transport on certain routers */
 
-  if ((r->info->ri_flags & ri_yestransport) != 0 &&
-      r->transport_name == NULL &&
-      !r->verify_only)
+  if (  (r->info->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);
 
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG, "%s router:\n  "
       "a transport is required for this router", r->name);
 
-  if ((r->info->ri_flags & ri_notransport) != 0 &&
-       r->transport_name != NULL)
+  if ((r->info->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);
 
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG, "%s router:\n  "
       "a transport must not be defined for this router", r->name);
 
@@ -263,22 +283,30 @@ for (r = routers; r; r = r->next)
   TRUE; otherwise its default is FALSE. */
 
   if (r->retry_use_local_part == TRUE_UNSET)
   TRUE; otherwise its default is FALSE. */
 
   if (r->retry_use_local_part == TRUE_UNSET)
-    r->retry_use_local_part = r->check_local_user;
+    r->retry_use_local_part =
+      r->check_local_user || r->local_parts || r->condition || r->prefix || r->suffix || r->senders || r->require_files;
 
   /* Build a host list if fallback hosts is set. */
 
 
   /* Build a host list if fallback hosts is set. */
 
-  host_build_hostlist(&(r->fallback_hostlist), r->fallback_hosts, FALSE);
+    {
+    int old_pool = store_pool;
+    store_pool = POOL_PERM;
+    host_build_hostlist(&r->fallback_hostlist, r->fallback_hosts, FALSE);
+    store_pool = old_pool;
+    }
 
   /* Check redirect_router and pass_router are valid */
 
 
   /* Check redirect_router and pass_router are valid */
 
-  if (r->redirect_router_name != NULL)
+  if (r->redirect_router_name)
     set_router(r, r->redirect_router_name, &(r->redirect_router), FALSE);
 
     set_router(r, r->redirect_router_name, &(r->redirect_router), FALSE);
 
-  if (r->pass_router_name != NULL)
+  if (r->pass_router_name)
     set_router(r, r->pass_router_name, &(r->pass_router), TRUE);
 
     set_router(r, r->pass_router_name, &(r->pass_router), TRUE);
 
+#ifdef notdef
   DEBUG(D_route) debug_printf("DSN: %s %s\n", r->name,
        r->dsn_lasthop ? "lasthop set" : "propagating DSN");
   DEBUG(D_route) debug_printf("DSN: %s %s\n", r->name,
        r->dsn_lasthop ? "lasthop set" : "propagating DSN");
+#endif
   }
 }
 
   }
 }
 
@@ -296,8 +324,7 @@ is finished, via this function. */
 void
 route_tidyup(void)
 {
 void
 route_tidyup(void)
 {
-router_instance *r;
-for (r = routers; r; r = r->next)
+for (router_instance * r = routers; r; r = r->next)
   if (r->info->tidyup) (r->info->tidyup)(r);
 }
 
   if (r->info->tidyup) (r->info->tidyup)(r);
 }
 
@@ -314,31 +341,40 @@ wildcard.
 Arguments:
   local_part    the local part to check
   prefixes      the list of prefixes
 Arguments:
   local_part    the local part to check
   prefixes      the list of prefixes
+  vp           if set, pointer to place for size of wildcard portion
 
 Returns:        length of matching prefix or zero
 */
 
 int
 
 Returns:        length of matching prefix or zero
 */
 
 int
-route_check_prefix(const uschar *local_part, const uschar *prefixes)
+route_check_prefix(const uschar * local_part, const uschar * prefixes,
+  unsigned * vp)
 {
 int sep = 0;
 uschar *prefix;
 const uschar *listptr = prefixes;
 {
 int sep = 0;
 uschar *prefix;
 const uschar *listptr = prefixes;
-uschar prebuf[64];
 
 
-while ((prefix = string_nextinlist(&listptr, &sep, prebuf, sizeof(prebuf))))
+while ((prefix = string_nextinlist(&listptr, &sep, NULL, 0)))
   {
   int plen = Ustrlen(prefix);
   if (prefix[0] == '*')
     {
   {
   int plen = Ustrlen(prefix);
   if (prefix[0] == '*')
     {
-    const uschar *p;
     prefix++;
     prefix++;
-    for (p = local_part + Ustrlen(local_part) - (--plen);
+    for (const uschar * p = local_part + Ustrlen(local_part) - (--plen);
          p >= local_part; p--)
          p >= local_part; p--)
-      if (strncmpic(prefix, p, plen) == 0) return plen + p - local_part;
+      if (strncmpic(prefix, p, plen) == 0)
+       {
+       unsigned vlen = p - local_part;
+       if (vp) *vp = vlen;
+       return plen + vlen;
+       }
     }
   else
     }
   else
-    if (strncmpic(prefix, local_part, plen) == 0) return plen;
+    if (strncmpic(prefix, local_part, plen) == 0)
+      {
+      if (vp) *vp = 0;
+      return plen;
+      }
   }
 
 return 0;
   }
 
 return 0;
@@ -357,32 +393,40 @@ is a wildcard.
 Arguments:
   local_part    the local part to check
   suffixes      the list of suffixes
 Arguments:
   local_part    the local part to check
   suffixes      the list of suffixes
+  vp           if set, pointer to place for size of wildcard portion
 
 Returns:        length of matching suffix or zero
 */
 
 int
 
 Returns:        length of matching suffix or zero
 */
 
 int
-route_check_suffix(const uschar *local_part, const uschar *suffixes)
+route_check_suffix(const uschar * local_part, const uschar * suffixes,
+  unsigned * vp)
 {
 int sep = 0;
 int alen = Ustrlen(local_part);
 uschar *suffix;
 const uschar *listptr = suffixes;
 {
 int sep = 0;
 int alen = Ustrlen(local_part);
 uschar *suffix;
 const uschar *listptr = suffixes;
-uschar sufbuf[64];
 
 
-while ((suffix = string_nextinlist(&listptr, &sep, sufbuf, sizeof(sufbuf))))
+while ((suffix = string_nextinlist(&listptr, &sep, NULL, 0)))
   {
   int slen = Ustrlen(suffix);
   if (suffix[slen-1] == '*')
     {
   {
   int slen = Ustrlen(suffix);
   if (suffix[slen-1] == '*')
     {
-    const uschar *p, *pend;
-    pend = local_part + alen - (--slen) + 1;
-    for (p = local_part; p < pend; p++)
-      if (strncmpic(suffix, p, slen) == 0) return alen - (p - local_part);
+    const uschar * pend = local_part + alen - (--slen) + 1;
+    for (const uschar * p = local_part; p < pend; p++)
+      if (strncmpic(suffix, p, slen) == 0)
+       {
+       int tlen = alen - (p - local_part);
+       if (vp) *vp = tlen - slen;
+       return tlen;
+       }
     }
   else
     if (alen > slen && strncmpic(suffix, local_part + alen - slen, slen) == 0)
     }
   else
     if (alen > slen && strncmpic(suffix, local_part + alen - slen, slen) == 0)
+      {
+      if (vp) *vp = 0;
       return slen;
       return slen;
+      }
   }
 
 return 0;
   }
 
 return 0;
@@ -568,14 +612,13 @@ gid_t gid = 0;            /* For picky compilers */
 BOOL ugid_set = FALSE;
 const uschar *listptr;
 uschar *check;
 BOOL ugid_set = FALSE;
 const uschar *listptr;
 uschar *check;
-uschar buffer[1024];
 
 if (!s) return OK;
 
 DEBUG(D_route) debug_printf("checking require_files\n");
 
 listptr = s;
 
 if (!s) return OK;
 
 DEBUG(D_route) debug_printf("checking require_files\n");
 
 listptr = s;
-while ((check = string_nextinlist(&listptr, &sep, buffer, sizeof(buffer))))
+while ((check = string_nextinlist(&listptr, &sep, NULL, 0)))
   {
   int rc;
   int eacces_code = 0;
   {
   int rc;
   int eacces_code = 0;
@@ -585,7 +628,7 @@ while ((check = string_nextinlist(&listptr, &sep, buffer, sizeof(buffer))))
 
   if (!ss)
     {
 
   if (!ss)
     {
-    if (expand_string_forcedfail) continue;
+    if (f.expand_string_forcedfail) continue;
     *perror = string_sprintf("failed to expand \"%s\" for require_files: %s",
       check, expand_string_message);
     goto RETURN_DEFER;
     *perror = string_sprintf("failed to expand \"%s\" for require_files: %s",
       check, expand_string_message);
     goto RETURN_DEFER;
@@ -698,10 +741,10 @@ while ((check = string_nextinlist(&listptr, &sep, buffer, sizeof(buffer))))
     otherwise. Save the old state for resetting on the wait. */
 
     oldsignal = signal(SIGCHLD, SIG_DFL);
     otherwise. Save the old state for resetting on the wait. */
 
     oldsignal = signal(SIGCHLD, SIG_DFL);
-    pid = fork();
+    pid = exim_fork(US"require-files");
 
     /* If fork() fails, reinstate the original error and behave as if
 
     /* If fork() fails, reinstate the original error and behave as if
-    this block of code were not present. This is the same behavious as happens
+    this block of code were not present. This is the same behaviour as happens
     when Exim is not running as root at this point. */
 
     if (pid < 0)
     when Exim is not running as root at this point. */
 
     if (pid < 0)
@@ -720,21 +763,20 @@ while ((check = string_nextinlist(&listptr, &sep, buffer, sizeof(buffer))))
       {
       exim_setugid(uid, gid, TRUE,
         string_sprintf("require_files check, file=%s", ss));
       {
       exim_setugid(uid, gid, TRUE,
         string_sprintf("require_files check, file=%s", ss));
-      if (route_check_access(ss, uid, gid, 4)) _exit(0);
+      if (route_check_access(ss, uid, gid, 4))
+       exim_underbar_exit(EXIT_SUCCESS);
       DEBUG(D_route) debug_printf("route_check_access() failed\n");
       DEBUG(D_route) debug_printf("route_check_access() failed\n");
-      _exit(1);
+      exim_underbar_exit(EXIT_FAILURE);
       }
 
     /* In the parent, wait for the child to finish */
 
     while (waitpid(pid, &status, 0) < 0)
       }
 
     /* In the parent, wait for the child to finish */
 
     while (waitpid(pid, &status, 0) < 0)
-     {
      if (errno != EINTR)  /* unexpected error, interpret as failure */
        {
        status = 1;
        break;
        }
      if (errno != EINTR)  /* unexpected error, interpret as failure */
        {
        status = 1;
        break;
        }
-     }
 
     signal(SIGCHLD, oldsignal);   /* restore */
     if ((status == 0) == invert) return SKIP;
 
     signal(SIGCHLD, oldsignal);   /* restore */
     if ((status == 0) == invert) return SKIP;
@@ -752,7 +794,7 @@ while ((check = string_nextinlist(&listptr, &sep, buffer, sizeof(buffer))))
     rc = -1;
     }
 
     rc = -1;
     }
 
-  /* Handle error returns from stat() or route_check_access(). The EACESS error
+  /* Handle error returns from stat() or route_check_access(). The EACCES error
   is handled specially. At present, we can force it to be treated as
   non-existence. Write the code so that it will be easy to add forcing for
   existence if required later. */
   is handled specially. At present, we can force it to be treated as
   non-existence. Write the code so that it will be easy to add forcing for
   existence if required later. */
@@ -833,7 +875,7 @@ deliver_localpart_data = NULL;
 sender_data = NULL;
 local_user_gid = (gid_t)(-1);
 local_user_uid = (uid_t)(-1);
 sender_data = NULL;
 local_user_gid = (gid_t)(-1);
 local_user_uid = (uid_t)(-1);
-search_find_defer = FALSE;
+f.search_find_defer = FALSE;
 
 /* Skip this router if not verifying and it has verify_only set */
 
 
 /* Skip this router if not verifying and it has verify_only set */
 
@@ -845,7 +887,7 @@ if ((verify == v_none || verify == v_expn) && r->verify_only)
 
 /* Skip this router if testing an address (-bt) and address_test is not set */
 
 
 /* Skip this router if testing an address (-bt) and address_test is not set */
 
-if (address_test_mode && !r->address_test)
+if (f.address_test_mode && !r->address_test)
   {
   DEBUG(D_route) debug_printf("%s router skipped: address_test is unset\n",
     r->name);
   {
   DEBUG(D_route) debug_printf("%s router skipped: address_test is unset\n",
     r->name);
@@ -910,7 +952,7 @@ if ((rc = route_check_dls(r->name, US"local_parts", r->local_parts,
 login of a local user. Note: the third argument to route_finduser() must be
 NULL here, to prevent a numeric string being taken as a numeric uid. If the
 user is found, set deliver_home to the home directory, and also set
 login of a local user. Note: the third argument to route_finduser() must be
 NULL here, to prevent a numeric string being taken as a numeric uid. If the
 user is found, set deliver_home to the home directory, and also set
-local_user_{uid,gid}.  */
+local_user_{uid,gid} and local_part_data.  */
 
 if (r->check_local_user)
   {
 
 if (r->check_local_user)
   {
@@ -921,6 +963,8 @@ if (r->check_local_user)
       r->name, addr->local_part);
     return SKIP;
     }
       r->name, addr->local_part);
     return SKIP;
     }
+  addr->prop.localpart_data =
+    deliver_localpart_data = string_copy(US (*pw)->pw_name);
   deliver_home = string_copy(US (*pw)->pw_dir);
   local_user_gid = (*pw)->pw_gid;
   local_user_uid = (*pw)->pw_uid;
   deliver_home = string_copy(US (*pw)->pw_dir);
   local_user_gid = (*pw)->pw_gid;
   local_user_uid = (*pw)->pw_uid;
@@ -934,26 +978,23 @@ confusing. */
 
 if (r->router_home_directory)
   {
 
 if (r->router_home_directory)
   {
-  uschar *router_home = expand_string(r->router_home_directory);
-  if (!router_home)
-    {
-    if (!expand_string_forcedfail)
-      {
-      *perror = string_sprintf("failed to expand \"%s\" for "
-        "router_home_directory: %s", r->router_home_directory,
-        expand_string_message);
-      return DEFER;
-      }
-    }
-  else
+  uschar * router_home = expand_string(r->router_home_directory);
+  if (router_home)
     {
     setflag(addr, af_home_expanded); /* Note set from router_home_directory */
     deliver_home = router_home;
     }
     {
     setflag(addr, af_home_expanded); /* Note set from router_home_directory */
     deliver_home = router_home;
     }
+  else if (!f.expand_string_forcedfail)
+    {
+    *perror = string_sprintf("failed to expand \"%s\" for "
+      "router_home_directory: %s", r->router_home_directory,
+      expand_string_message);
+    return DEFER;
+    }
   }
 
 /* Skip if the sender condition is not met. We leave this one till after the
   }
 
 /* Skip if the sender condition is not met. We leave this one till after the
-local user check so that $home is set - enabling the possiblity of letting
+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,
 individual recipients specify lists of acceptable/unacceptable senders. */
 
 if ((rc = route_check_dls(r->name, US"senders", r->senders, NULL,
@@ -979,10 +1020,10 @@ if ((rc = check_files(r->require_files, perror)) != OK)
 
 if (r->condition)
   {
 
 if (r->condition)
   {
-  DEBUG(D_route) debug_printf("checking \"condition\"\n");
+  DEBUG(D_route) debug_printf("checking \"condition\" \"%.80s\"...\n", r->condition);
   if (!expand_check_condition(r->condition, r->name, US"router"))
     {
   if (!expand_check_condition(r->condition, r->name, US"router"))
     {
-    if (search_find_defer)
+    if (f.search_find_defer)
       {
       *perror = US"condition check lookup defer";
       DEBUG(D_route) debug_printf("%s\n", *perror);
       {
       *perror = US"condition check lookup defer";
       DEBUG(D_route) debug_printf("%s\n", *perror);
@@ -1084,7 +1125,7 @@ route_finduser(const uschar *s, struct passwd **pw, uid_t *return_uid)
 BOOL cache_set = (Ustrcmp(lastname, s) == 0);
 
 DEBUG(D_uid) debug_printf("seeking password data for user \"%s\": %s\n", s,
 BOOL cache_set = (Ustrcmp(lastname, s) == 0);
 
 DEBUG(D_uid) debug_printf("seeking password data for user \"%s\": %s\n", s,
-  cache_set? "using cached result" : "cache not available");
+  cache_set ? "using cached result" : "cache not available");
 
 if (!cache_set)
   {
 
 if (!cache_set)
   {
@@ -1098,7 +1139,7 @@ if (!cache_set)
     return TRUE;
     }
 
     return TRUE;
     }
 
-  (void)string_format(lastname, sizeof(lastname), "%s", s);
+  string_format_nt(lastname, sizeof(lastname), "%s", s);
 
   /* Force failure if string length is greater than given maximum */
 
 
   /* Force failure if string length is greater than given maximum */
 
@@ -1164,7 +1205,7 @@ NIS or NFS whatever cause an incorrect refusal. It's a pity that getgrnam()
 doesn't have some kind of indication as to why it has failed.
 
 Arguments:
 doesn't have some kind of indication as to why it has failed.
 
 Arguments:
-  s           the group namd or textual form of the numerical gid
+  s           the group name or textual form of the numerical gid
   return_gid  return the gid via this address
 
 Returns:      TRUE if the group was found; FALSE otherwise
   return_gid  return the gid via this address
 
 Returns:      TRUE if the group was found; FALSE otherwise
@@ -1344,8 +1385,10 @@ new->prop.errors_address = parent->prop.errors_address;
 
 /* Copy the propagated flags and address_data from the original. */
 
 
 /* Copy the propagated flags and address_data from the original. */
 
-copyflag(new, addr, af_propagate);
+new->prop.ignore_error = addr->prop.ignore_error;
 new->prop.address_data = addr->prop.address_data;
 new->prop.address_data = addr->prop.address_data;
+new->prop.variables = NULL;
+tree_dup((tree_node **)&new->prop.variables, addr->prop.variables);
 new->dsn_flags = addr->dsn_flags;
 new->dsn_orcpt = addr->dsn_orcpt;
 
 new->dsn_flags = addr->dsn_flags;
 new->dsn_orcpt = addr->dsn_orcpt;
 
@@ -1388,6 +1431,90 @@ if (addr->transport && tree_search(tree_nonrecipients, addr->unique))
 
 
 
 
 
 
+/************************************************/
+/* Add router-assigned variables
+Return OK/DEFER/FAIL/PASS */
+
+static int
+set_router_vars(address_item * addr, const router_instance * r)
+{
+const uschar * varlist = r->set;
+tree_node ** root = (tree_node **) &addr->prop.variables;
+int sep = ';';
+
+if (!varlist) return OK;
+
+/* Walk the varlist, creating variables */
+
+for (uschar * ele; (ele = string_nextinlist(&varlist, &sep, NULL, 0)); )
+  {
+  const uschar * assignment = ele;
+  int esep = '=';
+  uschar * name = string_nextinlist(&assignment, &esep, NULL, 0);
+  uschar * val;
+  tree_node * node;
+
+  /* Variable name must exist and start "r_". */
+
+  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);
+    return FAIL;
+    }
+  name += 2;
+
+  while (isspace(*assignment)) assignment++;
+
+  if (!(val = expand_string(US assignment)))
+    if (f.expand_string_forcedfail)
+      {
+      int yield;
+      BOOL more;
+      DEBUG(D_route) debug_printf("forced failure in expansion of \"%s\" "
+         "(router variable): decline action taken\n", ele);
+
+      /* Expand "more" if necessary; DEFER => an expansion failed */
+
+      yield = exp_bool(addr, US"router", r->name, D_route,
+                     US"more", r->more, r->expand_more, &more);
+      if (yield != OK) return yield;
+
+      if (!more)
+       {
+       DEBUG(D_route)
+         debug_printf("\"more\"=false: skipping remaining routers\n");
+       router_name = NULL;
+       r = NULL;
+       return FAIL;
+       }
+      return PASS;
+      }
+    else
+      {
+      addr->message = string_sprintf("expansion of \"%s\" failed "
+       "in %s router: %s", ele, r->name, expand_string_message);
+      return DEFER;
+      }
+
+  if (!(node = tree_search(*root, name)))
+    {                          /* name should never be tainted */
+    node = store_get(sizeof(tree_node) + Ustrlen(name), GET_UNTAINTED);
+    Ustrcpy(node->name, name);
+    (void)tree_insertnode(root, node);
+    }
+  node->data.ptr = US val;
+  DEBUG(D_route) debug_printf("set r_%s%s = '%s'%s\n",
+                   name, is_tainted(name)?" (tainted)":"",
+                   val, is_tainted(val)?" (tainted)":"");
+
+  /* All expansions after this point need visibility of that variable */
+  router_var = *root;
+  }
+return OK;
+}
+
+
 /*************************************************
 *                 Route one address              *
 *************************************************/
 /*************************************************
 *                 Route one address              *
 *************************************************/
@@ -1439,7 +1566,6 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr)
   uschar *error;
   struct passwd *pw = NULL;
   struct passwd pwcopy;
   uschar *error;
   struct passwd *pw = NULL;
   struct passwd pwcopy;
-  address_item *parent;
   BOOL loop_detected = FALSE;
   BOOL more;
   int loopcount = 0;
   BOOL loop_detected = FALSE;
   BOOL more;
   int loopcount = 0;
@@ -1453,7 +1579,7 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr)
 
   /* There are some weird cases where logging is disabled */
 
 
   /* There are some weird cases where logging is disabled */
 
-  disable_logging = r->disable_logging;
+  f.disable_logging = r->disable_logging;
 
   /* Record the last router to handle the address, and set the default
   next router. */
 
   /* Record the last router to handle the address, and set the default
   next router. */
@@ -1470,10 +1596,10 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr)
   by  this router, even if it was different to the current address.
 
   Just in case someone does put it into a loop (possible with redirection
   by  this router, even if it was different to the current address.
 
   Just in case someone does put it into a loop (possible with redirection
-  continally adding to an address, for example), put a long stop counter on
+  continually adding to an address, for example), put a long stop counter on
   the number of parents. */
 
   the number of parents. */
 
-  for (parent = addr->parent; parent; parent = parent->parent)
+  for (address_item * parent = addr->parent; parent; parent = parent->parent)
     {
     if (parent->router == r)
       {
     {
     if (parent->router == r)
       {
@@ -1516,9 +1642,9 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr)
   /* Default no affixes and select whether to use a caseful or caseless local
   part in this router. */
 
   /* Default no affixes and select whether to use a caseful or caseless local
   part in this router. */
 
-  addr->prefix = addr->suffix = NULL;
-  addr->local_part = r->caseful_local_part?
-    addr->cc_local_part : addr->lc_local_part;
+  addr->prefix = addr->prefix_v = addr->suffix = addr->suffix_v = NULL;
+  addr->local_part = r->caseful_local_part
+    addr->cc_local_part : addr->lc_local_part;
 
   DEBUG(D_route) debug_printf("local_part=%s domain=%s\n", addr->local_part,
     addr->domain);
 
   DEBUG(D_route) debug_printf("local_part=%s domain=%s\n", addr->local_part,
     addr->domain);
@@ -1529,10 +1655,22 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr)
 
   if (r->prefix)
     {
 
   if (r->prefix)
     {
-    int plen = route_check_prefix(addr->local_part, r->prefix);
+    unsigned vlen;
+    int plen = route_check_prefix(addr->local_part, r->prefix, &vlen);
     if (plen > 0)
       {
     if (plen > 0)
       {
-      addr->prefix = string_copyn(addr->local_part, plen);
+      /* If the variable-part is zero-length then the prefix was not
+      wildcarded and we can detaint-copy it since it matches the
+      (non-expandable) router option.  Otherwise copy the (likely) tainted match
+      and the variable-part of the match from the local_part. */
+
+      if (vlen)
+       {
+       addr->prefix = string_copyn(addr->local_part, plen);
+       addr->prefix_v = string_copyn(addr->local_part, vlen);
+       }
+      else
+       addr->prefix = string_copyn_taint(addr->local_part, plen, GET_UNTAINTED);
       addr->local_part += plen;
       DEBUG(D_route) debug_printf("stripped prefix %s\n", addr->prefix);
       }
       addr->local_part += plen;
       DEBUG(D_route) debug_printf("stripped prefix %s\n", addr->prefix);
       }
@@ -1548,11 +1686,15 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr)
 
   if (r->suffix)
     {
 
   if (r->suffix)
     {
-    int slen = route_check_suffix(addr->local_part, r->suffix);
+    unsigned vlen;
+    int slen = route_check_suffix(addr->local_part, r->suffix, &vlen);
     if (slen > 0)
       {
       int lplen = Ustrlen(addr->local_part) - slen;
     if (slen > 0)
       {
       int lplen = Ustrlen(addr->local_part) - slen;
-      addr->suffix = addr->local_part + lplen;
+      addr->suffix = vlen
+       ? addr->local_part + lplen
+       : string_copy_taint(addr->local_part + lplen, GET_UNTAINTED);
+      addr->suffix_v = addr->suffix + Ustrlen(addr->suffix) - vlen;
       addr->local_part = string_copyn(addr->local_part, lplen);
       DEBUG(D_route) debug_printf("stripped suffix %s\n", addr->suffix);
       }
       addr->local_part = string_copyn(addr->local_part, lplen);
       DEBUG(D_route) debug_printf("stripped suffix %s\n", addr->suffix);
       }
@@ -1568,6 +1710,8 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr)
   the local part sorted. */
 
   router_name = r->name;
   the local part sorted. */
 
   router_name = r->name;
+  driver_srcfile = r->srcfile;
+  driver_srcline = r->srcline;
   deliver_set_expansions(addr);
 
   /* For convenience, the pre-router checks are in a separate function, which
   deliver_set_expansions(addr);
 
   /* For convenience, the pre-router checks are in a separate function, which
@@ -1575,7 +1719,7 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr)
 
   if ((rc = check_router_conditions(r, addr, verify, &pw, &error)) != OK)
     {
 
   if ((rc = check_router_conditions(r, addr, verify, &pw, &error)) != OK)
     {
-    router_name = NULL;
+    driver_srcfile = router_name = NULL; driver_srcline = 0;
     if (rc == SKIP) continue;
     addr->message = error;
     yield = rc;
     if (rc == SKIP) continue;
     addr->message = error;
     yield = rc;
@@ -1588,6 +1732,19 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr)
 
   search_error_message = NULL;
 
 
   search_error_message = NULL;
 
+  /* Add any variable-settings that are on the router, to the set on the
+  addr. Expansion is done here and not later when the addr is used.  There may
+  be multiple settings, gathered during readconf; this code gathers them during
+  router traversal.  On the addr string they are held as a variable tree, so
+  as to maintain the post-expansion taints separate. */
+
+  switch (set_router_vars(addr, r))
+    {
+    case OK:   break;
+    case PASS: continue;               /* with next router */
+    default:    goto ROUTE_EXIT;
+    }
+
   /* Finally, expand the address_data field in the router. Forced failure
   behaves as if the router declined. Any other failure is more serious. On
   success, the string is attached to the address for all subsequent processing.
   /* Finally, expand the address_data field in the router. Forced failure
   behaves as if the router declined. Any other failure is more serious. On
   success, the string is attached to the address for all subsequent processing.
@@ -1596,10 +1753,9 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr)
   if (r->address_data)
     {
     DEBUG(D_route) debug_printf("processing address_data\n");
   if (r->address_data)
     {
     DEBUG(D_route) debug_printf("processing address_data\n");
-    deliver_address_data = expand_string(r->address_data);
-    if (!deliver_address_data)
+    if (!(deliver_address_data = expand_string(r->address_data)))
       {
       {
-      if (expand_string_forcedfail)
+      if (f.expand_string_forcedfail)
         {
         DEBUG(D_route) debug_printf("forced failure in expansion of \"%s\" "
             "(address_data): decline action taken\n", r->address_data);
         {
         DEBUG(D_route) debug_printf("forced failure in expansion of \"%s\" "
             "(address_data): decline action taken\n", r->address_data);
@@ -1614,7 +1770,7 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr)
           {
           DEBUG(D_route)
             debug_printf("\"more\"=false: skipping remaining routers\n");
           {
           DEBUG(D_route)
             debug_printf("\"more\"=false: skipping remaining routers\n");
-         router_name = NULL;
+         driver_srcfile = router_name = NULL; driver_srcline = 0;
           r = NULL;
           break;
           }
           r = NULL;
           break;
           }
@@ -1651,23 +1807,22 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr)
     pw = &pwcopy;
     }
 
     pw = &pwcopy;
     }
 
-  /* Run the router, and handle the consequences. */
-
-  /* ... but let us check on DSN before. If this should be the last hop for DSN
-  set flag. */
+  /* If this should be the last hop for DSN flag the addr. */
 
   if (r->dsn_lasthop && !(addr->dsn_flags & rf_dsnlasthop))
 
   if (r->dsn_lasthop && !(addr->dsn_flags & rf_dsnlasthop))
-  {
+    {
     addr->dsn_flags |= rf_dsnlasthop;
     HDEBUG(D_route) debug_printf("DSN: last hop for %s\n", addr->address);
     addr->dsn_flags |= rf_dsnlasthop;
     HDEBUG(D_route) debug_printf("DSN: last hop for %s\n", addr->address);
-  }
+    }
+
+  /* Run the router, and handle the consequences. */
 
   HDEBUG(D_route) debug_printf("calling %s router\n", r->name);
 
   yield = (r->info->code)(r, addr, pw, verify, paddr_local, paddr_remote,
     addr_new, addr_succeed);
 
 
   HDEBUG(D_route) debug_printf("calling %s router\n", r->name);
 
   yield = (r->info->code)(r, addr, pw, verify, paddr_local, paddr_remote,
     addr_new, addr_succeed);
 
-  router_name = NULL;
+  driver_srcfile = router_name = NULL; driver_srcline = 0;
 
   if (yield == FAIL)
     {
 
   if (yield == FAIL)
     {
@@ -1678,9 +1833,10 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr)
   /* If succeeded while verifying but fail_verify is set, convert into
   a failure, and take it off the local or remote delivery list. */
 
   /* If succeeded while verifying but fail_verify is set, convert into
   a failure, and take it off the local or remote delivery list. */
 
-  if (((verify == v_sender && r->fail_verify_sender) ||
-       (verify == v_recipient && r->fail_verify_recipient)) &&
-      (yield == OK || yield == PASS))
+  if (  (  verify == v_sender && r->fail_verify_sender
+       || verify == v_recipient && r->fail_verify_recipient
+       )
+     && (yield == OK || yield == PASS))
     {
     addr->message = string_sprintf("%s router forced verify failure", r->name);
     if (*paddr_remote == addr) *paddr_remote = addr->next;
     {
     addr->message = string_sprintf("%s router forced verify failure", r->name);
     if (*paddr_remote == addr) *paddr_remote = addr->next;
@@ -1697,7 +1853,7 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr)
   HDEBUG(D_route)
     {
     debug_printf("%s router %s for %s\n", r->name,
   HDEBUG(D_route)
     {
     debug_printf("%s router %s for %s\n", r->name,
-      (yield == PASS)? "passed" : "declined", addr->address);
+      yield == PASS ? "passed" : "declined", addr->address);
     if (Ustrcmp(old_domain, addr->domain) != 0)
       debug_printf("domain %s rewritten\n", old_domain);
     }
     if (Ustrcmp(old_domain, addr->domain) != 0)
       debug_printf("domain %s rewritten\n", old_domain);
     }
@@ -1745,7 +1901,7 @@ if (!r)
       uschar *expmessage = expand_string(addr->router->cannot_route_message);
       if (!expmessage)
         {
       uschar *expmessage = expand_string(addr->router->cannot_route_message);
       if (!expmessage)
         {
-        if (!expand_string_forcedfail)
+        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,
             expand_string_message);
           log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
             "cannot_route_message in %s router: %s", addr->router->name,
             expand_string_message);
@@ -1761,12 +1917,8 @@ if (!r)
 
 if (yield == DEFER)
   {
 
 if (yield == DEFER)
   {
-  HDEBUG(D_route)
-    {
-    debug_printf("%s router: defer for %s\n", r->name, addr->address);
-    debug_printf("  message: %s\n", (addr->message == NULL)?
-      US"<none>" : addr->message);
-    }
+  HDEBUG(D_route) debug_printf("%s router: defer for %s\n  message: %s\n",
+      r->name, addr->address, addr->message ? addr->message : US"<none>");
   goto ROUTE_EXIT;
   }
 
   goto ROUTE_EXIT;
   }
 
@@ -1800,8 +1952,7 @@ if (r->translate_ip_address)
   {
   int rc;
   int old_pool = store_pool;
   {
   int rc;
   int old_pool = store_pool;
-  host_item *h;
-  for (h = addr->host_list; h; h = h->next)
+  for (host_item * h = addr->host_list; h; h = h->next)
     {
     uschar *newaddress;
     uschar *oldaddress, *oldname;
     {
     uschar *newaddress;
     uschar *oldaddress, *oldname;
@@ -1814,7 +1965,7 @@ if (r->translate_ip_address)
 
     if (!newaddress)
       {
 
     if (!newaddress)
       {
-      if (expand_string_forcedfail) continue;
+      if (f.expand_string_forcedfail) continue;
       addr->basic_errno = ERRNO_EXPANDFAIL;
       addr->message = string_sprintf("translate_ip_address expansion "
         "failed: %s", expand_string_message);
       addr->basic_errno = ERRNO_EXPANDFAIL;
       addr->message = string_sprintf("translate_ip_address expansion "
         "failed: %s", expand_string_message);
@@ -1866,16 +2017,14 @@ HDEBUG(D_route) debug_printf("routed by %s router%s\n", r->name,
 
 DEBUG(D_route)
   {
 
 DEBUG(D_route)
   {
-  host_item *h;
-
   debug_printf("  envelope to: %s\n", addr->address);
   debug_printf("  envelope to: %s\n", addr->address);
-  debug_printf("  transport: %s\n", (addr->transport == NULL)?
-    US"<none>" : addr->transport->name);
+  debug_printf("  transport: %s\n", addr->transport
+    ? addr->transport->name : US"<none>");
 
   if (addr->prop.errors_address)
     debug_printf("  errors to %s\n", addr->prop.errors_address);
 
 
   if (addr->prop.errors_address)
     debug_printf("  errors to %s\n", addr->prop.errors_address);
 
-  for (h = addr->host_list; h; h = h->next)
+  for (host_item * h = addr->host_list; h; h = h->next)
     {
     debug_printf("  host %s", h->name);
     if (h->address) debug_printf(" [%s]", h->address);
     {
     debug_printf("  host %s", h->name);
     if (h->address) debug_printf(" [%s]", h->address);
@@ -1897,26 +2046,27 @@ if (unseen && r->next)
 /* Unset the address expansions, and return the final result. */
 
 ROUTE_EXIT:
 /* Unset the address expansions, and return the final result. */
 
 ROUTE_EXIT:
-if (  yield == DEFER
-   && addr->message
-   && (  Ustrstr(addr->message, "failed to expand") != NULL
-      || Ustrstr(addr->message, "expansion of ") != NULL
-      )
-   && (  Ustrstr(addr->message, "mysql") != NULL
-      || Ustrstr(addr->message, "pgsql") != NULL
-      || Ustrstr(addr->message, "redis") != NULL
-      || Ustrstr(addr->message, "sqlite") != NULL
-      || Ustrstr(addr->message, "ldap:") != NULL
-      || Ustrstr(addr->message, "ldapdn:") != NULL
-      || Ustrstr(addr->message, "ldapm:") != NULL
-      )
-   )
-  addr->message = string_sprintf("Temporary internal error");
+if (yield == DEFER && addr->message)
+  addr->message = expand_hide_passwords(addr->message);
 
 deliver_set_expansions(NULL);
 
 deliver_set_expansions(NULL);
-router_name = NULL;
-disable_logging = FALSE;
+driver_srcfile = router_name = NULL; driver_srcline = 0;
+f.disable_logging = FALSE;
 return yield;
 }
 
 return yield;
 }
 
+
+
+/* For error messages, a string describing the config location associated
+with current processing.  NULL if we are not in a router. */
+/* Name only, for now */
+
+uschar *
+router_current_name(void)
+{
+if (!router_name) return NULL;
+return string_sprintf(" (router %s, %s %d)", router_name, driver_srcfile, driver_srcline);
+}
+
+#endif /*!MACRO_PREDEF*/
 /* End of route.c */
 /* End of route.c */