Allow underscore in dnslist lookups
[exim.git] / src / src / verify.c
index 3b0983919b44238f8e8180f735f85aae73938758..f653b6e0d27f8aef751d9afe04699de613f14a42 100644 (file)
@@ -1,10 +1,10 @@
-/* $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.55 2010/06/05 23:50:18 pdp Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2007 */
+/* Copyright (c) University of Cambridge 1995 - 2009 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Functions concerned with verifying things. The original code for callout
@@ -385,6 +385,14 @@ if (callout_overall < 0) callout_overall = 4 * callout;
 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. */
 
@@ -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);
 
-  /* 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";
@@ -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));
+    deliver_host = deliver_host_address = NULL;
+    deliver_domain = save_deliver_domain;
     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. */
@@ -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;
+  copyflag(vaddr, addr, af_pass_message);
   }
 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
+  vfprintf(f, format, ap);
+va_end(ap);
+}
+
+
+
 /*************************************************
 *            Verify an email address             *
 *************************************************/
@@ -951,8 +998,8 @@ if (parse_find_at(address) == 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;
     }
@@ -1216,24 +1263,25 @@ while (addr_new != NULL)
       {
       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)
-          fprintf(f, ": %s", strerror(addr->basic_errno));
+          respond_printf(f, ": %s", strerror(addr->basic_errno));
         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)
         {
-        fprintf(f, "%s\n    <-- %s", cr, p->address);
+        respond_printf(f, "%s\n    <-- %s", cr, p->address);
         p = p->parent;
         }
-      fprintf(f, "%s\n", cr);
+      respond_printf(f, "%s\n", cr);
       }
 
     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;
-      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)
-          fprintf(f, ": %s", strerror(addr->basic_errno));
+          respond_printf(f, ": %s", strerror(addr->basic_errno));
         if (addr->message != NULL)
-          fprintf(f, ": %s", addr->message);
+          respond_printf(f, ": %s", addr->message);
         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)
         {
-        fprintf(f, "%s\n    <-- %s", cr, p->address);
+        respond_printf(f, "%s\n    <-- %s", cr, p->address);
         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;
     }
@@ -1283,16 +1330,16 @@ while (addr_new != 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
-        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 ";
-      fprintf(f, "%s<%s>\r\n", ok_prefix, addr2->address);
+      respond_printf(f, "%s<%s>\r\n", ok_prefix, addr2->address);
       }
     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
-  dot separators instead of colons. */
+  dot separators instead of colons, except when the lookup type is "iplsearch".
+  */
 
   if (mac_islookup(search_type, lookup_absfilequery))
     {
@@ -2206,11 +2254,13 @@ if (iplookup)
     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);
-    (void)host_nmtoa(insize, incoming, mlen, buffer, '.');
+    (void)host_nmtoa(insize, incoming, mlen, buffer, sep);
     key = buffer;
     filename = semicolon + 1;
     }
@@ -2463,16 +2513,18 @@ return verify_check_this_host(listptr, sender_host_cache, NULL,
 
 
 /*************************************************
-*    Invert an IP address for a DNS black list   *
+*              Invert an IP address              *
 *************************************************/
 
-/*
+/* Originally just used for DNS xBL lists, now also used for the
+reverse_ip expansion operator.
+
 Arguments:
   buffer         where to put the answer
   address        the address to invert
 */
 
-static void
+void
 invert_address(uschar *buffer, uschar *address)
 {
 int bin[4];
@@ -2986,7 +3038,7 @@ while ((domain = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL
 
   for (s = domain; *s != 0; s++)
     {
-    if (!isalnum(*s) && *s != '-' && *s != '.')
+    if (!isalnum(*s) && *s != '-' && *s != '.' && *s != '_')
       {
       log_write(0, LOG_MAIN, "dnslists domain \"%s\" contains "
         "strange characters - is this right?", domain);
@@ -2998,7 +3050,7 @@ while ((domain = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL
 
   if (domain_txt != domain) for (s = domain_txt; *s != 0; s++)
     {
-    if (!isalnum(*s) && *s != '-' && *s != '.')
+    if (!isalnum(*s) && *s != '-' && *s != '.' && *s != '_')
       {
       log_write(0, LOG_MAIN, "dnslists domain \"%s\" contains "
         "strange characters - is this right?", domain_txt);
@@ -3018,6 +3070,7 @@ while ((domain = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL
     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);
       }
@@ -3052,6 +3105,7 @@ while ((domain = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL
       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;