Also add dnslookup router option "fail_defer_domains" to support it.
an address; if such a router is expected to handle "all remaining non-local
domains", then it is important to set &%no_more%&.
+The router will defer rather than decline if the domain
+is found in the &%fail_defer_domains%& router option.
+
Reasons for a &(dnslookup)& router to decline currently include:
.ilist
The domain does not exist in DNS
+.option fail_defer_domains dnslookup "domain list&!!" unset
+.cindex "MX record" "not found"
+DNS lookups for domains matching &%fail_defer_domains%&
+which find no matching record will cause the router to defer
+rather than the default behaviour of decline.
+This maybe be useful for queueing messages for a newly created
+domain while the DNS configuration is not ready.
+However, it will result in any message with mistyped domains
+also being queued.
+
+
.option mx_domains dnslookup "domain list&!!" unset
.cindex "MX record" "required to exist"
.cindex "SRV record" "required to exist"
legitimate reasons for this (host died, network died), but if it repeats a lot
for the same host, it indicates something odd.
+.vitem %&lookup%&
+A DNS lookup for a host failed.
+Note that a &%dnslookup%& router will need to have matched
+its &%fail_defer_domains%& option for this retry type to be usable.
+Also note that a &%manualroute%& router will probably need
+its &%host_find_failed%& option set to &%defer&%.
+
.vitem &%refused_MX%&
A connection to a host obtained from an MX record was refused.
printf("\n");
}
- /* Old-style domain record, without separate timestamps. This code can
- eventually be thrown away, say in 5 years' time (it's now Feb 2003). */
-
- else
- {
- printf("%s %s callout=%s postmaster=%s random=%s\n",
- print_time(((dbdata_generic *)value)->time_stamp),
- keybuffer,
- print_cache(callout->result),
- print_cache(callout->postmaster_result),
- print_cache(callout->random_result));
- }
-
break;
case type_ratelimit:
#define ERRNO_UIDFAIL (-29) /* Failed to get uid */
#define ERRNO_BADTRANSPORT (-30) /* Unset or non-existent transport */
#define ERRNO_MBXLENGTH (-31) /* MBX length mismatch */
-#define ERRNO_UNKNOWNHOST (-32) /* Lookup failed in smtp transport */
+#define ERRNO_UNKNOWNHOST (-32) /* Lookup failed routing or in smtp tpt */
#define ERRNO_FORMATUNKNOWN (-33) /* Can't match format in appendfile */
#define ERRNO_BADCREATE (-34) /* Creation outside home in appendfile */
#define ERRNO_LISTDEFER (-35) /* Can't check a list; lookup defer */
*/
uschar *
-readconf_retry_error(const uschar *pp, const uschar *p, int *basic_errno, int *more_errno)
+readconf_retry_error(const uschar *pp, const uschar *p,
+ int *basic_errno, int *more_errno)
{
int len;
const uschar *q = pp;
{ 'A', 'M', RTEF_CTOUT, RTEF_CTOUT|'A', RTEF_CTOUT|'M' };
for (i = 0; i < sizeof(extras)/sizeof(uschar *); i++)
- {
if (strncmpic(x, extras[i], xlen) == 0)
{
*more_errno = values[i];
break;
}
- }
if (i >= sizeof(extras)/sizeof(uschar *))
- {
if (strncmpic(x, US"DNS", xlen) == 0)
- {
log_write(0, LOG_MAIN|LOG_PANIC, "\"timeout_dns\" is no longer "
"available in retry rules (it has never worked) - treated as "
"\"timeout\"");
- }
- else return US"\"A\", \"MX\", or \"connect\" expected after \"timeout\"";
- }
+ else
+ return US"\"A\", \"MX\", or \"connect\" expected after \"timeout\"";
}
}
return string_sprintf("%.4s_4 must be followed by xx, dx, or dd, where "
"x is literal and d is any digit", pp);
- *basic_errno = (*pp == 'm')? ERRNO_MAIL4XX :
- (*pp == 'r')? ERRNO_RCPT4XX : ERRNO_DATA4XX;
+ *basic_errno = *pp == 'm' ? ERRNO_MAIL4XX :
+ *pp == 'r' ? ERRNO_RCPT4XX : ERRNO_DATA4XX;
*more_errno = x << 8;
}
else if (strncmpic(pp, US"tls_required", p - pp) == 0)
*basic_errno = ERRNO_TLSREQUIRED;
+else if (strncmpic(pp, US"lookup", p - pp) == 0)
+ *basic_errno = ERRNO_UNKNOWNHOST;
+
else if (len != 1 || Ustrncmp(pp, "*", 1) != 0)
return string_sprintf("unknown or malformed retry error \"%.*s\"", (int) (p-pp), pp);
*paddr = p;
switch (type)
{
- case 0:
- return readconf_readtime(pp, *p, FALSE);
- case 1:
- return readconf_readfixed(pp, *p);
+ case 0: return readconf_readtime(pp, *p, FALSE);
+ case 1: return readconf_readfixed(pp, *p);
}
return 0; /* Keep picky compilers happy */
}
retry_config *next;
const uschar *p;
-while ((p = get_config_line()) != NULL)
+while ((p = get_config_line()))
{
retry_rule **rchain;
const uschar *pp;
/* Test error names for things we understand. */
- if ((error = readconf_retry_error(pp, p, &(next->basic_errno),
- &(next->more_errno))) != NULL)
+ if ((error = readconf_retry_error(pp, p, &next->basic_errno,
+ &next->more_errno)))
log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "%s", error);
/* There may be an optional address list of senders to be used as another
switch (rule->rule)
{
case 'F': /* Fixed interval */
- rule->p1 = retry_arg(&p, 0);
- break;
+ rule->p1 = retry_arg(&p, 0);
+ break;
case 'G': /* Geometrically increasing intervals */
case 'H': /* Ditto, but with randomness */
- rule->p1 = retry_arg(&p, 0);
- rule->p2 = retry_arg(&p, 1);
- break;
+ rule->p1 = retry_arg(&p, 0);
+ rule->p2 = retry_arg(&p, 1);
+ break;
default:
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "unknown retry rule letter");
- break;
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "unknown retry rule letter");
+ break;
}
if (rule->timeout <= 0 || rule->p1 <= 0 ||
(void *)(offsetof(dnslookup_router_options_block, dnssec_request_domains)) },
{ "dnssec_require_domains", opt_stringptr,
(void *)(offsetof(dnslookup_router_options_block, dnssec_require_domains)) },
+ { "fail_defer_domains", opt_stringptr,
+ (void *)(offsetof(dnslookup_router_options_block, fail_defer_domains)) },
{ "mx_domains", opt_stringptr,
(void *)(offsetof(dnslookup_router_options_block, mx_domains)) },
{ "mx_fail_domains", opt_stringptr,
NULL, /* srv_fail_domains */
NULL, /* check_srv */
NULL, /* dnssec_request_domains */
- NULL /* dnssec_require_domains */
+ NULL, /* dnssec_require_domains */
+ NULL /* fail_defer_domains */
};
/* If an SRV check is required, expand the service name */
-if (ob->check_srv != NULL)
+if (ob->check_srv)
{
- srv_service = expand_string(ob->check_srv);
- if (srv_service == NULL && !expand_string_forcedfail)
+ if ( !(srv_service = expand_string(ob->check_srv))
+ && !expand_string_forcedfail)
{
addr->message = string_sprintf("%s router: failed to expand \"%s\": %s",
rblock->name, ob->check_srv, expand_string_message);
suppression of widening for sender addresses is silent because it is the
normal desirable behaviour. */
-if (ob->widen_domains != NULL &&
- (verify != v_sender || !ob->rewrite_headers || addr->parent != NULL))
+if ( ob->widen_domains
+ && (verify != v_sender || !ob->rewrite_headers || addr->parent))
{
listptr = ob->widen_domains;
widen = string_nextinlist(&listptr, &widen_sep, widen_buffer,
int flags = whichrrs;
BOOL removed = FALSE;
- if (pre_widen != NULL)
+ if (pre_widen)
{
h.name = pre_widen;
pre_widen = NULL;
}
- else if (widen != NULL)
+ else if (widen)
{
h.name = string_sprintf("%s.%s", addr->domain, widen);
widen = string_nextinlist(&listptr, &widen_sep, widen_buffer,
DEBUG(D_route) debug_printf("%s router widened %s to %s\n", rblock->name,
addr->domain, h.name);
}
- else if (post_widen != NULL)
+ else if (post_widen)
{
h.name = post_widen;
post_widen = NULL;
subsequently. We use the same logic as that for widen_domains above to avoid
requesting a header rewrite that cannot work. */
- if (verify != v_sender || !ob->rewrite_headers || addr->parent != NULL)
+ if (verify != v_sender || !ob->rewrite_headers || addr->parent)
{
if (ob->qualify_single) flags |= HOST_FIND_QUALIFY_SINGLE;
if (ob->search_parents) flags |= HOST_FIND_SEARCH_PARENTS;
the option is historical. */
if ((rc == HOST_FOUND || rc == HOST_FOUND_LOCAL) && h.mx < 0 &&
- ob->mx_domains != NULL)
- {
+ ob->mx_domains)
switch(match_isinlist(fully_qualified_name,
- (const uschar **)&(ob->mx_domains), 0,
+ CUSS &(ob->mx_domains), 0,
&domainlist_anchor, addr->domain_cache, MCL_DOMAIN, TRUE, NULL))
{
case DEFER:
rblock->name, fully_qualified_name);
continue;
}
- }
/* Deferral returns forthwith, and anything other than failure breaks the
loop. */
addr->message = US"an MX or SRV record indicated no SMTP service";
else
{
+ addr->basic_errno = ERRNO_UNKNOWNHOST;
addr->message = US"all relevant MX records point to non-existent hosts";
if (!allow_mx_to_ip && string_is_ip_address(h.name, NULL) != 0)
{
addr->message);
}
}
+ if (ob->fail_defer_domains)
+ {
+ switch(match_isinlist(fully_qualified_name,
+ CUSS &ob->fail_defer_domains, 0,
+ &domainlist_anchor, addr->domain_cache, MCL_DOMAIN, TRUE, NULL))
+ {
+ case DEFER:
+ addr->message = US"lookup defer for fail_defer_domains";
+ return DEFER;
+
+ case OK:
+ DEBUG(D_route) debug_printf("%s router: matched fail_defer_domains\n",
+ rblock->name);
+ return DEFER;
+ }
+ }
return DECLINE;
}
}
/* End of routers/dnslookup.c */
+/* vi: aw ai sw=2
+*/
uschar *check_srv;
uschar *dnssec_request_domains;
uschar *dnssec_require_domains;
+ uschar *fail_defer_domains;
} dnslookup_router_options_block;
/* Data for reading the private options. */
if (hff_code == hff_pass) return PASS;
if (hff_code == hff_decline) return DECLINE;
+ addr->basic_errno = ERRNO_UNKNOWNHOST;
addr->message =
string_sprintf("lookup of host \"%s\" failed in %s router%s",
h->name, rblock->name,
begin routers
+bydns:
+ driver = dnslookup
+ domains = mxt2.test.ex
+ transport = smtp
+ fail_defer_domains = *
+
all:
driver = manualroute
route_list = simple thishost.test.ex byname \
; complex thisloop.test.ex byname \
+ ; nonexist nonexist.test.ex byname \
; * 127.0.0.1 byname
self = send
+ host_find_failed = defer
transport = smtp
rcpt45x.test.ex rcpt_45x F,10d,2m
rcpt463.test.ex rcpt_463 F,10d,3m
rcpt4xx.test.ex rcpt_4xx F,10d,1m
+* lookup F,10d,30m
*@\N^\d * F,5d,1m
*@*.abcd.ex * F,5d,2m
1999-03-02 09:44:33 10HmbB-0005vi-00 H=thisloop.test.ex [ip4.ip4.ip4.ip4] Connection refused
1999-03-02 09:44:33 10HmbB-0005vi-00 H=thisloop.test.ex [127.0.0.1] Connection refused
1999-03-02 09:44:33 10HmbB-0005vi-00 == without@complex R=all T=smtp defer (dd): Connection refused
+1999-03-02 09:44:33 10HmbC-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss for userx@nonexist
+1999-03-02 09:44:33 10HmbC-0005vi-00 no IP address found for host nonexist.test.ex
+1999-03-02 09:44:33 10HmbC-0005vi-00 == userx@nonexist R=all defer (-32): lookup of host "nonexist.test.ex" failed in all router
+1999-03-02 09:44:33 10HmbD-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss for userx@mxt2.test.ex
+1999-03-02 09:44:33 10HmbD-0005vi-00 == userx@mxt2.test.ex R=bydns defer (-32): all relevant MX records point to non-existent hosts
****
dump retry
#
+# one for a lookup-failure (in manualroute)
+exim -odi userx@nonexist
+Test message
+****
+# one for a lookup-failure (in dnslookup)
+exim -odi userx@mxt2.test.ex
+Test message
+****
+dump retry
+#
#
exim -brt x@dark.star.ex
****
****
exim -brt x@not.not.ex
****
+exim -brt x@nonexist.test.ex lookup
+****
first failed = time last try = time2 next try = time2 + 900
T:thisloop.test.ex:999 dd 65 Connection refused
first failed = time last try = time2 next try = time2 + 900
++++++++++++++++++++++++++++
+ R:mxt2.test.ex -32 0 all relevant MX records point to non-existent hosts
+first failed = time last try = time2 next try = time2 + 1800
+ R:nonexist -32 0 lookup of host "nonexist.test.ex" failed in all router
+first failed = time last try = time2 next try = time2 + 1800
+ T:thishost.test.ex:127.0.0.1:999 dd 65 Connection refused
+first failed = time last try = time2 next try = time2 + 900
+ T:thishost.test.ex:999 dd 65 Connection refused
+first failed = time last try = time2 next try = time2 + 900
+ T:thisloop.test.ex:127.0.0.1:999 dd 65 Connection refused
+first failed = time last try = time2 next try = time2 + 900
+ T:thisloop.test.ex:ip4.ip4.ip4.ip4:999 dd 65 Connection refused
+first failed = time last try = time2 next try = time2 + 900
+ T:thisloop.test.ex:999 dd 65 Connection refused
+first failed = time last try = time2 next try = time2 + 900
Retry rule: *.star.ex * F,3d,10m;
Retry rule: lsearch*@;TESTSUITE/aux-fixed/0099.rlist * F,1d,3m;
Retry rule: !*.not.ex * F,2d,15m;
Retry rule: * * G,1d,1m,1.5;
Retry rule: * * G,2d,2m,1.5;
Retry rule: * * F,1w5d,2h30m;
+Retry rule: * * F,1w3d,30m;