-/* $Cambridge: exim/src/src/verify.c,v 1.25 2005/08/22 10:49:04 ph10 Exp $ */
+/* $Cambridge: exim/src/src/verify.c,v 1.36 2006/04/04 11:18:31 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
host_af = (Ustrchr(host->address, ':') == NULL)? AF_INET:AF_INET6;
- /* Expand and interpret the interface and port strings. This has to
- be delayed till now, because they may expand differently for different
- hosts. If there's a failure, log it, but carry on with the defaults. */
+ /* Expand and interpret the interface and port strings. The latter will not
+ be used if there is a host-specific port (e.g. from a manualroute router).
+ This has to be delayed till now, because they may expand differently for
+ different hosts. If there's a failure, log it, but carry on with the
+ defaults. */
deliver_host = host->name;
deliver_host_address = host->address;
deliver_domain = addr->domain;
+
if (!smtp_get_interface(tf->interface, host_af, addr, NULL, &interface,
US"callout") ||
!smtp_get_port(tf->port, addr, &port, US"callout"))
log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: %s", addr->address,
addr->message);
+
deliver_host = deliver_host_address = NULL;
deliver_domain = save_deliver_domain;
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
{
{
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;
}
+/*************************************************
+* Check for blind recipients *
+*************************************************/
+
+/* This function checks that every (envelope) recipient is mentioned in either
+the To: or Cc: header lines, thus detecting blind carbon copies.
+
+There are two ways of scanning that could be used: either scan the header lines
+and tick off the recipients, or scan the recipients and check the header lines.
+The original proposed patch did the former, but I have chosen to do the latter,
+because (a) it requires no memory and (b) will use fewer resources when there
+are many addresses in To: and/or Cc: and only one or two envelope recipients.
+
+Arguments: none
+Returns: OK if there are no blind recipients
+ FAIL if there is at least one blind recipient
+*/
+
+int
+verify_check_notblind(void)
+{
+int i;
+for (i = 0; i < recipients_count; i++)
+ {
+ header_line *h;
+ BOOL found = FALSE;
+ uschar *address = recipients_list[i].address;
+
+ for (h = header_list; !found && h != NULL; h = h->next)
+ {
+ uschar *colon, *s;
+
+ if (h->type != htype_to && h->type != htype_cc) continue;
+
+ colon = Ustrchr(h->text, ':');
+ s = colon + 1;
+ while (isspace(*s)) s++;
+
+ parse_allow_group = TRUE; /* Allow group syntax */
+
+ /* Loop for multiple addresses in the header */
+
+ while (*s != 0)
+ {
+ uschar *ss = parse_find_address_end(s, FALSE);
+ uschar *recipient,*errmess;
+ int terminator = *ss;
+ int start, end, domain;
+
+ /* Temporarily terminate the string at this point, and extract the
+ operative address within. */
+
+ *ss = 0;
+ recipient = parse_extract_address(s,&errmess,&start,&end,&domain,FALSE);
+ *ss = terminator;
+
+ /* If we found a valid recipient that has a domain, compare it with the
+ envelope recipient. Local parts are compared case-sensitively, domains
+ case-insensitively. By comparing from the start with length "domain", we
+ include the "@" at the end, which ensures that we are comparing the whole
+ local part of each address. */
+
+ if (recipient != NULL && domain != 0)
+ {
+ found = Ustrncmp(recipient, address, domain) == 0 &&
+ strcmpic(recipient + domain, address + domain) == 0;
+ if (found) break;
+ }
+
+ /* Advance to the next address */
+
+ s = ss + (terminator? 1:0);
+ while (isspace(*s)) s++;
+ } /* Next address */
+ } /* Next header (if found is false) */
+
+ if (!found) return FAIL;
+ } /* Next recipient */
+
+return OK;
+}
+
+
/*************************************************
* Find if verified sender *
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 we are doing an IP address only match, then all lookups must be IP
-address lookups. */
+address lookups, even if there is no "net-". */
if (isiponly)
{
}
/* Otherwise, if the item is of the form net[n]-lookup;<file|query> then it is
-a lookup on a masked IP network, in textual form. The net- stuff really only
-applies to single-key lookups where the key is implicit. For query-style
-lookups the key is specified in the query. From release 4.30, the use of net-
-for query style is no longer needed, but we retain it for backward
-compatibility. */
-
-else if (Ustrncmp(ss, "net", 3) == 0 && semicolon != NULL)
+a lookup on a masked IP network, in textual form. We obey this code even if we
+have already set iplookup, so as to skip over the "net-" prefix and to set the
+mask length. The net- stuff really only applies to single-key lookups where the
+key is implicit. For query-style lookups the key is specified in the query.
+From release 4.30, the use of net- for query style is no longer needed, but we
+retain it for backward compatibility. */
+
+if (Ustrncmp(ss, "net", 3) == 0 && semicolon != NULL)
{
mlen = 0;
for (t = ss + 3; isdigit(*t); t++) mlen = mlen * 10 + *t - '0';
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)
{
host_item *hh;
for (hh = &h; hh != NULL; hh = hh->next)
{
- if (Ustrcmp(hh->address, (Ustrchr(hh->address, ':') == NULL)?
- cb->host_ipv4 : cb->host_address) == 0)
- return OK;
+ if (host_is_in_net(hh->address, cb->host_address, 0)) return OK;
}
return FAIL;
}
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);