* 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
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 = {
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);
}
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
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))))
{
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 */
/* 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));
/* 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)?
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;
}
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;
}
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;
}
}
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;
}
}
/* 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;
}
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);
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 */