1 /* $Cambridge: exim/src/src/routers/manualroute.c,v 1.3 2005/09/12 15:09:55 ph10 Exp $ */
3 /*************************************************
4 * Exim - an Internet mail transport agent *
5 *************************************************/
7 /* Copyright (c) University of Cambridge 1995 - 2005 */
8 /* See the file NOTICE for conditions of use and distribution. */
12 #include "rf_functions.h"
13 #include "manualroute.h"
16 /* Options specific to the manualroute router. */
18 optionlist manualroute_router_options[] = {
19 { "host_find_failed", opt_stringptr,
20 (void *)(offsetof(manualroute_router_options_block, host_find_failed)) },
21 { "hosts_randomize", opt_bool,
22 (void *)(offsetof(manualroute_router_options_block, hosts_randomize)) },
23 { "route_data", opt_stringptr,
24 (void *)(offsetof(manualroute_router_options_block, route_data)) },
25 { "route_list", opt_stringptr,
26 (void *)(offsetof(manualroute_router_options_block, route_list)) },
27 { "same_domain_copy_routing", opt_bool|opt_public,
28 (void *)(offsetof(router_instance, same_domain_copy_routing)) }
31 /* Size of the options list. An extern variable has to be used so that its
32 address can appear in the tables drtables.c. */
34 int manualroute_router_options_count =
35 sizeof(manualroute_router_options)/sizeof(optionlist);
37 /* Default private options block for the manualroute router. */
39 manualroute_router_options_block manualroute_router_option_defaults = {
40 hff_freeze, /* host_find_failed code */
41 FALSE, /* hosts_randomize */
42 US"freeze", /* host_find_failed */
43 NULL, /* route_data */
51 /*************************************************
52 * Initialization entry point *
53 *************************************************/
55 /* Called for each instance, after its options have been read, to enable
56 consistency checks to be done, or anything else that needs to be set up. */
59 manualroute_router_init(router_instance *rblock)
61 manualroute_router_options_block *ob =
62 (manualroute_router_options_block *)(rblock->options_block);
64 /* Host_find_failed must be a recognized word */
66 if (Ustrcmp(ob->host_find_failed, "freeze") == 0)
67 ob->hff_code = hff_freeze;
68 else if (Ustrcmp(ob->host_find_failed, "decline") == 0)
69 ob->hff_code = hff_decline;
70 else if (Ustrcmp(ob->host_find_failed, "defer") == 0)
71 ob->hff_code = hff_defer;
72 else if (Ustrcmp(ob->host_find_failed, "pass") == 0)
73 ob->hff_code = hff_pass;
74 else if (Ustrcmp(ob->host_find_failed, "fail") == 0)
75 ob->hff_code = hff_fail;
77 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n "
78 "unrecognized setting for host_find_failed option", rblock->name);
80 /* One of route_list or route_data must be specified */
82 if ((ob->route_list == NULL && ob->route_data == NULL) ||
83 (ob->route_list != NULL && ob->route_data != NULL))
84 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n "
85 "route_list or route_data (but not both) must be specified",
92 /*************************************************
93 * Parse a route item *
94 *************************************************/
96 /* The format of a route list item is:
98 <domain> [<host[list]> [<options>]]
100 if obtained from route_list. The domain is absent if the string came from
101 route_data, in which case domain==NULL. The domain and the host list may be
105 s pointer to route list item
106 domain if not NULL, where to put the domain pointer
107 hostlist where to put the host[list] pointer
108 options where to put the options pointer
110 Returns: FALSE if domain expected and string is empty;
115 parse_route_item(uschar *s, uschar **domain, uschar **hostlist,
118 while (*s != 0 && isspace(*s)) s++;
122 if (*s == 0) return FALSE; /* missing data */
123 *domain = string_dequote(&s);
124 while (*s != 0 && isspace(*s)) s++;
127 *hostlist = string_dequote(&s);
128 while (*s != 0 && isspace(*s)) s++;
135 /*************************************************
137 *************************************************/
139 /* The manualroute router provides a manual routing facility (surprise,
140 surprise). The data that defines the routing can either be set in route_data
141 (which means it can be found by, for example, looking up the domain in a file),
142 or a list of domain patterns and their corresponding data can be provided in
145 /* See local README for interface details. This router returns:
148 . no pattern in route_list matched (route_data not set)
149 . route_data was an empty string (route_list not set)
150 . forced expansion failure in route_data (rf_expand_data)
151 . forced expansion of host list
152 . host_find_failed = decline
155 . transport not defined when needed
156 . lookup defer in route_list when matching domain pattern
157 . non-forced expansion failure in route_data
158 . non-forced expansion failure in host list
159 . unknown routing option
160 . host list missing for remote transport (not verifying)
161 . timeout etc on host lookup (pass_on_timeout not set)
162 . verifying the errors address caused a deferment or a big disaster such
163 as an expansion failure (rf_get_errors_address)
164 . expanding a headers_{add,remove} string caused a deferment or another
165 expansion error (rf_get_munge_headers)
166 . a problem in rf_get_transport: no transport when one is needed;
167 failed to expand dynamic transport; failed to find dynamic transport
168 . failure to expand or find a uid/gid (rf_get_ugid via rf_queue_add)
169 . host_find_failed = freeze or defer
170 . self = freeze or defer
173 . timeout etc on host lookup (pass_on_timeout set)
174 . host_find_failed = pass
181 . host_find_failed = fail
185 . added address to addr_local or addr_remote, as appropriate for the
186 type of transport; this includes the self="send" case.
190 manualroute_router_entry(
191 router_instance *rblock, /* data for this instantiation */
192 address_item *addr, /* address we are working on */
193 struct passwd *pw, /* passwd entry after check_local_user */
194 int verify, /* v_none/v_recipient/v_sender/v_expn */
195 address_item **addr_local, /* add it to this if it's local */
196 address_item **addr_remote, /* add it to this if it's remote */
197 address_item **addr_new, /* put new addresses on here */
198 address_item **addr_succeed) /* put old address here on success */
201 uschar *route_item = NULL;
202 uschar *options = NULL;
203 uschar *hostlist = NULL;
204 uschar *domain, *newhostlist, *listptr;
205 manualroute_router_options_block *ob =
206 (manualroute_router_options_block *)(rblock->options_block);
207 transport_instance *transport = NULL;
208 BOOL individual_transport_set = FALSE;
209 BOOL randomize = ob->hosts_randomize;
211 addr_new = addr_new; /* Keep picky compilers happy */
212 addr_succeed = addr_succeed;
214 DEBUG(D_route) debug_printf("%s router called for %s\n domain = %s\n",
215 rblock->name, addr->address, addr->domain);
217 /* The initialization check ensures that either route_list or route_data is
220 if (ob->route_list != NULL)
222 int sep = -(';'); /* Default is semicolon */
223 listptr = ob->route_list;
225 while ((route_item = string_nextinlist(&listptr, &sep, NULL, 0)) != NULL)
229 DEBUG(D_route) debug_printf("route_item = %s\n", route_item);
230 if (!parse_route_item(route_item, &domain, &hostlist, &options))
231 continue; /* Ignore blank items */
233 /* Check the current domain; if it matches, break the loop */
235 if ((rc = match_isinlist(addr->domain, &domain, UCHAR_MAX+1,
236 &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, &lookup_value)) == OK)
239 /* If there was a problem doing the check, defer */
243 addr->message = US"lookup defer in route_list";
248 if (route_item == NULL) return DECLINE; /* No pattern in the list matched */
251 /* Handle a single routing item in route_data. If it expands to an empty
256 route_item = rf_expand_data(addr, ob->route_data, &rc);
257 if (route_item == NULL) return rc;
258 (void) parse_route_item(route_item, NULL, &hostlist, &options);
259 if (hostlist[0] == 0) return DECLINE;
262 /* Expand the hostlist item. It may then pointing to an empty string, or to a
263 single host or a list of hosts; options is pointing to the rest of the
264 routelist item, which is either empty or contains various option words. */
266 DEBUG(D_route) debug_printf("original list of hosts = \"%s\" options = %s\n",
269 newhostlist = expand_string_copy(hostlist);
270 lookup_value = NULL; /* Finished with */
273 /* If the expansion was forced to fail, just decline. Otherwise there is a
274 configuration problem. */
276 if (newhostlist == NULL)
278 if (expand_string_forcedfail) return DECLINE;
279 addr->message = string_sprintf("%s router: failed to expand \"%s\": %s",
280 rblock->name, hostlist, expand_string_message);
283 else hostlist = newhostlist;
285 DEBUG(D_route) debug_printf("expanded list of hosts = \"%s\" options = %s\n",
288 /* Set default lookup type and scan the options */
290 lookup_type = lk_default;
292 while (*options != 0)
296 while (*options != 0 && !isspace(*options)) options++;
300 if (Ustrcmp(s, "randomize") == 0) randomize = TRUE;
301 else if (Ustrcmp(s, "no_randomize") == 0) randomize = FALSE;
302 else if (Ustrcmp(s, "byname") == 0) lookup_type = lk_byname;
303 else if (Ustrcmp(s, "bydns") == 0) lookup_type = lk_bydns;
306 transport_instance *t;
307 for (t = transports; t != NULL; t = t->next)
309 if (Ustrcmp(t->name, s) == 0)
312 individual_transport_set = TRUE;
318 s = string_sprintf("unknown routing option or transport name \"%s\"", s);
319 log_write(0, LOG_MAIN, "Error in %s router: %s", rblock->name, s);
320 addr->message = string_sprintf("error in router: %s", s);
328 while (*options != 0 && isspace(*options)) options++;
332 /* Set up the errors address, if any. */
334 rc = rf_get_errors_address(addr, rblock, verify, &(addr->p.errors_address));
335 if (rc != OK) return rc;
337 /* Set up the additional and removeable headers for this address. */
339 rc = rf_get_munge_headers(addr, rblock, &(addr->p.extra_headers),
340 &(addr->p.remove_headers));
341 if (rc != OK) return rc;
343 /* If an individual transport is not set, get the transport for this router, if
344 any. It might be expanded, or it might be unset if this router has verify_only
347 if (!individual_transport_set)
349 if (!rf_get_transport(rblock->transport_name, &(rblock->transport), addr,
352 transport = rblock->transport;
355 /* Deal with the case of a local transport. The host list is passed over as a
356 single text string that ends up in $host. */
358 if (transport != NULL && transport->info->local)
360 if (hostlist[0] != 0)
363 addr->host_list = h = store_get(sizeof(host_item));
364 h->name = string_copy(hostlist);
368 h->status = hstatus_unknown;
369 h->why = hwhy_unknown;
374 /* There is nothing more to do other than to queue the address for the
375 local transport, filling in any uid/gid. This can be done by the common
376 rf_queue_add() function. */
378 addr->transport = transport;
379 return rf_queue_add(addr, addr_local, addr_remote, rblock, pw)?
383 /* There is either no transport (verify_only) or a remote transport. A host
384 list is mandatory in either case, except when verifying, in which case the
385 address is just accepted. */
387 if (hostlist[0] == 0)
389 if (verify != v_none) goto ROUTED;
390 addr->message = string_sprintf("error in %s router: no host(s) specified "
391 "for domain %s", rblock->name, domain);
392 log_write(0, LOG_MAIN, "%s", addr->message);
396 /* Otherwise we finish the routing here by building a chain of host items
397 for the list of configured hosts, and then finding their addresses. */
399 host_build_hostlist(&(addr->host_list), hostlist, randomize);
400 rc = rf_lookup_hostlist(rblock, addr, rblock->ignore_target_hosts, lookup_type,
401 ob->hff_code, addr_new);
402 if (rc != OK) return rc;
404 /* Finally, since we have done all the routing here, there must be a transport
405 defined for these hosts. It will be a remote one, as a local transport is
406 dealt with above. However, we don't need one if verifying only. */
408 if (transport == NULL && verify == v_none)
410 log_write(0, LOG_MAIN, "Error in %s router: no transport defined",
412 addr->message = US"error in router: transport missing";
416 /* Fill in the transport, queue for remote delivery. The yield of
417 rf_queue_add() is always TRUE for a remote transport. */
421 addr->transport = transport;
422 (void)rf_queue_add(addr, addr_local, addr_remote, rblock, NULL);
426 /* End of routers/manualroute.c */