Start
[exim.git] / src / src / routers / manualroute.c
CommitLineData
0756eb3c
PH
1/* $Cambridge: exim/src/src/routers/manualroute.c,v 1.1 2004/10/07 13:10:02 ph10 Exp $ */
2
3/*************************************************
4* Exim - an Internet mail transport agent *
5*************************************************/
6
7/* Copyright (c) University of Cambridge 1995 - 2004 */
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[] = {
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)) }
29};
30
31/* Size of the options list. An extern variable has to be used so that its
32address can appear in the tables drtables.c. */
33
34int manualroute_router_options_count =
35 sizeof(manualroute_router_options)/sizeof(optionlist);
36
37/* Default private options block for the manualroute router. */
38
39manualroute_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 */
44 NULL /* route_list */
45};
46
47
48
49
50
51/*************************************************
52* Initialization entry point *
53*************************************************/
54
55/* Called for each instance, after its options have been read, to enable
56consistency checks to be done, or anything else that needs to be set up. */
57
58void
59manualroute_router_init(router_instance *rblock)
60{
61manualroute_router_options_block *ob =
62 (manualroute_router_options_block *)(rblock->options_block);
63
64/* Host_find_failed must be a recognized word */
65
66if (Ustrcmp(ob->host_find_failed, "freeze") == 0)
67 ob->hff_code = hff_freeze;
68else if (Ustrcmp(ob->host_find_failed, "decline") == 0)
69 ob->hff_code = hff_decline;
70else if (Ustrcmp(ob->host_find_failed, "defer") == 0)
71 ob->hff_code = hff_defer;
72else if (Ustrcmp(ob->host_find_failed, "pass") == 0)
73 ob->hff_code = hff_pass;
74else if (Ustrcmp(ob->host_find_failed, "fail") == 0)
75 ob->hff_code = hff_fail;
76else
77 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n "
78 "unrecognized setting for host_find_failed option", rblock->name);
79
80/* One of route_list or route_data must be specified */
81
82if ((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",
86 rblock->name);
87}
88
89
90
91
92/*************************************************
93* Parse a route item *
94*************************************************/
95
96/* The format of a route list item is:
97
98 <domain> [<host[list]> [<options>]]
99
100if obtained from route_list. The domain is absent if the string came from
101route_data, in which case domain==NULL. The domain and the host list may be
102enclosed in quotes.
103
104Arguments:
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
109
110Returns: FALSE if domain expected and string is empty;
111 TRUE otherwise
112*/
113
114static BOOL
115parse_route_item(uschar *s, uschar **domain, uschar **hostlist,
116 uschar **options)
117{
118while (*s != 0 && isspace(*s)) s++;
119
120if (domain != NULL)
121 {
122 if (*s == 0) return FALSE; /* missing data */
123 *domain = string_dequote(&s);
124 while (*s != 0 && isspace(*s)) s++;
125 }
126
127*hostlist = string_dequote(&s);
128while (*s != 0 && isspace(*s)) s++;
129*options = s;
130return TRUE;
131}
132
133
134
135/*************************************************
136* Main entry point *
137*************************************************/
138
139/* The manualroute router provides a manual routing facility (surprise,
140surprise). 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),
142or a list of domain patterns and their corresponding data can be provided in
143route_list. */
144
145/* See local README for interface details. This router returns:
146
147DECLINE
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
153
154DEFER
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
171
172PASS
173 . timeout etc on host lookup (pass_on_timeout set)
174 . host_find_failed = pass
175 . self = pass
176
177REROUTED
178 . self = reroute
179
180FAIL
181 . host_find_failed = fail
182 . self = fail
183
184OK
185 . added address to addr_local or addr_remote, as appropriate for the
186 type of transport; this includes the self="send" case.
187*/
188
189int
190manualroute_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 BOOL verify, /* TRUE when verifying */
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 */
199{
200int rc, lookup_type;
201uschar *route_item = NULL;
202uschar *options = NULL;
203uschar *hostlist = NULL;
204uschar *domain, *newhostlist, *listptr;
205manualroute_router_options_block *ob =
206 (manualroute_router_options_block *)(rblock->options_block);
207transport_instance *transport = NULL;
208BOOL individual_transport_set = FALSE;
209BOOL randomize = ob->hosts_randomize;
210
211addr_new = addr_new; /* Keep picky compilers happy */
212addr_succeed = addr_succeed;
213
214DEBUG(D_route) debug_printf("%s router called for %s\n domain = %s\n",
215 rblock->name, addr->address, addr->domain);
216
217/* The initialization check ensures that either route_list or route_data is
218set. */
219
220if (ob->route_list != NULL)
221 {
222 int sep = -(';'); /* Default is semicolon */
223 listptr = ob->route_list;
224
225 while ((route_item = string_nextinlist(&listptr, &sep, NULL, 0)) != NULL)
226 {
227 int rc;
228
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 */
232
233 /* Check the current domain; if it matches, break the loop */
234
235 if ((rc = match_isinlist(addr->domain, &domain, UCHAR_MAX+1,
236 &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, &lookup_value)) == OK)
237 break;
238
239 /* If there was a problem doing the check, defer */
240
241 if (rc == DEFER)
242 {
243 addr->message = US"lookup defer in route_list";
244 return DEFER;
245 }
246 }
247
248 if (route_item == NULL) return DECLINE; /* No pattern in the list matched */
249 }
250
251/* Handle a single routing item in route_data. If it expands to an empty
252string, decline. */
253
254else
255 {
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;
260 }
261
262/* Expand the hostlist item. It may then pointing to an empty string, or to a
263single host or a list of hosts; options is pointing to the rest of the
264routelist item, which is either empty or contains various option words. */
265
266DEBUG(D_route) debug_printf("original list of hosts = \"%s\" options = %s\n",
267 hostlist, options);
268
269newhostlist = expand_string_copy(hostlist);
270lookup_value = NULL; /* Finished with */
271expand_nmax = -1;
272
273/* If the expansion was forced to fail, just decline. Otherwise there is a
274configuration problem. */
275
276if (newhostlist == NULL)
277 {
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);
281 return DEFER;
282 }
283else hostlist = newhostlist;
284
285DEBUG(D_route) debug_printf("expanded list of hosts = \"%s\" options = %s\n",
286 hostlist, options);
287
288/* Set default lookup type and scan the options */
289
290lookup_type = lk_default;
291
292while (*options != 0)
293 {
294 int term;
295 uschar *s = options;
296 while (*options != 0 && !isspace(*options)) options++;
297 term = *options;
298 *options = 0;
299
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;
304 else
305 {
306 transport_instance *t;
307 for (t = transports; t != NULL; t = t->next)
308 {
309 if (Ustrcmp(t->name, s) == 0)
310 {
311 transport = t;
312 individual_transport_set = TRUE;
313 break;
314 }
315 }
316 if (t == NULL)
317 {
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);
321 return DEFER;
322 }
323 }
324
325 if (term != 0)
326 {
327 options++;
328 while (*options != 0 && isspace(*options)) options++;
329 }
330 }
331
332/* Set up the errors address, if any. */
333
334rc = rf_get_errors_address(addr, rblock, verify, &(addr->p.errors_address));
335if (rc != OK) return rc;
336
337/* Set up the additional and removeable headers for this address. */
338
339rc = rf_get_munge_headers(addr, rblock, &(addr->p.extra_headers),
340 &(addr->p.remove_headers));
341if (rc != OK) return rc;
342
343/* If an individual transport is not set, get the transport for this router, if
344any. It might be expanded, or it might be unset if this router has verify_only
345set. */
346
347if (!individual_transport_set)
348 {
349 if (!rf_get_transport(rblock->transport_name, &(rblock->transport), addr,
350 rblock->name, NULL))
351 return DEFER;
352 transport = rblock->transport;
353 }
354
355/* Deal with the case of a local transport. The host list is passed over as a
356single text string that ends up in $host. */
357
358if (transport != NULL && transport->info->local)
359 {
360 if (hostlist[0] != 0)
361 {
362 host_item *h;
363 addr->host_list = h = store_get(sizeof(host_item));
364 h->name = string_copy(hostlist);
365 h->address = NULL;
366 h->port = PORT_NONE;
367 h->mx = MX_NONE;
368 h->status = hstatus_unknown;
369 h->why = hwhy_unknown;
370 h->last_try = 0;
371 h->next = NULL;
372 }
373
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. */
377
378 addr->transport = transport;
379 return rf_queue_add(addr, addr_local, addr_remote, rblock, pw)?
380 OK : DEFER;
381 }
382
383/* There is either no transport (verify_only) or a remote transport. A host
384list is mandatory in either case, except when verifying, in which case the
385address is just accepted. */
386
387if (hostlist[0] == 0)
388 {
389 if (verify) 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);
393 return DEFER;
394 }
395
396/* Otherwise we finish the routing here by building a chain of host items
397for the list of configured hosts, and then finding their addresses. */
398
399host_build_hostlist(&(addr->host_list), hostlist, randomize);
400rc = rf_lookup_hostlist(rblock, addr, rblock->ignore_target_hosts, lookup_type,
401 ob->hff_code, addr_new);
402if (rc != OK) return rc;
403
404/* Finally, since we have done all the routing here, there must be a transport
405defined for these hosts. It will be a remote one, as a local transport is
406dealt with above. However, we don't need one if verifying only. */
407
408if (transport == NULL && !verify)
409 {
410 log_write(0, LOG_MAIN, "Error in %s router: no transport defined",
411 rblock->name);
412 addr->message = US"error in router: transport missing";
413 return DEFER;
414 }
415
416/* Fill in the transport, queue for remote delivery. The yield of
417rf_queue_add() is always TRUE for a remote transport. */
418
419ROUTED:
420
421addr->transport = transport;
422(void)rf_queue_add(addr, addr_local, addr_remote, rblock, NULL);
423return OK;
424}
425
426/* End of routers/manualroute.c */