Canonicize IPv6 addresses that are supplied via -bh or -bhc.
[users/jgh/exim.git] / src / src / verify.c
index bae410e6722c52464e16d6ab7110f8530027147b..cd2ae666c8f15b4661ed92a89c1d9f7b197bb65c 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/verify.c,v 1.1 2004/10/07 10:39:01 ph10 Exp $ */
+/* $Cambridge: exim/src/src/verify.c,v 1.6 2004/11/18 11:17:33 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -123,7 +123,8 @@ Arguments:
   portstring        "port" option from transport, or NULL
   protocolstring    "protocol" option from transport, or NULL
   callout           the per-command callout timeout
-  callout_overall   the overall callout timeout (if < 0; use 4*callout)
+  callout_overall   the overall callout timeout (if < 0 use 4*callout)
+  callout_connect   the callout connection timeout (if < 0 use callout)
   options           the verification options - these bits are used:
                       vopt_is_recipient => this is a recipient address
                       vopt_callout_no_cache => don't use callout cache
@@ -138,8 +139,8 @@ Returns:            OK/FAIL/DEFER
 
 static int
 do_callout(address_item *addr, host_item *host_list, transport_feedback *tf,
-  int callout, int callout_overall, int options, uschar *se_mailfrom,
-  uschar *pm_mailfrom)
+  int callout, int callout_overall, int callout_connect, int options, 
+  uschar *se_mailfrom, uschar *pm_mailfrom)
 {
 BOOL is_recipient = (options & vopt_is_recipient) != 0;
 BOOL callout_no_cache = (options & vopt_callout_no_cache) != 0;
@@ -150,6 +151,8 @@ BOOL done = FALSE;
 uschar *address_key;
 uschar *from_address;
 uschar *random_local_part = NULL;
+uschar **failure_ptr = is_recipient? 
+  &recipient_verify_failure : &sender_verify_failure;
 open_db dbblock;
 open_db *dbm_file = NULL;
 dbdata_callout_cache new_domain_record;
@@ -235,6 +238,7 @@ if (dbm_file != NULL)
       setflag(addr, af_verify_nsfail);
       addr->user_message = US"(result of an earlier callout reused).";
       yield = FAIL;
+      *failure_ptr = US"mail"; 
       goto END_CALLOUT;
       }
 
@@ -281,6 +285,7 @@ if (dbm_file != NULL)
           debug_printf("callout cache: domain does not accept "
             "RCPT TO:<postmaster@domain>\n");
         yield = FAIL;
+        *failure_ptr = US"postmaster"; 
         setflag(addr, af_verify_pmfail);
         addr->user_message = US"(result of earlier verification reused).";
         goto END_CALLOUT;
@@ -329,6 +334,7 @@ if (dbm_file != NULL)
       HDEBUG(D_verify)
         debug_printf("callout cache: address record is negative\n");
       addr->user_message = US"Previous (cached) callout verification failure";
+      *failure_ptr = US"recipient"; 
       yield = FAIL;
       }
     goto END_CALLOUT;
@@ -355,10 +361,11 @@ if (callout_random && callout_random_local_part != NULL)
       "callout_random_local_part: %s", expand_string_message);
   }
 
-/* Default the overall callout timeout if not set, and record the time we are
-starting so that we can enforce it. */
+/* Default the connect and overall callout timeouts if not set, and record the
+time we are starting so that we can enforce it. */
 
 if (callout_overall < 0) callout_overall = 4 * callout;
+if (callout_connect < 0) callout_connect = callout;
 callout_start_time = time(NULL);
 
 /* Now make connections to the hosts and do real callouts. The list of hosts
@@ -435,10 +442,10 @@ for (host = host_list; host != NULL && !done; host = host->next)
   outblock.authenticating = FALSE;
 
   /* Connect to the host; on failure, just loop for the next one, but we
-  set the error for the last one. */
+  set the error for the last one. Use the callout_connect timeout. */
 
   inblock.sock = outblock.sock =
-    smtp_connect(host, host_af, port, interface, callout, TRUE);
+    smtp_connect(host, host_af, port, interface, callout_connect, TRUE);
   if (inblock.sock < 0)
     {
     addr->message = string_sprintf("could not connect to %s [%s]: %s",
@@ -474,6 +481,7 @@ for (host = host_list; host != NULL && !done; host = host->next)
 
   if (!done)
     {
+    *failure_ptr = US"mail"; 
     if (errno == 0 && responsebuffer[0] == '5')
       {
       setflag(addr, af_verify_nsfail);
@@ -548,7 +556,10 @@ for (host = host_list; host != NULL && !done; host = host->next)
       if (done)
         new_address_record.result = ccache_accept;
       else if (errno == 0 && responsebuffer[0] == '5')
+        {
+        *failure_ptr = US"recipient";  
         new_address_record.result = ccache_reject;
+        } 
 
       /* Do postmaster check if requested */
 
@@ -575,6 +586,7 @@ for (host = host_list; host != NULL && !done; host = host->next)
           new_domain_record.postmaster_result = ccache_accept;
         else if (errno == 0 && responsebuffer[0] == '5')
           {
+          *failure_ptr = US"postmaster"; 
           setflag(addr, af_verify_pmfail);
           new_domain_record.postmaster_result = ccache_reject;
           }
@@ -782,9 +794,10 @@ Arguments:
                      vopt_callout_recippmaster => use postmaster for recipient
 
   callout          if > 0, specifies that callout is required, and gives timeout
-                     for individual connections and commands
+                     for individual commands
   callout_overall  if > 0, gives overall timeout for the callout function;
                    if < 0, a default is used (see do_callout())
+  callout_connect  the connection timeout for callouts                  
   se_mailfrom      when callout is requested to verify a sender, use this
                      in MAIL FROM; NULL => ""
   pm_mailfrom      when callout is requested, if non-NULL, do the postmaster
@@ -800,13 +813,13 @@ Returns:           OK      address verified
 
 int
 verify_address(address_item *vaddr, FILE *f, int options, int callout,
-  int callout_overall, uschar *se_mailfrom, uschar *pm_mailfrom, BOOL *routed)
+  int callout_overall, int callout_connect, uschar *se_mailfrom, 
+  uschar *pm_mailfrom, BOOL *routed)
 {
 BOOL allok = TRUE;
 BOOL full_info = (f == NULL)? FALSE : (debug_selector != 0);
 BOOL is_recipient = (options & vopt_is_recipient) != 0;
 BOOL expn         = (options & vopt_expn) != 0;
-
 int i;
 int yield = OK;
 int verify_type = expn? v_expn :
@@ -817,11 +830,17 @@ address_item *addr_new = NULL;
 address_item *addr_remote = NULL;
 address_item *addr_local = NULL;
 address_item *addr_succeed = NULL;
+uschar **failure_ptr = is_recipient? 
+  &recipient_verify_failure : &sender_verify_failure;
 uschar *ko_prefix, *cr;
 uschar *address = vaddr->address;
 uschar *save_sender;
 uschar null_sender[] = { 0 };             /* Ensure writeable memory */
 
+/* Clear, just in case */
+
+*failure_ptr = NULL;
+
 /* Set up a prefix and suffix for error message which allow us to use the same
 output statements both in EXPN mode (where an SMTP response is needed) and when
 debugging with an output file. */
@@ -842,6 +861,7 @@ if (parse_find_at(address) == NULL)
     if (f != NULL)
       fprintf(f, "%sA domain is required for \"%s\"%s\n", ko_prefix, address,
         cr);
+    *failure_ptr = US"qualify";     
     return FAIL;
     }
   address = rewrite_address_qualify(address, is_recipient);
@@ -1017,14 +1037,18 @@ while (addr_new != NULL)
           else
             {
             uschar *canonical_name;
-            host_item *host;
+            host_item *host, *nexthost;
             host_build_hostlist(&host_list, s, tf.hosts_randomize);
 
             /* Just ignore failures to find a host address. If we don't manage
-            to find any addresses, the callout will defer. */
+            to find any addresses, the callout will defer. Note that more than 
+            one address may be found for a single host, which will result in 
+            additional host items being inserted into the chain. Hence we must 
+            save the next host first. */
 
-            for (host = host_list; host != NULL; host = host->next)
+            for (host = host_list; host != NULL; host = nexthost)
               {
+              nexthost = host->next;
               if (tf.gethostbyname || string_is_ip_address(host->name, NULL))
                 (void)host_find_byname(host, NULL, &canonical_name, TRUE);
               else
@@ -1040,7 +1064,8 @@ while (addr_new != NULL)
           }
         }
 
-      /* Can only do a callout if we have at least one host! */
+      /* Can only do a callout if we have at least one host! If the callout 
+      fails, it will have set ${sender,recipient}_verify_failure. */
 
       if (host_list != NULL)
         {
@@ -1054,7 +1079,7 @@ while (addr_new != NULL)
         else
           {
           rc = do_callout(addr, host_list, &tf, callout, callout_overall,
-            options, se_mailfrom, pm_mailfrom);
+            callout_connect, options, se_mailfrom, pm_mailfrom);
           }
         }
       else
@@ -1064,6 +1089,10 @@ while (addr_new != NULL)
         }
       }
     }
+    
+  /* Otherwise, any failure is a routing failure */
+  
+  else *failure_ptr = US"route"; 
 
   /* A router may return REROUTED if it has set up a child address as a result
   of a change of domain name (typically from widening). In this case we always
@@ -1251,7 +1280,10 @@ else for (addr_list = addr_local, i = 0; i < 2; addr_list = addr_remote, i++)
     }
   }
 
-return yield;  /* Will be DEFER or FAIL if any one address has */
+/* Will be DEFER or FAIL if any one address has, only for full_info (which is 
+the -bv or -bt case). */
+
+return yield;  
 }
 
 
@@ -1426,6 +1458,7 @@ Arguments:
   log_msgptr       points to where to put a log error message
   callout          timeout for callout check (passed to verify_address())
   callout_overall  overall callout timeout (ditto)
+  callout_connect  connect callout timeout (ditto) 
   se_mailfrom      mailfrom for verify; NULL => ""
   pm_mailfrom      sender for pm callout check (passed to verify_address())
   options          callout options (passed to verify_address())
@@ -1439,8 +1472,8 @@ Returns:           result of the verification attempt: OK, FAIL, or DEFER;
 
 int
 verify_check_header_address(uschar **user_msgptr, uschar **log_msgptr,
-  int callout, int callout_overall, uschar *se_mailfrom, uschar *pm_mailfrom,
-  int options)
+  int callout, int callout_overall, int callout_connect, uschar *se_mailfrom, 
+  uschar *pm_mailfrom, int options)
 {
 static int header_types[] = { htype_sender, htype_reply_to, htype_from };
 int yield = FAIL;
@@ -1529,7 +1562,8 @@ for (i = 0; i < 3; i++)
           {
           vaddr = deliver_make_addr(address, FALSE);
           new_ok = verify_address(vaddr, NULL, options | vopt_fake_sender,
-            callout, callout_overall, se_mailfrom, pm_mailfrom, NULL);
+            callout, callout_overall, callout_connect, se_mailfrom, 
+            pm_mailfrom, NULL);
           }
         }
 
@@ -1832,7 +1866,8 @@ if (Ustrncmp(ss, "net", 3) == 0 && (semicolon = Ustrchr(ss, ';')) != NULL)
     /* Adjust parameters for the type of lookup. For a query-style
     lookup, there is no file name, and the "key" is just 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. */
+    appropriately, and reconverted to text form, with the mask appended. 
+    For IPv6 addresses, specify dot separators instead of colons. */
 
     if (mac_islookup(search_type, lookup_querystyle))
       {
@@ -1843,7 +1878,7 @@ if (Ustrncmp(ss, "net", 3) == 0 && (semicolon = Ustrchr(ss, ';')) != NULL)
       {
       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, '.');
       key = buffer;
       filename = semicolon + 1;
       }
@@ -2020,7 +2055,9 @@ int
 verify_check_this_host(uschar **listptr, unsigned int *cache_bits,
   uschar *host_name, uschar *host_address, uschar **valueptr)
 {
+int rc;
 unsigned int *local_cache_bits = cache_bits;
+uschar *save_host_address = deliver_host_address;
 check_host_block cb;
 cb.host_name = host_name;
 cb.host_address = host_address;
@@ -2034,9 +2071,26 @@ addresses. */
 cb.host_ipv4 = (Ustrncmp(host_address, "::ffff:", 7) == 0)?
   host_address + 7 : host_address;
 
-return match_check_list(listptr, 0, &hostlist_anchor, &local_cache_bits,
-  check_host, &cb, MCL_HOST,
-  (host_address == sender_host_address)? US"host" : host_address, valueptr);
+/* During the running of the check, put the IP address into $host_address. In 
+the case of calls from the smtp transport, it will already be there. However, 
+in other calls (e.g. when testing ignore_target_hosts), it won't. Just to be on 
+the safe side, any existing setting is preserved, though as I write this
+(November 2004) I can't see any cases where it is actually needed. */
+
+deliver_host_address = host_address;
+rc = match_check_list(
+       listptr,                                /* the list */
+       0,                                      /* separator character */
+       &hostlist_anchor,                       /* anchor pointer */
+       &local_cache_bits,                      /* cache pointer */
+       check_host,                             /* function for testing */
+       &cb,                                    /* argument for function */
+       MCL_HOST,                               /* type of check */
+       (host_address == sender_host_address)? 
+         US"host" : host_address,              /* text for debugging */
+       valueptr);                              /* where to pass back data */
+deliver_host_address = save_host_address;
+return rc; 
 }