chg: delay loading of File::FcntlLock
[exim.git] / src / src / routers / iplookup.c
index 96e9626df63236d37e80deddfcad1dfd7c826fd5..464112bc0ce8ed12705035037829cd1a327a5df0 100644 (file)
@@ -2,11 +2,15 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2015 */
+/* Copyright (c) The Exim Maintainers 2020 - 2024 */
+/* Copyright (c) University of Cambridge 1995 - 2018 */
 /* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
 
 
 #include "../exim.h"
+
+#ifdef ROUTER_IPLOOKUP         /* Remainder of file */
 #include "rf_functions.h"
 #include "iplookup.h"
 
 
 optionlist iplookup_router_options[] = {
   { "hosts",    opt_stringptr,
-      (void *)(offsetof(iplookup_router_options_block, hosts)) },
+      OPT_OFF(iplookup_router_options_block, hosts) },
   { "optional", opt_bool,
-      (void *)(offsetof(iplookup_router_options_block, optional)) },
+      OPT_OFF(iplookup_router_options_block, optional) },
   { "port",     opt_int,
-      (void *)(offsetof(iplookup_router_options_block, port)) },
+      OPT_OFF(iplookup_router_options_block, port) },
   { "protocol", opt_stringptr,
-      (void *)(offsetof(iplookup_router_options_block, protocol_name)) },
+      OPT_OFF(iplookup_router_options_block, protocol_name) },
   { "query",    opt_stringptr,
-      (void *)(offsetof(iplookup_router_options_block, query)) },
+      OPT_OFF(iplookup_router_options_block, query) },
   { "reroute",  opt_stringptr,
-      (void *)(offsetof(iplookup_router_options_block, reroute)) },
+      OPT_OFF(iplookup_router_options_block, reroute) },
   { "response_pattern", opt_stringptr,
-      (void *)(offsetof(iplookup_router_options_block, response_pattern)) },
+      OPT_OFF(iplookup_router_options_block, response_pattern) },
   { "timeout",  opt_time,
-      (void *)(offsetof(iplookup_router_options_block, timeout)) }
+      OPT_OFF(iplookup_router_options_block, timeout) }
 };
 
 /* Size of the options list. An extern variable has to be used so that its
@@ -44,6 +48,20 @@ address can appear in the tables drtables.c. */
 int iplookup_router_options_count =
   sizeof(iplookup_router_options)/sizeof(optionlist);
 
+
+#ifdef MACRO_PREDEF
+
+/* Dummy entries */
+iplookup_router_options_block iplookup_router_option_defaults = {0};
+void iplookup_router_init(driver_instance *rblock) {}
+int iplookup_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 iplookup router. */
 
 iplookup_router_options_block iplookup_router_option_defaults = {
@@ -69,36 +87,37 @@ iplookup_router_options_block iplookup_router_option_defaults = {
 consistency checks to be done, or anything else that needs to be set up. */
 
 void
-iplookup_router_init(router_instance *rblock)
+iplookup_router_init(driver_instance * r)
 {
-iplookup_router_options_block *ob =
-  (iplookup_router_options_block *)(rblock->options_block);
+router_instance * rblock = (router_instance *)r;
+iplookup_router_options_block * ob =
+  (iplookup_router_options_block *) r->options_block;
 
 /* A port and a host list must be given */
 
 if (ob->port < 0)
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n  "
-    "a port must be specified", rblock->name);
+    "a port must be specified", r->name);
 
-if (ob->hosts == NULL)
+if (!ob->hosts)
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n  "
-    "a host list must be specified", rblock->name);
+    "a host list must be specified", r->name);
 
 /* Translate protocol name into value */
 
-if (ob->protocol_name != NULL)
+if (ob->protocol_name)
   {
   if (Ustrcmp(ob->protocol_name, "udp") == 0) ob->protocol = ip_udp;
   else if (Ustrcmp(ob->protocol_name, "tcp") == 0) ob->protocol = ip_tcp;
   else log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n  "
-    "protocol not specified as udp or tcp", rblock->name);
+    "protocol not specified as udp or tcp", r->name);
   }
 
 /* If a response pattern is given, compile it now to get the error early. */
 
-if (ob->response_pattern != NULL)
+if (ob->response_pattern)
   ob->re_response_pattern =
-    regex_must_compile(ob->response_pattern, FALSE, TRUE);
+    regex_must_compile(ob->response_pattern, MCS_NOFLAGS, TRUE);
 }
 
 
@@ -146,43 +165,36 @@ uschar *reply;
 uschar *hostname, *reroute, *domain;
 const uschar *listptr;
 uschar host_buffer[256];
-host_item *host = store_get(sizeof(host_item));
+host_item *host = store_get(sizeof(host_item), GET_UNTAINTED);
 address_item *new_addr;
 iplookup_router_options_block *ob =
-  (iplookup_router_options_block *)(rblock->options_block);
-const pcre *re = ob->re_response_pattern;
+  (iplookup_router_options_block *)(rblock->drinst.options_block);
+const pcre2_code *re = ob->re_response_pattern;
 int count, query_len, rc;
 int sep = 0;
 
-addr_local = addr_local;    /* Keep picky compilers happy */
-addr_remote = addr_remote;
-addr_succeed = addr_succeed;
-pw = pw;
-
 DEBUG(D_route) debug_printf("%s router called for %s: domain = %s\n",
-  rblock->name, addr->address, addr->domain);
+  rblock->drinst.name, addr->address, addr->domain);
 
-reply = store_get(256);
+reply = store_get(256, GET_TAINTED);
 
 /* Build the query string to send. If not explicitly given, a default of
 "user@domain user@domain" is used. */
 
-if (ob->query == NULL)
+GET_OPTION("query");
+if (!ob->query)
   query = string_sprintf("%s@%s %s@%s", addr->local_part, addr->domain,
     addr->local_part, addr->domain);
 else
-  {
-  query = expand_string(ob->query);
-  if (query == NULL)
+  if (!(query = expand_string(ob->query)))
     {
     addr->message = string_sprintf("%s router: failed to expand %s: %s",
-      rblock->name, ob->query, expand_string_message);
+      rblock->drinst.name, ob->query, expand_string_message);
     return DEFER;
     }
-  }
 
 query_len = Ustrlen(query);
-DEBUG(D_route) debug_printf("%s router query is \"%s\"\n", rblock->name,
+DEBUG(D_route) debug_printf("%s router query is \"%s\"\n", rblock->drinst.name,
   string_printing(query));
 
 /* Now connect to the required port for each of the hosts in turn, until a
@@ -190,6 +202,7 @@ response it received. Initialization insists on the port being set and there
 being a host list. */
 
 listptr = ob->hosts;
+/* not expanded so should never be tainted */
 while ((hostname = string_nextinlist(&listptr, &sep, host_buffer,
        sizeof(host_buffer))))
   {
@@ -216,7 +229,8 @@ while ((hostname = string_nextinlist(&listptr, &sep, host_buffer,
 
   for (h = host; h; h = h->next)
     {
-    int host_af, query_socket;
+    int host_af;
+    client_conn_ctx query_cctx = {0};
 
     /* Skip any hosts for which we have no address */
 
@@ -225,26 +239,27 @@ while ((hostname = string_nextinlist(&listptr, &sep, host_buffer,
     /* Create a socket, for UDP or TCP, as configured. IPv6 addresses are
     detected by checking for a colon in the address. */
 
-    host_af = (Ustrchr(h->address, ':') != NULL)? AF_INET6 : AF_INET;
+    host_af = Ustrchr(h->address, ':') ? AF_INET6 : AF_INET;
 
-    query_socket = ip_socket(ob->protocol == ip_udp ? SOCK_DGRAM:SOCK_STREAM,
+    query_cctx.sock = ip_socket(ob->protocol == ip_udp ? SOCK_DGRAM:SOCK_STREAM,
       host_af);
-    if (query_socket < 0)
+    if (query_cctx.sock < 0)
       {
       if (ob->optional) return PASS;
       addr->message = string_sprintf("failed to create socket in %s router",
-        rblock->name);
+        rblock->drinst.name);
       return DEFER;
       }
 
     /* Connect to the remote host, under a timeout. In fact, timeouts can occur
     here only for TCP calls; for a UDP socket, "connect" always works (the
     router will timeout later on the read call). */
+/*XXX could take advantage of TFO */
 
-    if (ip_connect(query_socket, host_af, h->address,ob->port, ob->timeout,
-                       ob->protocol != ip_udp) < 0)
+    if (ip_connect(query_cctx.sock, host_af, h->address,ob->port, ob->timeout,
+               ob->protocol == ip_udp ? NULL : &tcp_fastopen_nodata) < 0)
       {
-      close(query_socket);
+      close(query_cctx.sock);
       DEBUG(D_route)
         debug_printf("connection to %s failed: %s\n", h->address,
           strerror(errno));
@@ -253,18 +268,18 @@ while ((hostname = string_nextinlist(&listptr, &sep, host_buffer,
 
     /* Send the query. If it fails, just continue with the next address. */
 
-    if (send(query_socket, query, query_len, 0) < 0)
+    if (send(query_cctx.sock, query, query_len, 0) < 0)
       {
       DEBUG(D_route) debug_printf("send to %s failed\n", h->address);
-      (void)close(query_socket);
+      (void)close(query_cctx.sock);
       continue;
       }
 
     /* Read the response and close the socket. If the read fails, try the
     next IP address. */
 
-    count = ip_recv(query_socket, reply, sizeof(reply) - 1, ob->timeout);
-    (void)close(query_socket);
+    count = ip_recv(&query_cctx, reply, sizeof(reply) - 1, time(NULL) + ob->timeout);
+    (void)close(query_cctx.sock);
     if (count <= 0)
       {
       DEBUG(D_route) debug_printf("%s from %s\n", (errno == ETIMEDOUT)?
@@ -277,7 +292,7 @@ while ((hostname = string_nextinlist(&listptr, &sep, host_buffer,
 
     reply[count] = 0;
     DEBUG(D_route) debug_printf("%s router received \"%s\" from %s\n",
-      rblock->name, string_printing(reply), h->address);
+      rblock->drinst.name, string_printing(reply), h->address);
     break;
     }
 
@@ -293,12 +308,12 @@ connect to any of the IP addresses, or timed out while reading or writing to
 those we have connected to. In all cases, we must pass if optional and
 defer otherwise. */
 
-if (hostname == NULL)
+if (!hostname)
   {
-  DEBUG(D_route) debug_printf("%s router failed to get anything\n", rblock->name);
+  DEBUG(D_route) debug_printf("%s router failed to get anything\n", rblock->drinst.name);
   if (ob->optional) return PASS;
   addr->message = string_sprintf("%s router: failed to communicate with any "
-    "host", rblock->name);
+    "host", rblock->drinst.name);
   return DEFER;
   }
 
@@ -312,7 +327,7 @@ if (re != NULL)
   if (!regex_match_and_setup(re, reply, 0, -1))
     {
     DEBUG(D_route) debug_printf("%s router: %s failed to match response %s\n",
-      rblock->name, ob->response_pattern, reply);
+      rblock->drinst.name, ob->response_pattern, reply);
     return DECLINE;
     }
   }
@@ -337,7 +352,7 @@ else
     if (Ustrcmp(query + query_len/2 + 1, reply+nn) != 0)
       {
       DEBUG(D_route) debug_printf("%s router: failed to match identification "
-        "in response %s\n", rblock->name, reply);
+        "in response %s\n", rblock->drinst.name, reply);
       return DECLINE;
       }
     }
@@ -348,28 +363,29 @@ else
 /* If an explicit rerouting string is specified, expand it. Otherwise, use
 what was sent back verbatim. */
 
-if (ob->reroute != NULL)
+GET_OPTION("reroute");
+if (ob->reroute)
   {
   reroute = expand_string(ob->reroute);
   expand_nmax = -1;
-  if (reroute == NULL)
+  if (!reroute)
     {
     addr->message = string_sprintf("%s router: failed to expand %s: %s",
-      rblock->name, ob->reroute, expand_string_message);
+      rblock->drinst.name, ob->reroute, expand_string_message);
     return DEFER;
     }
   }
-else reroute = reply;
+else
+  reroute = reply;
 
 /* We should now have a new address in the form user@domain. */
 
-domain = Ustrchr(reroute, '@');
-if (domain == NULL)
+if (!(domain = Ustrchr(reroute, '@')))
   {
   log_write(0, LOG_MAIN, "%s router: reroute string %s is not of the form "
-    "user@domain", rblock->name, reroute);
+    "user@domain", rblock->drinst.name, reroute);
   addr->message = string_sprintf("%s router: reroute string %s is not of the "
-    "form user@domain", rblock->name, reroute);
+    "form user@domain", rblock->drinst.name, reroute);
   return DEFER;
   }
 
@@ -379,17 +395,16 @@ the chain of new addressess. */
 new_addr = deliver_make_addr(reroute, TRUE);
 new_addr->parent = addr;
 
-copyflag(new_addr, addr, af_propagate);
 new_addr->prop = addr->prop;
 
 if (addr->child_count == USHRT_MAX)
   log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s router generated more than %d "
-    "child addresses for <%s>", rblock->name, USHRT_MAX, addr->address);
+    "child addresses for <%s>", rblock->drinst.name, USHRT_MAX, addr->address);
 addr->child_count++;
 new_addr->next = *addr_new;
 *addr_new = new_addr;
 
-/* Set up the errors address, if any, and the additional and removeable headers
+/* Set up the errors address, if any, and the additional and removable headers
 for this new address. */
 
 rc = rf_get_errors_address(addr, rblock, verify, &new_addr->prop.errors_address);
@@ -402,4 +417,30 @@ if (rc != OK) return rc;
 return OK;
 }
 
+
+
+# ifdef DYNLOOKUP
+#  define iplookup_router_info _router_info
+# endif
+
+router_info iplookup_router_info =
+{
+.drinfo = {
+  .driver_name =       US"iplookup",
+  .options =           iplookup_router_options,
+  .options_count =     &iplookup_router_options_count,
+  .options_block =     &iplookup_router_option_defaults,
+  .options_len =       sizeof(iplookup_router_options_block),
+  .init =              iplookup_router_init,
+# ifdef DYNLOOKUP
+  .dyn_magic =         ROUTER_MAGIC,
+# endif
+  },
+.code =                        iplookup_router_entry,
+.tidyup =              NULL,     /* no tidyup entry */
+.ri_flags =            ri_notransport
+};
+
+#endif /*!MACRO_PREDEF*/
+#endif /*ROUTER_IPLOOKUP*/
 /* End of routers/iplookup.c */