Callouts: r-verify option to use transport-defined mailfrom
[exim.git] / src / src / verify.c
index ad6b0afaf1d86fd9e09a112f9d9f0d4012fb5481..b48d17ee5b6c40b0d4e3e7a86921e2ec26336046 100644 (file)
@@ -364,7 +364,7 @@ if (addr->transport == cutthrough.addr.transport)
       deliver_host_address = host->address;
       deliver_host_port = host->port;
       deliver_domain = addr->domain;
-      transport_name = addr->transport->name;
+      transport_name = addr->transport->drinst.name;
 
       host_af = Ustrchr(host->address, ':') ? AF_INET6 : AF_INET;
 
@@ -389,6 +389,10 @@ if (addr->transport == cutthrough.addr.transport)
        uschar * resp = NULL;
 
        /* Match!  Send the RCPT TO, set done from the response */
+
+       DEBUG(D_verify)
+         debug_printf("already-open verify connection matches recipient\n");
+
        done =
             smtp_write_command(&ctctx, SCMD_FLUSH, "RCPT TO:<%.1000s>\r\n",
              transport_rcpt_address(addr,
@@ -441,8 +445,6 @@ if (addr->transport == cutthrough.addr.transport)
        }
       break;   /* host_list */
       }
-if (!done)
-  cancel_cutthrough_connection(TRUE, US"incompatible connection");
 return done;
 }
 
@@ -486,9 +488,10 @@ Arguments:
                       vopt_callout_no_cache => don't use callout cache
                       vopt_callout_fullpm => if postmaster check, do full one
                       vopt_callout_random => do the "random" thing
-                      vopt_callout_recipsender => use real sender for recipient
-                      vopt_callout_recippmaster => use postmaster for recipient
-                     vopt_callout_hold         => lazy close connection
+                      vopt_callout_recipsender => use original sender addres
+                      vopt_callout_r_pmaster   => use postmaster as sender
+                     vopt_callout_r_tptsender => use sender defined by tpt
+                     vopt_callout_hold        => lazy close connection
   se_mailfrom         MAIL FROM address for sender verify; NULL => ""
   pm_mailfrom         if non-NULL, do the postmaster check with this sender
 
@@ -520,21 +523,44 @@ new_domain_record.random_result = ccache_unknown;
 memset(&new_address_record, 0, sizeof(new_address_record));
 
 /* For a recipient callout, the key used for the address cache record must
-include the sender address if we are using the real sender in the callout,
-because that may influence the result of the callout. */
+include the sender address if we are using anything but a blank sender in the
+callout, because that may influence the result of the callout. */
 
 if (options & vopt_is_recipient)
-  if (options & vopt_callout_recipsender)
-    {
-    from_address = sender_address;
-    address_key = string_sprintf("%s/<%s>", addr->address, sender_address);
-    if (cutthrough.delivery) options |= vopt_callout_no_cache;
-    }
-  else if (options & vopt_callout_recippmaster)
+  if (options & ( vopt_callout_recipsender
+               | vopt_callout_r_tptsender
+               | vopt_callout_r_pmaster)
+     )
     {
-    from_address = string_sprintf("postmaster@%s", qualify_domain_sender);
-    address_key = string_sprintf("%s/<postmaster@%s>", addr->address,
-      qualify_domain_sender);
+    if (options & vopt_callout_recipsender)
+      from_address = sender_address;
+    else if (options & vopt_callout_r_tptsender)
+      {
+      transport_instance * tp = addr->transport;
+      from_address = addr->prop.errors_address
+                 ? addr->prop.errors_address : sender_address;
+      DEBUG(D_verify)
+       debug_printf(" return-path from routed addr: %s\n", from_address);
+
+      GET_OPTION("return_path");
+      if (tp->return_path)
+       {
+       uschar * new_return_path = expand_string(tp->return_path);
+       if (new_return_path)
+         from_address = new_return_path;
+       else if (!f.expand_string_forcedfail)
+         return DEFER;
+       DEBUG(D_verify)
+         debug_printf(" return-path from transport: %s\n", from_address);
+       }
+      }
+    else /* if (options & vopt_callout_recippmaster) */
+      from_address = string_sprintf("postmaster@%s", qualify_domain_sender);
+
+    address_key = string_sprintf("%s/<%s>", addr->address, from_address);
+    addr->return_path = from_address;          /* for cutthrough logging */
+    if (cutthrough.delivery)                   /* cutthrough previously req. */
+      options |= vopt_callout_no_cache;                /* in case called by verify= */
     }
   else
     {
@@ -565,13 +591,12 @@ if (!addr->transport)
   HDEBUG(D_verify) debug_printf("cannot callout via null transport\n");
   }
 
-else if (Ustrcmp(addr->transport->driver_name, "smtp") != 0)
+else if (Ustrcmp(addr->transport->drinst.driver_name, "smtp") != 0)
   log_write(0, LOG_MAIN|LOG_PANIC|LOG_CONFIG_FOR, "callout transport '%s': %s is non-smtp",
-    addr->transport->name, addr->transport->driver_name);
+    addr->transport->drinst.name, addr->transport->drinst.driver_name);
 else
   {
-  smtp_transport_options_block *ob =
-    (smtp_transport_options_block *)addr->transport->options_block;
+  smtp_transport_options_block * ob = addr->transport->drinst.options_block;
   smtp_context * sx = NULL;
 
   /* The information wasn't available in the cache, so we have to do a real
@@ -617,13 +642,19 @@ that conn for verification purposes (and later delivery also).  Simplest
 coding means skipping this whole loop and doing the append separately.  */
 
   /* Can we re-use an open cutthrough connection? */
-  if (  cutthrough.cctx.sock >= 0
-     && (options & (vopt_callout_recipsender | vopt_callout_recippmaster))
-       == vopt_callout_recipsender
-     && !random_local_part
-     && !pm_mailfrom
-     )
-    done = cutthrough_multi(addr, host_list, tf, &yield);
+
+  if (cutthrough.cctx.sock >= 0)
+    {
+    if (  !(options & vopt_callout_r_pmaster)
+       && !random_local_part
+       && !pm_mailfrom
+       && Ustrcmp(addr->return_path, cutthrough.addr.return_path) == 0
+       )
+      done = cutthrough_multi(addr, host_list, tf, &yield);
+
+    if (!done)
+      cancel_cutthrough_connection(TRUE, US"incompatible connection");
+    }
 
   /* If we did not use a cached connection, make connections to the hosts
   and do real callouts. The list of hosts is passed in as an argument. */
@@ -663,7 +694,7 @@ coding means skipping this whole loop and doing the append separately.  */
     deliver_host_address = host->address;
     deliver_host_port = host->port;
     deliver_domain = addr->domain;
-    transport_name = addr->transport->name;
+    transport_name = addr->transport->drinst.name;
 
     GET_OPTION("interface");
     if (  !smtp_get_interface(tf->interface, host_af, addr, &interface,
@@ -771,6 +802,7 @@ tls_retry_connection:
     sx->send_rset = TRUE;
     sx->completed_addr = FALSE;
 
+/*XXX do not want to write a cache record for ATRN */
     new_domain_record.result = old_domain_cache_result == ccache_reject_mfnull
       ? ccache_reject_mfnull : ccache_accept;
 
@@ -891,7 +923,7 @@ tls_retry_connection:
     /* Main verify.  For rcpt-verify use SIZE if we know it and we're not cacheing;
     for sndr-verify never use it. */
 
-    if (done)
+    if (done && !(options & vopt_atrn))
       {
       if (!(options & vopt_is_recipient  &&  options & vopt_callout_no_cache))
        sx->avoid_option = OPTION_SIZE;
@@ -1110,8 +1142,7 @@ no_conn:
        && rcpt_count == 1
        && done
        && yield == OK
-       &&    (options & (vopt_callout_recipsender|vopt_callout_recippmaster|vopt_success_on_redirect))
-          == vopt_callout_recipsender
+       && !(options & (vopt_callout_r_pmaster| vopt_success_on_redirect))
        && !random_local_part
        && !pm_mailfrom
        && cutthrough.cctx.sock < 0
@@ -1127,7 +1158,7 @@ no_conn:
       /* We assume no buffer in use in the outblock */
       cutthrough.cctx =                sx->cctx;
       cutthrough.nrcpt =       1;
-      cutthrough.transport =   addr->transport->name;
+      cutthrough.transport =   addr->transport->drinst.name;
       cutthrough.interface =   interface;
       cutthrough.snd_port =    sending_port;
       cutthrough.peer_options =        smtp_peer_options;
@@ -1159,7 +1190,7 @@ no_conn:
     else
       {
       /* Ensure no cutthrough on multiple verifies that were incompatible */
-      if (options & vopt_callout_recipsender)
+      if (options & (vopt_callout_recipsender | vopt_callout_r_tptsender))
         cancel_cutthrough_connection(TRUE, US"not usable for cutthrough");
       if (sx->send_quit && sx->cctx.sock >= 0)
        if (smtp_write_command(sx, SCMD_FLUSH, "QUIT\r\n") != -1)
@@ -1233,7 +1264,8 @@ if (!done)
 /* Come here from within the cache-reading code on fast-track exit. */
 
 END_CALLOUT:
-tls_modify_variables(&tls_in); /* return variables to inbound values */
+if (!(options & vopt_atrn))
+  tls_modify_variables(&tls_in);       /* return variables to inbound values */
 return yield;
 }
 
@@ -1243,10 +1275,10 @@ return yield;
    one was requested and a recipient-verify wasn't subsequently done.
 */
 int
-open_cutthrough_connection(address_item * addr)
+open_cutthrough_connection(address_item * addr, BOOL transport_sender)
 {
 address_item addr2;
-int rc;
+int vopt, rc;
 
 /* Use a recipient-verify-callout to set up the cutthrough connection. */
 /* We must use a copy of the address for verification, because it might
@@ -1255,9 +1287,12 @@ get rewritten. */
 addr2 = *addr;
 HDEBUG(D_acl) debug_printf_indent("----------- %s cutthrough setup ------------\n",
   rcpt_count > 1 ? "more" : "start");
-rc = verify_address(&addr2, NULL,
-       vopt_is_recipient | vopt_callout_recipsender | vopt_callout_no_cache,
-       CUTTHROUGH_CMD_TIMEOUT, -1, -1,
+
+vopt = transport_sender
+  ? vopt_is_recipient | vopt_callout_r_tptsender | vopt_callout_no_cache
+  : vopt_is_recipient | vopt_callout_recipsender | vopt_callout_no_cache;
+
+rc = verify_address(&addr2, NULL, vopt, CUTTHROUGH_CMD_TIMEOUT, -1, -1,
        NULL, NULL, NULL);
 addr->message = addr2.message;
 addr->user_message = addr2.user_message;
@@ -1670,9 +1705,10 @@ Arguments:
 
                      vopt_callout_fullpm => if postmaster check, do full one
                      vopt_callout_no_cache => don't use callout cache
-                     vopt_callout_random => do the "random" thing
-                     vopt_callout_recipsender => use real sender for recipient
+                     vopt_callout_random   => do the "random" thing
+                     vopt_callout_recipsender  => use real sender for recipient
                      vopt_callout_recippmaster => use postmaster for recipient
+                    vopt_callout_r_tptsender  => use sender as defined by tpt
 
   callout          if > 0, specifies that callout is required, and gives timeout
                      for individual commands
@@ -1706,16 +1742,12 @@ int yield = OK;
 int verify_type = expn ? v_expn :
    f.address_test_mode ? v_none :
           options & vopt_is_recipient ? v_recipient : v_sender;
-address_item * addr_list;
-address_item * addr_new = NULL;
-address_item * addr_remote = NULL;
-address_item * addr_local = NULL;
-address_item * addr_succeed = NULL;
+address_item * addr_list, * addr_new = NULL, * addr_remote = NULL;
+address_item * addr_local = NULL, * addr_succeed = NULL;
 uschar ** failure_ptr = options & vopt_is_recipient
   ? &recipient_verify_failure : &sender_verify_failure;
 uschar * ko_prefix, * cr;
-const uschar * address = vaddr->address;
-const uschar * save_sender;
+const uschar * address = vaddr->address, * save_sender;
 uschar null_sender[] = { 0 };             /* Ensure writeable memory */
 
 /* Clear, just in case */
@@ -1853,7 +1885,7 @@ while (addr_new)
         fprintf(fp, "\n*** Error in setting up pipe, file, or autoreply:\n"
           "%s\n", addr->message);
       else if (allow)
-        fprintf(fp, "\n  transport = %s\n", addr->transport->name);
+        fprintf(fp, "\n  transport = %s\n", addr->transport->drinst.name);
       else
         fprintf(fp, " *** forbidden ***\n");
       }
@@ -1916,7 +1948,9 @@ while (addr_new)
       sending a message to this address. */
 
       if ((tp = addr->transport))
-       if (!tp->info->local)
+       {
+       transport_info * ti = tp->drinst.info;
+       if (!ti->local)
          {
          (void)(tp->setup)(tp, addr, &tf, 0, 0, NULL);
 
@@ -1942,7 +1976,7 @@ while (addr_new)
              {
              log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand list of hosts "
                "\"%s\" in %s transport for callout: %s", tf.hosts,
-               tp->name, expand_string_message);
+               tp->drinst.name, expand_string_message);
              }
            else
              {
@@ -1968,10 +2002,9 @@ while (addr_new)
                else
                  {
                  const dnssec_domains * dsp = NULL;
-                 if (Ustrcmp(tp->driver_name, "smtp") == 0)
+                 if (Ustrcmp(tp->drinst.driver_name, "smtp") == 0)
                    {
-                   smtp_transport_options_block * ob =
-                       (smtp_transport_options_block *) tp->options_block;
+                   smtp_transport_options_block * ob = tp->drinst.options_block;
                    dsp = &ob->dnssec;
                    }
 
@@ -1983,15 +2016,17 @@ while (addr_new)
            }
          }
        else if (  options & vopt_quota
-               && Ustrcmp(tp->driver_name, "appendfile") == 0)
+               && Ustrcmp(tp->drinst.driver_name, "appendfile") == 0)
          local_verify = TRUE;
+       }
 
       /* 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)
         {
-        HDEBUG(D_verify) debug_printf("Attempting full verification using callout\n");
+        HDEBUG(D_verify)
+         debug_printf("Attempting full verification using callout\n");
         if (host_checking && !f.host_checking_callout)
           {
           HDEBUG(D_verify)
@@ -2000,14 +2035,11 @@ while (addr_new)
           }
         else
           {
-#ifndef DISABLE_TLS
          deliver_set_expansions(addr);
-#endif
           rc = do_callout(addr, host_list, &tf, callout, callout_overall,
             callout_connect, options, se_mailfrom, pm_mailfrom);
-#ifndef DISABLE_TLS
          deliver_set_expansions(NULL);
-#endif
+
          if (  options & vopt_is_recipient
             && rc == OK
                         /* set to "random", with OK, for an accepted random */
@@ -2209,7 +2241,7 @@ if (allok && !addr_local && !addr_remote)
 for (addr_list = addr_local, i = 0; i < 2; addr_list = addr_remote, i++)
   while (addr_list)
     {
-    address_item *addr = addr_list;
+    address_item * addr = addr_list;
     transport_instance * tp = addr->transport;
 
     addr_list = addr->next;
@@ -2235,13 +2267,14 @@ for (addr_list = addr_local, i = 0; i < 2; addr_list = addr_remote, i++)
     /* Show router, and transport */
 
     fprintf(fp, "router = %s, transport = %s\n",
-      addr->router->drinst.name, tp ? tp->name : US"unset");
+      addr->router->drinst.name, tp ? tp->drinst.name : US"unset");
 
     /* Show any hosts that are set up by a router unless the transport
     is going to override them; fiddle a bit to get a nice format. */
 
     if (addr->host_list && tp && !tp->overrides_hosts)
       {
+      transport_info * ti = tp->drinst.info;
       int maxlen = 0;
       int maxaddlen = 0;
       for (host_item * h = addr->host_list; h; h = h->next)
@@ -2257,7 +2290,7 @@ for (addr_list = addr_local, i = 0; i < 2; addr_list = addr_remote, i++)
 
        if (h->address)
          fprintf(fp, "[%s%-*c", h->address, maxaddlen+1 - Ustrlen(h->address), ']');
-       else if (tp->info->local)
+       else if (ti->local)
          fprintf(fp, " %-*s ", maxaddlen, "");  /* Omit [unknown] for local */
        else
          fprintf(fp, "[%s%-*c", "unknown", maxaddlen+1 - 7, ']');
@@ -2276,7 +2309,8 @@ the -bv or -bt case). */
 
 out:
 verify_mode = NULL;
-tls_modify_variables(&tls_in); /* return variables to inbound values */
+if (!(options & vopt_atrn))
+  tls_modify_variables(&tls_in);       /* return variables to inbound values */
 
 return yield;
 }
@@ -3057,19 +3091,16 @@ else
 
 if (iplookup)
   {
-  int insize;
-  int search_type;
-  int incoming[4];
-  void *handle;
-  uschar *filename, *key, *result;
+  const lookup_info * li;
+  int incoming[4], insize;
+  void * handle;
+  uschar * filename, * key, * result;
   uschar buffer[64];
 
   /* Find the search type */
 
-  search_type = search_findtype(t, endname - t);
-
-  if (search_type < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s",
-    search_error_message);
+  if (!(li = search_findtype(t, endname - t)))
+    log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s", search_error_message);
 
   /* 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 query-style with a file
@@ -3079,7 +3110,7 @@ if (iplookup)
   dot separators instead of colons, except when the lookup type is "iplsearch".
   */
 
-  if (mac_islookup(search_type, lookup_absfilequery))
+  if (mac_islookup(li, lookup_absfilequery))
     {
     filename = semicolon + 1;
     key = filename;
@@ -3087,18 +3118,17 @@ if (iplookup)
     filename = string_copyn(filename, key - filename);
     Uskip_whitespace(&key);
     }
-  else if (mac_islookup(search_type, lookup_querystyle))
+  else if (mac_islookup(li, lookup_querystyle))
     {
     filename = NULL;
     key = semicolon + 1;
     }
   else   /* Single-key style */
     {
-    int sep = (Ustrcmp(lookup_list[search_type]->name, "iplsearch") == 0)?
-      ':' : '.';
+    int sep = Ustrcmp(li->name, "iplsearch") == 0 ? ':' : '.';
     insize = host_aton(cb->host_address, incoming);
     host_mask(insize, incoming, mlen);
-    (void)host_nmtoa(insize, incoming, mlen, buffer, sep);
+    (void) host_nmtoa(insize, incoming, mlen, buffer, sep);
     key = buffer;
     filename = semicolon + 1;
     }
@@ -3106,7 +3136,7 @@ if (iplookup)
   /* Now do the actual lookup; note that there is no search_close() because
   of the caching arrangements. */
 
-  if (!(handle = search_open(filename, search_type, 0, NULL, NULL)))
+  if (!(handle = search_open(filename, li, 0, NULL, NULL)))
     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s", search_error_message);
 
   result = search_find(handle, filename, key, -1, NULL, 0, 0, NULL, opts);
@@ -3180,20 +3210,21 @@ on spec. */
 if ((semicolon = Ustrchr(ss, ';')))
   {
   const uschar * affix, * opts;
-  int partial, affixlen, starflags, id;
+  int partial, affixlen, starflags;
+  const lookup_info * li;
 
   *semicolon = 0;
-  id = search_findtype_partial(ss, &partial, &affix, &affixlen, &starflags,
+  li = search_findtype_partial(ss, &partial, &affix, &affixlen, &starflags,
          &opts);
   *semicolon=';';
 
-  if (id < 0)                           /* Unknown lookup type */
+  if (!li)                             /* Unknown lookup type */
     {
     log_write(0, LOG_MAIN|LOG_PANIC, "%s in host list item \"%s\"",
       search_error_message, ss);
     return DEFER;
     }
-  isquery = mac_islookup(id, lookup_querystyle|lookup_absfilequery);
+  isquery = mac_islookup(li, lookup_querystyle|lookup_absfilequery);
   }
 
 if (isquery)
@@ -3214,13 +3245,16 @@ do a check on the name and all its aliases. */
 if (!sender_host_name)
   {
   HDEBUG(D_host_lookup)
-    debug_printf("sender host name required, to match against %s\n", ss);
+    debug_printf_indent("sender host name required, to match against %s\n", ss);
+  expand_level++;
   if (host_lookup_failed || host_name_lookup() != OK)
     {
+    expand_level--;
     *error = string_sprintf("failed to find host name for %s",
       sender_host_address);;
     return ERROR;
     }
+  expand_level--;
   host_build_sender_fullhost();
   }
 
@@ -3476,7 +3510,7 @@ if ((rc = verify_address(&vaddr, NULL, vopt_is_recipient | vopt_quota,
   }
 
 DEBUG(D_verify) debug_printf_indent("verify_quota: len %d\n", len);
-write(1, msg, len);
+if (write(1, msg, len) != 0) ;
 return;
 }