When checking the local_parts router precondition after a
[exim.git] / src / src / routers / manualroute.c
CommitLineData
c456d9bb 1/* $Cambridge: exim/src/src/routers/manualroute.c,v 1.6 2007/03/13 15:32:48 ph10 Exp $ */
0756eb3c
PH
2
3/*************************************************
4* Exim - an Internet mail transport agent *
5*************************************************/
6
184e8823 7/* Copyright (c) University of Cambridge 1995 - 2007 */
0756eb3c
PH
8/* See the file NOTICE for conditions of use and distribution. */
9
10
11#include "../exim.h"
12#include "rf_functions.h"
13#include "manualroute.h"
14
15
16/* Options specific to the manualroute router. */
17
18optionlist manualroute_router_options[] = {
c456d9bb
PH
19 { "host_all_ignored", opt_stringptr,
20 (void *)(offsetof(manualroute_router_options_block, host_all_ignored)) },
0756eb3c
PH
21 { "host_find_failed", opt_stringptr,
22 (void *)(offsetof(manualroute_router_options_block, host_find_failed)) },
23 { "hosts_randomize", opt_bool,
24 (void *)(offsetof(manualroute_router_options_block, hosts_randomize)) },
25 { "route_data", opt_stringptr,
26 (void *)(offsetof(manualroute_router_options_block, route_data)) },
27 { "route_list", opt_stringptr,
28 (void *)(offsetof(manualroute_router_options_block, route_list)) },
29 { "same_domain_copy_routing", opt_bool|opt_public,
30 (void *)(offsetof(router_instance, same_domain_copy_routing)) }
31};
32
33/* Size of the options list. An extern variable has to be used so that its
34address can appear in the tables drtables.c. */
35
36int manualroute_router_options_count =
37 sizeof(manualroute_router_options)/sizeof(optionlist);
38
39/* Default private options block for the manualroute router. */
40
41manualroute_router_options_block manualroute_router_option_defaults = {
c456d9bb
PH
42 -1, /* host_all_ignored code (unset) */
43 -1, /* host_find_failed code (unset) */
0756eb3c 44 FALSE, /* hosts_randomize */
c456d9bb 45 US"defer", /* host_all_ignored */
0756eb3c
PH
46 US"freeze", /* host_find_failed */
47 NULL, /* route_data */
48 NULL /* route_list */
49};
50
51
c456d9bb
PH
52/* Names and values for host_find_failed and host_all_ignored. */
53
54static uschar *hff_names[] = {
55 US"ignore", /* MUST be first - not valid for host_all_ignored */
56 US"decline",
57 US"defer",
58 US"fail",
59 US"freeze",
60 US"pass" };
61
62static int hff_codes[] = { hff_ignore, hff_decline, hff_defer, hff_fail,
63 hff_freeze, hff_pass };
64
65static int hff_count= sizeof(hff_codes)/sizeof(int);
0756eb3c
PH
66
67
68
69/*************************************************
70* Initialization entry point *
71*************************************************/
72
73/* Called for each instance, after its options have been read, to enable
74consistency checks to be done, or anything else that needs to be set up. */
75
76void
77manualroute_router_init(router_instance *rblock)
78{
79manualroute_router_options_block *ob =
80 (manualroute_router_options_block *)(rblock->options_block);
c456d9bb 81int i;
0756eb3c
PH
82
83/* Host_find_failed must be a recognized word */
84
c456d9bb
PH
85for (i = 0; i < hff_count; i++)
86 {
87 if (Ustrcmp(ob->host_find_failed, hff_names[i]) == 0)
88 {
89 ob->hff_code = hff_codes[i];
90 break;
91 }
92 }
93if (ob->hff_code < 0)
0756eb3c
PH
94 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n "
95 "unrecognized setting for host_find_failed option", rblock->name);
96
c456d9bb
PH
97for (i = 1; i < hff_count; i++) /* NB starts at 1 to skip "ignore" */
98 {
99 if (Ustrcmp(ob->host_all_ignored, hff_names[i]) == 0)
100 {
101 ob->hai_code = hff_codes[i];
102 break;
103 }
104 }
105if (ob->hai_code < 0)
106 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n "
107 "unrecognized setting for host_all_ignored option", rblock->name);
108
0756eb3c
PH
109/* One of route_list or route_data must be specified */
110
111if ((ob->route_list == NULL && ob->route_data == NULL) ||
112 (ob->route_list != NULL && ob->route_data != NULL))
113 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n "
114 "route_list or route_data (but not both) must be specified",
115 rblock->name);
116}
117
118
119
120
121/*************************************************
122* Parse a route item *
123*************************************************/
124
125/* The format of a route list item is:
126
127 <domain> [<host[list]> [<options>]]
128
129if obtained from route_list. The domain is absent if the string came from
130route_data, in which case domain==NULL. The domain and the host list may be
131enclosed in quotes.
132
133Arguments:
134 s pointer to route list item
135 domain if not NULL, where to put the domain pointer
136 hostlist where to put the host[list] pointer
137 options where to put the options pointer
138
139Returns: FALSE if domain expected and string is empty;
140 TRUE otherwise
141*/
142
143static BOOL
144parse_route_item(uschar *s, uschar **domain, uschar **hostlist,
145 uschar **options)
146{
147while (*s != 0 && isspace(*s)) s++;
148
149if (domain != NULL)
150 {
151 if (*s == 0) return FALSE; /* missing data */
152 *domain = string_dequote(&s);
153 while (*s != 0 && isspace(*s)) s++;
154 }
155
156*hostlist = string_dequote(&s);
157while (*s != 0 && isspace(*s)) s++;
158*options = s;
159return TRUE;
160}
161
162
163
164/*************************************************
165* Main entry point *
166*************************************************/
167
168/* The manualroute router provides a manual routing facility (surprise,
169surprise). The data that defines the routing can either be set in route_data
170(which means it can be found by, for example, looking up the domain in a file),
171or a list of domain patterns and their corresponding data can be provided in
172route_list. */
173
174/* See local README for interface details. This router returns:
175
176DECLINE
177 . no pattern in route_list matched (route_data not set)
178 . route_data was an empty string (route_list not set)
179 . forced expansion failure in route_data (rf_expand_data)
180 . forced expansion of host list
181 . host_find_failed = decline
182
183DEFER
184 . transport not defined when needed
185 . lookup defer in route_list when matching domain pattern
186 . non-forced expansion failure in route_data
187 . non-forced expansion failure in host list
188 . unknown routing option
189 . host list missing for remote transport (not verifying)
190 . timeout etc on host lookup (pass_on_timeout not set)
191 . verifying the errors address caused a deferment or a big disaster such
192 as an expansion failure (rf_get_errors_address)
193 . expanding a headers_{add,remove} string caused a deferment or another
194 expansion error (rf_get_munge_headers)
195 . a problem in rf_get_transport: no transport when one is needed;
196 failed to expand dynamic transport; failed to find dynamic transport
197 . failure to expand or find a uid/gid (rf_get_ugid via rf_queue_add)
198 . host_find_failed = freeze or defer
199 . self = freeze or defer
200
201PASS
202 . timeout etc on host lookup (pass_on_timeout set)
203 . host_find_failed = pass
204 . self = pass
205
206REROUTED
207 . self = reroute
208
209FAIL
210 . host_find_failed = fail
211 . self = fail
212
213OK
214 . added address to addr_local or addr_remote, as appropriate for the
215 type of transport; this includes the self="send" case.
216*/
217
218int
219manualroute_router_entry(
220 router_instance *rblock, /* data for this instantiation */
221 address_item *addr, /* address we are working on */
222 struct passwd *pw, /* passwd entry after check_local_user */
fd6de02e 223 int verify, /* v_none/v_recipient/v_sender/v_expn */
0756eb3c
PH
224 address_item **addr_local, /* add it to this if it's local */
225 address_item **addr_remote, /* add it to this if it's remote */
226 address_item **addr_new, /* put new addresses on here */
227 address_item **addr_succeed) /* put old address here on success */
228{
229int rc, lookup_type;
230uschar *route_item = NULL;
231uschar *options = NULL;
232uschar *hostlist = NULL;
233uschar *domain, *newhostlist, *listptr;
234manualroute_router_options_block *ob =
235 (manualroute_router_options_block *)(rblock->options_block);
236transport_instance *transport = NULL;
237BOOL individual_transport_set = FALSE;
238BOOL randomize = ob->hosts_randomize;
239
240addr_new = addr_new; /* Keep picky compilers happy */
241addr_succeed = addr_succeed;
242
243DEBUG(D_route) debug_printf("%s router called for %s\n domain = %s\n",
244 rblock->name, addr->address, addr->domain);
245
246/* The initialization check ensures that either route_list or route_data is
247set. */
248
249if (ob->route_list != NULL)
250 {
251 int sep = -(';'); /* Default is semicolon */
252 listptr = ob->route_list;
253
254 while ((route_item = string_nextinlist(&listptr, &sep, NULL, 0)) != NULL)
255 {
256 int rc;
257
258 DEBUG(D_route) debug_printf("route_item = %s\n", route_item);
259 if (!parse_route_item(route_item, &domain, &hostlist, &options))
260 continue; /* Ignore blank items */
261
262 /* Check the current domain; if it matches, break the loop */
263
264 if ((rc = match_isinlist(addr->domain, &domain, UCHAR_MAX+1,
265 &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, &lookup_value)) == OK)
266 break;
267
268 /* If there was a problem doing the check, defer */
269
270 if (rc == DEFER)
271 {
272 addr->message = US"lookup defer in route_list";
273 return DEFER;
274 }
275 }
276
277 if (route_item == NULL) return DECLINE; /* No pattern in the list matched */
278 }
279
280/* Handle a single routing item in route_data. If it expands to an empty
281string, decline. */
282
283else
284 {
285 route_item = rf_expand_data(addr, ob->route_data, &rc);
286 if (route_item == NULL) return rc;
287 (void) parse_route_item(route_item, NULL, &hostlist, &options);
288 if (hostlist[0] == 0) return DECLINE;
289 }
290
291/* Expand the hostlist item. It may then pointing to an empty string, or to a
292single host or a list of hosts; options is pointing to the rest of the
293routelist item, which is either empty or contains various option words. */
294
295DEBUG(D_route) debug_printf("original list of hosts = \"%s\" options = %s\n",
296 hostlist, options);
297
298newhostlist = expand_string_copy(hostlist);
299lookup_value = NULL; /* Finished with */
300expand_nmax = -1;
301
302/* If the expansion was forced to fail, just decline. Otherwise there is a
303configuration problem. */
304
305if (newhostlist == NULL)
306 {
307 if (expand_string_forcedfail) return DECLINE;
308 addr->message = string_sprintf("%s router: failed to expand \"%s\": %s",
309 rblock->name, hostlist, expand_string_message);
310 return DEFER;
311 }
312else hostlist = newhostlist;
313
314DEBUG(D_route) debug_printf("expanded list of hosts = \"%s\" options = %s\n",
315 hostlist, options);
316
317/* Set default lookup type and scan the options */
318
319lookup_type = lk_default;
320
321while (*options != 0)
322 {
323 int term;
324 uschar *s = options;
325 while (*options != 0 && !isspace(*options)) options++;
326 term = *options;
327 *options = 0;
328
329 if (Ustrcmp(s, "randomize") == 0) randomize = TRUE;
330 else if (Ustrcmp(s, "no_randomize") == 0) randomize = FALSE;
331 else if (Ustrcmp(s, "byname") == 0) lookup_type = lk_byname;
332 else if (Ustrcmp(s, "bydns") == 0) lookup_type = lk_bydns;
333 else
334 {
335 transport_instance *t;
336 for (t = transports; t != NULL; t = t->next)
337 {
338 if (Ustrcmp(t->name, s) == 0)
339 {
340 transport = t;
341 individual_transport_set = TRUE;
342 break;
343 }
344 }
345 if (t == NULL)
346 {
347 s = string_sprintf("unknown routing option or transport name \"%s\"", s);
348 log_write(0, LOG_MAIN, "Error in %s router: %s", rblock->name, s);
349 addr->message = string_sprintf("error in router: %s", s);
350 return DEFER;
351 }
352 }
353
354 if (term != 0)
355 {
356 options++;
357 while (*options != 0 && isspace(*options)) options++;
358 }
359 }
360
361/* Set up the errors address, if any. */
362
363rc = rf_get_errors_address(addr, rblock, verify, &(addr->p.errors_address));
364if (rc != OK) return rc;
365
366/* Set up the additional and removeable headers for this address. */
367
368rc = rf_get_munge_headers(addr, rblock, &(addr->p.extra_headers),
369 &(addr->p.remove_headers));
370if (rc != OK) return rc;
371
372/* If an individual transport is not set, get the transport for this router, if
373any. It might be expanded, or it might be unset if this router has verify_only
374set. */
375
376if (!individual_transport_set)
377 {
378 if (!rf_get_transport(rblock->transport_name, &(rblock->transport), addr,
379 rblock->name, NULL))
380 return DEFER;
381 transport = rblock->transport;
382 }
383
384/* Deal with the case of a local transport. The host list is passed over as a
385single text string that ends up in $host. */
386
387if (transport != NULL && transport->info->local)
388 {
389 if (hostlist[0] != 0)
390 {
391 host_item *h;
392 addr->host_list = h = store_get(sizeof(host_item));
393 h->name = string_copy(hostlist);
394 h->address = NULL;
395 h->port = PORT_NONE;
396 h->mx = MX_NONE;
397 h->status = hstatus_unknown;
398 h->why = hwhy_unknown;
399 h->last_try = 0;
400 h->next = NULL;
401 }
402
403 /* There is nothing more to do other than to queue the address for the
404 local transport, filling in any uid/gid. This can be done by the common
405 rf_queue_add() function. */
406
407 addr->transport = transport;
408 return rf_queue_add(addr, addr_local, addr_remote, rblock, pw)?
409 OK : DEFER;
410 }
411
412/* There is either no transport (verify_only) or a remote transport. A host
413list is mandatory in either case, except when verifying, in which case the
414address is just accepted. */
415
416if (hostlist[0] == 0)
417 {
fd6de02e 418 if (verify != v_none) goto ROUTED;
0756eb3c
PH
419 addr->message = string_sprintf("error in %s router: no host(s) specified "
420 "for domain %s", rblock->name, domain);
421 log_write(0, LOG_MAIN, "%s", addr->message);
422 return DEFER;
423 }
424
425/* Otherwise we finish the routing here by building a chain of host items
426for the list of configured hosts, and then finding their addresses. */
427
428host_build_hostlist(&(addr->host_list), hostlist, randomize);
429rc = rf_lookup_hostlist(rblock, addr, rblock->ignore_target_hosts, lookup_type,
430 ob->hff_code, addr_new);
431if (rc != OK) return rc;
432
c456d9bb
PH
433/* If host_find_failed is set to "ignore", it is possible for all the hosts to
434be ignored, in which case we will end up with an empty host list. What happens
435is controlled by host_all_ignored. */
436
437if (addr->host_list == NULL)
438 {
439 int i;
440 DEBUG(D_route) debug_printf("host_find_failed ignored every host\n");
441 if (ob->hai_code == hff_decline) return DECLINE;
442 if (ob->hai_code == hff_pass) return PASS;
443
444 for (i = 0; i < hff_count; i++)
445 if (ob->hai_code == hff_codes[i]) break;
446
447 addr->message = string_sprintf("lookup failed for all hosts in %s router: "
448 "host_find_failed=ignore host_all_ignored=%s", rblock->name, hff_names[i]);
449
450 if (ob->hai_code == hff_defer) return DEFER;
451 if (ob->hai_code == hff_fail) return FAIL;
452
453 addr->special_action = SPECIAL_FREEZE;
454 return DEFER;
455 }
456
0756eb3c
PH
457/* Finally, since we have done all the routing here, there must be a transport
458defined for these hosts. It will be a remote one, as a local transport is
459dealt with above. However, we don't need one if verifying only. */
460
fd6de02e 461if (transport == NULL && verify == v_none)
0756eb3c
PH
462 {
463 log_write(0, LOG_MAIN, "Error in %s router: no transport defined",
464 rblock->name);
465 addr->message = US"error in router: transport missing";
466 return DEFER;
467 }
468
469/* Fill in the transport, queue for remote delivery. The yield of
470rf_queue_add() is always TRUE for a remote transport. */
471
472ROUTED:
473
474addr->transport = transport;
475(void)rf_queue_add(addr, addr_local, addr_remote, rblock, NULL);
476return OK;
477}
478
479/* End of routers/manualroute.c */