1 /* $Cambridge: exim/src/src/routers/iplookup.c,v 1.8 2006/10/09 14:36:25 ph10 Exp $ */
3 /*************************************************
4 * Exim - an Internet mail transport agent *
5 *************************************************/
7 /* Copyright (c) University of Cambridge 1995 - 2006 */
8 /* See the file NOTICE for conditions of use and distribution. */
12 #include "rf_functions.h"
16 /* IP connection types */
22 /* Options specific to the iplookup router. */
24 optionlist iplookup_router_options[] = {
25 { "hosts", opt_stringptr,
26 (void *)(offsetof(iplookup_router_options_block, hosts)) },
27 { "optional", opt_bool,
28 (void *)(offsetof(iplookup_router_options_block, optional)) },
30 (void *)(offsetof(iplookup_router_options_block, port)) },
31 { "protocol", opt_stringptr,
32 (void *)(offsetof(iplookup_router_options_block, protocol_name)) },
33 { "query", opt_stringptr,
34 (void *)(offsetof(iplookup_router_options_block, query)) },
35 { "reroute", opt_stringptr,
36 (void *)(offsetof(iplookup_router_options_block, reroute)) },
37 { "response_pattern", opt_stringptr,
38 (void *)(offsetof(iplookup_router_options_block, response_pattern)) },
39 { "timeout", opt_time,
40 (void *)(offsetof(iplookup_router_options_block, timeout)) }
43 /* Size of the options list. An extern variable has to be used so that its
44 address can appear in the tables drtables.c. */
46 int iplookup_router_options_count =
47 sizeof(iplookup_router_options)/sizeof(optionlist);
49 /* Default private options block for the iplookup router. */
51 iplookup_router_options_block iplookup_router_option_defaults = {
53 ip_udp, /* protocol */
55 NULL, /* protocol_name */
57 NULL, /* query; NULL => local_part@domain */
58 NULL, /* response_pattern; NULL => don't apply regex */
59 NULL, /* reroute; NULL => just used returned data */
60 NULL, /* re_response_pattern; compiled pattern */
66 /*************************************************
67 * Initialization entry point *
68 *************************************************/
70 /* Called for each instance, after its options have been read, to enable
71 consistency checks to be done, or anything else that needs to be set up. */
74 iplookup_router_init(router_instance *rblock)
76 iplookup_router_options_block *ob =
77 (iplookup_router_options_block *)(rblock->options_block);
79 /* A port and a host list must be given */
82 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n "
83 "a port must be specified", rblock->name);
85 if (ob->hosts == NULL)
86 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n "
87 "a host list must be specified", rblock->name);
89 /* Translate protocol name into value */
91 if (ob->protocol_name != NULL)
93 if (Ustrcmp(ob->protocol_name, "udp") == 0) ob->protocol = ip_udp;
94 else if (Ustrcmp(ob->protocol_name, "tcp") == 0) ob->protocol = ip_tcp;
95 else log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n "
96 "protocol not specified as udp or tcp", rblock->name);
99 /* If a response pattern is given, compile it now to get the error early. */
101 if (ob->response_pattern != NULL)
102 ob->re_response_pattern =
103 regex_must_compile(ob->response_pattern, FALSE, TRUE);
108 /*************************************************
110 *************************************************/
112 /* See local README for interface details. This router returns:
115 . pattern or identification match on returned data failed
118 . failed to expand the query or rerouting string
119 . failed to create socket ("optional" not set)
120 . failed to find a host, failed to connect, timed out ("optional" not set)
121 . rerouting string is not in the form localpart@domain
122 . verifying the errors address caused a deferment or a big disaster such
123 as an expansion failure (rf_get_errors_address)
124 . expanding a headers_{add,remove} string caused a deferment or another
125 expansion error (rf_get_munge_headers)
128 . failed to create socket ("optional" set)
129 . failed to find a host, failed to connect, timed out ("optional" set)
132 . new address added to addr_new
136 iplookup_router_entry(
137 router_instance *rblock, /* data for this instantiation */
138 address_item *addr, /* address we are working on */
139 struct passwd *pw, /* passwd entry after check_local_user */
140 int verify, /* v_none/v_recipient/v_sender/v_expn */
141 address_item **addr_local, /* add it to this if it's local */
142 address_item **addr_remote, /* add it to this if it's remote */
143 address_item **addr_new, /* put new addresses on here */
144 address_item **addr_succeed) /* put old address here on success */
146 uschar *query = NULL;
148 uschar *hostname, *reroute, *domain, *listptr;
149 uschar host_buffer[256];
150 host_item *host = store_get(sizeof(host_item));
151 address_item *new_addr;
152 iplookup_router_options_block *ob =
153 (iplookup_router_options_block *)(rblock->options_block);
154 const pcre *re = ob->re_response_pattern;
155 int count, query_len, rc;
158 addr_local = addr_local; /* Keep picky compilers happy */
159 addr_remote = addr_remote;
160 addr_succeed = addr_succeed;
163 DEBUG(D_route) debug_printf("%s router called for %s: domain = %s\n",
164 rblock->name, addr->address, addr->domain);
166 /* Build the query string to send. If not explicitly given, a default of
167 "user@domain user@domain" is used. */
169 if (ob->query == NULL)
170 query = string_sprintf("%s@%s %s@%s", addr->local_part, addr->domain,
171 addr->local_part, addr->domain);
174 query = expand_string(ob->query);
177 addr->message = string_sprintf("%s router: failed to expand %s: %s",
178 rblock->name, ob->query, expand_string_message);
183 query_len = Ustrlen(query);
184 DEBUG(D_route) debug_printf("%s router query is \"%s\"\n", rblock->name,
185 string_printing(query));
187 /* Now connect to the required port for each of the hosts in turn, until a
188 response it received. Initialization insists on the port being set and there
189 being a host list. */
192 while ((hostname = string_nextinlist(&listptr, &sep, host_buffer,
193 sizeof(host_buffer))) != NULL)
197 DEBUG(D_route) debug_printf("calling host %s\n", hostname);
199 host->name = hostname;
200 host->address = NULL;
201 host->port = PORT_NONE;
205 if (string_is_ip_address(host->name, NULL) != 0)
206 host->address = host->name;
209 int rc = host_find_byname(host, NULL, HOST_FIND_QUALIFY_SINGLE, NULL, TRUE);
210 if (rc == HOST_FIND_FAILED || rc == HOST_FIND_AGAIN) continue;
213 /* Loop for possible multiple IP addresses for the given name. */
215 for (h = host; h != NULL; h = h->next)
217 int host_af, query_socket;
219 /* Skip any hosts for which we have no address */
221 if (h->address == NULL) continue;
223 /* Create a socket, for UDP or TCP, as configured. IPv6 addresses are
224 detected by checking for a colon in the address. */
226 host_af = (Ustrchr(h->address, ':') != NULL)? AF_INET6 : AF_INET;
227 query_socket = ip_socket((ob->protocol == ip_udp)? SOCK_DGRAM:SOCK_STREAM,
229 if (query_socket < 0)
231 if (ob->optional) return PASS;
232 addr->message = string_sprintf("failed to create socket in %s router",
237 /* Connect to the remote host, under a timeout. In fact, timeouts can occur
238 here only for TCP calls; for a UDP socket, "connect" always works (the
239 router will timeout later on the read call). */
241 if (ip_connect(query_socket, host_af, h->address,ob->port, ob->timeout) < 0)
245 debug_printf("connection to %s failed: %s\n", h->address,
250 /* Send the query. If it fails, just continue with the next address. */
252 if (send(query_socket, query, query_len, 0) < 0)
254 DEBUG(D_route) debug_printf("send to %s failed\n", h->address);
255 (void)close(query_socket);
259 /* Read the response and close the socket. If the read fails, try the
262 count = ip_recv(query_socket, reply, sizeof(reply) - 1, ob->timeout);
263 (void)close(query_socket);
266 DEBUG(D_route) debug_printf("%s from %s\n", (errno == ETIMEDOUT)?
267 "timed out" : "recv failed", h->address);
272 /* Success; break the loop */
275 DEBUG(D_route) debug_printf("%s router received \"%s\" from %s\n",
276 rblock->name, string_printing(reply), h->address);
280 /* If h == NULL we have tried all the IP addresses and failed on all of them,
281 so we must continue to try more host names. Otherwise we have succeeded. */
283 if (h != NULL) break;
287 /* If hostname is NULL, we have failed to find any host, or failed to
288 connect to any of the IP addresses, or timed out while reading or writing to
289 those we have connected to. In all cases, we must pass if optional and
292 if (hostname == NULL)
294 DEBUG(D_route) debug_printf("%s router failed to get anything\n", rblock->name);
295 if (ob->optional) return PASS;
296 addr->message = string_sprintf("%s router: failed to communicate with any "
297 "host", rblock->name);
302 /* If a response pattern was supplied, match the returned string against it. A
303 failure to match causes the router to decline. After a successful match, the
304 numerical variables for expanding the rerouted address are set up. */
308 if (!regex_match_and_setup(re, reply, 0, -1))
310 DEBUG(D_route) debug_printf("%s router: %s failed to match response %s\n",
311 rblock->name, ob->response_pattern, reply);
317 /* If no response pattern was supplied, set up $0 as the response up to the
318 first white space (if any). Also, if no query was specified, check that what
319 follows the white space matches user@domain. */
324 while (reply[n] != 0 && !isspace(reply[n])) n++;
326 expand_nstring[0] = reply;
327 expand_nlength[0] = n;
329 if (ob->query == NULL)
332 while (isspace(reply[nn])) nn++;
333 if (Ustrcmp(query + query_len/2 + 1, reply+nn) != 0)
335 DEBUG(D_route) debug_printf("%s router: failed to match identification "
336 "in response %s\n", rblock->name, reply);
341 reply[n] = 0; /* Terminate for the default case */
344 /* If an explicit rerouting string is specified, expand it. Otherwise, use
345 what was sent back verbatim. */
347 if (ob->reroute != NULL)
349 reroute = expand_string(ob->reroute);
353 addr->message = string_sprintf("%s router: failed to expand %s: %s",
354 rblock->name, ob->reroute, expand_string_message);
358 else reroute = reply;
360 /* We should now have a new address in the form user@domain. */
362 domain = Ustrchr(reroute, '@');
365 log_write(0, LOG_MAIN, "%s router: reroute string %s is not of the form "
366 "user@domain", rblock->name, reroute);
367 addr->message = string_sprintf("%s router: reroute string %s is not of the "
368 "form user@domain", rblock->name, reroute);
372 /* Create a child address with the old one as parent. Put the new address on
373 the chain of new addressess. */
375 new_addr = deliver_make_addr(reroute, TRUE);
376 new_addr->parent = addr;
378 copyflag(new_addr, addr, af_propagate);
379 new_addr->p = addr->p;
382 new_addr->next = *addr_new;
383 *addr_new = new_addr;
385 /* Set up the errors address, if any, and the additional and removeable headers
386 for this new address. */
388 rc = rf_get_errors_address(addr, rblock, verify, &(new_addr->p.errors_address));
389 if (rc != OK) return rc;
391 rc = rf_get_munge_headers(addr, rblock, &(new_addr->p.extra_headers),
392 &(new_addr->p.remove_headers));
393 if (rc != OK) return rc;
398 /* End of routers/iplookup.c */