/*************************************************
*     Exim - an Internet mail transport agent    *
*************************************************/

/* Copyright (c) University of Cambridge 1995 - 1999 */
/* See the file NOTICE for conditions of use and distribution. */

#include "../exim.h"
#include "smtp.h"

#define PENDING          256
#define PENDING_DEFER   (PENDING + DEFER)
#define PENDING_OK      (PENDING + OK)


/* Options specific to the smtp transport. They must be in alphabetic
order (note that "_" comes before the lower case letters). Some live in the
transport_instance block so as to be publicly visible; these are flagged with
opt_public. */

optionlist smtp_transport_options[] = {
  { "allow_localhost",      opt_bool,
      (void *)(offsetof(smtp_transport_options_block, allow_localhost)) },
  { "batch_max",            opt_int,
      (void *)(offsetof(smtp_transport_options_block, batch_max)) },
  { "command_timeout",      opt_time,
      (void *)(offsetof(smtp_transport_options_block, command_timeout)) },
  { "connect_timeout",      opt_time,
      (void *)(offsetof(smtp_transport_options_block, connect_timeout)) },
  { "data_timeout",         opt_time,
      (void *)(offsetof(smtp_transport_options_block, data_timeout)) },
  { "delay_after_cutoff", opt_bool,
      (void *)(offsetof(smtp_transport_options_block, delay_after_cutoff)) },
  { "dns_qualify_single",   opt_bool,
      (void *)(offsetof(smtp_transport_options_block, dns_qualify_single)) },
  { "dns_search_parents",   opt_bool,
      (void *)(offsetof(smtp_transport_options_block, dns_search_parents)) },
  { "fallback_hosts",       opt_stringptr,
      (void *)(offsetof(smtp_transport_options_block, fallback_hosts)) },
  { "final_timeout",        opt_time,
      (void *)(offsetof(smtp_transport_options_block, final_timeout)) },
  { "gethostbyname",        opt_bool,
      (void *)(offsetof(smtp_transport_options_block, gethostbyname)) },
  { "hosts",                opt_stringptr,
      (void *)(offsetof(smtp_transport_options_block, hosts)) },
  { "hosts_override",       opt_bool,
      (void *)(offsetof(smtp_transport_options_block, hosts_override)) },
  { "interface",            opt_stringptr,
      (void *)(offsetof(smtp_transport_options_block, interface)) },
  { "keepalive",            opt_bool,
      (void *)(offsetof(smtp_transport_options_block, keepalive)) },
  { "max_rcpt",             opt_int,
      (void *)(offsetof(smtp_transport_options_block, max_rcpt)) },
/***
  { "max_rcpt_serialize",   opt_bool,
      (void *)(offsetof(smtp_transport_options_block, max_rcpt_serialize)) },
***/

  { "multi_domain",         opt_bool | opt_public,
      (void *)(offsetof(transport_instance, multi_domain)) },
  { "mx_domains",           opt_stringptr,
      (void *)(offsetof(smtp_transport_options_block, mx_domains)) },
  { "mx_domains_except",    opt_stringptr,
      (void *)(offsetof(smtp_transport_options_block, mx_domains_except)) },
  { "non_mx_domains",       opt_stringptr|opt_hidden,
      (void *)(offsetof(smtp_transport_options_block, mx_domains_except)) },
  { "retry_include_ip_address", opt_bool,
      (void *)(offsetof(smtp_transport_options_block, retry_include_ip_address)) },
  { "serialize_hosts",      opt_stringptr,
      (void *)(offsetof(smtp_transport_options_block, serialize_hosts)) },
  { "serialize_nets",       opt_stringptr,
      (void *)(offsetof(smtp_transport_options_block, serialize_nets)) },
  { "service",              opt_stringptr,
      (void *)(offsetof(smtp_transport_options_block, service)) },
  { "size_addition",        opt_int,
      (void *)(offsetof(smtp_transport_options_block, size_addition)) }
};

/* Size of the options list. An extern variable has to be used so that its
address can appear in the tables drtables.c. */

int smtp_transport_options_count =
  sizeof(smtp_transport_options)/sizeof(optionlist);

/* Default private options block for the smtp transport. */

smtp_transport_options_block smtp_transport_option_defaults = {
  NULL,            /* hosts */
  NULL,            /* fallback_hosts */
  NULL,            /* hostlist */
  NULL,            /* fallback_hostlist */
  NULL,            /* interface */
  "smtp",          /* service */
  NULL,            /* mx_domains */
  NULL,            /* mx_domains_except */
  NULL,            /* re_mx_domains */
  NULL,            /* re_mx_domains_except */
  NULL,            /* serialize_hosts */
  NULL,            /* re_serialize_hosts */
  NULL,            /* serialize_nets */
  NULL,            /* serialize_netlist */
  0,               /* batch_max */
  5*60,            /* command_timeout */
  0,               /* connect_timeout; 0 => system default */
  5*60,            /* data timeout */
  10*60,           /* final timeout */
  1024,            /* size_addition */
  0,               /* max_rcpt */
  FALSE,           /* allow_localhost */
  FALSE,           /* gethostbyname */
  TRUE,            /* dns_qualify_single */
  FALSE,           /* dns_search_parents */
  TRUE,            /* delay_after_cutoff */
  FALSE,           /* hosts_override */
  TRUE,            /* keepalive */
  FALSE,           /* max_rcpt_serialize */
  TRUE             /* retry_include_ip_address */
};


/* Local statics */

static int   deliver_socket;
static int   send_rc;
static char *smtp_command;

#ifdef SUPPORT_DSN
static int   rf_list[] = { rf_notify_never, rf_notify_success,
  rf_notify_failure, rf_notify_delay };

static char *rf_names[] = { "NEVER", "SUCCESS", "FAILURE", "DELAY" };
#endif


/*************************************************
*             Setup entry point                  *
*************************************************/

/* This function is called when the transport is about to be used,
but before running it in a sub-process. This enables setup work to
be done that will be remembered and used in all subprocesses. We use
it to handle the net serialization lists and fallback hosts.

Arguments:
  tblock    pointer to the transport instance block
  addrlist  list of addresses about to be transported
  errmsg    place for error message

Returns:  OK always (FAIL, DEFER not used)
*/

static int
smtp_transport_setup(transport_instance *tblock, address_item *addrlist,
  char **errmsg)
{
smtp_transport_options_block *ob =
  (smtp_transport_options_block *)(tblock->options_block);

errmsg = errmsg;    /* Keep picky compilers happy */

if (ob->serialize_nets != NULL && ob->serialize_netlist == NULL)
  verify_setup_netlist(ob->serialize_nets, &(ob->serialize_netlist));

/* Set the fallback host list for all the addresses that don't have fallback
host lists, provided that the local host wasn't present in the original host
list. */

if (!addrlist->local_host_removed)
  {
  for (; addrlist != NULL; addrlist = addrlist->next)
    if (addrlist->fallback_hosts == NULL)
      addrlist->fallback_hosts = ob->fallback_hostlist;
  }

return OK;
}



/*************************************************
*          Initialization entry point            *
*************************************************/

/* Called for each instance, after its options have been read, to
enable consistency checks to be done, or anything else that needs
to be set up.

Argument:   pointer to the transport instance block
Returns:    nothing
*/

void
smtp_transport_init(transport_instance *tblock)
{
smtp_transport_options_block *ob =
  (smtp_transport_options_block *)(tblock->options_block);
char *s;
char *listptr;
char buffer[1024];

/* Set up the setup entry point, to be called before subprocesses for this
transport. */

tblock->setup = smtp_transport_setup;

/*** This bit of code isn't really right and consequently is excluded. The
interactions between an external address selection and an internal selection
need to be worked out properly some day. Meanwhile, just set the external max
addresses to the internal RCPT limit. ***/

tblock->max_addresses = ob->max_rcpt;

#ifdef NEVER
/* If max_rcpt is set and max_rcpt_serialize is not set, copy the value into
the max_addresses field in the public transport block so that we never get
more than that many addresses at once and others can be sent in parallel.
Otherwise, if batch_max is set, put an outer limit on that will ....
NEEDS MORE THOUGHT. */

if (ob->max_rcpt > 0)
  {
  if (!ob->max_rcpt_serialize)
    tblock->max_addresses = ob->max_rcpt;
  else if (ob->batch_max > 0)
    tblock->max_addresses = ob->max_rcpt * ob->batch_max;
  }
#endif


/* Complain if any of the timeouts are zero. */

if (ob->command_timeout <= 0 || ob->data_timeout <= 0 ||
    ob->final_timeout <= 0)
  log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
    "command, data, or final timeout value is zero for %s transport",
      tblock->name);

/* If hosts_override is set and there are local hosts, set the global
flag that stops verify from showing router hosts. */

if (ob->hosts_override && ob->hosts != NULL) tblock->overrides_hosts = TRUE;

/* If there are any fallback hosts listed, build a chain of host items
for them, but do not do any lookups at this time. */

host_build_hostlist(&(ob->fallback_hostlist), ob->fallback_hosts);

/* If serialize_nets is set, check their syntax; an item starting with
a slash is a file name, whose contents can't be checked here. */

listptr = ob->serialize_nets;
for (s = string_nextinlist(&listptr, ':', buffer, sizeof(buffer));
     s != NULL;
     s = string_nextinlist(&listptr, ':', buffer, sizeof(buffer)))
  {
  char *error;
  if (*s != '/' && !string_is_ip_network(s, &error))
    log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
      "error in IP net specification %s for %s transport: %s", s,
        tblock->name, error);
  }

/* If an interface is set, check that it is a valid IP address. */

if (ob->interface != NULL && string_is_ip_address(ob->interface) == 0)
  log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
    "\"%s\" is not a valid IP address for the \"interface\" option of the "
    "%s transport", ob->interface, tblock->name);
}



/*************************************************
*              Read SMTP response                *
*************************************************/

/* This function reads an SMTP response with a timeout, and returns the
response in the given buffer. It also analyzes the first digit of the reply
code and returns FALSE if it is not acceptable. The following is an abstract of
the theory of reply codes from RFC 821:

  1yz   Positive Preliminary reply
  2yz   Positive Completion reply
  3yz   Positive Intermediate reply
  4yz   Transient Negative Completion reply
  5yz   Permanent Negative Completion reply

For all responses:
  1yz => error (no SMTP commands expect this reply)
  4yz => fail, but try again
  5yz => fail, but don't try again

For all except DATA (including after final '.'):
  2yz => success
  3yz => error

For DATA:
  2yz => error
  3yz => send data

A zero is added after any data that is read, to make it a valid C string. FALSE
is also returned after a reading error. In this case buffer[0] will be zero,
and the error code will be in errno.

Arguments:
  buffer    where to put the response
  size      the size of the buffer
  datacmd   TRUE if this call follows the DATA command
  dotcmt    TRUE if this call follows the "." command
  ob        points to the transport's options block

Returns:    TRUE if a valid, non-error response was received; else FALSE
*/

static BOOL
read_response(char *buffer, int size, BOOL datacmd, BOOL dotcmd,
  smtp_transport_options_block *ob)
{
int count, rc;
fd_set select_inset;
struct timeval tv;
char *ptr = buffer;
char *readptr = buffer;

/* If send_rc is negative, writing the command that precedes this
read failed; give a failure return; check_response() picks this up. */

if (send_rc < 0) return FALSE;

/* Ensure errno starts out zero */

errno = 0;

/* Loop for handling SMTP responses that do not all come in one packet
(multiline or otherwise). Each call to recv is timed by means of the
timeout in the select() function. This works on all OS - and is more
efficient that the use of signal() and alarm(). */

for (;;)
  {
  DEBUG(9) debug_printf("SMTP response timeout = %d\n",
    dotcmd? ob->final_timeout : ob->command_timeout);

  /* If buffer is too full, something has gone wrong. */

  if (size < 10)
    {
    *readptr = 0;
    errno = ERRNO_SMTPFORMAT;
    return FALSE;
    }

  /* Loop to cover select() getting interrupted, and the possibility of
  select() returning with a positive result but no ready descriptor. Is
  this in fact possible? */

  for (;;)
    {
    FD_ZERO (&select_inset);
    FD_SET (deliver_socket, &select_inset);
    tv.tv_sec = (dotcmd? ob->final_timeout : ob->command_timeout);
    tv.tv_usec = 0;
    rc = select(deliver_socket + 1, (SELECT_ARG2_TYPE *)&select_inset,
      NULL, NULL, &tv);

    /* If some interrupt arrived, just retry. We presume this to be rare,
    but it can happen (e.g. the SIGUSR1 signal sent by exiwhat causes
    select() to exit). */

    if (rc < 0 && errno == EINTR)
      {
      DEBUG(9) debug_printf("EINTR while selecting for SMTP response\n");
      continue;
      }

    /* Handle a timeout, and treat any other error as a timeout */

    if (rc <= 0)
      {
      errno = ETIMEDOUT;
      buffer[0] = 0;
      return FALSE;
      }

    /* If the socket is ready, initialize empty buffer in case nothing gets
    read, then read the response and break out of this select retry loop. */

    if (FD_ISSET(deliver_socket, &select_inset))
      {
      *readptr = 0;
      count = recv(deliver_socket, readptr, size-1, 0);
      break;
      }
    }

  /* Handle an EOF (i.e. close down of the connection). */

  if (count == 0)
    {
    buffer[0] = 0;
    return FALSE;
    }

  /* Any other error in reading returns FALSE, leaving errno unchanged. */

  else if (count < 0)
    {
    buffer[0] = 0;
    return FALSE;
    }

  /* Adjust size in case we have to read another packet, and adjust the
  count to be the length of the line we are about to inspect. */

  size -= count;
  count += readptr - ptr;

  /* See if the final two characters in the buffer are \r\n. If not, we
  have to read another packet. At least, that is what we should do on a strict
  interpretation of the RFC. In practice, it seems that there are sites sending
  only LF at the ends of responses and other MTAs cope with this. So we have to
  follow the crowd. */

  /*** if (count < 2 || ptr[count-1] != '\n' || ptr[count-2] != '\r') ***/

  if (ptr[count-1] != '\n')
    {
    DEBUG(9)
      {
      int i;
      debug_printf("SMTP input line incomplete in one buffer:\n  ");
      for (i = 0; i < count; i++)
        {
        int c = (uschar)(ptr[i]);
        if (mac_isprint(c)) debug_printf("%c", c); else debug_printf("<%d>", c);
        }
      debug_printf("\n");
      }
    readptr = ptr + count;
    continue;
    }

  /* Ensure the buffer contains a C string, and remove any whitespace at the
  end of it. This gets rid of CR, LF etc. at the end. Show it, if debugging,
  formatting multi-line responses. */

  while (count > 0 && isspace(ptr[count-1])) count--;
  ptr[count] = 0;

  DEBUG(1)
    {
    char *s = ptr;
    char *t = ptr;

    while (*t != 0)
      {
      while (*t != 0 && *t != '\n') t++;
      debug_printf("  %s %*s\n", (s == ptr)? "SMTP<<" : "      ", t-s, s);
      if (*t == 0) break;
      s = t = t + 1;
      }
    }

  /* Check the format of the response: it must start with three digits; if
  these are followed by a space or end of line, the response is complete. If
  they are followed by '-' this is a multi-line response and we must look for
  another line until the final line is reached. The only use made of multi-line
  responses is to pass them back as error messages. We therefore just
  concatenate them all within the buffer, which should be large enough to
  accept any reasonable number of lines. A multiline response may already
  have been read in one packet - hence the loop here. */

  for(;;)
    {
    char *p;
    if (count < 3 ||
       !isdigit(ptr[0]) || !isdigit(ptr[1]) || !isdigit(ptr[2]) ||
       (ptr[3] != '-' && ptr[3] != ' ' && ptr[3] != 0))
      {
      errno = ERRNO_SMTPFORMAT;    /* format error */
      return FALSE;
      }

    /* If a single-line response, exit the loop */

    if (ptr[3] != '-') break;

    /* For a multi-line response see if the next line is already read, and if
    so, stay in this loop to check it. */

    p = ptr + 3;
    while (*(++p) != 0)
      {
      if (*p == '\n')
        {
        ptr = ++p;
        break;
        }
      }
    if (*p == 0) break;   /* No more lines to check */
    }

  /* End of response. If the last of the lines we are looking at is the final
  line, we are done. Otherwise more data has to be read. */

  if (ptr[3] != '-') break;

  /* Move the reading pointer upwards in the buffer and insert \n in case this
  is an error message that subsequently gets printed. Set the scanning pointer
  to the reading pointer position. */

  ptr += count;
  *ptr++ = '\n';
  size--;
  readptr = ptr;
  }

/*Return a value that depends on the SMTP return code. */

return (strchr("145", buffer[0]) == NULL) &&
  ((datacmd && buffer[0] == '3') || (!datacmd && buffer[0] == '2'));
}




/*************************************************
*             Write SMTP command                 *
*************************************************/

/* After writing the command, point smtp_command at the buffer so that it
can be reflected in any error message. The return from send is interpreted by
the read_response() and check_response() functions.

Arguments:   a format and optional data values; the format starts with one
             of HELO, MAIL FROM, RCPT TO, DATA, ".", or QUIT.
Returns:     nothing
*/

static void
write_command(char *format, ...)
{
int count;
va_list ap;
va_start(ap, format);
if (!string_vformat(big_buffer, BIG_BUFFER_SIZE, format, ap))
  log_write(0, LOG_PANIC_DIE, "overlong write_command in smtp transport");
va_end(ap);
count = (int)strlen(big_buffer);
send_rc = send(deliver_socket, big_buffer, count, 0);
big_buffer[count-2] = 0;     /* remove \r\n for debug and error message */
DEBUG(1) debug_printf("  SMTP>> %s\n", big_buffer);
smtp_command = big_buffer;
}



/*************************************************
*      Tidy SMTP command for error message       *
*************************************************/

/* If smtp_command points to big_buffer, tidy the command therein so that
it looks nicer in an error message.

Arguments:   none
Returns:     smtp_command, tidied if relevant
*/

static char *
tidy_smtp_command(void)
{
if (smtp_command == big_buffer)
  {
  int i;
  int count = (int)strlen(big_buffer);
  for (i = 0; i < 4; i++) big_buffer[i] = toupper(big_buffer[i]);
  if (strncmp(big_buffer, "MAIL", 4) == 0 ||
      strncmp(big_buffer, "RCPT", 4) == 0)
    {
    for (; big_buffer[i] != ':'; i++) big_buffer[i] = toupper(big_buffer[i]);
    memmove(big_buffer+i+2, big_buffer+i+1, count-i);
    big_buffer[i+1] = ' ';
    }
  }
return smtp_command;
}





/*************************************************
*   Set delivery info into all active addresses  *
*************************************************/

/* Only addresses whose status is >= PENDING are relevant. A lesser
status means that an address is not currently being processed.

Arguments:
  addrlist     points to a chain of addresses
  errno_value  to put in each address's errno field
  msg          to put in each address's message field
  rc           to put in each address's transport_return field

Returns:       nothing
*/

static
void set_errno(address_item *addrlist, int errno_value, char *msg, int rc)
{
address_item *addr;
for (addr = addrlist; addr != NULL; addr = addr->next)
  {
  if (addr->transport_return < PENDING) continue;
  addr->basic_errno = errno_value;
  if (msg != NULL) addr->message = msg;
  addr->transport_return = rc;
  }
}



/*************************************************
*          Check an SMTP response                *
*************************************************/

/* This function is given an errno code and the SMTP response buffer
to analyse, together with the host identification for generating messages. It
sets an appropriate message and puts the first digit of the response code into
the yield variable. If no response was actually read, a suitable digit is
chosen.

Arguments:
  host         the current host, to get its name for messages
  errno_value  pointer to the errno value
  more_errno   from the top address for use with ERRNO_FILTER_FAIL
  buffer       the SMTP response buffer
  yield        where to put a one-digit SMTP response code
  message      where to put an errror message

Returns:       TRUE if an SMTP "QUIT" command should be sent, else FALSE
*/

static BOOL check_response(host_item *host, int *errno_value, int more_errno,
  char *buffer, int *yield, char **message)
{
*yield = '4';    /* Default setting is to give a temporary error */

/* Handle failure of sending the command */

if (send_rc < 0)
  {
  *message = string_sprintf("send() to %s [%s] failed",
    host->name, host->address, strerror(*errno_value));
  return FALSE;
  }

/* Handle response timeout */

if (*errno_value == ETIMEDOUT)
  {
  *message = string_sprintf("SMTP timeout while connected to %s [%s] "
    "after %s", host->name, host->address, tidy_smtp_command());
  if (transport_count > 0)
    *message = string_sprintf("%s (%d bytes written)", *message,
      transport_count);
  return FALSE;
  }

/* Handle malformed SMTP response */

if (*errno_value == ERRNO_SMTPFORMAT)
  {
  *message = string_sprintf("Malformed SMTP response from %s [%s] after %s: %s",
    host->name, host->address, tidy_smtp_command(), string_printing(buffer));
  return FALSE;
  }

/* Handle a failed filter process error; can't send QUIT as we mustn't
end the DATA. */

if (*errno_value == ERRNO_FILTER_FAIL)
  {
  *message = string_sprintf("delivery filter process failed (%d)", more_errno);
  return FALSE;
  }

/* Handle a failed add_headers expansion; can't send QUIT as we mustn't
end the DATA. */

if (*errno_value == ERRNO_ADDHEADER_FAIL)
  {
  *message = string_sprintf("failed to expand added headers: %s",
    expand_string_message);
  return FALSE;
  }

/* Handle failure to write a complete data block */

if (*errno_value == ERRNO_WRITEINCOMPLETE)
  {
  *message = string_sprintf("failed to write a data block");
  return FALSE;
  }

/* Handle error responses from the remote mailer. */

if (buffer[0] != 0)
  {
  char *s = string_printing(buffer);
  *message = string_sprintf("SMTP error from remote mailer after %s: "
    "host %s [%s]: %s", tidy_smtp_command(), host->name, host->address, s);
  *yield = buffer[0];
  return TRUE;
  }

/* No data was read. If there is no errno, this must be the EOF (i.e.
connection closed) case, which causes deferral. Otherwise, put the host's
identity in the message, leaving the errno value to be interpreted as well. In
all cases, we have to assume the connection is now dead. */

if (*errno_value == 0)
  {
  *errno_value = ERRNO_SMTPCLOSED;
  *message = string_sprintf("Remote host %s [%s] closed connection after %s",
    host->name, host->address, tidy_smtp_command());
  }
else *message = string_sprintf("%s [%s]", host->name, host->address);

return FALSE;
}



#ifdef WAIT_FOR_QUIT
/* This code is not currently in use; see comments at the point it is called
below. */

/*************************************************
*          Check the final SMTP response         *
*************************************************/

/* This function is called only after sending QUIT. There isn't anything that
can be done after errors here, but we log the incident.

Arguments:
  host      points to the current host
  buffer    the buffer read after QUIT

Returns:    nothing
*/

static void
check_final_response(host_item *host, char *buffer)
{
if (errno == ETIMEDOUT)
  log_write(4, LOG_MAIN, "SMTP timeout while connected to %s [%s] "
    "after %s (%d bytes written)", host->name, host->address,
    tidy_smtp_command(), transport_count);

/* Handle malformed SMTP response */

else if (errno == ERRNO_SMTPFORMAT)
  log_write(0, LOG_MAIN, "Malformed SMTP response from %s [%s] after %s: %s",
    host->name, host->address, tidy_smtp_command(), buffer);

/* Handle non-timeout errors. */

else if (buffer[0] != 0)
  {
  char *s = string_printing(buffer);
  log_write(0, LOG_MAIN, "SMTP error from %s [%s] after %s: %s", host->name,
    host->address, tidy_smtp_command(), s);
  }

/* No data was read. If there is no errno, this must be the EOF (i.e.
connection closed) case. */

else if (errno == 0)
  log_write(4, LOG_MAIN, "host %s [%s] closed connection after %s", host->name,
    host->address, tidy_smtp_command());
}
#endif





/*************************************************
*          Write error message to logs           *
*************************************************/

/* This writes to the main log and to the message log.

Arguments:
  addr     the address item containing error information
  host     the current host

Returns:   nothing
*/

static void
write_logs(address_item *addr, host_item *host)
{
if (addr->message != NULL)
  {
  char *message = addr->message;
  if (addr->basic_errno > 0)
    message = string_sprintf("%s: %s", message, strerror(addr->basic_errno));
  log_write(0, LOG_MAIN, "%s", message);
  fprintf(message_log, "%s %s\n", tod_stamp(tod_log), message);
  }
else
  {
  log_write(0, LOG_MAIN, "%s [%s]: %s",
    host->name,
    host->address,
    strerror(addr->basic_errno));
  fprintf(message_log, "%s %s [%s]: %s\n",
    tod_stamp(tod_log),
    host->name,
    host->address,
    strerror(addr->basic_errno));
  }
fflush(message_log);
}



/*************************************************
*       Deliver address list to given host       *
*************************************************/

/* If continue_hostname is not null, we get here only when continuing to
deliver down an existing channel. The channel was passed as the standard
input.

Otherwise, we have to make a connection to the remote host, and do the
initial protocol exchange.

Arguments:
  addrlist        chain of potential addresses to deliver; only those whose
                  transport_return field is set to PENDING_DEFER are currently
                  being processed; others should be skipped - they have either
                  been delivered to an earlier host or IP address, or been
                  failed by one of them.
  host            host to deliver to
  port            TCP/IP port to use, in network byte order
  tblock          transport instance block
  copy_host       TRUE if host set in addr->transported must be copied, because
                    it is specific to this call of the transport
  keepalive       TRUE if keepalive should be set on the socket
  message_defer   set TRUE if yield is OK, but all addresses were deferred
                    because of a non-recipient, non-host failure, that is, a
                    4xx response to MAIL FROM, DATA, or ".". This is a defer
                    that is specific to the message.

Returns:          OK    - the connection was made and the delivery attempted;
                          the result for each address is in its data block.
                  DEFER - the connection could not be made, or something failed
                          while setting up the SMTP session, or there was a
                          non-message-specific error, such as a timeout.
                  ERROR - a filter command is specified for this transport,
                          and there was a problem setting it up; OR
                          add_headers is specified for this transport, and
                          the string failed to expand
*/

static int
smtp_deliver(address_item *addrlist, host_item *host, int port,
  transport_instance *tblock, BOOL copy_host, BOOL keepalive,
  BOOL *message_defer)
{
address_item *addr;
address_item *first_addr = addrlist;
int yield = OK;
int address_count;
BOOL ok = FALSE;
BOOL send_rset = TRUE;
BOOL send_quit = TRUE;
BOOL setting_up = TRUE;
smtp_transport_options_block *ob =
  (smtp_transport_options_block *)(tblock->options_block);
int max_rcpt = ob->max_rcpt;
char *p;
char new_message_id[MESSAGE_ID_LENGTH + 1];
char buffer[4096];

#if HAVE_IPV6
struct sockaddr_in6 s_in;
#else
struct sockaddr_in s_in;
#endif

*message_defer = FALSE;
smtp_command = "initial connection";
if (max_rcpt == 0) max_rcpt = 999999;

/* Make a connection to the host if this isn't a continued delivery. */

if (continue_hostname == NULL)
  {
  BOOL esmtp;
  int rc, save_errno, host_af;

  /* Create a socket, and connect it to the remote host. IPv6 addresses are
  detected by checking for a colon in the address. A failure to connect
  causes a DEFER error. */

  host_af = (strchr(host->address, ':') != NULL)? AF_INET6 : AF_INET;
  deliver_socket = socket(host_af, SOCK_STREAM, 0);
  if (deliver_socket < 0)
    log_write(0, LOG_PANIC_DIE, "socket creation failed: %s", strerror(errno));

  DEBUG(1) debug_printf("Connecting to %s [%s] ... ", host->name, host->address);

  /* Clear the socket block */

  memset(&s_in, 0, sizeof(s_in));

  /* Bind to a specific interface if requested. On an IPv6 system, this has
  to be of the same family as the address we are calling. */

  if (ob->interface != NULL)
    {
    #if HAVE_IPV6
    if (strchr(ob->interface, ':') != NULL)
      {
      rc = inet_pton(AF_INET6, ob->interface, &s_in.sin6_addr);
      s_in.sin6_family = AF_INET6;
      }
    else
      {
      rc = inet_pton(AF_INET, ob->interface, &s_in.sin6_flowinfo);
      s_in.sin6_family = AF_INET;
      }

    if (rc != 1)
      log_write(0, LOG_PANIC_DIE, "unable to parse \"%s\"", ob->interface);

    if (s_in.sin6_family == host_af)
      {
      s_in.sin6_port = 0;
      rc = bind(deliver_socket, (struct sockaddr *)&s_in, sizeof(s_in));
      }

    /* Handle IPv4 binding */

    #else
    s_in.sin_family = AF_INET;
    s_in.sin_port = 0;
    s_in.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(ob->interface);
    rc = bind(deliver_socket, (struct sockaddr *)&s_in, sizeof(s_in));
    #endif

    /* Check that it bound successfully. */

    if (rc < 0)
      log_write(0, LOG_PANIC_DIE, "unable to bind outgoing SMTP call to %s: %s",
        ob->interface, strerror(errno));
    }

  /* Set up the remote address and port on an IPv6 system. You wouldn't have
  thought it would take much to put the IPv6 address in the same place as the
  IPv4 one, but no... */

  #if HAVE_IPV6
  s_in.sin6_family = host_af;
  s_in.sin6_port = port;

  if (host_af == AF_INET6)
    {
    if (inet_pton(host_af, host->address, &s_in.sin6_addr) != 1)
      log_write(0, LOG_PANIC_DIE, "unable to parse \"%s\"", host->address);
    }
  else
    {
    if (inet_pton(host_af, host->address, &s_in.sin6_flowinfo) != 1)
      log_write(0, LOG_PANIC_DIE, "unable to parse \"%s\"", host->address);
    }

  /* Set up the address and port on an IPv4 system. */

  #else
  s_in.sin_family = AF_INET;
  s_in.sin_port = port;
  s_in.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(host->address);
  #endif

  /* If no connection timeout is set, just call connect() without setting
  a timer, thereby allowing the inbuilt timeout to operate. */

  sigalrm_seen = FALSE;

  if (ob->connect_timeout > 0)
    {
    os_non_restarting_signal(SIGALRM, sigalrm_handler);
    alarm(ob->connect_timeout);
    }

  rc = connect(deliver_socket, (struct sockaddr *)&s_in, sizeof(s_in));
  save_errno = errno;

  if (ob->connect_timeout > 0)
    {
    alarm(0);
    signal(SIGALRM, SIG_IGN);
    }

  /* A failure whose error code is "Interrupted system call" is in fact
  an externally applied timeout if the signal handler has been run. */

  if (rc < 0)
    {
    if (save_errno == EINTR && sigalrm_seen) save_errno = ETIMEDOUT;
    set_errno(addrlist, save_errno, NULL, DEFER);
    close(deliver_socket);
    DEBUG(1) debug_printf("failed\n");
    return DEFER;
    }

  DEBUG(1) debug_printf("connected\n");

  /* Add keepalive to the socket if requested. */

  if (keepalive)
    {
    int fodder = 1;
    if (setsockopt(deliver_socket, SOL_SOCKET, SO_KEEPALIVE,
        (char *)(&fodder), sizeof(fodder)) != 0)
      log_write(4, LOG_MAIN, "send setsockopt(SO_KEEPALIVE) failed on "
	"connection to %s: %s", host->address, strerror(errno));
    }

  /* Now we run the SMTP sending protocol. The first thing is to wait for an
  initial OK response. The dreaded "goto" is nevertheless a reasonably clean way
  of programming this kind of logic, where you want to escape on any error. */

  if (!read_response(buffer, sizeof(buffer), FALSE, FALSE, ob))
    goto RESPONSE_FAILED;

/** Debugging without sending a message
addrlist->transport_return = DEFER;
goto SEND_QUIT;
**/

  /* Tell the remote who we are...

  EHLO processing could be added here, but since at the moment there is no
  need for obtaining the additional information that EHLO provides, don't
  bother with it, thereby avoiding the hassle of implementing all the
  necessary fudges to cope with non-RFC821-conforming mailers.

  February 1998: A convention has evolved that ESMTP-speaking MTAs include the
  string "ESMTP" in their greeting lines, so make Exim send EHLO if the
  greeting is of this form. The assumption was that the far end supports it
  properly... but experience shows that there are some that give 5xx responses,
  even though the banner includes "ESMTP". Cope with that case.

  Exim originally sent "Helo" at this point and ran for nearly a year that way.
  Then somebody tried it with a Microsoft mailer... It seems that all other
  mailers use upper case for some reason (the RFC is quite clear about case
  independence) so, for peace of mind, I gave in. */

  esmtp = strstr(buffer, "ESMTP") != NULL;

  write_command("%s %s\r\n", esmtp? "EHLO" : "HELO", primary_hostname);
  if (!read_response(buffer, sizeof(buffer), FALSE, FALSE, ob))
    {
    if (!esmtp || errno != 0 || buffer[0] == 0 ||
      (write_command("HELO %s\r\n", primary_hostname),
       !read_response(buffer, sizeof(buffer), FALSE, FALSE, ob)))
    goto RESPONSE_FAILED;
    }

  /* If the response to EHLO specified support for the SIZE parameter, note
  this, provided size_addition is non-negative. */

  smtp_use_size = esmtp && ob->size_addition >= 0 &&
    pcre_exec(regex_SIZE, NULL, buffer, (int)strlen(buffer),
      PCRE_EOPT, NULL, 0) >= 0;
  DEBUG(9) debug_printf("use_size=%d\n", smtp_use_size);

  /* Note if the response to EHLO specifies support for DSN, but only if
  configured to support DSN. */

  #ifdef SUPPORT_DSN
  smtp_use_dsn = dsn &&
    esmtp && pcre_exec(regex_DSN, NULL, buffer, (int)strlen(buffer),
      PCRE_EOPT, NULL, 0) >= 0;
  DEBUG(9) debug_printf("use_dsn=%d\n", smtp_use_dsn);
  #endif
  }

/* For continuing deliveries down the same channel, the socket is the standard
input, and smtp_use_size and smtp_use_dsn are already set. */

else deliver_socket = fileno(stdin);

/* The setting up of the SMTP call is now complete. Any subsequent errors are
message-specific. */

setting_up = FALSE;

/* If there is a filter command specified for this transport, we can now
set it up. This cannot be done until the identify of the host is known. */

if (tblock->filter_command != NULL)
  {
  BOOL rc;
  char buffer[64];
  sprintf(buffer, "%.50s transport", tblock->name);
  deliver_host = host->name;
  deliver_host_address = host->address;
  rc = transport_set_up_command(&transport_filter_argv, tblock->filter_command,
    TRUE, DEFER, addrlist, buffer, NULL);
  deliver_host = deliver_host_address = NULL;

  /* On failure, copy the error to all addresses, abandon the SMTP call, and
  yield ERROR. */

  if (!rc)
    {
    set_errno(addrlist->next, addrlist->basic_errno, addrlist->message,
      DEFER);
    yield = ERROR;
    goto SEND_QUIT;
    }
  }


/* For messages that have more than the maximum number of recipients, we want
to send several transactions down the same SMTP connection. This optimization
was added to Exim after the following code was already working. The simplest
way to put it in without disturbing the code was to use a goto to jump back to
this point when there is another transaction to handle.

********
This facility isn't currently used because the external max_addresses value is
set to the internal max_rcpt value. However, the coding is left in situ because
it may be needed one day.
********
*/

SEND_MESSAGE:
address_count = 0;
ok = FALSE;
send_rset = TRUE;


/* Initiate a message transfer. The user_null_sender flag is set if a local
message was received with "-f <>" on the command line from a non-trusted user.
The sender of the message is still present in the From: or Sender: header
lines; only the envelope is affected. For a trusted user, using "-f <>" causes
the sender to be set up as mailer-daemon@qualify_domain in a From: line if
there isn't one. If we know the receiving MTA supports the SIZE qualification,
send it, adding somethingto the message size to allow for imprecision and
things that get added en route. However, if we underestimate it, we are
probably no worse off then if we hadn't used SIZE at all.

Exim now keeps the number of lines in a message, so we can give an accurate
value for the original message, but we need some additional to handle added
headers. (Double "." characters don't get included in the count.)

For an overlapping period we must cope with old spool files that don't have the
linecount value. In that case, we stick to the previous guess, which adds 16K
to the message size. This code is being written on 15Apr98 and can be cleaned
up in a couple of years' time. */

p = buffer;
*p = 0;

if (smtp_use_size)
  {
  int additional = (message_linecount < 0)? 16*1024 :
    message_linecount + ob->size_addition;
  sprintf(p, " SIZE=%d", message_size + additional);
  while (*p) p++;
  }

#ifdef SUPPORT_DSN
if (smtp_use_dsn)
  {
  if (dsn_ret == dsn_ret_hdrs)
    {
    strcpy(p, " RET=HDRS");
    while (*p) p++;
    }
  else if (dsn_ret == dsn_ret_full)
    {
    strcpy(p, " RET=FULL");
    while (*p) p++;
    }
  if (dsn_envid != NULL)
    string_format(p, sizeof(buffer) - (p-buffer), " ENVID=%s", dsn_envid);
  }
#endif

write_command("MAIL FROM:<%s>%s\r\n", user_null_sender? "" : return_path,
  buffer);

if (!read_response(buffer, sizeof(buffer), FALSE, FALSE, ob))
  goto RESPONSE_FAILED;


/* Pass over all the recipient addresses and note whether any of them are
accepted. Handle both conventional and source-routed addresses. The relevant
addresses to be handled by this host have status PENDING_DEFER. Send only up to
max_rcpt at a time, leaving first_addr pointing to the next one if not all
are sent. **** The code for doing that is never currently exercised, since
the external max_addresses count is set to the internal max_rcpt value.
However, the code is left in for possible future use. **** */

for (addr = first_addr;
     address_count < max_rcpt && addr != NULL;
     addr = addr->next)
  {
  if (addr->transport_return != PENDING_DEFER) continue;

  address_count++;

  /* Handle any DSN options */

  p = buffer;
  *p = 0;

  #ifdef SUPPORT_DSN
  if (smtp_use_dsn)
    {
    if ((addr->dsn_flags & rf_dsnflags) != 0)
      {
      int i;
      BOOL first = TRUE;
      strcpy(p, " NOTIFY=");
      while (*p) p++;
      for (i = 0; i < 4; i++)
        {
        if ((addr->dsn_flags & rf_list[i]) != 0)
          {
          if (!first) *p++ = ',';
          first = FALSE;
          strcpy(p, rf_names[i]);
          while (*p) p++;
          }
        }
      }

    if (addr->dsn_orcpt != NULL)
      string_format(p, sizeof(buffer) - (p-buffer), " ORCPT=%s",
        addr->dsn_orcpt);
    }
  #endif

  /* Now send the RCPT command */

  if (addr->local_part[0] == ',' || addr->local_part[0] == ':')
    write_command("RCPT TO:<@%s%s>%s\r\n", addr->domain, addr->local_part,
      buffer);
  else
    write_command("RCPT TO:<%s@%s>%s\r\n", addr->local_part, addr->domain,
      buffer);

  /* The remote is permitted to reject some addresses while accepting others.
  However certain errors clearly abort the whole process. Set the value in
  transport_return to PENDING_OK if the address is accepted. If there is a
  subsequent general error, it will get reset accordingly. If not, it will
  get converted to OK at the end. */

  if (read_response(buffer, sizeof(buffer), FALSE, FALSE, ob))
    {
    ok = TRUE;
    addr->transport_return = PENDING_OK;

    /* If addr->dr_retry_exists is set, there was a routing or directing
    delay on this address; ensure that any address-specific retry record is
    expunged. */

    if (addr->dr_retry_exists)
      retry_add_item(addr, (addr->local)? "D" : "R", TRUE, NULL, rf_delete);
    }

  /* Address was rejected. A timeout on RCPT TO is handled specially, as
  it can often mean that there is a problem with just this one address. We
  want to delay retries on this address, but not on any other addresses for
  the same host, though of course they must all get a DEFER status now, because
  we can't proceed in this delivery, but setting up a delay should let the
  other addresses through without this one in some future delivery attempt.

  By leaving the yield as OK, the host won't get put on a retry list. If the
  timeout was (e.g.) a network problem, and just happened to strike after RCPT
  TO at random, we aren't delaying the host when we should, if only we knew!
  However, it seems unlikely that such a timeout will always strike at exactly
  the same point in the dialogue, so eventually Exim should notice that the
  connection to the host is in a bad way. */

  else if (errno == ETIMEDOUT)
    {
    int save_errno = errno;
    char * message = string_sprintf("SMTP timeout while connected to %s [%s] "
      "after %s", host->name, host->address, tidy_smtp_command());
    set_errno(addrlist, save_errno, message, DEFER);
    retry_add_item(addr, (addr->local)? "D" : "R", TRUE, NULL, 0);
    ok = send_quit = FALSE;
    host->update_waiting = FALSE;
    break;
    }

  /* Handle non-timeout errors. Non-receipt of a response causes a defer for
  the host.  Otherwise, set up the appropriate code for this address, and
  add a retry item if the response was 4xx. */

  else
    {
    char *s;
    if (errno != 0 || buffer[0] == 0) goto RESPONSE_FAILED;

    s = string_printing(buffer);
    addr->message =
      string_sprintf("SMTP error from remote mailer after %s: "
        "host %s [%s]: %s", tidy_smtp_command(), host->name, host->address, s);
    fprintf(message_log, "%s %s\n", tod_stamp(tod_log), addr->message);

    if (buffer[0] == '5')
      addr->transport_return = FAIL;
    else
      {
      /* Log temporary errors if there are more hosts to be tried. */
      if (host->next != NULL) log_write(0, LOG_MAIN, "%s", addr->message);
      addr->transport_return = DEFER;

      /* Do not put this message on the list of those waiting for this host,
      as otherwise it is likely to be tried too often. */

      host->update_waiting = FALSE;

      /* Add a retry item for the address so that it doesn't get tried
      again too soon. */

      retry_add_item(addr, (addr->local)? "D" : "R", TRUE, NULL, 0);
      }
    }
  }


/* Save the first address of the next batch. */

first_addr = addr;


/** Debugging without sending a message
addrlist->transport_return = DEFER;
goto SEND_QUIT;
**/


/* Now prepare to send the text of the message if there were any good
recipients. If there were no good recipients, just set ok TRUE, since we have
handled address-specific errors already. */

if (!ok) ok = TRUE; else
  {
  write_command("DATA\r\n");
  if (!read_response(buffer, sizeof(buffer), TRUE, FALSE, ob))
    goto RESPONSE_FAILED;

  /* OK to send the message itself, with SMTP dot-handling protocol. Set
  the appropriate timeout value to be used for each chunk. The SIGALRM handler
  must be set up here. (Haven't been able to make it work using select()
  for writing yet.) */

  sigalrm_seen = FALSE;
  os_non_restarting_signal(SIGALRM, sigalrm_handler);
  transport_write_timeout = ob->data_timeout;
  smtp_command = "sending data block";
  DEBUG(1) debug_printf("  SMTP>> writing message and terminating \".\"\n");
  ok = transport_write_message(addrlist, deliver_socket,
    topt_use_crlf | topt_smtp_dots | topt_end_dot | topt_final_nl |
      (tblock->body_only? topt_no_headers : 0) |
      (tblock->headers_only? topt_no_body : 0) |
      (tblock->return_path_add? topt_add_return_path : 0) |
      (tblock->delivery_date_add? topt_add_delivery_date : 0) |
      (tblock->envelope_to_add? topt_add_envelope_to : 0),
    0, tblock->add_headers, tblock->remove_headers);
  transport_write_timeout = 0;   /* for subsequent transports */
  signal(SIGALRM, SIG_IGN);

  /* Failure can either be some kind of I/O disaster (including timeout),
  or the failure of a transport filter or the expansion of added headers. */

  if (!ok)
    {
    buffer[0] = 0;              /* There hasn't been a response */
    goto RESPONSE_FAILED;
    }

  /* We used to send the terminating "." explicitly here, but because of
  buffering effects at both ends of TCP/IP connections, you don't gain
  anything by keeping it separate, so it might as well go in the final
  data buffer for efficiency. This is now done by setting the topt_end_dot
  flag above. */

  /*** write_command(".\r\n"); ***/

  smtp_command = "end of data";
  ok = read_response(buffer, sizeof(buffer), FALSE, TRUE, ob);

  /* If all went well, mark the recipient addresses as completed,
  record which host/IPaddress they were delivered to, and cut out
  RSET when sending another message down the same channel. Write the
  completed addresses to the journal now so that they are recorded in
  case there is a crash of hardware or software before the spool gets
  updated. Also record the final SMTP confirmation if needed. */

  if (ok)
    {
    int flag = '=';
    int len;
    host_item *thost;

    char *conf = NULL;
    send_rset = FALSE;

    /* Make a copy of the host if it is local to this invocation
    of the transport. */

    if (copy_host)
      {
      thost = store_get(sizeof(host_item));
      *thost = *host;
      thost->name = string_copy(host->name);
      thost->address = string_copy(host->address);
      }
    else thost = host;

    /* Set up confirmation if needed */

    if (log_smtp_confirmation)
      {
      char *s = string_printing(buffer);
      conf = (s == buffer)? string_copy(s) : s;
      }

    /* Process all transported addresses */

    for (addr = addrlist; addr != first_addr; addr = addr->next)
      {
      if (addr->transport_return == PENDING_OK)
        {
        addr->transport_return = OK;
        addr->transported = thost;
        addr->special_action = flag;
        addr->message = conf;
        flag = '-';

        /* Just carry on after write error, as it may prove possible to
        update the spool file later. */

        sprintf(buffer, "N%s\n", addr->orig);
        len = (int)strlen(buffer);
        if (write(journal_fd, buffer, len) != len)
          log_write(0, LOG_MAIN|LOG_PANIC, "failed to write journal for "
            "address %s: %s", addr->orig, strerror(errno));
        }
      }

    /* Ensure the journal file is pushed out to disc. */

    if (fsync(journal_fd) < 0)
      log_write(0, LOG_MAIN|LOG_PANIC, "failed to fsync journal: %s",
        strerror(errno));
    }
  }


/* Handle general (not specific to one address) failures here. The value of ok
is used to skip over this code on the falling through case. A timeout causes a
deferral. Other errors may defer or fail according to the response code, and
may set up a special errno value, e.g. after connection chopped, which is
assumed if errno == 0 and there is no text in the buffer. If control reaches
here during the setting up phase (i.e. before MAIL FROM) then always defer, as
the problem is not related to this specific message. */

if (!ok)
  {
  int code, save_errno;
  char *message;

  RESPONSE_FAILED:

  save_errno = errno;
  ok = FALSE;        /* For when gone to */
  message = NULL;
  send_quit = check_response(host, &save_errno, addrlist->more_errno,
    buffer, &code, &message);

  /* If the failure happened while setting up the call, then defer all
  addresses and yield DEFER, indicating that this host shouldn't be
  tried again for a while. This should also be the action if there was
  an I/O error or a timeout or other transporting error, indicated by errno
  being non-zero, except for the case of failed add_headers expansion, when
  the yield should be ERROR, to stop it trying other hosts.

  However, handle timeouts after MAIL FROM or "." and loss of connection after
  "." specially. They can indicate a problem with the sender address or with
  the contents of the message rather than a real error on the connection.
  Therefore, treat these cases in the same way as a 4xx response.

  The following condition tests for NOT these special cases. */

  if (setting_up ||
       (save_errno != 0 &&
         (save_errno != ETIMEDOUT ||
           (strncmp(smtp_command,"MAIL",4) != 0 &&
            strncmp(smtp_command,"end ",4) != 0)) &&
         (save_errno != ERRNO_SMTPCLOSED ||
            strncmp(smtp_command,"end ",4) != 0)
       )
     )
    {
    yield = (save_errno == ERRNO_ADDHEADER_FAIL)? ERROR : DEFER;
    set_errno(addrlist, save_errno, message, yield);
    }

  /* Otherwise we have a message-specific error response from the remote
  host. This is one of
    (a) negative response or timeout after "mail from"
    (b) negative response after "data"
    (c) negative response or timeout or dropped connection after "."
  It won't be a negative response or timeout after "rcpt to", as that is dealt
  with separately above. The action in all cases is to set an appropriate
  error code for all the addresses, but to leave yield set to OK because
  the host itself has not failed. [It might in practice have failed for a
  timeout after MAIL FROM, or "." but if so, we'll discover that at the next
  delivery attempt.] For a temporary error, set the message_defer flag, and
  write to the logs for information if this is not the last host. The error for
  the last host will be logged as part of the address's log line. */

  else
    {
    set_errno(addrlist, save_errno, message, (code == '5')? FAIL : DEFER);

    /* If there's an errno, the message contains just the identity of
    the host. */

    if (code == '4')
      {
      if (save_errno > 0)
        message = string_sprintf("%s: %s", message, strerror(save_errno));
      if (host->next != NULL) log_write(0, LOG_MAIN, "%s", message);
      fprintf(message_log, "%s %s\n", tod_stamp(tod_log), message);
      *message_defer = TRUE;
      }
    }
  }


/* If all has gone well, send_quit will be set TRUE, implying we can end the
SMTP session tidily. However, if there were too many addresses to send in one
message (indicated by first_addr being non-NULL) we want to carry on with the
rest of them. Also, it is desirable to send more than one message down the SMTP
connection if there are several waiting, provided we haven't already sent so
many as to hit the configured limit. The function transport_check_waiting looks
for a waiting message and returns its id. Then transport_pass_socket tries to
set up a continued delivery by passing the socket on to another process. The
variable send_rset is FALSE if a message has just been successfully transfered.

If we are already sending down a continued channel, there may be further
addresses not yet delivered that are aimed at the same host. In this case,
continue_more will be true, and all we should do is send RSET if necessary,
and return, leaving the channel open. */

DEBUG(9) debug_printf("ok=%d send_quit=%d send_rset=%d continue_more=%d "
  "yield=%d first_address=%d\n", ok, send_quit, send_rset, continue_more,
  yield, first_addr);

if (ok && send_quit)
  {
  BOOL more;
  if (first_addr != NULL || continue_more ||
    transport_check_waiting(tblock->name, host->name, ob->batch_max,
      new_message_id, &more))
    {
    if (send_rset)
      {
      write_command("RSET\r\n");
      ok = read_response(buffer, sizeof(buffer), FALSE, TRUE, ob);
      }

    /* Either RSET was not needed, or it succeeded */

    if (ok)
      {
      if (first_addr != NULL)      /**** shouldn't currently happen ****/
        {
        continue_sequence++;
        goto SEND_MESSAGE;
        }
      if (continue_more) return yield;

      /* Pass the socket to a new Exim process and set passed_channel so that
      a queue-running process knows to wait for all its descendents (by finding
      their pids in a file). */

      if (transport_pass_socket(tblock->name, host->name, new_message_id,
          deliver_socket))
        {
        send_quit = FALSE;
        passed_channel = TRUE;
        }
      }

    /* If RSET failed and there are addresses left, they get deferred. */

    else
      {
      char *msg;
      send_quit = check_response(host, &errno, 0, buffer, &yield, &msg);
      set_errno(first_addr, errno, msg, DEFER);
      }
    }
  }

/* End off tidily with QUIT unless the connection has died or the socket has
been passed to another process. There has been discussion on the net about what
to do after sending QUIT. The wording of the RFC suggests that it is necessary
to wait for a response, but on the other hand, there isn't anything one can do
with an error response, other than log it. Exim used to do that. However,
further discussion suggested that it is positively advantageous not to wait for
the response, but to close the session immediately. This is supposed to move
the TCP/IP TIME_WAIT state from the server to the client, thereby removing some
load from the server. (Hosts that are both servers and clients may not see much
difference, of course.) Further discussion indicated that this was safe to do
on Unix systems which have decent implementations of TCP/IP that leave the
connection around for a while (TIME_WAIT) after the application has gone away.
This enables the response sent by the server to be properly ACKed rather than
timed out, as can happen on broken TCP/IP implementations on other OS.

However, we keep the old code around in an ifdef, just in case. This change is
being made on 31-Jul-98. Tidy up the code in due course. */

SEND_QUIT:

if (send_quit)
  {
  write_command("QUIT\r\n");

  #ifdef WAIT_FOR_QUIT
  if (!read_response(buffer, sizeof(buffer), FALSE, FALSE, ob))
    check_final_response(host, buffer);
  #endif
  }

/* Close the socket, and return the appropriate value, setting
continue_transport and continue_hostname NULL to prevent any other addresses
that may include the host from trying to re-use a continuation socket. If all
went well and continue_more is set, we shouldn't actually get here if there are
further addresses, as the return above will be taken. However, writing RSET
might have failed, or there may be other addresses whose hosts are specified in
the transports, and therefore not visible at top level, in which case
continue_more won't get set. When delivering down an old channel,
remote_max_parallel is forced to 1, so these values will be seen by subsequent
deliveries. */

close(deliver_socket);
continue_transport = NULL;
continue_hostname = NULL;
return yield;
}




/*************************************************
*              Closedown entry point             *
*************************************************/

/* This function is called when exim is passed an open smtp channel
from another incarnation, but the message which it has been asked
to deliver no longer exists. The channel is on stdin.

We might do fancy things like looking for another message to send down
the channel, but if the one we sought has gone, it has probably been
delivered by some other process that itself will seek further messages,
so just close down our connection.

Argument:   pointer to the transport instance block
Returns:    nothing
*/

void
smtp_transport_closedown(transport_instance *tblock)
{
char buffer[256];
smtp_transport_options_block *ob =
  (smtp_transport_options_block *)(tblock->options_block);
deliver_socket = fileno(stdin);
write_command("QUIT\r\n");
(void) read_response(buffer, sizeof(buffer), FALSE, FALSE, ob);
close(deliver_socket);
}



/*************************************************
*              Main entry point                  *
*************************************************/

/* See local README for interface details. As this is a remote transport, it is
given a chain of addresses to be delivered in one connection, if possible. */

void smtp_transport_entry(
  transport_instance *tblock,      /* data for this instantiation */
  address_item *addrlist)          /* addresses we are working on */
{
int cutoff_retry;
int port;
int hosts_defer = 0;
int hosts_fail  = 0;
int hosts_looked_up = 0;
int hosts_mx_to_nonexist = 0;
int hosts_retry = 0;
int hosts_serial = 0;
int hosts_total = 0;
address_item *addr;
BOOL expired = TRUE;
BOOL continuing = continue_hostname != NULL;
char *continue_host_address = NULL;
char *expanded_hosts = NULL;
smtp_transport_options_block *ob =
  (smtp_transport_options_block *)(tblock->options_block);
host_item *hostlist = addrlist->host_list;
host_item *host;

DEBUG(2) debug_printf("%s transport entered\n", tblock->name);
DEBUG(9)
  {
  for (addr = addrlist; addr != NULL; addr = addr->next)
    debug_printf("  %s\n", addr->orig);
  }

/* If this is a delivery down an existing channel, find the IP address to
which we are connected. */

if (continuing)
  {
  #if HAVE_IPV6
  struct sockaddr_in6 continue_sock;
  #else
  struct sockaddr continue_sock;
  #endif

  int size = sizeof(continue_sock);
  if (getpeername(fileno(stdin), (struct sockaddr *)(&continue_sock), &size)
      == 0)
    continue_host_address = host_ntoa(-1, (struct sockaddr *)(&continue_sock),
      NULL);

  DEBUG(9) debug_printf("already connected to %s [%s]\n", continue_hostname,
    continue_host_address);
  }

/* If a host list is not defined for the addresses - they must all have the
same one in order to be passed to a single transport - or if the transport has
a host list with hosts_override set, use the host list supplied with the
transport. It is an error for this not to exist. */

if (hostlist == NULL || (ob->hosts_override && ob->hosts != NULL))
  {
  if (ob->hosts == NULL)
    log_write(0, LOG_PANIC_DIE, "%s transport called with no hosts set",
      tblock->name);

  DEBUG(9) debug_printf("using the transport's hosts: %s\n", ob->hosts);

  /* If the transport's host list contains no '$' characters, it is fixed and
  therefore a chain of hosts can be built once and for all, and remembered
  for subsequent use by other calls to this transport. If, on the other hand,
  the host list does contain '$', we have to expand it each time. In the fixed
  case, as the hosts string will never be used again, it doesn't matter that
  we replace all the : characters with zeros. */

  if (ob->hostlist == NULL)
    {
    char *s = ob->hosts;

    if (strchr(s, '$') != NULL)
      {
      expanded_hosts = expand_string(s);
      if (expanded_hosts == NULL)
        {
        addrlist->message = string_sprintf("failed to expand hostlist %s in %s "
          "transport: %s", s, tblock->name, expand_string_message);
        addrlist->transport_return = search_find_defer? DEFER : PANIC;
        return;
        }
      DEBUG(9) debug_printf("Expanded host list %s to %s\n", s, expanded_hosts);
      s = expanded_hosts;
      }

    host_build_hostlist(&hostlist, s);

    /* If there was no expansion of hosts, save the host list for
    next time. */

    if (expanded_hosts == NULL) ob->hostlist = hostlist;
    }

  /* This is not the first time this transport has been run in this delivery;
  the host list was built previously. */

  else hostlist = ob->hostlist;
  }



/* This old way of handling fall back hosts, by trying them per address,
is no longer used. New code to consolidate written 27-May-98. Remove this
old stuff when the new code is well established. */

#ifdef never
/* If there are any fallback hosts, add them onto the end of the
hostlist chain unless the address was routed via MX records and had local
host items removed. Their addresses will be looked up only if necessary. */

if (ob->fallback_hostlist != NULL && !addrlist->local_host_removed)
  {
  for (host = hostlist; host->next != NULL; host = host->next);
  host->next = ob->fallback_hostlist;
  }
#endif



/* Sort out the service, i.e. the port number. We want the port number in
network byte order, and that's what getservbyname() produces, so we have
to use htons() if the configuration specified a port by number instead of
by name. */

if (isdigit(*ob->service))
  {
  char *end;
  port = (int)htons((unsigned short)strtol(ob->service, &end, 0));
  if (end != ob->service + (int)strlen(ob->service))
    log_write(0, LOG_PANIC_DIE, "Invalid SMTP service: %s", ob->service);
  }
else
  {
  struct servent *smtp_service = getservbyname(ob->service, "tcp");
  if (smtp_service == NULL)
    log_write(0, LOG_PANIC_DIE, "TCP service \"%s\" not found", ob->service);
  port = smtp_service->s_port;
  }


/* For each host-plus-IP-address on the list:

.  If this is a continued delivery and the host isn't the one with the
   current connection, skip.

.  If the status is unusable (i.e. previously failed or retry checked), skip.

.  If no IP address set, get the address, either by turning the name into
   an address, calling gethostbyname if gethostbyname is on, or by calling
   the DNS. The DNS may yield multiple addresses, in which case insert the
   extra ones into the list.

.  Get the retry data if not previously obtained for this address and set the
   field which remembers the state of this address. Skip if the retry time is
   not reached. If not, remember whether retry data was found. The retry string
   contains both the name and the IP address.

.  Scan the list of addresses and mark those whose status is DEFER as
   PENDING_DEFER. These are the only ones that will be processed in this cycle
   of the hosts loop.

.  Make a delivery attempt - addresses marked PENDING_DEFER will be tried.
   Some addresses may be successfully delivered, others may fail, and yet
   others may get temporary errors and so get marked DEFER.

.  The return from the delivery attempt is OK if a connection was made and a
   valid SMTP dialogue was completed. Otherwise it is DEFER.

.  If OK, add a "remove" retry item for this host/IPaddress, if any.

.  If fail to connect, or other defer state, add a retry item.

.  If there are any addresses whose status is still DEFER, carry on to the
   next host/IPaddress, otherwise return.

If we get to the end of the list, all hosts have deferred at least one address,
or not reached their retry times. If delay_after_cutoff is unset, it requests a
delivery attempt to those hosts whose last try was before the arrival time of
the current message. To cope with this, we have to go round the loop a second
time. After that, set the status and error data for any addresses that haven't
had it set already. */

for (cutoff_retry = 0; expired &&
     cutoff_retry < ((ob->delay_after_cutoff)? 1 : 2);
     cutoff_retry++)
  {
  for (host = hostlist; host != NULL; host = host->next)
    {
    int rc;
    char *rs;
    BOOL serialized = FALSE;
    BOOL host_is_expired = FALSE;
    BOOL message_defer = FALSE;
    address_item *first_addr = NULL;
    char *retry_host_key = NULL;
    char *retry_message_key = NULL;

    /* Set the flag requesting that this host be added to the waiting
    database if the delivery fails temporarily or if we are running with
    queue_smtp or a 2-stage queue run. This gets unset for certain
    kinds of error, typically those that are specific to the message. */

    host->update_waiting = TRUE;

    /* If the address hasn't yet been obtained from the host name, look it up
    now, unless the host is already marked as unusable. If it is marked as
    unusable, it means that the router was unable to find its IP address (in
    the DNS or wherever) OR we are in the 2nd time round the cutoff loop, and
    the lookup failed last time. We don't get this far if *all* MX records
    point to non-existent hosts; that is treated as a hard error.

    We can just skip this host entirely. When the hosts came from the router,
    the address will timeout based on the other host(s); when the address is
    looked up below, there is an explicit retry record added.

    Note that we mustn't skip unusable hosts if the address is not unset; they
    may be needed as expired hosts on the 2nd time round the cutoff loop. */

    if (host->address == NULL)
      {
      if (host->status >= hstatus_unusable)
        {
        DEBUG(9) debug_printf("%s has no address and is unusable - skipping\n",
          host->name);
        continue;
        }

      DEBUG(9) debug_printf("getting address for %s\n", host->name);

      hosts_looked_up++;

      /* If the "name" is in fact an IP address, just copy it over and check
      for being a local interface; otherwise call host_findxx() to look it up
      with or without the DNS. */

      if (string_is_ip_address(host->name))
        {
        host->address = host->name;
        host->mx = -1;
        rc = host_scan_for_local_hosts(host, host, FALSE);
        }
      else
        {
        char *canonical_name;
        if (ob->gethostbyname)
          rc = host_find_byname(host, &canonical_name, FALSE);

        /* Do an MX-only lookup if the name is *not* in mx_domains_except
        and *is* in mx_domains. */

        else
          {
          BOOL mx_only =

            (ob->mx_domains_except == NULL ||
            !match_isinlist(host->name, ob->mx_domains_except,
            &(ob->re_mx_domains_except), TRUE)) &&

            (ob->mx_domains != NULL &&
            match_isinlist(host->name, ob->mx_domains,
            &(ob->re_mx_domains), TRUE));

          rc = host_find_bydns(host, mx_only, FALSE, ob->dns_qualify_single,
            ob->dns_search_parents, &canonical_name, NULL);
          }
        }

      /* Remember if any hosts have MX records pointing to non-existent
      hosts so we can give the right error message if all are like this. */

      if (rc == HOST_FIND_FAILED && host->mx >= 0 && host->address == NULL)
        hosts_mx_to_nonexist++;

      /* Failure to find the host at this time (usually DNS temporary failure)
      is really a kind of routing failure rather than a transport failure.
      Therefore we add a retry item of the routing kind, not to stop us trying
      to look this name up here again, but to ensure the address gets timed
      out if the failures go on long enough. A complete failure at this point
      commonly points to a configuration error, but the best action is still
      to carry on for the next host. */

      if (rc == HOST_FIND_AGAIN || rc == HOST_FIND_FAILED)
        {
        retry_add_item(addrlist, "R", FALSE, host->name, 0);
        expired = FALSE;
        if (rc == HOST_FIND_AGAIN) hosts_defer++; else hosts_fail++;
        DEBUG(2) debug_printf("rc = %s for %s\n", (rc == HOST_FIND_AGAIN)?
          "HOST_FIND_AGAIN" : "HOST_FIND_FAILED", host->name);
        host->status = hstatus_unusable;
        continue;
        }

      /* If the host is actually the local host, we may have a problem, or
      there may be some cunning configuration going on. In the problem case,
      log things and give up. The default transport status is already DEFER. */

      if (rc == HOST_FOUND_LOCAL && !ob->allow_localhost)
        {
        for (addr = addrlist; addr != NULL; addr = addr->next)
          {
          addr->special_action = SPECIAL_FREEZE;
          addr->message = string_sprintf("%s transport found host %s to be "
            "local", tblock->name, host->name);
          }
        goto END_TRANSPORT;
        }
      }   /* End of block for IP address lookup */

    /* If this is a continued delivery, we are interested only in the host
    which matches the name of the existing open channel. The check is put
    here after the local host lookup, in case the name gets expanded as a
    result of the lookup. Set expired FALSE, to save the outer loop executing
    twice. */

    if (continuing && (strcmp(continue_hostname, host->name) != 0 ||
                       strcmp(continue_host_address, host->address) != 0))
      {
      expired = FALSE;
      continue;      /* With next host */
      }

    /* If the queue_smtp option is set, we don't actually want to attempt
    any deliveries, except to designated domains. When doing a 2-stage
    queue run there are no exceptions. */

    if (queue_smtp && (queue_2stage ||
        !match_isinlist(addrlist->domain, queue_smtp_except,
          &re_queue_smtp_except, TRUE)))
      {
      expired = FALSE;
      continue;      /* With next host */
      }

    /* Count hosts being considered - purely for an intelligent comment
    if none are usable. */

    hosts_total++;

    /* The first time round the outer loop, check the status of the host by
    inspecting the retry data. The second time round, we are interested only
    in expired hosts that haven't been tried since this message arrived. */

    if (cutoff_retry == 0)
      {
      /* Ensure the status of the address is set by checking retry data if
      necessary. There maybe host-specific retry data (applicable to all
      messages) and also data for retries of a specific message at this host.
      If either of these retry records are actually read, the keys used are
      returned to save recomputing them later. */

      host_is_expired = retry_check_address(host, ob->retry_include_ip_address,
        &retry_host_key, &retry_message_key);

      DEBUG(2) debug_printf("%s [%s] status = %s\n", host->name,
        (host->address == NULL)? "" : host->address,
        (host->status == hstatus_usable)? "usable" :
        (host->status == hstatus_unusable)? "unusable" :
        (host->status == hstatus_unusable_expired)? "unusable (expired)" : "?");

      /* Skip this address if not usable at this time, noting if it wasn't
      actually expired, both locally and in the address. */

      switch (host->status)
        {
        case hstatus_unusable:
        expired = FALSE;
        addrlist->retry_skipped = TRUE;
        /* Fall through */

        case hstatus_unusable_expired:
        switch (host->why)
          {
          case hwhy_retry: hosts_retry++; break;
          case hwhy_failed:  hosts_fail++; break;
          case hwhy_deferred: hosts_defer++; break;
          }

        /* If there was a retry message key, implying that previously there
        was a message-specific defer, we don't want to update the list of
        messages waiting for this host. */

        if (retry_message_key != NULL) host->update_waiting = FALSE;
        continue;   /* With the next host or IP address */
        }
      }

    /* Second time round the loop: if the address is set but expired, and
    the message is newer than the last try, let it through. */

    else
      {
      if (host->address == NULL ||
          host->status != hstatus_unusable_expired ||
          host->last_try > received_time)
        continue;
      DEBUG(2)
        debug_printf("trying expired host %s [%s]\n",
          host->name, host->address);
      host_is_expired = TRUE;
      }

    /* Setting "expired=FALSE" doesn't actually mean not all hosts are expired;
    it remains TRUE only if all hosts are expired and none are actually tried.
    */

    expired = FALSE;

    /* If this host is listed as one to which access must be serialized,
    see if another Exim process has a connection to it, and if so, skip
    this host. If not, update the database to record our connection to it
    and remember this for later deletion. Do not do any of this if we are
    sending the message down a pre-existing connection. */

    if (!continuing)
      {
      serialized = match_isinlist(host->name, ob->serialize_hosts,
          &(ob->re_serialize_hosts), FALSE) ||
        (ob->serialize_nets != NULL &&
          match_net_isinlist(host->address, ob->serialize_netlist));

      /* Serialization required */

      if (serialized && !transport_check_serialized(tblock->name, host->name))
        {
        DEBUG(2) debug_printf("skipping host %s because another Exim process "
          "is connected to it\n", host->name);
        hosts_serial++;
        continue;
        }
      }

    /* OK, we have an IP address that is not waiting for its retry time to
    arrive (it might be expired) OR (second time round the loop) we have an
    expired host that hasn't been tried since the message arrived. Have a go
    at delivering the message to it, but previous delivery attempts may have
    left error stuff set up; flush that first, and record whether we got here
    via an MX record or not in the more_errno field of the address. We are
    interested only in addresses that are still marked DEFER - others may
    have got delivered to a previously considered IP address. Set their
    status to PENDING_DEFER to indicate which ones are relevant this time.
    Save the first one for use later. */

    for (addr = addrlist; addr != NULL; addr = addr->next)
      {
      if (addr->transport_return != DEFER) continue;
      if (first_addr == NULL) first_addr = addr;
      addr->transport_return = PENDING_DEFER;
      addr->basic_errno = 0;
      addr->more_errno = (host->mx >= 0)? 'M' : 'A';
      addr->message = NULL;
      }

    DEBUG(2) debug_printf("delivering %s to %s [%s] (%s%s)\n",
      message_id, host->name, host->address, addrlist->orig,
      (addrlist->next == NULL)? "" : ", ...");

    set_process_info("delivering %s to %s [%s] (%s%s)",
      message_id, host->name, host->address, addrlist->orig,
      (addrlist->next == NULL)? "" : ", ...");

    /* This is not for real; don't do the delivery. If there are
    any remaining hosts, list them. */

    if (dont_deliver)
      {
      host_item *host2;
      debug_printf("*** delivery by %s transport bypassed by -N option\n",
        tblock->name);
      set_errno(addrlist, 0, NULL, OK);
      for (addr = addrlist; addr != NULL; addr = addr->next)
        {
        addr->transported = host;
        addr->special_action = '*';
        addr->message = "delivery bypassed by -N option";
        }
      debug_printf("*** host and remaining hosts:\n");
      for (host2 = host; host2 != NULL; host2 = host2->next)
        debug_printf("    %s [%s]\n", host2->name,
          (host2->address == NULL)? "unset" : host2->address);
      rc = OK;
      }

    /* This is for real; attempt the delivery */

    else
      rc = smtp_deliver(addrlist, host, port, tblock, expanded_hosts != NULL,
        ob->keepalive, &message_defer);

    /* Yield is one of:
       OK     => connection made, each address contains its result;
                   message_defer is set for message-specific defers (when all
                   recipients are marked defer)
       DEFER  => there was a non-message-specific delivery problem;
       ERROR  => there was a problem setting up the arguments for a filter,
                 or there was a problem with expanding added headers
    */

    rs = (rc == OK)? "OK" : (rc == DEFER)? "DEFER" : (rc == ERROR)?
      "ERROR" : "?";

    set_process_info("delivering %s: just tried %s [%s] for %s%s: result %s",
      message_id, host->name, host->address, addrlist->orig,
      (addrlist->next == NULL)? "" : " (& others)", rs);

    /* Release serialization if set up */

    if (serialized) transport_end_serialized(tblock->name, host->name);

    /* If the result is not OK, there was a non-message-specific problem.
    If it's DEFER, we need to write to the logs saying what happened for this
    particular host. If all hosts defer there will be a general message written
    at the end. */

    if (rc == DEFER) write_logs(first_addr, host);

    /* If the result is DEFER, or if a host retry record is known to exist, we
    need to add an item to the retry chain for updating the retry database
    at the end of delivery. We only need to add the item to the top address,
    of course. Also, if DEFER, we mark the IP address unusable so as to skip it
    for any other delivery attempts using the same address. (It is copied into
    the unusable tree at the outer level, so even if different address blocks
    contain the same address, it still won't get tried again.) */

    if (rc == DEFER || retry_host_key != NULL)
      {
      if (retry_host_key == NULL)
        {
        retry_host_key = ob->retry_include_ip_address?
          string_sprintf("T:%s:%s", host->name, host->address) :
          string_sprintf("T:%s", host->name);
        }
      retry_add_item(first_addr, "T", FALSE, retry_host_key + 2,
        rf_host | ((rc != DEFER)? rf_delete : 0));

      /* We may have tried an expired host, if its retry time has come; ensure
      the status reflects the expiry for the benefit of any other addresses. */

      if (rc == DEFER)
        {
        host->status = (host_is_expired)?
          hstatus_unusable_expired : hstatus_unusable;
        host->why = hwhy_deferred;
        }
      }

    /* If message_defer is set (host was OK, but every recipient got deferred
    because of some message-specific problem), or if that had happened
    previously so that a message retry key exists, add an appropriate item
    to the retry chain. Note that if there was a message defer but now there is
    a host defer, the message defer record gets deleted. That seems perfectly
    reasonable. Also, stop the message from being remembered as waiting
    for this host. */

    if (message_defer || retry_message_key != NULL)
      {
      if (retry_message_key == NULL)
        {
        retry_message_key = ob->retry_include_ip_address?
          string_sprintf("T:%s:%s:%s", host->name, host->address, message_id) :
          string_sprintf("T:%s:%s", host->name, message_id);
        }
      retry_add_item(addrlist, "T", FALSE, retry_message_key+2,
        rf_message | rf_host | (message_defer? 0 : rf_delete));
      host->update_waiting = FALSE;
      }

    /* Any return other than DEFER (OK or ERROR) means that the addresses have
    got their final statuses filled in for this host. If no addresses are
    marked DEFER, we are done with this chain of addresses. Otherwise let
    the loop run to try other hosts with the deferred addresses. However,
    if the result is ERROR, we don't want to do this, as a failing filter
    set-up or add_headers expansion is likely to fail for any host we try. */

    if (rc != DEFER)
      {
      BOOL some_deferred = FALSE;
      if (rc != ERROR) for (addr = addrlist; addr != NULL; addr = addr->next)
        {
        if (addr->transport_return == DEFER)
          {
          some_deferred = TRUE;
          break;
          }
        }

      /* If none deferred or result was ERROR, return. */

      if (!some_deferred)
        {
        DEBUG(2) debug_printf("Leaving %s transport\n", tblock->name);
        return;
        }
      }

    /* If we were trying to deliver down an existing channel and failed,
    that's it for this message. Don't try any other hosts. */

    if (continuing) break;
    }

  /* This is the end of the loop that repeats iff expired is TRUE and
  ob->delay_after_cutoff is FALSE. The second time round we will
  try those hosts that haven't been tried since the message arrived. */

  DEBUG(2)
    {
    debug_printf("all IP addresses skipped or deferred at least one address\n");
    if (expired && !ob->delay_after_cutoff && cutoff_retry == 0)
      debug_printf("retrying IP addresses not tried since message arrived\n");
    }
  }


/* Get here if all IP addresses are skipped or defer at least one address. Add
a standard message to each deferred address if there hasn't been an error, that
is, if it hasn't actually been tried this time. The variable "expired" will be
FALSE if any deliveries were actually tried, or if there was at least one host
that was not expired. That is, it is TRUE only if no deliveries were tried and
all hosts were expired. If a delivery has been tried, an error code will be
set, and the failing of the message is handled by the retry code later.

If queue_smtp is set, or this transport was called to send a subsequent message
down an existing TCP/IP connection, and something caused the host not to be
found, we end up here, but can detect these cases and handle them specially. */

for (addr = addrlist; addr != NULL; addr = addr->next)
  {
  if (queue_smtp)    /* no deliveries attempted */
    {
    addr->transport_return = DEFER;
    addr->basic_errno = 0;
    addr->message = "queue_smtp option set";
    }

  else if (addr->transport_return == DEFER &&
       (addr->basic_errno == ERRNO_UNKNOWNERROR || addr->basic_errno == 0) &&
       addr->message == NULL)
    {
    addr->basic_errno = ERRNO_HRETRY;
    if (continue_hostname != NULL)
      {
      addr->message = "no host found for existing SMTP connection";
      }
    else if (expired)
      {
      addr->message = (ob->delay_after_cutoff)?
        "retry time not reached for any host after a long failure period" :
        "all hosts have been failing for a long time and were last tried "
          "after this message arrived";
      addr->transport_return = FAIL;
      }
    else
      {
      if (hosts_total == 0 &&        /* No hosts actually tried */
          hosts_looked_up > 0 &&     /* Some were looked up here */
          hosts_looked_up == hosts_mx_to_nonexist)
        addr->message = "all relevant MX records point to non-existent hosts";

      else if (hosts_retry == hosts_total)
        addr->message = "retry time not reached for any host";
      else if (hosts_fail == hosts_total)
        addr->message = "all host address lookups failed permanently";
      else if (hosts_defer == hosts_total)
        addr->message = "all host address lookups failed temporarily";
      else if (hosts_serial == hosts_total)
        addr->message = "connection limit reached for all hosts";
      else if (hosts_fail+hosts_defer == hosts_total)
        addr->message = "all host address lookups failed";
      else addr->message = "some host address lookups failed and retry time "
        "not reached for other hosts or connection limit reached";
      }
    }
  }

/* Update the database which keeps information about which messages are waiting
for which hosts to become available. Each host in the list has a flag which is
set if the data is to be updated. For some message-specific errors, the flag is
turned off because we don't want follow-on deliveries in those cases. */

transport_update_waiting(hostlist, tblock->name);

END_TRANSPORT:

DEBUG(2) debug_printf("Leaving %s transport\n", tblock->name);
}

/* End of transport/smtp.c */
