1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) University of Cambridge 1995 - 2018 */
6 /* See the file NOTICE for conditions of use and distribution. */
10 #include "rf_functions.h"
14 /* IP connection types */
20 /* Options specific to the iplookup router. */
22 optionlist iplookup_router_options[] = {
23 { "hosts", opt_stringptr,
24 OPT_OFF(iplookup_router_options_block, hosts) },
25 { "optional", opt_bool,
26 OPT_OFF(iplookup_router_options_block, optional) },
28 OPT_OFF(iplookup_router_options_block, port) },
29 { "protocol", opt_stringptr,
30 OPT_OFF(iplookup_router_options_block, protocol_name) },
31 { "query", opt_stringptr,
32 OPT_OFF(iplookup_router_options_block, query) },
33 { "reroute", opt_stringptr,
34 OPT_OFF(iplookup_router_options_block, reroute) },
35 { "response_pattern", opt_stringptr,
36 OPT_OFF(iplookup_router_options_block, response_pattern) },
37 { "timeout", opt_time,
38 OPT_OFF(iplookup_router_options_block, timeout) }
41 /* Size of the options list. An extern variable has to be used so that its
42 address can appear in the tables drtables.c. */
44 int iplookup_router_options_count =
45 sizeof(iplookup_router_options)/sizeof(optionlist);
51 iplookup_router_options_block iplookup_router_option_defaults = {0};
52 void iplookup_router_init(router_instance *rblock) {}
53 int iplookup_router_entry(router_instance *rblock, address_item *addr,
54 struct passwd *pw, int verify, address_item **addr_local,
55 address_item **addr_remote, address_item **addr_new,
56 address_item **addr_succeed) {return 0;}
58 #else /*!MACRO_PREDEF*/
61 /* Default private options block for the iplookup router. */
63 iplookup_router_options_block iplookup_router_option_defaults = {
65 ip_udp, /* protocol */
67 NULL, /* protocol_name */
69 NULL, /* query; NULL => local_part@domain */
70 NULL, /* response_pattern; NULL => don't apply regex */
71 NULL, /* reroute; NULL => just used returned data */
72 NULL, /* re_response_pattern; compiled pattern */
78 /*************************************************
79 * Initialization entry point *
80 *************************************************/
82 /* Called for each instance, after its options have been read, to enable
83 consistency checks to be done, or anything else that needs to be set up. */
86 iplookup_router_init(router_instance *rblock)
88 iplookup_router_options_block *ob =
89 (iplookup_router_options_block *)(rblock->options_block);
91 /* A port and a host list must be given */
94 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n "
95 "a port must be specified", rblock->name);
97 if (ob->hosts == NULL)
98 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n "
99 "a host list must be specified", rblock->name);
101 /* Translate protocol name into value */
103 if (ob->protocol_name != NULL)
105 if (Ustrcmp(ob->protocol_name, "udp") == 0) ob->protocol = ip_udp;
106 else if (Ustrcmp(ob->protocol_name, "tcp") == 0) ob->protocol = ip_tcp;
107 else log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n "
108 "protocol not specified as udp or tcp", rblock->name);
111 /* If a response pattern is given, compile it now to get the error early. */
113 if (ob->response_pattern != NULL)
114 ob->re_response_pattern =
115 regex_must_compile(ob->response_pattern, FALSE, TRUE);
120 /*************************************************
122 *************************************************/
124 /* See local README for interface details. This router returns:
127 . pattern or identification match on returned data failed
130 . failed to expand the query or rerouting string
131 . failed to create socket ("optional" not set)
132 . failed to find a host, failed to connect, timed out ("optional" not set)
133 . rerouting string is not in the form localpart@domain
134 . verifying the errors address caused a deferment or a big disaster such
135 as an expansion failure (rf_get_errors_address)
136 . expanding a headers_{add,remove} string caused a deferment or another
137 expansion error (rf_get_munge_headers)
140 . failed to create socket ("optional" set)
141 . failed to find a host, failed to connect, timed out ("optional" set)
144 . new address added to addr_new
148 iplookup_router_entry(
149 router_instance *rblock, /* data for this instantiation */
150 address_item *addr, /* address we are working on */
151 struct passwd *pw, /* passwd entry after check_local_user */
152 int verify, /* v_none/v_recipient/v_sender/v_expn */
153 address_item **addr_local, /* add it to this if it's local */
154 address_item **addr_remote, /* add it to this if it's remote */
155 address_item **addr_new, /* put new addresses on here */
156 address_item **addr_succeed) /* put old address here on success */
158 uschar *query = NULL;
160 uschar *hostname, *reroute, *domain;
161 const uschar *listptr;
162 uschar host_buffer[256];
163 host_item *host = store_get(sizeof(host_item), FALSE);
164 address_item *new_addr;
165 iplookup_router_options_block *ob =
166 (iplookup_router_options_block *)(rblock->options_block);
167 const pcre *re = ob->re_response_pattern;
168 int count, query_len, rc;
171 addr_local = addr_local; /* Keep picky compilers happy */
172 addr_remote = addr_remote;
173 addr_succeed = addr_succeed;
176 DEBUG(D_route) debug_printf("%s router called for %s: domain = %s\n",
177 rblock->name, addr->address, addr->domain);
179 reply = store_get(256, TRUE); /* tainted data */
181 /* Build the query string to send. If not explicitly given, a default of
182 "user@domain user@domain" is used. */
184 if (ob->query == NULL)
185 query = string_sprintf("%s@%s %s@%s", addr->local_part, addr->domain,
186 addr->local_part, addr->domain);
189 query = expand_string(ob->query);
192 addr->message = string_sprintf("%s router: failed to expand %s: %s",
193 rblock->name, ob->query, expand_string_message);
198 query_len = Ustrlen(query);
199 DEBUG(D_route) debug_printf("%s router query is \"%s\"\n", rblock->name,
200 string_printing(query));
202 /* Now connect to the required port for each of the hosts in turn, until a
203 response it received. Initialization insists on the port being set and there
204 being a host list. */
207 while ((hostname = string_nextinlist(&listptr, &sep, host_buffer,
208 sizeof(host_buffer))))
212 DEBUG(D_route) debug_printf("calling host %s\n", hostname);
214 host->name = hostname;
215 host->address = NULL;
216 host->port = PORT_NONE;
220 if (string_is_ip_address(host->name, NULL) != 0)
221 host->address = host->name;
224 /*XXX might want dnssec request/require on an iplookup router? */
225 int rc = host_find_byname(host, NULL, HOST_FIND_QUALIFY_SINGLE, NULL, TRUE);
226 if (rc == HOST_FIND_FAILED || rc == HOST_FIND_AGAIN) continue;
229 /* Loop for possible multiple IP addresses for the given name. */
231 for (h = host; h; h = h->next)
234 client_conn_ctx query_cctx = {0};
236 /* Skip any hosts for which we have no address */
238 if (!h->address) continue;
240 /* Create a socket, for UDP or TCP, as configured. IPv6 addresses are
241 detected by checking for a colon in the address. */
243 host_af = (Ustrchr(h->address, ':') != NULL)? AF_INET6 : AF_INET;
245 query_cctx.sock = ip_socket(ob->protocol == ip_udp ? SOCK_DGRAM:SOCK_STREAM,
247 if (query_cctx.sock < 0)
249 if (ob->optional) return PASS;
250 addr->message = string_sprintf("failed to create socket in %s router",
255 /* Connect to the remote host, under a timeout. In fact, timeouts can occur
256 here only for TCP calls; for a UDP socket, "connect" always works (the
257 router will timeout later on the read call). */
258 /*XXX could take advantage of TFO */
260 if (ip_connect(query_cctx.sock, host_af, h->address,ob->port, ob->timeout,
261 ob->protocol == ip_udp ? NULL : &tcp_fastopen_nodata) < 0)
263 close(query_cctx.sock);
265 debug_printf("connection to %s failed: %s\n", h->address,
270 /* Send the query. If it fails, just continue with the next address. */
272 if (send(query_cctx.sock, query, query_len, 0) < 0)
274 DEBUG(D_route) debug_printf("send to %s failed\n", h->address);
275 (void)close(query_cctx.sock);
279 /* Read the response and close the socket. If the read fails, try the
282 count = ip_recv(&query_cctx, reply, sizeof(reply) - 1, time(NULL) + ob->timeout);
283 (void)close(query_cctx.sock);
286 DEBUG(D_route) debug_printf("%s from %s\n", (errno == ETIMEDOUT)?
287 "timed out" : "recv failed", h->address);
292 /* Success; break the loop */
295 DEBUG(D_route) debug_printf("%s router received \"%s\" from %s\n",
296 rblock->name, string_printing(reply), h->address);
300 /* If h == NULL we have tried all the IP addresses and failed on all of them,
301 so we must continue to try more host names. Otherwise we have succeeded. */
307 /* If hostname is NULL, we have failed to find any host, or failed to
308 connect to any of the IP addresses, or timed out while reading or writing to
309 those we have connected to. In all cases, we must pass if optional and
312 if (hostname == NULL)
314 DEBUG(D_route) debug_printf("%s router failed to get anything\n", rblock->name);
315 if (ob->optional) return PASS;
316 addr->message = string_sprintf("%s router: failed to communicate with any "
317 "host", rblock->name);
322 /* If a response pattern was supplied, match the returned string against it. A
323 failure to match causes the router to decline. After a successful match, the
324 numerical variables for expanding the rerouted address are set up. */
328 if (!regex_match_and_setup(re, reply, 0, -1))
330 DEBUG(D_route) debug_printf("%s router: %s failed to match response %s\n",
331 rblock->name, ob->response_pattern, reply);
337 /* If no response pattern was supplied, set up $0 as the response up to the
338 first white space (if any). Also, if no query was specified, check that what
339 follows the white space matches user@domain. */
344 while (reply[n] != 0 && !isspace(reply[n])) n++;
346 expand_nstring[0] = reply;
347 expand_nlength[0] = n;
349 if (ob->query == NULL)
352 while (isspace(reply[nn])) nn++;
353 if (Ustrcmp(query + query_len/2 + 1, reply+nn) != 0)
355 DEBUG(D_route) debug_printf("%s router: failed to match identification "
356 "in response %s\n", rblock->name, reply);
361 reply[n] = 0; /* Terminate for the default case */
364 /* If an explicit rerouting string is specified, expand it. Otherwise, use
365 what was sent back verbatim. */
367 if (ob->reroute != NULL)
369 reroute = expand_string(ob->reroute);
373 addr->message = string_sprintf("%s router: failed to expand %s: %s",
374 rblock->name, ob->reroute, expand_string_message);
378 else reroute = reply;
380 /* We should now have a new address in the form user@domain. */
382 domain = Ustrchr(reroute, '@');
385 log_write(0, LOG_MAIN, "%s router: reroute string %s is not of the form "
386 "user@domain", rblock->name, reroute);
387 addr->message = string_sprintf("%s router: reroute string %s is not of the "
388 "form user@domain", rblock->name, reroute);
392 /* Create a child address with the old one as parent. Put the new address on
393 the chain of new addressess. */
395 new_addr = deliver_make_addr(reroute, TRUE);
396 new_addr->parent = addr;
398 new_addr->prop = addr->prop;
400 if (addr->child_count == USHRT_MAX)
401 log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s router generated more than %d "
402 "child addresses for <%s>", rblock->name, USHRT_MAX, addr->address);
404 new_addr->next = *addr_new;
405 *addr_new = new_addr;
407 /* Set up the errors address, if any, and the additional and removable headers
408 for this new address. */
410 rc = rf_get_errors_address(addr, rblock, verify, &new_addr->prop.errors_address);
411 if (rc != OK) return rc;
413 rc = rf_get_munge_headers(addr, rblock, &new_addr->prop.extra_headers,
414 &new_addr->prop.remove_headers);
415 if (rc != OK) return rc;
420 #endif /*!MACRO_PREDEF*/
421 /* End of routers/iplookup.c */