1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) University of Cambridge 1995 - 2015 */
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 (void *)(offsetof(iplookup_router_options_block, hosts)) },
25 { "optional", opt_bool,
26 (void *)(offsetof(iplookup_router_options_block, optional)) },
28 (void *)(offsetof(iplookup_router_options_block, port)) },
29 { "protocol", opt_stringptr,
30 (void *)(offsetof(iplookup_router_options_block, protocol_name)) },
31 { "query", opt_stringptr,
32 (void *)(offsetof(iplookup_router_options_block, query)) },
33 { "reroute", opt_stringptr,
34 (void *)(offsetof(iplookup_router_options_block, reroute)) },
35 { "response_pattern", opt_stringptr,
36 (void *)(offsetof(iplookup_router_options_block, response_pattern)) },
37 { "timeout", opt_time,
38 (void *)(offsetof(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);
47 /* Default private options block for the iplookup router. */
49 iplookup_router_options_block iplookup_router_option_defaults = {
51 ip_udp, /* protocol */
53 NULL, /* protocol_name */
55 NULL, /* query; NULL => local_part@domain */
56 NULL, /* response_pattern; NULL => don't apply regex */
57 NULL, /* reroute; NULL => just used returned data */
58 NULL, /* re_response_pattern; compiled pattern */
64 /*************************************************
65 * Initialization entry point *
66 *************************************************/
68 /* Called for each instance, after its options have been read, to enable
69 consistency checks to be done, or anything else that needs to be set up. */
72 iplookup_router_init(router_instance *rblock)
74 iplookup_router_options_block *ob =
75 (iplookup_router_options_block *)(rblock->options_block);
77 /* A port and a host list must be given */
80 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n "
81 "a port must be specified", rblock->name);
83 if (ob->hosts == NULL)
84 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n "
85 "a host list must be specified", rblock->name);
87 /* Translate protocol name into value */
89 if (ob->protocol_name != NULL)
91 if (Ustrcmp(ob->protocol_name, "udp") == 0) ob->protocol = ip_udp;
92 else if (Ustrcmp(ob->protocol_name, "tcp") == 0) ob->protocol = ip_tcp;
93 else log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n "
94 "protocol not specified as udp or tcp", rblock->name);
97 /* If a response pattern is given, compile it now to get the error early. */
99 if (ob->response_pattern != NULL)
100 ob->re_response_pattern =
101 regex_must_compile(ob->response_pattern, FALSE, TRUE);
106 /*************************************************
108 *************************************************/
110 /* See local README for interface details. This router returns:
113 . pattern or identification match on returned data failed
116 . failed to expand the query or rerouting string
117 . failed to create socket ("optional" not set)
118 . failed to find a host, failed to connect, timed out ("optional" not set)
119 . rerouting string is not in the form localpart@domain
120 . verifying the errors address caused a deferment or a big disaster such
121 as an expansion failure (rf_get_errors_address)
122 . expanding a headers_{add,remove} string caused a deferment or another
123 expansion error (rf_get_munge_headers)
126 . failed to create socket ("optional" set)
127 . failed to find a host, failed to connect, timed out ("optional" set)
130 . new address added to addr_new
134 iplookup_router_entry(
135 router_instance *rblock, /* data for this instantiation */
136 address_item *addr, /* address we are working on */
137 struct passwd *pw, /* passwd entry after check_local_user */
138 int verify, /* v_none/v_recipient/v_sender/v_expn */
139 address_item **addr_local, /* add it to this if it's local */
140 address_item **addr_remote, /* add it to this if it's remote */
141 address_item **addr_new, /* put new addresses on here */
142 address_item **addr_succeed) /* put old address here on success */
144 uschar *query = NULL;
146 uschar *hostname, *reroute, *domain;
147 const uschar *listptr;
148 uschar host_buffer[256];
149 host_item *host = store_get(sizeof(host_item));
150 address_item *new_addr;
151 iplookup_router_options_block *ob =
152 (iplookup_router_options_block *)(rblock->options_block);
153 const pcre *re = ob->re_response_pattern;
154 int count, query_len, rc;
157 addr_local = addr_local; /* Keep picky compilers happy */
158 addr_remote = addr_remote;
159 addr_succeed = addr_succeed;
162 DEBUG(D_route) debug_printf("%s router called for %s: domain = %s\n",
163 rblock->name, addr->address, addr->domain);
165 reply = store_get(256);
167 /* Build the query string to send. If not explicitly given, a default of
168 "user@domain user@domain" is used. */
170 if (ob->query == NULL)
171 query = string_sprintf("%s@%s %s@%s", addr->local_part, addr->domain,
172 addr->local_part, addr->domain);
175 query = expand_string(ob->query);
178 addr->message = string_sprintf("%s router: failed to expand %s: %s",
179 rblock->name, ob->query, expand_string_message);
184 query_len = Ustrlen(query);
185 DEBUG(D_route) debug_printf("%s router query is \"%s\"\n", rblock->name,
186 string_printing(query));
188 /* Now connect to the required port for each of the hosts in turn, until a
189 response it received. Initialization insists on the port being set and there
190 being a host list. */
193 while ((hostname = string_nextinlist(&listptr, &sep, host_buffer,
194 sizeof(host_buffer))) != NULL)
198 DEBUG(D_route) debug_printf("calling host %s\n", hostname);
200 host->name = hostname;
201 host->address = NULL;
202 host->port = PORT_NONE;
206 if (string_is_ip_address(host->name, NULL) != 0)
207 host->address = host->name;
210 /*XXX might want dnssec request/require on an iplookup router? */
211 int rc = host_find_byname(host, NULL, HOST_FIND_QUALIFY_SINGLE, NULL, TRUE);
212 if (rc == HOST_FIND_FAILED || rc == HOST_FIND_AGAIN) continue;
215 /* Loop for possible multiple IP addresses for the given name. */
217 for (h = host; h != NULL; h = h->next)
219 int host_af, query_socket;
221 /* Skip any hosts for which we have no address */
223 if (h->address == NULL) continue;
225 /* Create a socket, for UDP or TCP, as configured. IPv6 addresses are
226 detected by checking for a colon in the address. */
228 host_af = (Ustrchr(h->address, ':') != NULL)? AF_INET6 : AF_INET;
229 query_socket = ip_socket((ob->protocol == ip_udp)? SOCK_DGRAM:SOCK_STREAM,
231 if (query_socket < 0)
233 if (ob->optional) return PASS;
234 addr->message = string_sprintf("failed to create socket in %s router",
239 /* Connect to the remote host, under a timeout. In fact, timeouts can occur
240 here only for TCP calls; for a UDP socket, "connect" always works (the
241 router will timeout later on the read call). */
243 if (ip_connect(query_socket, host_af, h->address,ob->port, ob->timeout) < 0)
247 debug_printf("connection to %s failed: %s\n", h->address,
252 /* Send the query. If it fails, just continue with the next address. */
254 if (send(query_socket, query, query_len, 0) < 0)
256 DEBUG(D_route) debug_printf("send to %s failed\n", h->address);
257 (void)close(query_socket);
261 /* Read the response and close the socket. If the read fails, try the
264 count = ip_recv(query_socket, reply, sizeof(reply) - 1, ob->timeout);
265 (void)close(query_socket);
268 DEBUG(D_route) debug_printf("%s from %s\n", (errno == ETIMEDOUT)?
269 "timed out" : "recv failed", h->address);
274 /* Success; break the loop */
277 DEBUG(D_route) debug_printf("%s router received \"%s\" from %s\n",
278 rblock->name, string_printing(reply), h->address);
282 /* If h == NULL we have tried all the IP addresses and failed on all of them,
283 so we must continue to try more host names. Otherwise we have succeeded. */
285 if (h != NULL) break;
289 /* If hostname is NULL, we have failed to find any host, or failed to
290 connect to any of the IP addresses, or timed out while reading or writing to
291 those we have connected to. In all cases, we must pass if optional and
294 if (hostname == NULL)
296 DEBUG(D_route) debug_printf("%s router failed to get anything\n", rblock->name);
297 if (ob->optional) return PASS;
298 addr->message = string_sprintf("%s router: failed to communicate with any "
299 "host", rblock->name);
304 /* If a response pattern was supplied, match the returned string against it. A
305 failure to match causes the router to decline. After a successful match, the
306 numerical variables for expanding the rerouted address are set up. */
310 if (!regex_match_and_setup(re, reply, 0, -1))
312 DEBUG(D_route) debug_printf("%s router: %s failed to match response %s\n",
313 rblock->name, ob->response_pattern, reply);
319 /* If no response pattern was supplied, set up $0 as the response up to the
320 first white space (if any). Also, if no query was specified, check that what
321 follows the white space matches user@domain. */
326 while (reply[n] != 0 && !isspace(reply[n])) n++;
328 expand_nstring[0] = reply;
329 expand_nlength[0] = n;
331 if (ob->query == NULL)
334 while (isspace(reply[nn])) nn++;
335 if (Ustrcmp(query + query_len/2 + 1, reply+nn) != 0)
337 DEBUG(D_route) debug_printf("%s router: failed to match identification "
338 "in response %s\n", rblock->name, reply);
343 reply[n] = 0; /* Terminate for the default case */
346 /* If an explicit rerouting string is specified, expand it. Otherwise, use
347 what was sent back verbatim. */
349 if (ob->reroute != NULL)
351 reroute = expand_string(ob->reroute);
355 addr->message = string_sprintf("%s router: failed to expand %s: %s",
356 rblock->name, ob->reroute, expand_string_message);
360 else reroute = reply;
362 /* We should now have a new address in the form user@domain. */
364 domain = Ustrchr(reroute, '@');
367 log_write(0, LOG_MAIN, "%s router: reroute string %s is not of the form "
368 "user@domain", rblock->name, reroute);
369 addr->message = string_sprintf("%s router: reroute string %s is not of the "
370 "form user@domain", rblock->name, reroute);
374 /* Create a child address with the old one as parent. Put the new address on
375 the chain of new addressess. */
377 new_addr = deliver_make_addr(reroute, TRUE);
378 new_addr->parent = addr;
380 copyflag(new_addr, addr, af_propagate);
381 new_addr->prop = addr->prop;
383 if (addr->child_count == SHRT_MAX)
384 log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s router generated more than %d "
385 "child addresses for <%s>", rblock->name, SHRT_MAX, addr->address);
387 new_addr->next = *addr_new;
388 *addr_new = new_addr;
390 /* Set up the errors address, if any, and the additional and removeable headers
391 for this new address. */
393 rc = rf_get_errors_address(addr, rblock, verify, &new_addr->prop.errors_address);
394 if (rc != OK) return rc;
396 rc = rf_get_munge_headers(addr, rblock, &new_addr->prop.extra_headers,
397 &new_addr->prop.remove_headers);
398 if (rc != OK) return rc;
403 /* End of routers/iplookup.c */