Copyright year bumps for substantive changes 2017
[exim.git] / src / src / routers / dnslookup.c
index 7789d46363ab9d514f8dc2b13c195ed19397f790..5017efbee8793a552263166c42f3b6924e292029 100644 (file)
@@ -1,10 +1,8 @@
-/* $Cambridge: exim/src/src/routers/dnslookup.c,v 1.1 2004/10/07 13:10:02 ph10 Exp $ */
-
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2004 */
+/* Copyright (c) University of Cambridge 1995 - 2017 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 #include "../exim.h"
@@ -20,6 +18,8 @@ optionlist dnslookup_router_options[] = {
       (void *)(offsetof(dnslookup_router_options_block, check_secondary_mx)) },
   { "check_srv",          opt_stringptr,
       (void *)(offsetof(dnslookup_router_options_block, check_srv)) },
+  { "fail_defer_domains", opt_stringptr,
+      (void *)(offsetof(dnslookup_router_options_block, fail_defer_domains)) },
   { "mx_domains",         opt_stringptr,
       (void *)(offsetof(dnslookup_router_options_block, mx_domains)) },
   { "mx_fail_domains",    opt_stringptr,
@@ -44,6 +44,22 @@ address can appear in the tables drtables.c. */
 int dnslookup_router_options_count =
   sizeof(dnslookup_router_options)/sizeof(optionlist);
 
+
+#ifdef MACRO_PREDEF
+
+/* Dummy entries */
+dnslookup_router_options_block dnslookup_router_option_defaults = {0};
+void dnslookup_router_init(router_instance *rblock) {}
+int dnslookup_router_entry(router_instance *rblock, address_item *addr,
+  struct passwd *pw, int verify, address_item **addr_local,
+  address_item **addr_remote, address_item **addr_new,
+  address_item **addr_succeed) {return 0;}
+
+#else   /*!MACRO_PREDEF*/
+
+
+
+
 /* Default private options block for the dnslookup router. */
 
 dnslookup_router_options_block dnslookup_router_option_defaults = {
@@ -55,7 +71,8 @@ dnslookup_router_options_block dnslookup_router_option_defaults = {
   NULL,            /* mx_domains */
   NULL,            /* mx_fail_domains */
   NULL,            /* srv_fail_domains */
-  NULL             /* check_srv */
+  NULL,            /* check_srv */
+  NULL             /* fail_defer_domains */
 };
 
 
@@ -128,7 +145,7 @@ dnslookup_router_entry(
   router_instance *rblock,        /* data for this instantiation */
   address_item *addr,             /* address we are working on */
   struct passwd *pw,              /* passwd entry after check_local_user */
-  BOOL verify,                    /* TRUE when verifying */
+  int verify,                     /* v_none/v_recipient/v_sender/v_expn */
   address_item **addr_local,      /* add it to this if it's local */
   address_item **addr_remote,     /* add it to this if it's remote */
   address_item **addr_new,        /* put new addresses on here */
@@ -142,10 +159,10 @@ dnslookup_router_options_block *ob =
   (dnslookup_router_options_block *)(rblock->options_block);
 uschar *srv_service = NULL;
 uschar *widen = NULL;
-uschar *pre_widen = addr->domain;
-uschar *post_widen = NULL;
-uschar *fully_qualified_name;
-uschar *listptr;
+const uschar *pre_widen = addr->domain;
+const uschar *post_widen = NULL;
+const uschar *fully_qualified_name;
+const uschar *listptr;
 uschar widen_buffer[256];
 
 addr_new = addr_new;          /* Keep picky compilers happy */
@@ -157,10 +174,10 @@ DEBUG(D_route)
 
 /* If an SRV check is required, expand the service name */
 
-if (ob->check_srv != NULL)
+if (ob->check_srv)
   {
-  srv_service = expand_string(ob->check_srv);
-  if (srv_service == NULL && !expand_string_forcedfail)
+  if (  !(srv_service = expand_string(ob->check_srv))
+     && !expand_string_forcedfail)
     {
     addr->message = string_sprintf("%s router: failed to expand \"%s\": %s",
       rblock->name, ob->check_srv, expand_string_message);
@@ -177,9 +194,22 @@ precedence over global names. For example, if the domain is "xxx.ch" it might
 be something in the "ch" toplevel domain, but it also might be xxx.ch.xyz.com.
 The choice of pre- or post-widening affects which takes precedence. If ever
 somebody comes up with some kind of requirement for pre-widening, presumably
-with some conditions under which it is done, it can be selected here. */
-
-if (ob->widen_domains != NULL)
+with some conditions under which it is done, it can be selected here.
+
+The rewrite_headers option works only when routing an address at transport
+time, because the alterations to the headers are not persistent so must be
+worked out immediately before they are used. Sender addresses are routed for
+verification purposes, but never at transport time, so any header changes that
+you might expect as a result of sender domain widening do not occur. Therefore
+we do not perform widening when verifying sender addresses; however, widening
+sender addresses is OK if we do not have to rewrite the headers. A corollary
+of this is that if the current address is not the original address, then it
+does not appear in the message header so it is also OK to widen. The
+suppression of widening for sender addresses is silent because it is the
+normal desirable behaviour. */
+
+if (  ob->widen_domains
+   && (verify != v_sender || !ob->rewrite_headers || addr->parent))
   {
   listptr = ob->widen_domains;
   widen = string_nextinlist(&listptr, &widen_sep, widen_buffer,
@@ -203,12 +233,12 @@ for (;;)
   int flags = whichrrs;
   BOOL removed = FALSE;
 
-  if (pre_widen != NULL)
+  if (pre_widen)
     {
     h.name = pre_widen;
     pre_widen = NULL;
     }
-  else if (widen != NULL)
+  else if (widen)
     {
     h.name = string_sprintf("%s.%s", addr->domain, widen);
     widen = string_nextinlist(&listptr, &widen_sep, widen_buffer,
@@ -216,7 +246,7 @@ for (;;)
     DEBUG(D_route) debug_printf("%s router widened %s to %s\n", rblock->name,
       addr->domain, h.name);
     }
-  else if (post_widen != NULL)
+  else if (post_widen)
     {
     h.name = post_widen;
     post_widen = NULL;
@@ -240,13 +270,18 @@ for (;;)
 
   /* Unfortunately, we cannot set the mx_only option in advance, because the
   DNS lookup may extend an unqualified name. Therefore, we must do the test
-  subsequently. */
+  subsequently. We use the same logic as that for widen_domains above to avoid
+  requesting a header rewrite that cannot work. */
 
-  if (ob->qualify_single) flags |= HOST_FIND_QUALIFY_SINGLE;
-  if (ob->search_parents) flags |= HOST_FIND_SEARCH_PARENTS;
+  if (verify != v_sender || !ob->rewrite_headers || addr->parent)
+    {
+    if (ob->qualify_single) flags |= HOST_FIND_QUALIFY_SINGLE;
+    if (ob->search_parents) flags |= HOST_FIND_SEARCH_PARENTS;
+    }
 
-  rc = host_find_bydns(&h, rblock->ignore_target_hosts, flags, srv_service,
-    ob->srv_fail_domains, ob->mx_fail_domains, &fully_qualified_name, &removed);
+  rc = host_find_bydns(&h, CUS rblock->ignore_target_hosts, flags, srv_service,
+    ob->srv_fail_domains, ob->mx_fail_domains,
+    &rblock->dnssec, &fully_qualified_name, &removed);
   if (removed) setflag(addr, af_local_host_removed);
 
   /* If host found with only address records, test for the domain's being in
@@ -254,9 +289,9 @@ for (;;)
   the option is historical. */
 
   if ((rc == HOST_FOUND || rc == HOST_FOUND_LOCAL) && h.mx < 0 &&
-       ob->mx_domains != NULL)
-    {
-    switch(match_isinlist(fully_qualified_name, &(ob->mx_domains), 0,
+       ob->mx_domains)
+    switch(match_isinlist(fully_qualified_name,
+          CUSS &(ob->mx_domains), 0,
           &domainlist_anchor, addr->domain_cache, MCL_DOMAIN, TRUE, NULL))
       {
       case DEFER:
@@ -268,11 +303,15 @@ for (;;)
         rblock->name, fully_qualified_name);
       continue;
       }
-    }
 
   /* Deferral returns forthwith, and anything other than failure breaks the
   loop. */
 
+  if (rc == HOST_FIND_SECURITY)
+    {
+    addr->message = US"host lookup done insecurely";
+    return DEFER;
+    }
   if (rc == HOST_FIND_AGAIN)
     {
     if (rblock->pass_on_timeout)
@@ -287,9 +326,25 @@ for (;;)
 
   if (rc != HOST_FIND_FAILED) break;
 
-  /* Check to see if the failure is the result of MX records pointing
-  to non-existent domains, and if so, set an appropriate error message; the
-  case of an SRV record pointing to "." is another special case that we can
+  if (ob->fail_defer_domains)
+    switch(match_isinlist(fully_qualified_name,
+         CUSS &ob->fail_defer_domains, 0,
+         &domainlist_anchor, addr->domain_cache, MCL_DOMAIN, TRUE, NULL))
+      {
+      case DEFER:
+       addr->message = US"lookup defer for fail_defer_domains option";
+       return DEFER;
+
+      case OK:
+       DEBUG(D_route) debug_printf("%s router: matched fail_defer_domains\n",
+         rblock->name);
+       addr->message = US"missing MX, or all MXs point to missing A records,"
+         " and defer requested";
+       return DEFER;
+      }
+  /* Check to see if the failure is the result of MX records pointing to
+  non-existent domains, and if so, set an appropriate error message; the case
+  of an MX or SRV record pointing to "." is another special case that we can
   detect. Otherwise "unknown mail domain" is used, which is confusing. Also, in
   this case don't do the widening. We need check only the first host to see if
   its MX has been filled in, but there is no address, because if there were any
@@ -300,12 +355,14 @@ for (;;)
 
   if (h.mx >= 0 && h.address == NULL)
     {
+    setflag(addr, af_pass_message);   /* This is not a security risk */
     if (h.name[0] == 0)
-      addr->message = US"an SRV record indicated no SMTP service";
+      addr->message = US"an MX or SRV record indicated no SMTP service";
     else
       {
+      addr->basic_errno = ERRNO_UNKNOWNHOST;
       addr->message = US"all relevant MX records point to non-existent hosts";
-      if (!allow_mx_to_ip && string_is_ip_address(h.name, NULL))
+      if (!allow_mx_to_ip && string_is_ip_address(h.name, NULL) != 0)
         {
         addr->user_message =
           string_sprintf("It appears that the DNS operator for %s\n"
@@ -378,13 +435,13 @@ else if (ob->check_secondary_mx && !testflag(addr, af_local_host_removed))
 
 /* Set up the errors address, if any. */
 
-rc = rf_get_errors_address(addr, rblock, verify, &(addr->p.errors_address));
+rc = rf_get_errors_address(addr, rblock, verify, &addr->prop.errors_address);
 if (rc != OK) return rc;
 
 /* Set up the additional and removeable headers for this address. */
 
-rc = rf_get_munge_headers(addr, rblock, &(addr->p.extra_headers),
-  &(addr->p.remove_headers));
+rc = rf_get_munge_headers(addr, rblock, &addr->prop.extra_headers,
+  &addr->prop.remove_headers);
 if (rc != OK) return rc;
 
 /* Get store in which to preserve the original host item, chained on
@@ -405,4 +462,7 @@ return rf_queue_add(addr, addr_local, addr_remote, rblock, pw)?
   OK : DEFER;
 }
 
+#endif   /*!MACRO_PREDEF*/
 /* End of routers/dnslookup.c */
+/* vi: aw ai sw=2
+*/