-/* $Cambridge: exim/src/src/verify.c,v 1.44 2006/12/11 14:15:59 ph10 Exp $ */
+/* $Cambridge: exim/src/src/verify.c,v 1.47 2007/01/30 15:10:59 ph10 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2006 */
+/* Copyright (c) University of Cambridge 1995 - 2007 */
/* See the file NOTICE for conditions of use and distribution. */
/* Functions concerned with verifying things. The original code for callout
static tree_node *dnsbl_cache = NULL;
+/* Bits for match_type in one_check_dnsbl() */
+
+#define MT_NOT 1
+#define MT_ALL 2
+
+
/*************************************************
* Retrieve a callout cache record *
log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: %s", addr->address,
addr->message);
- /* Expand the helo_data string to find the host name to use. */
-
- if (tf->helo_data != NULL)
- {
- uschar *s = expand_string(tf->helo_data);
- if (s == NULL)
- log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: failed to expand transport's "
- "helo_data value for callout: %s", addr->address,
- expand_string_message);
- else active_hostname = s;
- }
-
- deliver_host = deliver_host_address = NULL;
- deliver_domain = save_deliver_domain;
-
/* Set HELO string according to the protocol */
if (Ustrcmp(tf->protocol, "lmtp") == 0) helo = US"LHLO";
{
addr->message = string_sprintf("could not connect to %s [%s]: %s",
host->name, host->address, strerror(errno));
+ deliver_host = deliver_host_address = NULL;
+ deliver_domain = save_deliver_domain;
continue;
}
+ /* Expand the helo_data string to find the host name to use. */
+
+ if (tf->helo_data != NULL)
+ {
+ uschar *s = expand_string(tf->helo_data);
+ if (s == NULL)
+ log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: failed to expand transport's "
+ "helo_data value for callout: %s", addr->address,
+ expand_string_message);
+ else active_hostname = s;
+ }
+
+ deliver_host = deliver_host_address = NULL;
+ deliver_domain = save_deliver_domain;
+
/* 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. */
reversed if IP address)
iplist the list of matching IP addresses, or NULL for "any"
bitmask true if bitmask matching is wanted
- invert_result true if result to be inverted
+ match_type condition for 'succeed' result
+ 0 => Any RR in iplist (=)
+ 1 => No RR in iplist (!=)
+ 2 => All RRs in iplist (==)
+ 3 => Some RRs not in iplist (!==)
+ the two bits are defined as MT_NOT and MT_ALL
defer_return what to return for a defer
Returns: OK if lookup succeeded
static int
one_check_dnsbl(uschar *domain, uschar *domain_txt, uschar *keydomain,
- uschar *prepend, uschar *iplist, BOOL bitmask, BOOL invert_result,
+ uschar *prepend, uschar *iplist, BOOL bitmask, int match_type,
int defer_return)
{
dns_answer dnsa;
if (iplist != NULL)
{
- int ipsep = ',';
- uschar ip[46];
- uschar *ptr = iplist;
-
- while (string_nextinlist(&ptr, &ipsep, ip, sizeof(ip)) != NULL)
+ for (da = cb->rhs; da != NULL; da = da->next)
{
+ int ipsep = ',';
+ uschar ip[46];
+ uschar *ptr = iplist;
+ uschar *res;
+
/* Handle exact matching */
+
if (!bitmask)
{
- for (da = cb->rhs; da != NULL; da = da->next)
+ while ((res = string_nextinlist(&ptr, &ipsep, ip, sizeof(ip))) != NULL)
{
if (Ustrcmp(CS da->address, ip) == 0) break;
}
}
+
/* Handle bitmask matching */
+
else
{
int address[4];
ignore IPv6 addresses. The default mask is 0, which always matches.
We change this only for IPv4 addresses in the list. */
- if (host_aton(ip, address) == 1) mask = address[0];
+ if (host_aton(da->address, address) == 1) mask = address[0];
/* Scan the returned addresses, skipping any that are IPv6 */
- for (da = cb->rhs; da != NULL; da = da->next)
+ while ((res = string_nextinlist(&ptr, &ipsep, ip, sizeof(ip))) != NULL)
{
- if (host_aton(da->address, address) != 1) continue;
- if ((address[0] & mask) == mask) break;
+ if (host_aton(ip, address) != 1) continue;
+ if ((address[0] & mask) == address[0]) break;
}
}
- /* Break out if a match has been found */
+ /* If either
+
+ (a) An IP address in an any ('=') list matched, or
+ (b) No IP address in an all ('==') list matched
+
+ then we're done searching. */
- if (da != NULL) break;
+ if (((match_type & MT_ALL) != 0) == (res == NULL)) break;
}
- /* If either
+ /* If da == NULL, either
- (a) No IP address in a positive list matched, or
- (b) An IP address in a negative list did match
+ (a) No IP address in an any ('=') list matched, or
+ (b) An IP address in an all ('==') list didn't match
- then behave as if the DNSBL lookup had not succeeded, i.e. the host is
- not on the list. */
+ so behave as if the DNSBL lookup had not succeeded, i.e. the host is not on
+ the list. */
- if (invert_result != (da == NULL))
+ if ((match_type == MT_NOT || match_type == MT_ALL) != (da == NULL))
{
HDEBUG(D_dnsbl)
{
+ uschar *res = NULL;
+ switch(match_type)
+ {
+ case 0:
+ res = US"was no match";
+ break;
+ case MT_NOT:
+ res = US"was an exclude match";
+ break;
+ case MT_ALL:
+ res = US"was an IP address that did not match";
+ break;
+ case MT_NOT|MT_ALL:
+ res = US"were no IP addresses that did not match";
+ break;
+ }
debug_printf("=> but we are not accepting this block class because\n");
- debug_printf("=> there was %s match for %c%s\n",
- invert_result? "an exclude":"no", bitmask? '&' : '=', iplist);
+ debug_printf("=> there %s for %s%c%s\n",
+ res,
+ ((match_type & MT_ALL) == 0)? "" : "=",
+ bitmask? '&' : '=', iplist);
}
return FAIL;
}
if (domain_txt != domain)
return one_check_dnsbl(domain_txt, domain_txt, keydomain, prepend, NULL,
- FALSE, invert_result, defer_return);
+ FALSE, match_type, defer_return);
/* If there is no alternate domain, look up a TXT record in the main domain
if it has not previously been cached. */
{
int sep = 0;
int defer_return = FAIL;
-BOOL invert_result = FALSE;
uschar *list = *listptr;
uschar *domain;
uschar *s;
{
int rc;
BOOL bitmask = FALSE;
+ int match_type = 0;
uschar *domain_txt;
uschar *comma;
uschar *iplist;
if (key != NULL) *key++ = 0;
/* See if there's a list of addresses supplied after the domain name. This is
- introduced by an = or a & character; if preceded by ! we invert the result.
- */
+ introduced by an = or a & character; if preceded by = we require all matches
+ and if preceded by ! we invert the result. */
iplist = Ustrchr(domain, '=');
if (iplist == NULL)
iplist = Ustrchr(domain, '&');
}
- if (iplist != NULL)
+ if (iplist != NULL) /* Found either = or & */
{
- if (iplist > domain && iplist[-1] == '!')
+ if (iplist > domain && iplist[-1] == '!') /* Handle preceding ! */
{
- invert_result = TRUE;
+ match_type |= MT_NOT;
iplist[-1] = 0;
}
- *iplist++ = 0;
+
+ *iplist++ = 0; /* Terminate domain, move on */
+
+ /* If we found = (bitmask == FALSE), check for == or =& */
+
+ if (!bitmask && (*iplist == '=' || *iplist == '&'))
+ {
+ bitmask = *iplist++ == '&';
+ match_type |= MT_ALL;
+ }
}
/* If there is a comma in the domain, it indicates that a second domain for
if (sender_host_address == NULL) return FAIL; /* can never match */
if (revadd[0] == 0) invert_address(revadd, sender_host_address);
rc = one_check_dnsbl(domain, domain_txt, sender_host_address, revadd,
- iplist, bitmask, invert_result, defer_return);
+ iplist, bitmask, match_type, defer_return);
if (rc == OK)
{
dnslist_domain = string_copy(domain_txt);
}
rc = one_check_dnsbl(domain, domain_txt, keydomain, prepend, iplist,
- bitmask, invert_result, defer_return);
+ bitmask, match_type, defer_return);
if (rc == OK)
{