Fix to EXPN not working under TLS. Fixes bug #744
[exim.git] / src / src / verify.c
index 3b0983919b44238f8e8180f735f85aae73938758..ab7e2756fae0dd825a3e8ffd76322cd7bdbca3cf 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/verify.c,v 1.46 2007/01/17 11:17:58 ph10 Exp $ */
+/* $Cambridge: exim/src/src/verify.c,v 1.52 2008/09/29 11:41:07 nm4 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -385,6 +385,14 @@ if (callout_overall < 0) callout_overall = 4 * callout;
 if (callout_connect < 0) callout_connect = callout;
 callout_start_time = time(NULL);
 
 if (callout_connect < 0) callout_connect = callout;
 callout_start_time = time(NULL);
 
+/* Before doing a real callout, if this is an SMTP connection, flush the SMTP
+output because a callout might take some time. When PIPELINING is active and
+there are many recipients, the total time for doing lots of callouts can add up
+and cause the client to time out. So in this case we forgo the PIPELINING
+optimization. */
+
+if (smtp_out != NULL && !disable_callout_flush) mac_smtp_fflush();
+
 /* Now make connections to the hosts and do real callouts. The list of hosts
 is passed in as an argument. */
 
 /* Now make connections to the hosts and do real callouts. The list of hosts
 is passed in as an argument. */
 
@@ -442,21 +450,6 @@ for (host = host_list; host != NULL && !done; host = host->next)
     log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: %s", addr->address,
       addr->message);
 
     log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: %s", addr->address,
       addr->message);
 
-  /* Expand the helo_data string to find the host name to use. */
-
-  if (tf->helo_data != NULL)
-    {
-    uschar *s = expand_string(tf->helo_data);
-    if (s == NULL)
-      log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: failed to expand transport's "
-        "helo_data value for callout: %s", addr->address,
-        expand_string_message);
-    else active_hostname = s;
-    }
-
-  deliver_host = deliver_host_address = NULL;
-  deliver_domain = save_deliver_domain;
-
   /* Set HELO string according to the protocol */
 
   if (Ustrcmp(tf->protocol, "lmtp") == 0) helo = US"LHLO";
   /* Set HELO string according to the protocol */
 
   if (Ustrcmp(tf->protocol, "lmtp") == 0) helo = US"LHLO";
@@ -487,9 +480,26 @@ for (host = host_list; host != NULL && !done; host = host->next)
     {
     addr->message = string_sprintf("could not connect to %s [%s]: %s",
         host->name, host->address, strerror(errno));
     {
     addr->message = string_sprintf("could not connect to %s [%s]: %s",
         host->name, host->address, strerror(errno));
+    deliver_host = deliver_host_address = NULL;
+    deliver_domain = save_deliver_domain;
     continue;
     }
 
     continue;
     }
 
+  /* Expand the helo_data string to find the host name to use. */
+
+  if (tf->helo_data != NULL)
+    {
+    uschar *s = expand_string(tf->helo_data);
+    if (s == NULL)
+      log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: failed to expand transport's "
+        "helo_data value for callout: %s", addr->address,
+        expand_string_message);
+    else active_hostname = s;
+    }
+
+  deliver_host = deliver_host_address = NULL;
+  deliver_domain = save_deliver_domain;
+
   /* Wait for initial response, and send HELO. The smtp_write_command()
   function leaves its command in big_buffer. This is used in error responses.
   Initialize it in case the connection is rejected. */
   /* Wait for initial response, and send HELO. The smtp_write_command()
   function leaves its command in big_buffer. This is used in error responses.
   Initialize it in case the connection is rejected. */
@@ -845,6 +855,7 @@ if (addr != vaddr)
   vaddr->basic_errno = addr->basic_errno;
   vaddr->more_errno = addr->more_errno;
   vaddr->p.address_data = addr->p.address_data;
   vaddr->basic_errno = addr->basic_errno;
   vaddr->more_errno = addr->more_errno;
   vaddr->p.address_data = addr->p.address_data;
+  copyflag(vaddr, addr, af_pass_message);
   }
 return yield;
 }
   }
 return yield;
 }
@@ -852,6 +863,42 @@ return yield;
 
 
 
 
 
 
+/**************************************************
+* printf that automatically handles TLS if needed *
+***************************************************/
+
+/* This function is used by verify_address() as a substitute for all fprintf()
+calls; a direct fprintf() will not produce output in a TLS SMTP session, such
+as a response to an EXPN command.  smtp_in.c makes smtp_printf available but
+that assumes that we always use the smtp_out FILE* when not using TLS or the
+ssl buffer when we are.  Instead we take a FILE* parameter and check to see if
+that is smtp_out; if so, smtp_printf() with TLS support, otherwise regular
+fprintf().
+
+Arguments:
+  f           the candidate FILE* to write to
+  format      format string
+  ...         optional arguments
+
+Returns:
+              nothing
+*/
+
+static void PRINTF_FUNCTION(2,3)
+respond_printf(FILE *f, char *format, ...)
+{
+va_list ap;
+
+va_start(ap, format);
+if (smtp_out && (f == smtp_out))
+  smtp_vprintf(format, ap);
+else
+  fprintf(f, format, ap);
+va_end(ap);
+}
+
+
+
 /*************************************************
 *            Verify an email address             *
 *************************************************/
 /*************************************************
 *            Verify an email address             *
 *************************************************/
@@ -951,8 +998,8 @@ if (parse_find_at(address) == NULL)
   if ((options & vopt_qualify) == 0)
     {
     if (f != NULL)
   if ((options & vopt_qualify) == 0)
     {
     if (f != NULL)
-      fprintf(f, "%sA domain is required for \"%s\"%s\n", ko_prefix, address,
-        cr);
+      respond_printf(f, "%sA domain is required for \"%s\"%s\n",
+        ko_prefix, address, cr);
     *failure_ptr = US"qualify";
     return FAIL;
     }
     *failure_ptr = US"qualify";
     return FAIL;
     }
@@ -1216,24 +1263,25 @@ while (addr_new != NULL)
       {
       address_item *p = addr->parent;
 
       {
       address_item *p = addr->parent;
 
-      fprintf(f, "%s%s %s", ko_prefix, full_info? addr->address : address,
+      respond_printf(f, "%s%s %s", ko_prefix,
+        full_info? addr->address : address,
         address_test_mode? "is undeliverable" : "failed to verify");
       if (!expn && admin_user)
         {
         if (addr->basic_errno > 0)
         address_test_mode? "is undeliverable" : "failed to verify");
       if (!expn && admin_user)
         {
         if (addr->basic_errno > 0)
-          fprintf(f, ": %s", strerror(addr->basic_errno));
+          respond_printf(f, ": %s", strerror(addr->basic_errno));
         if (addr->message != NULL)
         if (addr->message != NULL)
-          fprintf(f, ": %s", addr->message);
+          respond_printf(f, ": %s", addr->message);
         }
 
       /* Show parents iff doing full info */
 
       if (full_info) while (p != NULL)
         {
         }
 
       /* Show parents iff doing full info */
 
       if (full_info) while (p != NULL)
         {
-        fprintf(f, "%s\n    <-- %s", cr, p->address);
+        respond_printf(f, "%s\n    <-- %s", cr, p->address);
         p = p->parent;
         }
         p = p->parent;
         }
-      fprintf(f, "%s\n", cr);
+      respond_printf(f, "%s\n", cr);
       }
 
     if (!full_info) return copy_error(vaddr, addr, FAIL);
       }
 
     if (!full_info) return copy_error(vaddr, addr, FAIL);
@@ -1248,28 +1296,27 @@ while (addr_new != NULL)
     if (f != NULL)
       {
       address_item *p = addr->parent;
     if (f != NULL)
       {
       address_item *p = addr->parent;
-      fprintf(f, "%s%s cannot be resolved at this time", ko_prefix,
+      respond_printf(f, "%s%s cannot be resolved at this time", ko_prefix,
         full_info? addr->address : address);
       if (!expn && admin_user)
         {
         if (addr->basic_errno > 0)
         full_info? addr->address : address);
       if (!expn && admin_user)
         {
         if (addr->basic_errno > 0)
-          fprintf(f, ": %s", strerror(addr->basic_errno));
+          respond_printf(f, ": %s", strerror(addr->basic_errno));
         if (addr->message != NULL)
         if (addr->message != NULL)
-          fprintf(f, ": %s", addr->message);
+          respond_printf(f, ": %s", addr->message);
         else if (addr->basic_errno <= 0)
         else if (addr->basic_errno <= 0)
-          fprintf(f, ": unknown error");
+          respond_printf(f, ": unknown error");
         }
 
       /* Show parents iff doing full info */
 
       if (full_info) while (p != NULL)
         {
         }
 
       /* Show parents iff doing full info */
 
       if (full_info) while (p != NULL)
         {
-        fprintf(f, "%s\n    <-- %s", cr, p->address);
+        respond_printf(f, "%s\n    <-- %s", cr, p->address);
         p = p->parent;
         }
         p = p->parent;
         }
-      fprintf(f, "%s\n", cr);
+      respond_printf(f, "%s\n", cr);
       }
       }
-
     if (!full_info) return copy_error(vaddr, addr, DEFER);
       else if (yield == OK) yield = DEFER;
     }
     if (!full_info) return copy_error(vaddr, addr, DEFER);
       else if (yield == OK) yield = DEFER;
     }
@@ -1283,16 +1330,16 @@ while (addr_new != NULL)
     if (addr_new == NULL)
       {
       if (addr_local == NULL && addr_remote == NULL)
     if (addr_new == NULL)
       {
       if (addr_local == NULL && addr_remote == NULL)
-        fprintf(f, "250 mail to <%s> is discarded\r\n", address);
+        respond_printf(f, "250 mail to <%s> is discarded\r\n", address);
       else
       else
-        fprintf(f, "250 <%s>\r\n", address);
+        respond_printf(f, "250 <%s>\r\n", address);
       }
     else while (addr_new != NULL)
       {
       address_item *addr2 = addr_new;
       addr_new = addr2->next;
       if (addr_new == NULL) ok_prefix = US"250 ";
       }
     else while (addr_new != NULL)
       {
       address_item *addr2 = addr_new;
       addr_new = addr2->next;
       if (addr_new == NULL) ok_prefix = US"250 ";
-      fprintf(f, "%s<%s>\r\n", ok_prefix, addr2->address);
+      respond_printf(f, "%s<%s>\r\n", ok_prefix, addr2->address);
       }
     return OK;
     }
       }
     return OK;
     }
@@ -2191,7 +2238,8 @@ if (iplookup)
   name, we have to fish the file off the start of the query. For a single-key
   lookup, the key is the current IP address, masked appropriately, and
   reconverted to text form, with the mask appended. For IPv6 addresses, specify
   name, we have to fish the file off the start of the query. For a single-key
   lookup, the key is the current IP address, masked appropriately, and
   reconverted to text form, with the mask appended. For IPv6 addresses, specify
-  dot separators instead of colons. */
+  dot separators instead of colons, except when the lookup type is "iplsearch".
+  */
 
   if (mac_islookup(search_type, lookup_absfilequery))
     {
 
   if (mac_islookup(search_type, lookup_absfilequery))
     {
@@ -2206,11 +2254,13 @@ if (iplookup)
     filename = NULL;
     key = semicolon + 1;
     }
     filename = NULL;
     key = semicolon + 1;
     }
-  else
+  else   /* Single-key style */
     {
     {
+    int sep = (Ustrcmp(lookup_list[search_type].name, "iplsearch") == 0)?
+      ':' : '.';
     insize = host_aton(cb->host_address, incoming);
     host_mask(insize, incoming, mlen);
     insize = host_aton(cb->host_address, incoming);
     host_mask(insize, incoming, mlen);
-    (void)host_nmtoa(insize, incoming, mlen, buffer, '.');
+    (void)host_nmtoa(insize, incoming, mlen, buffer, sep);
     key = buffer;
     filename = semicolon + 1;
     }
     key = buffer;
     filename = semicolon + 1;
     }
@@ -3018,6 +3068,7 @@ while ((domain = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL
     if (rc == OK)
       {
       dnslist_domain = string_copy(domain_txt);
     if (rc == OK)
       {
       dnslist_domain = string_copy(domain_txt);
+      dnslist_matched = string_copy(sender_host_address);
       HDEBUG(D_dnsbl) debug_printf("=> that means %s is listed at %s\n",
         sender_host_address, dnslist_domain);
       }
       HDEBUG(D_dnsbl) debug_printf("=> that means %s is listed at %s\n",
         sender_host_address, dnslist_domain);
       }
@@ -3052,6 +3103,7 @@ while ((domain = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL
       if (rc == OK)
         {
         dnslist_domain = string_copy(domain_txt);
       if (rc == OK)
         {
         dnslist_domain = string_copy(domain_txt);
+        dnslist_matched = string_copy(keydomain);
         HDEBUG(D_dnsbl) debug_printf("=> that means %s is listed at %s\n",
           keydomain, dnslist_domain);
         return OK;
         HDEBUG(D_dnsbl) debug_printf("=> that means %s is listed at %s\n",
           keydomain, dnslist_domain);
         return OK;