-/* $Cambridge: exim/src/src/verify.c,v 1.29 2005/11/28 10:07:55 ph10 Exp $ */
+/* $Cambridge: exim/src/src/verify.c,v 1.38 2006/09/05 13:24:10 ph10 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2005 */
+/* Copyright (c) University of Cambridge 1995 - 2006 */
/* See the file NOTICE for conditions of use and distribution. */
/* Functions concerned with verifying things. The original code for callout
BOOL callout_random = (options & vopt_callout_random) != 0;
int yield = OK;
+int old_domain_cache_result = ccache_accept;
BOOL done = FALSE;
uschar *address_key;
uschar *from_address;
if (cache_record != NULL)
{
- /* If an early command (up to and including MAIL FROM:<>) was rejected,
- there is no point carrying on. The callout fails. */
-
- if (cache_record->result == ccache_reject)
+ /* In most cases, if an early command (up to and including MAIL FROM:<>)
+ was rejected, there is no point carrying on. The callout fails. However, if
+ we are doing a recipient verification with use_sender or use_postmaster
+ set, a previous failure of MAIL FROM:<> doesn't count, because this time we
+ will be using a non-empty sender. We have to remember this situation so as
+ not to disturb the cached domain value if this whole verification succeeds
+ (we don't want it turning into "accept"). */
+
+ old_domain_cache_result = cache_record->result;
+
+ if (cache_record->result == ccache_reject ||
+ (*from_address == 0 && cache_record->result == ccache_reject_mfnull))
{
setflag(addr, af_verify_nsfail);
HDEBUG(D_verify)
continue;
}
- /* Wait for initial response, and then run the initial SMTP commands. The
- smtp_write_command() function leaves its command in big_buffer. This is
- used in error responses. Initialize it in case the connection is
- rejected. */
+ /* Wait for initial response, and send HELO. The smtp_write_command()
+ function leaves its command in big_buffer. This is used in error responses.
+ Initialize it in case the connection is rejected. */
Ustrcpy(big_buffer, "initial connection");
done =
smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer),
'2', callout) &&
-
smtp_write_command(&outblock, FALSE, "%s %s\r\n", helo,
smtp_active_hostname) >= 0 &&
smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer),
- '2', callout) &&
+ '2', callout);
+
+ /* Failure to accept HELO is cached; this blocks the whole domain for all
+ senders. I/O errors and defer responses are not cached. */
+
+ if (!done)
+ {
+ *failure_ptr = US"mail"; /* At or before MAIL */
+ if (errno == 0 && responsebuffer[0] == '5')
+ {
+ setflag(addr, af_verify_nsfail);
+ new_domain_record.result = ccache_reject;
+ }
+ }
+
+ /* Send the MAIL command */
+ else done =
smtp_write_command(&outblock, FALSE, "MAIL FROM:<%s>\r\n",
from_address) >= 0 &&
smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer),
'2', callout);
- /* If the host gave an initial error, or does not accept HELO or MAIL
- FROM:<>, arrange to cache this information, but don't record anything for an
- I/O error or a defer. Do not cache rejections when a non-empty sender has
- been used, because that blocks the whole domain for all senders. */
+ /* If the host does not accept MAIL FROM:<>, arrange to cache this
+ information, but again, don't record anything for an I/O error or a defer. Do
+ not cache rejections of MAIL when a non-empty sender has been used, because
+ that blocks the whole domain for all senders. */
if (!done)
{
- *failure_ptr = US"mail";
+ *failure_ptr = US"mail"; /* At or before MAIL */
if (errno == 0 && responsebuffer[0] == '5')
{
setflag(addr, af_verify_nsfail);
- if (from_address[0] == 0) new_domain_record.result = ccache_reject;
+ if (from_address[0] == 0)
+ new_domain_record.result = ccache_reject_mfnull;
}
}
/* Otherwise, proceed to check a "random" address (if required), then the
given address, and the postmaster address (if required). Between each check,
issue RSET, because some servers accept only one recipient after MAIL
- FROM:<>. */
+ FROM:<>.
+
+ Before doing this, set the result in the domain cache record to "accept",
+ unless its previous value was ccache_reject_mfnull. In that case, the domain
+ rejects MAIL FROM:<> and we want to continue to remember that. When that is
+ the case, we have got here only in the case of a recipient verification with
+ a non-null sender. */
else
{
- new_domain_record.result = ccache_accept;
+ new_domain_record.result =
+ (old_domain_cache_result == ccache_reject_mfnull)?
+ ccache_reject_mfnull: ccache_accept;
/* Do the random local part check first */
The value of the result field in the new_domain record is ccache_unknown if
there was an error before or with MAIL FROM:, and errno was not zero,
implying some kind of I/O error. We don't want to write the cache in that case.
-Otherwise the value is ccache_accept or ccache_reject. */
+Otherwise the value is ccache_accept, ccache_reject, or ccache_reject_mfnull. */
if (!callout_no_cache && new_domain_record.result != ccache_unknown)
{
vaddr->user_message = addr->user_message;
vaddr->basic_errno = addr->basic_errno;
vaddr->more_errno = addr->more_errno;
+ vaddr->p.address_data = addr->p.address_data;
}
return yield;
}
if (addr->transport != NULL && !addr->transport->info->local)
{
- (void)(addr->transport->setup)(addr->transport, addr, &tf, NULL);
+ (void)(addr->transport->setup)(addr->transport, addr, &tf, 0, 0, NULL);
/* If the transport has hosts and the router does not, or if the
transport is configured to override the router's hosts, we must build a
{
nexthost = host->next;
if (tf.gethostbyname ||
- string_is_ip_address(host->name, NULL) > 0)
+ string_is_ip_address(host->name, NULL) != 0)
(void)host_find_byname(host, NULL, &canonical_name, TRUE);
else
{
discarded, usually because of the use of :blackhole: in an alias file. */
if (allok && addr_local == NULL && addr_remote == NULL)
+ {
fprintf(f, "mail to %s is discarded\n", address);
+ return yield;
+ }
-else for (addr_list = addr_local, i = 0; i < 2; addr_list = addr_remote, i++)
+for (addr_list = addr_local, i = 0; i < 2; addr_list = addr_remote, i++)
{
while (addr_list != NULL)
{
if(addr->p.srs_sender)
fprintf(f, " [srs = %s]", addr->p.srs_sender);
#endif
+
+ /* If the address is a duplicate, show something about it. */
+
+ if (!testflag(addr, af_pfr))
+ {
+ tree_node *tnode;
+ if ((tnode = tree_search(tree_duplicates, addr->unique)) != NULL)
+ fprintf(f, " [duplicate, would not be delivered]");
+ else tree_add_duplicate(addr->unique, addr);
+ }
+
+ /* Now show its parents */
+
while (p != NULL)
{
fprintf(f, "\n <-- %s", p->address);
{
uschar *verb = US"is";
uschar *t = ss;
+ uschar *tt = colon;
int len;
/* Arrange not to include any white space at the end in the
- error message. */
+ error message or the header name. */
while (t > s && isspace(t[-1])) t--;
+ while (tt > h->text && isspace(tt[-1])) tt--;
- /* Add the address which failed to the error message, since in a
+ /* Add the address that failed to the error message, since in a
header with very many addresses it is sometimes hard to spot
which one is at fault. However, limit the amount of address to
quote - cases have been seen where, for example, a missing double
}
*msgptr = string_printing(
- string_sprintf("%s: failing address in \"%.*s\" header %s: %.*s",
- errmess, colon - h->text, h->text, verb, len, s));
+ string_sprintf("%s: failing address in \"%.*s:\" header %s: %.*s",
+ errmess, tt - h->text, h->text, verb, len, s));
return FAIL;
}
BOOL iplookup = FALSE;
BOOL isquery = FALSE;
BOOL isiponly = cb->host_name != NULL && cb->host_name[0] == 0;
-uschar *t = ss;
+uschar *t;
uschar *semicolon;
uschar **aliases;
/* If the pattern is an IP address, optionally followed by a bitmask count, do
a (possibly masked) comparision with the current IP address. */
-if (string_is_ip_address(ss, &maskoffset) > 0)
+if (string_is_ip_address(ss, &maskoffset) != 0)
return (host_is_in_net(cb->host_address, ss, maskoffset)? OK : FAIL);
+/* The pattern is not an IP address. A common error that people make is to omit
+one component of an IPv4 address, either by accident, or believing that, for
+example, 1.2.3/24 is the same as 1.2.3.0/24, or 1.2.3 is the same as 1.2.3.0,
+which it isn't. (Those applications that do accept 1.2.3 as an IP address
+interpret it as 1.2.0.3 because the final component becomes 16-bit - this is an
+ancient specification.) To aid in debugging these cases, we give a specific
+error if the pattern contains only digits and dots or contains a slash preceded
+only by digits and dots (a slash at the start indicates a file name and of
+course slashes may be present in lookups, but not preceded only by digits and
+dots). */
+
+for (t = ss; isdigit(*t) || *t == '.'; t++);
+if (*t == 0 || (*t == '/' && t != ss))
+ {
+ *error = US"malformed IPv4 address or address mask";
+ return ERROR;
+ }
+
/* See if there is a semicolon in the pattern */
semicolon = Ustrchr(ss, ';');
if (mlen == 0 && t == ss+3) mlen = -1; /* No mask supplied */
iplookup = (*t++ == '-');
}
+else t = ss;
/* Do the IP address lookup if that is indeed what we have */
h.name = ss;
h.address = NULL;
h.mx = MX_NONE;
+
rc = host_find_byname(&h, NULL, NULL, FALSE);
if (rc == HOST_FOUND || rc == HOST_FOUND_LOCAL)
{
while ((keydomain = string_nextinlist(&key, &keysep, keybuffer,
sizeof(keybuffer))) != NULL)
{
- if (string_is_ip_address(keydomain, NULL) > 0)
+ if (string_is_ip_address(keydomain, NULL) != 0)
{
uschar keyrevadd[128];
invert_address(keyrevadd, keydomain);