Produce a more useful error message if an SMTP transport's hosts
[exim.git] / src / src / transports / smtp.c
index 0bc5b533ee77872c801a1fedd738e3bb91d5cbea..e8354ad7a449b1f88c6f1a945fcccf3abcced536 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/transports/smtp.c,v 1.34 2007/02/06 14:19:00 ph10 Exp $ */
+/* $Cambridge: exim/src/src/transports/smtp.c,v 1.40 2008/09/05 16:59:48 fanf2 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -39,7 +39,7 @@ optionlist smtp_transport_options[] = {
       (void *)offsetof(smtp_transport_options_block, data_timeout) },
   { "delay_after_cutoff", opt_bool,
       (void *)offsetof(smtp_transport_options_block, delay_after_cutoff) },
-#ifdef EXPERIMENTAL_DOMAINKEYS
+  #if (defined EXPERIMENTAL_DOMAINKEYS) || (defined EXPERIMENTAL_DKIM)
   { "dk_canon", opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, dk_canon) },
   { "dk_domain", opt_stringptr,
@@ -52,7 +52,19 @@ optionlist smtp_transport_options[] = {
       (void *)offsetof(smtp_transport_options_block, dk_selector) },
   { "dk_strict", opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, dk_strict) },
-#endif
+  { "dkim_canon", opt_stringptr,
+      (void *)offsetof(smtp_transport_options_block, dkim_canon) },
+  { "dkim_domain", opt_stringptr,
+      (void *)offsetof(smtp_transport_options_block, dkim_domain) },
+  { "dkim_private_key", opt_stringptr,
+      (void *)offsetof(smtp_transport_options_block, dkim_private_key) },
+  { "dkim_selector", opt_stringptr,
+      (void *)offsetof(smtp_transport_options_block, dkim_selector) },
+  { "dkim_sign_headers", opt_stringptr,
+      (void *)offsetof(smtp_transport_options_block, dkim_sign_headers) },
+  { "dkim_strict", opt_stringptr,
+      (void *)offsetof(smtp_transport_options_block, dkim_strict) },
+  #endif
   { "dns_qualify_single",   opt_bool,
       (void *)offsetof(smtp_transport_options_block, dns_qualify_single) },
   { "dns_search_parents",   opt_bool,
@@ -77,6 +89,8 @@ optionlist smtp_transport_options[] = {
       (void *)offsetof(smtp_transport_options_block, hosts) },
   { "hosts_avoid_esmtp",    opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, hosts_avoid_esmtp) },
+  { "hosts_avoid_pipelining", opt_stringptr,
+      (void *)offsetof(smtp_transport_options_block, hosts_avoid_pipelining) },
   #ifdef SUPPORT_TLS
   { "hosts_avoid_tls",      opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, hosts_avoid_tls) },
@@ -160,6 +174,7 @@ smtp_transport_options_block smtp_transport_option_defaults = {
   NULL,                /* hosts_require_auth */
   NULL,                /* hosts_require_tls */
   NULL,                /* hosts_avoid_tls */
+  NULL,                /* hosts_avoid_pipelining */
   NULL,                /* hosts_avoid_esmtp */
   NULL,                /* hosts_nopass_tls */
   5*60,                /* command_timeout */
@@ -192,13 +207,19 @@ smtp_transport_options_block smtp_transport_option_defaults = {
   NULL,                /* tls_verify_certificates */
   TRUE                 /* tls_tempfail_tryclear */
   #endif
-  #ifdef EXPERIMENTAL_DOMAINKEYS
+  #if (defined EXPERIMENTAL_DOMAINKEYS) || (defined EXPERIMENTAL_DKIM)
  ,NULL,                /* dk_canon */
   NULL,                /* dk_domain */
   NULL,                /* dk_headers */
   NULL,                /* dk_private_key */
   NULL,                /* dk_selector */
   NULL                 /* dk_strict */
+ ,NULL,                /* dkim_canon */
+  NULL,                /* dkim_domain */
+  NULL,                /* dkim_private_key */
+  NULL,                /* dkim_selector */
+  NULL,                /* dkim_sign_headers */
+  NULL                 /* dkim_strict */
   #endif
 };
 
@@ -786,7 +807,8 @@ return yield;
 
 /* 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.
+input. TLS is never active on a passed channel; the previous process always
+closes it down before passing the connection on.
 
 Otherwise, we have to make a connection to the remote host, and do the
 initial protocol exchange.
@@ -883,6 +905,11 @@ outblock.ptr = outbuffer;
 outblock.cmd_count = 0;
 outblock.authenticating = FALSE;
 
+/* Reset the parameters of a TLS session. */
+
+tls_cipher = NULL;
+tls_peerdn = NULL;
+
 /* If an authenticated_sender override has been specified for this transport
 instance, expand it. If the expansion is forced to fail, and there was already
 an authenticated_sender for this message, the original value will be used.
@@ -914,7 +941,8 @@ if (continue_hostname == NULL)
   {
   inblock.sock = outblock.sock =
     smtp_connect(host, host_af, port, interface, ob->connect_timeout,
-      ob->keepalive);
+      ob->keepalive);   /* This puts port into host->port */
+
   if (inblock.sock < 0)
     {
     set_errno(addrlist, (errno == ETIMEDOUT)? ERRNO_CONNECTTIMEOUT : errno,
@@ -1038,6 +1066,7 @@ else
   {
   inblock.sock = outblock.sock = fileno(stdin);
   smtp_command = big_buffer;
+  host->port = port;    /* Record the port that was used */
   }
 
 /* If TLS is available on this connection, whether continued or not, attempt to
@@ -1189,9 +1218,12 @@ if (continue_hostname == NULL
       PCRE_EOPT, NULL, 0) >= 0;
 
   /* Note whether the server supports PIPELINING. If hosts_avoid_esmtp matched
-  the current host, esmtp will be false, so PIPELINING can never be used. */
+  the current host, esmtp will be false, so PIPELINING can never be used. If
+  the current host matches hosts_avoid_pipelining, don't do it. */
 
   smtp_use_pipelining = esmtp &&
+    verify_check_this_host(&(ob->hosts_avoid_pipelining), NULL, host->name,
+      host->address, NULL) != OK &&
     pcre_exec(regex_PIPELINING, NULL, CS buffer, Ustrlen(CS buffer), 0,
       PCRE_EOPT, NULL, 0) >= 0;
 
@@ -1225,14 +1257,25 @@ if (continue_hostname == NULL
       DEBUG(D_transport) debug_printf("scanning authentication mechanisms\n");
 
       /* Scan the configured authenticators looking for one which is configured
-      for use as a client and whose name matches an authentication mechanism
-      supported by the server. If one is found, attempt to authenticate by
-      calling its client function. */
+      for use as a client, which is not suppressed by client_condition, and
+      whose name matches an authentication mechanism supported by the server.
+      If one is found, attempt to authenticate by calling its client function.
+      */
 
       for (au = auths; !smtp_authenticated && au != NULL; au = au->next)
         {
         uschar *p = names;
-        if (!au->client) continue;
+        if (!au->client ||
+            (au->client_condition != NULL &&
+             !expand_check_condition(au->client_condition, au->name,
+               US"client authenticator")))
+          {
+          DEBUG(D_transport) debug_printf("skipping %s authenticator: %s\n",
+            au->name,
+            (au->client)? "client_condition is false" :
+                          "not configured as a client");
+          continue;
+          }
 
         /* Loop to scan supported server mechanisms */
 
@@ -1548,23 +1591,24 @@ if (!ok) ok = TRUE; else
   DEBUG(D_transport|D_v)
     debug_printf("  SMTP>> writing message and terminating \".\"\n");
   transport_count = 0;
-#ifdef EXPERIMENTAL_DOMAINKEYS
-  if ( (ob->dk_private_key != NULL) && (ob->dk_selector != NULL) )
-    ok = dk_transport_write_message(addrlist, inblock.sock,
-      topt_use_crlf | topt_end_dot | topt_escape_headers |
-        (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,            /* No size limit */
-      tblock->add_headers, tblock->remove_headers,
-      US".", US"..",    /* Escaping strings */
-      tblock->rewrite_rules, tblock->rewrite_existflags,
-      ob->dk_private_key, ob->dk_domain, ob->dk_selector,
-      ob->dk_canon, ob->dk_headers, ob->dk_strict);
-  else
-#endif
+#if (defined EXPERIMENTAL_DOMAINKEYS) || (defined EXPERIMENTAL_DKIM)
+  ok = dkim_transport_write_message(addrlist, inblock.sock,
+    topt_use_crlf | topt_end_dot | topt_escape_headers |
+      (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,            /* No size limit */
+    tblock->add_headers, tblock->remove_headers,
+    US".", US"..",    /* Escaping strings */
+    tblock->rewrite_rules, tblock->rewrite_existflags,
+    ob->dkim_private_key, ob->dkim_domain, ob->dkim_selector,
+    ob->dkim_canon, ob->dkim_strict, ob->dkim_sign_headers,
+    ob->dk_private_key, ob->dk_domain, ob->dk_selector,
+    ob->dk_canon, ob->dk_headers, ob->dk_strict
+    );
+#else
   ok = transport_write_message(addrlist, inblock.sock,
     topt_use_crlf | topt_end_dot | topt_escape_headers |
       (tblock->body_only? topt_no_headers : 0) |
@@ -1576,6 +1620,7 @@ if (!ok) ok = TRUE; else
     tblock->add_headers, tblock->remove_headers,
     US".", US"..",    /* Escaping strings */
     tblock->rewrite_rules, tblock->rewrite_existflags);
+#endif
 
   /* transport_write_message() uses write() because it is called from other
   places to write to non-sockets. This means that under some OS (e.g. Solaris)
@@ -2219,6 +2264,15 @@ if (hostlist == NULL || (ob->hosts_override && ob->hosts != NULL))
 
     host_build_hostlist(&hostlist, s, ob->hosts_randomize);
 
+    /* Check that the expansion yielded something useful. */
+    if (hostlist == NULL)
+      {
+      addrlist->message =
+        string_sprintf("%s transport has empty hosts setting", tblock->name);
+      addrlist->transport_return = PANIC;
+      return FALSE;   /* Only top address has status */
+      }
+
     /* If there was no expansion of hosts, save the host list for
     next time. */