/*************************************************
*     Exim - an Internet mail transport agent    *
*************************************************/

/* Copyright (c) University of Cambridge 1995 - 1999 */
/* See the file NOTICE for conditions of use and distribution. */

/* Functions concerned with routing, and the list of generic router options. */


#include "exim.h"



/* Generic options for routers, all of which live inside router_instance
data blocks and which therefore have the opt_public flag set. */

optionlist optionlist_routers[] = {
  { "*expand_group",      opt_stringptr | opt_hidden | opt_public,
                 (void *)(offsetof(router_instance, expand_gid)) },
  { "*expand_transport",  opt_stringptr|opt_public|opt_hidden,
                 (void *)offsetof(router_instance, expand_transport) },
  { "*expand_user",       opt_stringptr | opt_hidden | opt_public,
                 (void *)(offsetof(router_instance, expand_uid)) },
  { "*set_group",         opt_bool | opt_hidden | opt_public,
                 (void *)(offsetof(router_instance, gid_set)) },
  { "*set_user",          opt_bool | opt_hidden | opt_public,
                 (void *)(offsetof(router_instance, uid_set)) },
  { "condition",          opt_stringptr|opt_public,
                 (void *)offsetof(router_instance, condition) },
  { "debug_print",        opt_stringptr | opt_public,
                 (void *)offsetof(router_instance, debug_string) },
  { "domains",            opt_stringptr|opt_public,
                 (void *)offsetof(router_instance, domains) },
  { "driver",             opt_stringptr|opt_public,
                 (void *)offsetof(router_instance, driver_name) },
  { "errors_to",          opt_stringptr|opt_public,
                 (void *)(offsetof(router_instance, errors_to)) },
  { "except_domains",     opt_stringptr|opt_public,
                 (void *)offsetof(router_instance, except_domains) },
  { "except_local_parts", opt_stringptr|opt_public,
                 (void *)offsetof(router_instance, except_local_parts) },
  { "except_senders",     opt_stringptr|opt_public,
                 (void *)offsetof(router_instance, except_senders) },
  { "fail_verify",        opt_bool_verify|opt_hidden|opt_public,
                 (void *)offsetof(router_instance, fail_verify_sender) },
  { "fail_verify_recipient", opt_bool|opt_public,
                 (void *)offsetof(router_instance, fail_verify_recipient) },
  { "fail_verify_sender", opt_bool|opt_public,
                 (void *)offsetof(router_instance, fail_verify_sender) },
  { "fallback_hosts",     opt_stringptr|opt_public,
                 (void *)offsetof(router_instance, fallback_hosts) },
  { "group",              opt_expand_gid | opt_public,
                 (void *)(offsetof(router_instance, gid)) },
  { "headers_add",        opt_stringptr|opt_public,
                 (void *)offsetof(router_instance, extra_headers) },
  { "headers_remove",     opt_stringptr|opt_public,
                 (void *)offsetof(router_instance, remove_headers) },
  { "initgroups",         opt_bool | opt_public,
                 (void *)(offsetof(router_instance, initgroups)) },
  { "local_parts",        opt_stringptr|opt_public,
                 (void *)offsetof(router_instance, local_parts) },
  { "more",               opt_bool|opt_public,
                 (void *)offsetof(router_instance, more) },
  { "pass_on_timeout",    opt_bool|opt_public,
                 (void *)offsetof(router_instance, pass_on_timeout) },
  { "require_files",      opt_stringptr|opt_public,
                 (void *)offsetof(router_instance, require_files) },
  { "self",               opt_stringptr|opt_public,
                 (void *)(offsetof(router_instance, self)) },
  { "senders",            opt_stringptr|opt_public,
                 (void *)offsetof(router_instance, senders) },
  { "transport",          opt_transportptr|opt_public,
                 (void *)offsetof(router_instance, transport) },
  { "unseen",             opt_bool|opt_public,
                 (void *)offsetof(router_instance, unseen) },
  { "user",               opt_expand_uid | opt_public,
                 (void *)(offsetof(router_instance, uid)) },
  { "verify",             opt_bool_verify|opt_hidden|opt_public,
                 (void *)offsetof(router_instance, verify_sender) },
  { "verify_only",        opt_bool|opt_public,
                 (void *)offsetof(router_instance, verify_only) },
  { "verify_recipient",   opt_bool|opt_public,
                 (void *)offsetof(router_instance, verify_recipient) },
  { "verify_sender",      opt_bool|opt_public,
                 (void *)offsetof(router_instance, verify_sender) }
};

int optionlist_routers_size =
  sizeof(optionlist_routers)/sizeof(optionlist);


/*************************************************
*             Initialize router list             *
*************************************************/

/* Read the routers 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
routers == NULL. We use generic code in readconf to do the work. */

void
route_init(void)
{
router_instance *r;

readconf_driver_init("router",
  (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);

/* A router may not have more=FALSE and unseen=TRUE. The "self" option
needs to be decoded into a code value and possibly a new domain string
and a rewrite boolean. */

for (r = routers; r != NULL; r = r->next)
  {
  char *s = r->self;

  if (r->unseen && !r->more )
    log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
      "the combination of \"unseen\" and \"no_more\" is not permitted on "
      "a router,\n  but was set for the %s router", r->name);

  if      (strcmp(s, "freeze") == 0)    r->self_code = self_freeze;
  else if (strcmp(s, "defer") == 0)     r->self_code = self_defer;
  else if (strcmp(s, "send") == 0)      r->self_code = self_send;
  else if (strcmp(s, "fail_soft") == 0) r->self_code = self_fail;
  else if (strcmp(s, "fail_hard") == 0) r->self_code = self_forcefail;
  else if (strcmp(s, "local") == 0)     r->self_code = self_local;
  else if (strncmp(s, "reroute:", 8) == 0)
    {
    s += 8;
    while (isspace(*s)) s++;
    if (strncmp(s, "rewrite:", 8) == 0)
      {
      r->self_rewrite = TRUE;
      s += 8;
      while (isspace(*s)) s++;
      }
    r->self = s;
    r->self_code = self_reroute;
    }
  else log_write(0, LOG_PANIC_DIE|LOG_CONFIG2, "%s router:\n  "
      "%s is not valid for the self option", r->name, s);

  /* Build a host list if fallback hosts is set */

  host_build_hostlist(&(r->fallback_hostlist), r->fallback_hosts);
  }
}



/*************************************************
*             Tidy up after routing              *
*************************************************/

/* Routers are entitled to keep hold of certain resources in their instance
blocks so as to save setting them up each time. An example is an open file.
Such routers must provide a tidyup entry point which is called when all routing
is finished, via this function. */

void
route_tidyup(void)
{
router_instance *r;
for (r = routers; r != NULL; r = r->next)
  if (r->info->tidyup != NULL) (r->info->tidyup)(r);
}




/*************************************************
*        Queue address for transport             *
*************************************************/

/* This function is called to put an address onto the local or remote transport
queue, as appropriate. The same action is required by some (but not all) of the
directors, and so they use this too. The fields in the router block that are
used by this function have identical counterparts in the director block, to
enable this to work. The generic uid/gid options are inspected and put into the
address if they are set. For a remote transport, if there are fallback hosts,
they are added to the address.

When the driver is for verifying only, a transport need not be set, in which
case it doesn't actually matter which queue the address gets put on.

Arguments:
  addr          the address, with the transport field set (if not verify only)
  addr_local    the local transport chain
  addr_remote   the remote transport chain,
  rblock        the router block
  type          "director" or "router"

Returns:        FALSE on error
*/

BOOL
route_queue(address_item *addr, address_item **addr_local,
  address_item **addr_remote, router_instance *rblock, char *type)
{
if (addr->transport != NULL && addr->transport->info->local)
  {
  ugid_block ugid;
  if (!direct_get_ugid((director_instance *)rblock, type, addr, &ugid))
   return FALSE;
  direct_set_ugid(addr, &ugid);
  addr->next = *addr_local;
  *addr_local = addr;
  }
else
  {
  addr->next = *addr_remote;
  *addr_remote = addr;
  addr->fallback_hosts = rblock->fallback_hostlist;
  }

DEBUG(2) debug_printf("queued for %s transport: local_part=%s domain=%s\n",
  (addr->transport == NULL)? "<unset>" : addr->transport->name,
  addr->local_part, addr->domain);

return TRUE;
}




/*************************************************
*       Get transport for a driver               *
*************************************************/

/* If expand_transport is set on a driver, it must be expanded each time and
used as a transport name. This action is identical for both routers and
directors, and this function is called from both of them.

Arguments:
  tp            the fixed transport from the driver (may be NULL)
  expand_tp     the expansion string (may be NULL)
  addr          the address being processed
  tpptr         where to put the answer
  driver_name   for error messages

Returns:        TRUE if a transport (or NULL if tp == expand_tp == NULL) has
                been placed in tpptr; FALSE if there's a problem, in which case
                addr->message contains a message, and addr->basic_errno has
                ERRNO_BADEXTRANSPORT set in it.
*/

BOOL
route_get_transport(transport_instance *tp, char *expand_tp, address_item *addr,
  transport_instance **tpptr, char *driver_name)
{
char *ss;

if (expand_tp == NULL)
  {
  *tpptr = tp;   /* Use fixed transport, or NULL */
  return TRUE;
  }

ss = expand_string(expand_tp);
if (ss == NULL)
  {
  addr->basic_errno = ERRNO_BADEXTRANSPORT;
  addr->message = string_sprintf("failed to expand dynamic transport "
    "\"%s\" in %s driver: %s", expand_tp, driver_name, expand_string_message);
  return FALSE;
  }

for (tp = transports; tp != NULL; tp = tp->next)
  {
  if (strcmp(tp->name, ss) == 0)
    {
    DEBUG(9) debug_printf("dynamic transport \"%s\" set\n", ss);
    *tpptr = tp;
    return TRUE;
    }
  }

addr->basic_errno = ERRNO_BADEXTRANSPORT;
addr->message = string_sprintf("dynamic transport \"%s\" not found "
  "in %s driver", ss, driver_name);
return FALSE;
}



/*************************************************
*             Check for driver skipping          *
*************************************************/

/* This function performs various checks to see whether a driver should be
skipped. Many of the checks are identical for both routers and directors. The
relevant data is arranged to be in the same place in the options blocks for
both types of driver so that this function can be used for both types of
driver for the common cases. Hence it is globally accessible.

Arguments:
  dr           pointer to router or director instance block
  local_part   local part of the address being handled
  domain       ditto domain
  verify       the verification type
  pmore        address of the "more" flag
  driver_type  either "router" or "director"

Returns:       TRUE if this driver is to be skipped
*/


BOOL
route_skip_driver(router_instance *dr, char *local_part, char *domain,
  int verify, BOOL *pmore, char *driver_type)
{
char *domains = expand_string_panic(dr->domains, dr->name, driver_type);
char *except_domains = expand_string_panic(dr->except_domains, dr->name,
  driver_type);
char *local_parts = expand_string_panic(dr->local_parts, dr->name,
  driver_type);
char *except_local_parts = expand_string_panic(dr->except_local_parts,
  dr->name, driver_type);

/* Variables to hold data from lookup of a domain or local part. */

domain_data = NULL;
local_part_data = NULL;

/* Skip this driver if not verifying and it has verify_only set */

if ((verify == v_none || verify == v_expn) && dr->verify_only)
  {
  DEBUG(9) debug_printf("%s %s skipped: verify_only set\n",
    dr->name, driver_type);
  return TRUE;
  }

/* Skip this driver if domain mismatch */

if (domains != NULL &&
     !match_isinlist_get(domain, domains,
       (domains == dr->domains)? &(dr->re_domains) : NULL,
       TRUE, &domain_data))
  {
  DEBUG(9) debug_printf("%s %s skipped: domain mismatch\n",
    dr->name, driver_type);
  return TRUE;
  }

if (except_domains != NULL &&
     match_isinlist(domain, except_domains,
       (except_domains == dr->except_domains)? &(dr->re_except_domains) : NULL,
       TRUE))
  {
  DEBUG(9) debug_printf("%s %s skipped: except domain mismatch\n",
    dr->name, driver_type);
  return TRUE;
  }

/* Skip this driver if local part mismatch */

if (local_parts != NULL &&
     !match_isinlist_get(local_part, local_parts,
       (local_parts == dr->local_parts)? &(dr->re_local_parts) : NULL,
       FALSE, &local_part_data))
  {
  DEBUG(9) debug_printf("%s %s skipped: local part mismatch\n",
    dr->name, driver_type);
  return TRUE;
  }

if (except_local_parts != NULL &&
     match_isinlist(local_part, except_local_parts,
       (except_local_parts == dr->except_local_parts)?
         &(dr->re_except_local_parts) : NULL,
       FALSE))
  {
  DEBUG(9) debug_printf("%s %s skipped: except local part mismatch\n",
    dr->name, driver_type);
  return TRUE;
  }

/* If this driver's "more" flag is FALSE, arrange that no subsequent
drivers are called. */

*pmore = dr->more;
DEBUG(9)
  {
  if (!dr->more) debug_printf("%s %s has more set FALSE\n", dr->name,
    driver_type);
  }

/* Skip this driver if verifying and it hasn't got the appropriate verify flag
set. */

if ((verify == v_sender && !dr->verify_sender) ||
    (verify == v_recipient && !dr->verify_recipient))
  {
  DEBUG(9) debug_printf("%s %s skipped: verify %d %d %d\n",
    dr->name, driver_type, verify, dr->verify_sender, dr->verify_recipient);
  return TRUE;
  }

/* Nothing in this function calls for skipping the driver; specific code for
routers and directors may yet do so later. */

return FALSE;
}



/*************************************************
*            Handle an unseen routing            *
*************************************************/

/* This function is called when an address is routed by a router with "unseen"
set, or directed by a director with "unseen" set. It must make a clone of the
address, for handling by subsequent drivers. The clone will fall through to the
next router or director, by the anti-looping rules. The original address must
be replaced by an invented "parent" which has the routed/directed addres plus
the clone as its children. This is necessary in case the address is at the top
level - we don't want to mark it complete until both deliveries have been done.

A new unique field must be made, so that the record of the delivery isn't a
record of the original address, and checking for already delivered has
therefore to be done here. If the delivery has happened, then take the base
address off whichever delivery queue it is on - it will always be the top item.

Arguments:
  name         router or director name
  addr         address that was routed/directed
  addr_local   chain of local-delivery addresses
  addr_remote  chain of remote-delivery addresses
  addr_new     chain for newly created addresses

Returns:       nothing
*/

void
route_unseen(char *name, address_item *addr, address_item **addr_local,
  address_item **addr_remote, address_item **addr_new)
{
address_item *parent = deliver_make_addr(addr->orig, TRUE);
address_item *new = deliver_make_addr(addr->orig, TRUE);

/* The invented parent is a copy that replaces the original; note that
this copies its parent pointer. */

*parent = *addr;

/* The routed/directed address gets a new parent. */

addr->parent = parent;
parent->child_count++;

/* Set up the cloned address and put it on the new address queue */

new->parent = parent;
new->ignore_error |= addr->ignore_error;
new->errors_address = addr->errors_address;
new->next = *addr_new;
*addr_new = new;

DEBUG(2) debug_printf("\"unseen\" set: replicated %s\n", addr->orig);

/* Make a new unique field, to distinguish from the normal one,
and check for pre-delivery of this unseen thing. */

addr->unique = string_sprintf("%s/%s", addr->unique, name);

if (tree_search_addr(tree_nonrecipients, addr->unique, FALSE) != NULL)
  {
  DEBUG(2)
    debug_printf("\"unseen\" delivery previously done - discarded\n");
  if (*addr_remote == addr) *addr_remote = addr->next;
  if (*addr_local == addr) *addr_local = addr->next;
  }

/* If it wasn't previously delivered, and is indeed on a delivery queue,
increment the new parent's child count. */

else if (*addr_remote == addr || *addr_local == addr) parent->child_count++;
}



/*************************************************
*                 Route one address              *
*************************************************/

/* This function is passed in one address item, for processing by the
routers. It has been determined that the address is (apparently) not for one of
the local domains. The action can be one of:

  . add the address to the chain on addr_remote for remote delivery and
      return OK;
  . return FAIL;
  . return DEFER;
  . return ISLOCAL - this means that the address turned out to be local
      after all - usually as the result of DNS expansion to fully qualified.
  . return PASS - meaning the router did something to the address, and wants
      it passed to the next router.

Although FAIL and PASS are conceptually different, in fact they have the same
effect, except for debugging output.

The verify flag is set if this is being called for verification rather than
delivery. If the router doesn't have its "verify" flag set, it is skipped.

Arguments:
  addr         address to route
  addr_local   chain of local-delivery addresses
  addr_remote  chain of remote-delivery addresses
  addr_new     chain for newly created addresses
  verify       v_none if not verifying
               v_sender if verifying a sender address
               v_recipient if verifying a recipient address

Returns:       OK, FAIL, DEFER, ISLOCAL or PASS (see above)
*/

int
route_address(address_item *addr, address_item **addr_local,
  address_item **addr_remote, address_item **addr_new, int verify)
{
BOOL more = TRUE;
router_instance *r;

DEBUG(9) debug_printf(">>>>>>>>>>>>>>>>>>>>>>>>\n"
  "routing %s, domain %s\n", addr->orig, addr->domain);

/* Set the domain used by the routers; a router is permitted to change this and
yield PASS, causing subsequent routers to use the new name, without changing
addr->domain, which is the envelope address. Equally, a router is also
permitted to change the envelope address. */

addr->route_domain = addr->domain;

/* Loop through all router instances. */

for (r = routers; more && r != NULL; r = r->next)
  {
  char *old_route_domain = addr->route_domain;
  char *senders, *except_senders;
  address_item *parent;
  BOOL loop_detected = FALSE;
  int yield = FALSE;
  int rc;

  /* Check the conditions that are common to both routers and directors. We
  need the expansions set in case anything therein is expanded. */

  deliver_set_expansions(addr);
  if (route_skip_driver(r, addr->local_part, addr->route_domain,
    verify, &more, "router")) continue;

  /* Loop protection: If this address has a parent with the same address that
  was routed by this router, we skip this router. This prevents a looping
  states when a new address is created by the use of "unseen" on a router.
  That is (at present) the only time a router can generate another address
  and hence make an address a parent; however, forwarding of local addresses
  can of course cause there to be parents at this point even without the
  use of "unseen". */

  for (parent = addr->parent; parent != NULL; parent = parent->parent)
    {
    if (strcmpic(parent->local_part, addr->local_part) == 0 &&
        strcmpic(parent->domain, addr->domain) == 0 &&
        parent->router == r)
      {
      DEBUG(9) debug_printf("%s router skipped: previously routed %s@%s\n",
        r->name, addr->local_part, addr->domain);
      loop_detected = TRUE;
      break;
      }
    }
  if (loop_detected) continue;

  /* Before calling the router, output any debugging string. Also, unset the
  rewrite flag; routers must explicitly request rewriting. */

  debug_print_string(r->debug_string);
  addr->rewrite_headers = FALSE;

  /* Do file existence tests - must do after setting expansion values
  as the string is expanded - and if they succeed, do any necessary sender
  checks, and run the router. Otherwise set an appropriate yield value. */

  switch(match_exists(r->require_files))
    {
    case DEFER:
    addr->message = string_sprintf("file existence defer in %s router: %s",
      r->name, strerror(errno));
    yield = DEFER;
    break;

    case FAIL:
    DEBUG(9) debug_printf("%s router: file existence failure\n", r->name);
    yield = FAIL;
    break;

    case OK:
    senders = expand_string_panic(r->senders, r->name, "router");
    except_senders = expand_string_panic(r->except_senders, r->name, "router");
    if ((except_senders != NULL &&
         (rc = match_sender(except_senders,
          (except_senders == r->except_senders)?
            &(r->re_except_senders) : NULL)) == OK)
        ||
        (senders != NULL &&
         (rc = match_sender(senders,
           (senders == r->senders)? &(r->re_senders) : NULL)) != OK))
      {
      if (rc == DEFER)
        {
        addr->message = string_sprintf("sender check defer in %s router",
          r->name);
        yield = DEFER;
        }
      DEBUG(9) debug_printf("%s router: sender match failure\n",
        r->name);
      yield = FAIL;
      }
    else if (r->condition != NULL &&
          !expand_check_condition(r->condition, r->name, "router"))
      {
      DEBUG(9) debug_printf("%s router: condition failure; more set TRUE\n",
        r->name);
      yield = FAIL;
      more = TRUE;      /* Condition tests are logically before "more" */
      }
    else
      {
      addr->local_host_removed = FALSE;
      yield = (r->info->code)(r, addr, addr_local, addr_remote,
        verify != v_none);
      }
    break;
    }

  deliver_set_expansions(NULL);

  /* 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))
    {
    addr->message = string_sprintf("%s router forced verify failure",
      r->name);
    if (*addr_remote == addr) *addr_remote = addr->next;
    if (*addr_local == addr) *addr_local = addr->next;
    break;
    }

  /* Router modified the address; loop for next router. */

  if (yield == PASS)
    {
    DEBUG(2) debug_printf("%s router passed, rewriting %s as %s\n", r->name,
      old_route_domain, addr->route_domain);
    continue;
    }

  /* Failed to handle this address; loop for next router. */

  if (yield == FAIL)
    {
    DEBUG(2) debug_printf("%s router failed\n", r->name);
    continue;
    }

  /* Router wants this address to be failed; do not loop for next router. */

  if (yield == FORCEFAIL)
    {
    DEBUG(2) debug_printf("%s router forced address failure\n", r->name);
    break;
    }

  /* Deferral means we are finished with this address, as does an internal
  or configuration failure. */

  if (yield == DEFER || yield == ERROR)
    {
    DEBUG(2)
      {
      if (yield == DEFER)
        {
        debug_printf("%s router deferred %s\n", r->name, addr->route_domain);
        debug_printf("  message: %s\n", (addr->message == NULL)?
          "<none>" : addr->message);
        }
      else
        {
        debug_printf("%s router: error for %s\n", r->name, addr->route_domain);
        debug_printf("  message: %s\n", (addr->message == NULL)?
          "<none>" : addr->message);
        }
      }

    addr->router = r;
    return yield;
    }


  /* The yield is either OK or ISLOCAL. In both cases we rewrite envelope and
  headers if requested before finishing with this address. The rewriting takes
  its data from what the router did to the route_address, which may no longer
  be the same as the envelope address (in which case the rewriting probably
  isn't relevant there or in the headers, but the code does support it). */

  if (addr->rewrite_headers &&
      strcmp(old_route_domain, addr->route_domain) != 0)
    {
    DEBUG(9) debug_printf("rewriting after %s router\n", r->name);

    /* First the domain in the envelope, if it was the same as the old routing
    domain. */

    if (strcmp(addr->domain, old_route_domain) == 0)
      addr->domain = addr->route_domain;

    /* Now the headers, except when verifying, when there aren't any! The
    first header is always "Received:" so we can skip it. There may not
    always be headers, however. In "-bt" mode for instance, there won't be
    any. When a header gets rewritten, set the header_changed flag to force the
    spool header file to be rewritten. */

    if (verify == v_none && header_list != NULL)
      {
      header_line *h;
      for (h = header_list->next; h != NULL; h = h->next)
        {
        header_line *newh =
          rewrite_header(h, old_route_domain, addr->route_domain);
        if (newh != NULL)
          {
          h = newh;
          header_changed = TRUE;
          }
        }
      }
    }


  /* If the yield was ISLOCAL, an apparently remote address was really that
  of a local domain (e.g. foo got looked up in the DNS and turned into
  foo.co.uk, which is one of the local domains). Alternatively, a remote
  domain had its name changed as a result of routing (to another non-local
  domain), but its lowest numbered MX record points to this host. Again,
  this could be foo turning into foo.co.uk.

  We return ISLOCAL so that the expanded address can be re-processed by the
  directors or routers, as appropriate. If rewrite_headers is not set we
  won't have processed the domain above, so do it here. This doesn't happen
  for domain expansion, but it can happen when MX->self causes re-routing. */

  if (yield == ISLOCAL)
    {
    DEBUG(2) debug_printf("%s router found %s to be local, or locally MX'd\n",
      r->name, old_route_domain);
    if (!addr->rewrite_headers && strcmp(addr->domain, old_route_domain) == 0)
      addr->domain = addr->route_domain;
    return ISLOCAL;
    }


  /* The only remaining possibility is that the router succeeded. */

  DEBUG(2)
    {
    debug_printf("routed by %s router%s:\n  deliver to ", r->name,
      (r->unseen)? " (unseen)" : "");

    if (addr->local_part[0] == ',' || addr->local_part[0] == ':')
      debug_printf("@%s%s\n", addr->domain, addr->local_part);
    else
      debug_printf("%s@%s\n", addr->local_part, addr->domain);

    debug_printf("  transport: %s\n", (addr->transport == NULL)?
      "<none>" : addr->transport->name);

    if (addr->errors_address != NULL)
      debug_printf("  errors to %s\n", addr->errors_address);

    if (addr->host_list != NULL)
      {
      host_item *h;
      for (h = addr->host_list; h != NULL; h = h->next)
        {
        debug_printf("  host %s ", h->name);
        if (h->address != NULL)
          debug_printf("[%s] ", h->address);
        if (h->mx >= 0) debug_printf("MX=%d", h->mx);
        debug_printf("\n");
        }
      }
    }

  /* Record which router did it, and return success, having cleared any
  temporary error message set by a router that failed, and handled the
  "unseen" option. */

  addr->router = r;
  addr->message = NULL;
  if (r->unseen)
    route_unseen(r->name, addr, addr_local, addr_remote, addr_new);
  return OK;
  }

/* No routers accepted this address; fail it, with a default message if nothing
is set. */

if (addr->message == NULL)
  addr->message = string_sprintf("unrouteable mail domain \"%s\"",
    addr->route_domain);

deliver_set_expansions(NULL);    /* In case final one was skipped */
return FAIL;
}

/* End of route.c */
