e70c7bf3dd252d8ce6d2018641811c9c26a81d66
[users/jgh/exim.git] / src / src / routers / manualroute.c
1 /* $Cambridge: exim/src/src/routers/manualroute.c,v 1.3 2005/09/12 15:09:55 ph10 Exp $ */
2
3 /*************************************************
4 *     Exim - an Internet mail transport agent    *
5 *************************************************/
6
7 /* Copyright (c) University of Cambridge 1995 - 2005 */
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
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)) }
29 };
30
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. */
33
34 int manualroute_router_options_count =
35   sizeof(manualroute_router_options)/sizeof(optionlist);
36
37 /* Default private options block for the manualroute router. */
38
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 */
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
56 consistency checks to be done, or anything else that needs to be set up. */
57
58 void
59 manualroute_router_init(router_instance *rblock)
60 {
61 manualroute_router_options_block *ob =
62   (manualroute_router_options_block *)(rblock->options_block);
63
64 /* Host_find_failed must be a recognized word */
65
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;
76 else
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
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",
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
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
102 enclosed in quotes.
103
104 Arguments:
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
110 Returns:    FALSE if domain expected and string is empty;
111             TRUE otherwise
112 */
113
114 static BOOL
115 parse_route_item(uschar *s, uschar **domain, uschar **hostlist,
116   uschar **options)
117 {
118 while (*s != 0 && isspace(*s)) s++;
119
120 if (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);
128 while (*s != 0 && isspace(*s)) s++;
129 *options = s;
130 return TRUE;
131 }
132
133
134
135 /*************************************************
136 *              Main entry point                  *
137 *************************************************/
138
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
143 route_list. */
144
145 /* See local README for interface details. This router returns:
146
147 DECLINE
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
154 DEFER
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
172 PASS
173   . timeout etc on host lookup (pass_on_timeout set)
174   . host_find_failed = pass
175   . self = pass
176
177 REROUTED
178   . self = reroute
179
180 FAIL
181   . host_find_failed = fail
182   . self = fail
183
184 OK
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
189 int
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 */
199 {
200 int rc, lookup_type;
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;
210
211 addr_new = addr_new;           /* Keep picky compilers happy */
212 addr_succeed = addr_succeed;
213
214 DEBUG(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
218 set. */
219
220 if (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
252 string, decline. */
253
254 else
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
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. */
265
266 DEBUG(D_route) debug_printf("original list of hosts = \"%s\" options = %s\n",
267   hostlist, options);
268
269 newhostlist = expand_string_copy(hostlist);
270 lookup_value = NULL;                        /* Finished with */
271 expand_nmax = -1;
272
273 /* If the expansion was forced to fail, just decline. Otherwise there is a
274 configuration problem. */
275
276 if (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   }
283 else hostlist = newhostlist;
284
285 DEBUG(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
290 lookup_type = lk_default;
291
292 while (*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
334 rc = rf_get_errors_address(addr, rblock, verify, &(addr->p.errors_address));
335 if (rc != OK) return rc;
336
337 /* Set up the additional and removeable headers for this address. */
338
339 rc = rf_get_munge_headers(addr, rblock, &(addr->p.extra_headers),
340   &(addr->p.remove_headers));
341 if (rc != OK) return rc;
342
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
345 set. */
346
347 if (!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
356 single text string that ends up in $host. */
357
358 if (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
384 list is mandatory in either case, except when verifying, in which case the
385 address is just accepted. */
386
387 if (hostlist[0] == 0)
388   {
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);
393   return DEFER;
394   }
395
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. */
398
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;
403
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. */
407
408 if (transport == NULL && verify == v_none)
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
417 rf_queue_add() is always TRUE for a remote transport. */
418
419 ROUTED:
420
421 addr->transport = transport;
422 (void)rf_queue_add(addr, addr_local, addr_remote, rblock, NULL);
423 return OK;
424 }
425
426 /* End of routers/manualroute.c */