Treat ECONNRESET the same as an end-of-file indication.
[exim.git] / src / src / transports / smtp.c
index 5c73110715399e9d2550945e93749d7363f5e492..8deaeeadd892cfb8caf0d0b899f2b60e3e1c4626 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/transports/smtp.c,v 1.14 2005/08/02 09:24:45 ph10 Exp $ */
+/* $Cambridge: exim/src/src/transports/smtp.c,v 1.19 2005/12/22 14:54:50 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -93,6 +93,8 @@ optionlist smtp_transport_options[] = {
       (void *)offsetof(smtp_transport_options_block, interface) },
   { "keepalive",            opt_bool,
       (void *)offsetof(smtp_transport_options_block, keepalive) },
+  { "lmtp_ignore_quota",    opt_bool,
+      (void *)offsetof(smtp_transport_options_block, lmtp_ignore_quota) },
   { "max_rcpt",             opt_int | opt_public,
       (void *)offsetof(transport_instance, max_addresses) },
   { "multi_domain",         opt_bool | opt_public,
@@ -163,6 +165,7 @@ smtp_transport_options_block smtp_transport_option_defaults = {
   FALSE,               /* hosts_override */
   FALSE,               /* hosts_randomize */
   TRUE,                /* keepalive */
+  FALSE,               /* lmtp_ignore_quota */
   TRUE                 /* retry_include_ip_address */
   #ifdef SUPPORT_TLS
  ,NULL,                /* tls_certificate */
@@ -326,8 +329,8 @@ this particular type of timeout.
 Returns:       nothing
 */
 
-static
-void set_errno(address_item *addrlist, int errno_value, uschar *msg, int rc,
+static void
+set_errno(address_item *addrlist, int errno_value, uschar *msg, int rc,
   BOOL pass_message)
 {
 address_item *addr;
@@ -459,11 +462,12 @@ if (buffer[0] != 0)
   }
 
 /* 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. */
+connection closed) case, which causes deferral. An explicit connection reset
+error has the same effect. 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)
+if (*errno_value == 0 || *errno_value == ECONNRESET)
   {
   *errno_value = ERRNO_SMTPCLOSED;
   *message = US string_sprintf("Remote host %s [%s] closed connection "
@@ -502,14 +506,14 @@ if (addr->message != NULL)
   }
 else
   {
-  log_write(0, LOG_MAIN, "%s [%s]: %s",
-    host->name,
-    host->address,
-    strerror(addr->basic_errno));
-  deliver_msglog("%s %s [%s]: %s\n",
-    tod_stamp(tod_log),
-    host->name,
-    host->address,
+  uschar *msg =
+    ((log_extra_selector & LX_outgoing_port) != 0)?
+    string_sprintf("%s [%s]:%d", host->name, host->address,
+      (host->port == PORT_NONE)? 25 : host->port)
+    :
+    string_sprintf("%s [%s]", host->name, host->address);
+  log_write(0, LOG_MAIN, "%s %s", msg, strerror(addr->basic_errno));
+  deliver_msglog("%s %s %s\n", tod_stamp(tod_log), msg,
     strerror(addr->basic_errno));
   }
 }
@@ -746,7 +750,7 @@ Arguments:
                   failed by one of them.
   host            host to deliver to
   host_af         AF_INET or AF_INET6
-  port            TCP/IP port to use, in host byte order
+  port            default TCP/IP port to use, in host byte order
   interface       interface to bind to, or NULL
   tblock          transport instance block
   copy_host       TRUE if host set in addr->host_used must be copied, because
@@ -796,6 +800,7 @@ BOOL pass_message = FALSE;
 smtp_inblock inblock;
 smtp_outblock outblock;
 int max_rcpt = tblock->max_addresses;
+uschar *igquotstr = US"";
 uschar *local_authenticated_sender = authenticated_sender;
 uschar *helo_data;
 uschar *message = NULL;
@@ -947,6 +952,13 @@ goto SEND_QUIT;
       ob->command_timeout)) goto RESPONSE_FAILED;
     }
 
+  /* Set IGNOREQUOTA if the response to LHLO specifies support and the
+  lmtp_ignore_quota option was set. */
+
+  igquotstr = (lmtp && ob->lmtp_ignore_quota &&
+    pcre_exec(regex_IGNOREQUOTA, NULL, CS buffer, Ustrlen(CS buffer), 0,
+      PCRE_EOPT, NULL, 0) >= 0)? US" IGNOREQUOTA" : US"";
+
   /* Set tls_offered if the response to EHLO specifies support for STARTTLS. */
 
   #ifdef SUPPORT_TLS
@@ -1081,6 +1093,13 @@ if (continue_hostname == NULL
   int require_auth;
   uschar *fail_reason = US"server did not advertise AUTH support";
 
+  /* Set for IGNOREQUOTA if the response to LHLO specifies support and the
+  lmtp_ignore_quota option was set. */
+
+  igquotstr = (lmtp && ob->lmtp_ignore_quota &&
+    pcre_exec(regex_IGNOREQUOTA, NULL, CS buffer, Ustrlen(CS buffer), 0,
+      PCRE_EOPT, NULL, 0) >= 0)? US" IGNOREQUOTA" : US"";
+
   /* If the response to EHLO specified support for the SIZE parameter, note
   this, provided size_addition is non-negative. */
 
@@ -1343,8 +1362,8 @@ for (addr = first_addr;
   yield as OK, because this error can often mean that there is a problem with
   just one address, so we don't want to delay the host. */
 
-  count = smtp_write_command(&outblock, no_flush, "RCPT TO:<%s>\r\n",
-    transport_rcpt_address(addr, tblock->rcpt_include_affixes));
+  count = smtp_write_command(&outblock, no_flush, "RCPT TO:<%s>%s\r\n",
+    transport_rcpt_address(addr, tblock->rcpt_include_affixes), igquotstr);
   if (count < 0) goto SEND_FAILED;
   if (count > 0)
     {
@@ -1559,11 +1578,10 @@ if (!ok) ok = TRUE; else
         }
 
       /* SMTP, or success return from LMTP for this address. Pass back the
-      actual port used. */
+      actual host that was used. */
 
       addr->transport_return = OK;
       addr->more_errno = delivery_time;
-      thost->port = port;
       addr->host_used = thost;
       addr->special_action = flag;
       addr->message = conf;
@@ -2112,12 +2130,9 @@ else if (ob->hosts_randomize && hostlist->mx == MX_NONE && !continuing)
   }
 
 
-/* Sort out the port.  Set up a string for adding to the retry key if the port
-number is not the standard SMTP port. */
+/* Sort out the default port.  */
 
 if (!smtp_get_port(ob->port, addrlist, &port, tid)) return FALSE;
-pistring = string_sprintf(":%d", port);
-if (Ustrcmp(pistring, ":25") == 0) pistring = US"";
 
 
 /* For each host-plus-IP-address on the list:
@@ -2221,6 +2236,8 @@ for (cutoff_retry = 0; expired &&
 
     if (host->address == NULL)
       {
+      int new_port;
+      host_item *hh;
       uschar *canonical_name;
 
       if (host->status >= hstatus_unusable)
@@ -2232,12 +2249,19 @@ for (cutoff_retry = 0; expired &&
 
       DEBUG(D_transport) debug_printf("getting address for %s\n", host->name);
 
+      /* The host name is permitted to have an attached port. Find it, and
+      strip it from the name. Just remember it for now. */
+
+      new_port = host_item_get_port(host);
+
+      /* Count hosts looked up */
+
       hosts_looked_up++;
 
       /* Find by name if so configured, or if it's an IP address. We don't
       just copy the IP address, because we need the test-for-local to happen. */
 
-      if (ob->gethostbyname || string_is_ip_address(host->name, NULL) > 0)
+      if (ob->gethostbyname || string_is_ip_address(host->name, NULL) != 0)
         rc = host_find_byname(host, NULL, &canonical_name, TRUE);
       else
         {
@@ -2248,6 +2272,11 @@ for (cutoff_retry = 0; expired &&
           &canonical_name, NULL);
         }
 
+      /* Update the host (and any additional blocks, resulting from
+      multihoming) with a host-specific port, if any. */
+
+      for (hh = host; hh != nexthost; hh = hh->next) hh->port = new_port;
+
       /* 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
@@ -2341,6 +2370,14 @@ for (cutoff_retry = 0; expired &&
     deliver_host = host->name;
     deliver_host_address = host->address;
 
+    /* Set up a string for adding to the retry key if the port number is not
+    the standard SMTP port. A host may have its own port setting that overrides
+    the default. */
+
+    pistring = string_sprintf(":%d", (host->port == PORT_NONE)?
+      port : host->port);
+    if (Ustrcmp(pistring, ":25") == 0) pistring = US"";
+
     /* Select IPv4 or IPv6, and choose an outgoing interface. If the interface
     string changes upon expansion, we must add it to the key that is used for
     retries, because connections to the same host from a different interface