X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=src%2Fsrc%2Froute.c;h=cd7e5d535f67525412294722138da4af85d2d8f4;hb=7cd171b76e5bd3cb825c2a8720bc1fe4ad9b37e0;hp=a80ecb62dfb578e1c04faba5298faedddb4ee8ae;hpb=7f45268c9e24a11001679f1e50b1ac77b43e8aa9;p=exim.git diff --git a/src/src/route.c b/src/src/route.c index a80ecb62d..cd7e5d535 100644 --- a/src/src/route.c +++ b/src/src/route.c @@ -1,10 +1,8 @@ -/* $Cambridge: exim/src/src/route.c,v 1.5 2005/03/15 11:37:21 ph10 Exp $ */ - /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2005 */ +/* Copyright (c) University of Cambridge 1995 - 2014 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions concerned with routing, and the list of generic router options. */ @@ -50,16 +48,22 @@ optionlist optionlist_routers[] = { (void *)(offsetof(router_instance, caseful_local_part)) }, { "check_local_user", opt_bool | opt_public, (void *)(offsetof(router_instance, check_local_user)) }, - { "condition", opt_stringptr|opt_public, + { "condition", opt_stringptr|opt_public|opt_rep_con, (void *)offsetof(router_instance, condition) }, { "debug_print", opt_stringptr | opt_public, (void *)offsetof(router_instance, debug_string) }, { "disable_logging", opt_bool | opt_public, (void *)offsetof(router_instance, disable_logging) }, + { "dnssec_request_domains", opt_stringptr|opt_public, + (void *)offsetof(router_instance, dnssec.request) }, + { "dnssec_require_domains", opt_stringptr|opt_public, + (void *)offsetof(router_instance, dnssec.require) }, { "domains", opt_stringptr|opt_public, (void *)offsetof(router_instance, domains) }, { "driver", opt_stringptr|opt_public, (void *)offsetof(router_instance, driver_name) }, + { "dsn_lasthop", opt_bool|opt_public, + (void *)offsetof(router_instance, dsn_lasthop) }, { "errors_to", opt_stringptr|opt_public, (void *)(offsetof(router_instance, errors_to)) }, { "expn", opt_bool|opt_public, @@ -74,9 +78,9 @@ optionlist optionlist_routers[] = { (void *)offsetof(router_instance, fallback_hosts) }, { "group", opt_expand_gid | opt_public, (void *)(offsetof(router_instance, gid)) }, - { "headers_add", opt_stringptr|opt_public, + { "headers_add", opt_stringptr|opt_public|opt_rep_str, (void *)offsetof(router_instance, extra_headers) }, - { "headers_remove", opt_stringptr|opt_public, + { "headers_remove", opt_stringptr|opt_public|opt_rep_str, (void *)offsetof(router_instance, remove_headers) }, { "ignore_target_hosts",opt_stringptr|opt_public, (void *)offsetof(router_instance, ignore_target_hosts) }, @@ -272,6 +276,13 @@ for (r = routers; r != NULL; r = r->next) if (r->pass_router_name != NULL) set_router(r, r->pass_router_name, &(r->pass_router), TRUE); + + DEBUG(D_route) { + if (r->dsn_lasthop == FALSE) + debug_printf("DSN: %s propagating DSN\n", r->name); + else + debug_printf("DSN: %s lasthop set\n", r->name); + } } } @@ -312,11 +323,11 @@ Returns: length of matching prefix or zero */ int -route_check_prefix(uschar *local_part, uschar *prefixes) +route_check_prefix(const uschar *local_part, const uschar *prefixes) { int sep = 0; uschar *prefix; -uschar *listptr = prefixes; +const uschar *listptr = prefixes; uschar prebuf[64]; while ((prefix = string_nextinlist(&listptr, &sep, prebuf, sizeof(prebuf))) @@ -325,7 +336,7 @@ while ((prefix = string_nextinlist(&listptr, &sep, prebuf, sizeof(prebuf))) int plen = Ustrlen(prefix); if (prefix[0] == '*') { - uschar *p; + const uschar *p; prefix++; for (p = local_part + Ustrlen(local_part) - (--plen); p >= local_part; p--) @@ -356,12 +367,12 @@ Returns: length of matching suffix or zero */ int -route_check_suffix(uschar *local_part, uschar *suffixes) +route_check_suffix(const uschar *local_part, const uschar *suffixes) { int sep = 0; int alen = Ustrlen(local_part); uschar *suffix; -uschar *listptr = suffixes; +const uschar *listptr = suffixes; uschar sufbuf[64]; while ((suffix = string_nextinlist(&listptr, &sep, sufbuf, sizeof(sufbuf))) @@ -370,7 +381,7 @@ while ((suffix = string_nextinlist(&listptr, &sep, sufbuf, sizeof(sufbuf))) int slen = Ustrlen(suffix); if (suffix[slen-1] == '*') { - uschar *p, *pend; + const uschar *p, *pend; pend = local_part + alen - (--slen) + 1; for (p = local_part; p < pend; p++) if (strncmpic(suffix, p, slen) == 0) return alen - (p - local_part); @@ -413,9 +424,9 @@ Returns: OK item is in list */ static int -route_check_dls(uschar *rname, uschar *type, uschar *list, tree_node - **anchorptr, unsigned int *cache_bits, int listtype, uschar *domloc, - uschar **ldata, BOOL caseless, uschar **perror) +route_check_dls(uschar *rname, uschar *type, const uschar *list, + tree_node **anchorptr, unsigned int *cache_bits, int listtype, + const uschar *domloc, const uschar **ldata, BOOL caseless, uschar **perror) { int rc; @@ -435,7 +446,7 @@ else { uschar *address = (sender_address == NULL)? US"" : sender_address; rc = match_address_list(address, TRUE, TRUE, &list, cache_bits, -1, 0, - &sender_data); + CUSS &sender_data); } switch(rc) @@ -563,14 +574,15 @@ Returns: OK if s == NULL or all tests are as required SKIP otherwise */ -int -check_files(uschar *s, uschar **perror) +static int +check_files(const uschar *s, uschar **perror) { int sep = 0; /* List has default separators */ uid_t uid = 0; /* For picky compilers */ gid_t gid = 0; /* For picky compilers */ BOOL ugid_set = FALSE; -uschar *check, *listptr; +const uschar *listptr; +uschar *check; uschar buffer[1024]; if (s == NULL) return OK; @@ -578,8 +590,7 @@ if (s == NULL) return OK; DEBUG(D_route) debug_printf("checking require_files\n"); listptr = s; -while ((check = string_nextinlist(&listptr, &sep, buffer, sizeof(buffer))) - != NULL) +while ((check = string_nextinlist(&listptr, &sep, buffer, sizeof(buffer)))) { int rc; int eacces_code = 0; @@ -825,6 +836,7 @@ check_router_conditions(router_instance *r, address_item *addr, int verify, { int rc; uschar *check_local_part; +unsigned int *localpart_cache; /* Reset variables to hold a home directory and data from lookup of a domain or local part, and ensure search_find_defer is unset, in case there aren't any @@ -877,20 +889,25 @@ if (verify == v_expn && !r->expn) /* Skip this router if there's a domain mismatch. */ if ((rc = route_check_dls(r->name, US"domains", r->domains, &domainlist_anchor, - addr->domain_cache, TRUE, addr->domain, &deliver_domain_data, MCL_DOMAIN, - perror)) != OK) + addr->domain_cache, TRUE, addr->domain, CUSS &deliver_domain_data, + MCL_DOMAIN, perror)) != OK) return rc; /* Skip this router if there's a local part mismatch. We want to pass over the caseful local part, so that +caseful can restore it, even if this router is handling local parts caselessly. However, we can't just pass cc_local_part, because that doesn't have the prefix or suffix stripped. A bit of massaging is -required. */ +required. Also, we only use the match cache for local parts that have not had +a prefix or suffix stripped. */ if (addr->prefix == NULL && addr->suffix == NULL) + { + localpart_cache = addr->localpart_cache; check_local_part = addr->cc_local_part; + } else { + localpart_cache = NULL; check_local_part = string_copy(addr->cc_local_part); if (addr->prefix != NULL) check_local_part += Ustrlen(addr->prefix); @@ -899,9 +916,9 @@ else } if ((rc = route_check_dls(r->name, US"local_parts", r->local_parts, - &localpartlist_anchor, addr->localpart_cache, MCL_LOCALPART, - check_local_part, &deliver_localpart_data, !r->caseful_local_part, - perror)) != OK) + &localpartlist_anchor, localpart_cache, MCL_LOCALPART, + check_local_part, CUSS &deliver_localpart_data, + !r->caseful_local_part, perror)) != OK) return rc; /* If the check_local_user option is set, check that the local_part is the @@ -1075,9 +1092,14 @@ static uschar lastgecos[128]; static uschar lastshell[128]; BOOL -route_finduser(uschar *s, struct passwd **pw, uid_t *return_uid) +route_finduser(const uschar *s, struct passwd **pw, uid_t *return_uid) { -if (Ustrcmp(lastname, s) != 0) +BOOL cache_set = (Ustrcmp(lastname, s) == 0); + +DEBUG(D_uid) debug_printf("seeking password data for user \"%s\": %s\n", s, + cache_set? "using cached result" : "cache not available"); + +if (!cache_set) { int i = 0; @@ -1104,6 +1126,7 @@ if (Ustrcmp(lastname, s) != 0) else for (;;) { + errno = 0; if ((lastpw = getpwnam(CS s)) != NULL) break; if (++i > finduser_retries) break; sleep(1); @@ -1122,14 +1145,25 @@ if (Ustrcmp(lastname, s) != 0) pwcopy.pw_shell = CS lastshell; lastpw = &pwcopy; } + + else DEBUG(D_uid) + { + if (errno != 0) debug_printf("getpwnam(%s) failed: %s\n", s, + strerror(errno)); + } + } + +if (lastpw == NULL) + { + DEBUG(D_uid) debug_printf("getpwnam() returned NULL (user not found)\n"); + return FALSE; } else { - DEBUG(D_uid) debug_printf("finduser used cached passwd data for %s\n", s); + DEBUG(D_uid) debug_printf("getpwnam() succeeded uid=%d gid=%d\n", + lastpw->pw_uid, lastpw->pw_gid); } -if (lastpw == NULL) return FALSE; - if (return_uid != NULL) *return_uid = lastpw->pw_uid; if (pw != NULL) *pw = lastpw; @@ -1271,67 +1305,6 @@ return yield; -/************************************************* -* Sort out "more" or "unseen" * -*************************************************/ - -/* These values are usually fixed boolean values, but they are permitted to be -expanded strings. - -Arguments: - addr address being routed - rname the router name - oname the option name - bvalue the router's boolean value - svalue the router's string value - rvalue where to put the returned value - -Returns: OK value placed in rvalue - DEFER expansion failed -*/ - -static int -exp_bool(address_item *addr, uschar *rname, uschar *oname, BOOL bvalue, - uschar *svalue, BOOL *rvalue) -{ -uschar *expanded; -if (svalue == NULL) { *rvalue = bvalue; return OK; } - -expanded = expand_string(svalue); -if (expanded == NULL) - { - if (expand_string_forcedfail) - { - DEBUG(D_route) debug_printf("expansion of \"%s\" forced failure\n", oname); - *rvalue = bvalue; - return OK; - } - addr->message = string_sprintf("failed to expand \"%s\" in %s router: %s", - oname, rname, expand_string_message); - DEBUG(D_route) debug_printf("%s\n", addr->message); - return DEFER; - } - -DEBUG(D_route) debug_printf("expansion of \"%s\" yields \"%s\"\n", oname, - expanded); - -if (strcmpic(expanded, US"true") == 0 || strcmpic(expanded, US"yes") == 0) - *rvalue = TRUE; -else if (strcmpic(expanded, US"false") == 0 || strcmpic(expanded, US"no") == 0) - *rvalue = FALSE; -else - { - addr->message = string_sprintf("\"%s\" is not a valid value for the " - "\"%s\" option in the %s router", expanded, oname, rname); - return DEFER; - } - -return OK; -} - - - - /************************************************* * Handle an unseen routing * *************************************************/ @@ -1373,8 +1346,8 @@ from the original address' parent, if present, otherwise unset. */ *parent = *addr; parent->child_count = 2; -parent->p.errors_address = - (addr->parent == NULL)? NULL : addr->parent->p.errors_address; +parent->prop.errors_address = + (addr->parent == NULL)? NULL : addr->parent->prop.errors_address; /* The routed address gets a new parent. */ @@ -1385,12 +1358,14 @@ was set from the original parent (or to NULL) - see above. We do NOT want to take the errors address from the unseen router. */ new->parent = parent; -new->p.errors_address = parent->p.errors_address; +new->prop.errors_address = parent->prop.errors_address; /* Copy the propagated flags and address_data from the original. */ copyflag(new, addr, af_propagate); -new->p.address_data = addr->p.address_data; +new->prop.address_data = addr->prop.address_data; +new->dsn_flags = addr->dsn_flags; +new->dsn_orcpt = addr->dsn_orcpt; /* As it has turned out, we haven't set headers_add or headers_remove for the @@ -1466,7 +1441,7 @@ route_address(address_item *addr, address_item **paddr_local, int yield = OK; BOOL unseen; router_instance *r, *nextr; -uschar *old_domain = addr->domain; +const uschar *old_domain = addr->domain; HDEBUG(D_route) { @@ -1612,6 +1587,7 @@ for (r = (addr->start_router == NULL)? routers : addr->start_router; /* Set the expansion variables now that we have the affixes and the case of the local part sorted. */ + router_name = r->name; deliver_set_expansions(addr); /* For convenience, the pre-router checks are in a separate function, which @@ -1619,6 +1595,7 @@ for (r = (addr->start_router == NULL)? routers : addr->start_router; if ((rc = check_router_conditions(r, addr, verify, &pw, &error)) != OK) { + router_name = NULL; if (rc == SKIP) continue; addr->message = error; yield = rc; @@ -1649,14 +1626,15 @@ for (r = (addr->start_router == NULL)? routers : addr->start_router; /* Expand "more" if necessary; DEFER => an expansion failed */ - yield = exp_bool(addr, r->name, US"more", r->more, r->expand_more, - &more); + yield = exp_bool(addr, US"router", r->name, D_route, + US"more", r->more, r->expand_more, &more); if (yield != OK) goto ROUTE_EXIT; if (!more) { DEBUG(D_route) debug_printf("\"more\"=false: skipping remaining routers\n"); + router_name = NULL; r = NULL; break; } @@ -1671,7 +1649,7 @@ for (r = (addr->start_router == NULL)? routers : addr->start_router; goto ROUTE_EXIT; } } - addr->p.address_data = deliver_address_data; + addr->prop.address_data = deliver_address_data; } /* We are finally cleared for take-off with this router. Clear the the flag @@ -1695,10 +1673,21 @@ for (r = (addr->start_router == NULL)? routers : addr->start_router; /* Run the router, and handle the consequences. */ +/* ... but let us check on DSN before. If this should be the last hop for DSN + set flag +*/ + if ((r->dsn_lasthop == TRUE) && ((addr->dsn_flags & rf_dsnlasthop) == 0)) + { + addr->dsn_flags |= rf_dsnlasthop; + HDEBUG(D_route) debug_printf("DSN: last hop for %s\n", addr->address); + } + HDEBUG(D_route) debug_printf("calling %s router\n", r->name); - yield = (r->info->code)(r, addr, pw, verify != v_none, paddr_local, - paddr_remote, addr_new, addr_succeed); + yield = (r->info->code)(r, addr, pw, verify, paddr_local, paddr_remote, + addr_new, addr_succeed); + + router_name = NULL; if (yield == FAIL) { @@ -1745,7 +1734,8 @@ for (r = (addr->start_router == NULL)? routers : addr->start_router; { /* Expand "more" if necessary */ - yield = exp_bool(addr, r->name, US"more", r->more, r->expand_more, &more); + yield = exp_bool(addr, US"router", r->name, D_route, + US"more", r->more, r->expand_more, &more); if (yield != OK) goto ROUTE_EXIT; if (!more) @@ -1854,7 +1844,7 @@ if (r->translate_ip_address != NULL) DEBUG(D_route) debug_printf("%s [%s] translated to %s\n", h->name, h->address, newaddress); - if (string_is_ip_address(newaddress, NULL) > 0) + if (string_is_ip_address(newaddress, NULL) != 0) { h->address = newaddress; continue; @@ -1867,7 +1857,7 @@ if (r->translate_ip_address != NULL) h->mx = MX_NONE; store_pool = POOL_PERM; - rc = host_find_byname(h, NULL, NULL, TRUE); + rc = host_find_byname(h, NULL, HOST_FIND_QUALIFY_SINGLE, NULL, TRUE); store_pool = old_pool; if (rc == HOST_FIND_FAILED || rc == HOST_FIND_AGAIN) @@ -1885,8 +1875,8 @@ if (r->translate_ip_address != NULL) /* See if this is an unseen routing; first expand the option if necessary. DEFER can be given if the expansion fails */ -yield = exp_bool(addr, r->name, US"unseen", r->unseen, r->expand_unseen, - &unseen); +yield = exp_bool(addr, US"router", r->name, D_route, + US"unseen", r->unseen, r->expand_unseen, &unseen); if (yield != OK) goto ROUTE_EXIT; /* Debugging output recording a successful routing */ @@ -1905,8 +1895,8 @@ DEBUG(D_route) debug_printf(" transport: %s\n", (addr->transport == NULL)? US"" : addr->transport->name); - if (addr->p.errors_address != NULL) - debug_printf(" errors to %s\n", addr->p.errors_address); + if (addr->prop.errors_address != NULL) + debug_printf(" errors to %s\n", addr->prop.errors_address); for (h = addr->host_list; h != NULL; h = h->next) { @@ -1915,6 +1905,7 @@ DEBUG(D_route) if (h->mx >= 0) debug_printf(" MX=%d", h->mx); else if (h->mx != MX_NONE) debug_printf(" rgroup=%d", h->mx); if (h->port != PORT_NONE) debug_printf(" port=%d", h->port); + if (h->dnssec != DS_UNK) debug_printf(" dnssec=%s", h->dnssec==DS_YES ? "yes" : "no"); debug_printf("\n"); } } @@ -1929,7 +1920,27 @@ if (unseen && r->next != NULL) /* Unset the address expansions, and return the final result. */ ROUTE_EXIT: +if (yield == DEFER) { + if ( + ((Ustrstr(addr->message, "failed to expand") != NULL) || (Ustrstr(addr->message, "expansion of ") != NULL)) && + ( + Ustrstr(addr->message, "mysql") != NULL || + Ustrstr(addr->message, "pgsql") != NULL || +#ifdef EXPERIMENTAL_REDIS + Ustrstr(addr->message, "redis") != NULL || +#endif + Ustrstr(addr->message, "sqlite") != NULL || + Ustrstr(addr->message, "ldap:") != NULL || + Ustrstr(addr->message, "ldapdn:") != NULL || + Ustrstr(addr->message, "ldapm:") != NULL + ) + ) { + addr->message = string_sprintf("Temporary internal error"); + } +} + deliver_set_expansions(NULL); +router_name = NULL; disable_logging = FALSE; return yield; }