1 /* $Cambridge: exim/src/src/routers/manualroute.c,v 1.2 2005/01/04 10:00:44 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 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 */
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
) 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
)
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 */