-/* $Cambridge: exim/src/src/verify.c,v 1.12 2005/01/12 15:41:27 ph10 Exp $ */
+/* $Cambridge: exim/src/src/verify.c,v 1.37 2006/06/30 15:36:08 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
options the verification options - these bits are used:
vopt_is_recipient => this is a recipient address
vopt_callout_no_cache => don't use callout cache
+ vopt_callout_fullpm => if postmaster check, do full one
vopt_callout_random => do the "random" thing
vopt_callout_recipsender => use real sender for recipient
vopt_callout_recippmaster => use postmaster for recipient
static int
do_callout(address_item *addr, host_item *host_list, transport_feedback *tf,
- int callout, int callout_overall, int callout_connect, int options,
+ int callout, int callout_overall, int callout_connect, int options,
uschar *se_mailfrom, uschar *pm_mailfrom)
{
BOOL is_recipient = (options & vopt_is_recipient) != 0;
uschar *address_key;
uschar *from_address;
uschar *random_local_part = NULL;
-uschar **failure_ptr = is_recipient?
+uschar *save_deliver_domain = deliver_domain;
+uschar **failure_ptr = is_recipient?
&recipient_verify_failure : &sender_verify_failure;
open_db dbblock;
open_db *dbm_file = NULL;
setflag(addr, af_verify_nsfail);
addr->user_message = US"(result of an earlier callout reused).";
yield = FAIL;
- *failure_ptr = US"mail";
+ *failure_ptr = US"mail";
goto END_CALLOUT;
}
debug_printf("callout cache: domain does not accept "
"RCPT TO:<postmaster@domain>\n");
yield = FAIL;
- *failure_ptr = US"postmaster";
+ *failure_ptr = US"postmaster";
setflag(addr, af_verify_pmfail);
addr->user_message = US"(result of earlier verification reused).";
goto END_CALLOUT;
HDEBUG(D_verify)
debug_printf("callout cache: address record is negative\n");
addr->user_message = US"Previous (cached) callout verification failure";
- *failure_ptr = US"recipient";
+ *failure_ptr = US"recipient";
yield = FAIL;
}
goto END_CALLOUT;
smtp_outblock outblock;
int host_af;
int port = 25;
+ BOOL send_quit = TRUE;
uschar *helo = US"HELO";
uschar *interface = NULL; /* Outgoing interface to use; NULL => any */
uschar inbuffer[4096];
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;
/* Set HELO string according to the protocol */
if (!done)
{
- *failure_ptr = US"mail";
+ *failure_ptr = US"mail";
if (errno == 0 && responsebuffer[0] == '5')
{
setflag(addr, af_verify_nsfail);
smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer),
'2', callout) &&
- smtp_write_command(&outblock, FALSE, "MAIL FROM:<>\r\n") >= 0 &&
+ smtp_write_command(&outblock, FALSE, "MAIL FROM:<%s>\r\n",
+ from_address) >= 0 &&
smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer),
'2', callout);
}
if (new_domain_record.random_result != ccache_accept && done)
{
+ /* Get the rcpt_include_affixes flag from the transport if there is one,
+ but assume FALSE if there is not. */
+
done =
smtp_write_command(&outblock, FALSE, "RCPT TO:<%.1000s>\r\n",
- addr->address) >= 0 &&
+ transport_rcpt_address(addr,
+ (addr->transport == NULL)? FALSE :
+ addr->transport->rcpt_include_affixes)) >= 0 &&
smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer),
'2', callout);
new_address_record.result = ccache_accept;
else if (errno == 0 && responsebuffer[0] == '5')
{
- *failure_ptr = US"recipient";
+ *failure_ptr = US"recipient";
new_address_record.result = ccache_reject;
- }
+ }
- /* Do postmaster check if requested */
+ /* Do postmaster check if requested; if a full check is required, we
+ check for RCPT TO:<postmaster> (no domain) in accordance with RFC 821. */
if (done && pm_mailfrom != NULL)
{
smtp_read_response(&inblock, responsebuffer,
sizeof(responsebuffer), '2', callout) &&
+ /* First try using the current domain */
+
+ ((
smtp_write_command(&outblock, FALSE,
"RCPT TO:<postmaster@%.1000s>\r\n", addr->domain) >= 0 &&
smtp_read_response(&inblock, responsebuffer,
- sizeof(responsebuffer), '2', callout);
+ sizeof(responsebuffer), '2', callout)
+ )
+
+ ||
+
+ /* If that doesn't work, and a full check is requested,
+ try without the domain. */
+
+ (
+ (options & vopt_callout_fullpm) != 0 &&
+ smtp_write_command(&outblock, FALSE,
+ "RCPT TO:<postmaster>\r\n") >= 0 &&
+ smtp_read_response(&inblock, responsebuffer,
+ sizeof(responsebuffer), '2', callout)
+ ));
+
+ /* Sort out the cache record */
new_domain_record.postmaster_stamp = time(NULL);
new_domain_record.postmaster_result = ccache_accept;
else if (errno == 0 && responsebuffer[0] == '5')
{
- *failure_ptr = US"postmaster";
+ *failure_ptr = US"postmaster";
setflag(addr, af_verify_pmfail);
new_domain_record.postmaster_result = ccache_reject;
}
}
} /* Random not accepted */
- } /* MAIL FROM:<> accepted */
+ } /* MAIL FROM: accepted */
/* For any failure of the main check, other than a negative response, we just
close the connection and carry on. We can identify a negative response by the
if (errno == ETIMEDOUT)
{
HDEBUG(D_verify) debug_printf("SMTP timeout\n");
+ send_quit = FALSE;
}
else if (errno == 0)
{
/* End the SMTP conversation and close the connection. */
- (void)smtp_write_command(&outblock, FALSE, "QUIT\r\n");
- close(inblock.sock);
+ if (send_quit) (void)smtp_write_command(&outblock, FALSE, "QUIT\r\n");
+ (void)close(inblock.sock);
} /* Loop through all hosts, while !done */
/* If we get here with done == TRUE, a successful callout happened, and yield
However, there may be domain-specific information to cache in both cases.
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,
+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. */
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;
}
rewriting and messages from callouts
vopt_qualify => qualify an unqualified address; else error
vopt_expn => called from SMTP EXPN command
+ vopt_success_on_redirect => when a new address is generated
+ the verification instantly succeeds
These ones are used by do_callout() -- the options variable
is passed to it.
+ vopt_callout_fullpm => if postmaster check, do full one
vopt_callout_no_cache => don't use callout cache
vopt_callout_random => do the "random" thing
vopt_callout_recipsender => use real sender for recipient
for individual commands
callout_overall if > 0, gives overall timeout for the callout function;
if < 0, a default is used (see do_callout())
- callout_connect the connection timeout for callouts
+ callout_connect the connection timeout for callouts
se_mailfrom when callout is requested to verify a sender, use this
in MAIL FROM; NULL => ""
pm_mailfrom when callout is requested, if non-NULL, do the postmaster
int
verify_address(address_item *vaddr, FILE *f, int options, int callout,
- int callout_overall, int callout_connect, uschar *se_mailfrom,
+ int callout_overall, int callout_connect, uschar *se_mailfrom,
uschar *pm_mailfrom, BOOL *routed)
{
BOOL allok = TRUE;
BOOL full_info = (f == NULL)? FALSE : (debug_selector != 0);
BOOL is_recipient = (options & vopt_is_recipient) != 0;
BOOL expn = (options & vopt_expn) != 0;
+BOOL success_on_redirect = (options & vopt_success_on_redirect) != 0;
int i;
int yield = OK;
int verify_type = expn? v_expn :
address_item *addr_remote = NULL;
address_item *addr_local = NULL;
address_item *addr_succeed = NULL;
-uschar **failure_ptr = is_recipient?
+uschar **failure_ptr = is_recipient?
&recipient_verify_failure : &sender_verify_failure;
uschar *ko_prefix, *cr;
uschar *address = vaddr->address;
if (f != NULL)
fprintf(f, "%sA domain is required for \"%s\"%s\n", ko_prefix, address,
cr);
- *failure_ptr = US"qualify";
+ *failure_ptr = US"qualify";
return FAIL;
}
address = rewrite_address_qualify(address, is_recipient);
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
if (tf.hosts != NULL && (host_list == NULL || tf.hosts_override))
{
uschar *s;
+ uschar *save_deliver_domain = deliver_domain;
+ uschar *save_deliver_localpart = deliver_localpart;
host_list = NULL; /* Ignore the router's hosts */
deliver_domain = addr->domain;
deliver_localpart = addr->local_part;
s = expand_string(tf.hosts);
- deliver_domain = deliver_localpart = NULL;
+ deliver_domain = save_deliver_domain;
+ deliver_localpart = save_deliver_localpart;
if (s == NULL)
{
host_build_hostlist(&host_list, s, tf.hosts_randomize);
/* Just ignore failures to find a host address. If we don't manage
- to find any addresses, the callout will defer. Note that more than
- one address may be found for a single host, which will result in
- additional host items being inserted into the chain. Hence we must
+ to find any addresses, the callout will defer. Note that more than
+ one address may be found for a single host, which will result in
+ additional host items being inserted into the chain. Hence we must
save the next host first. */
for (host = host_list; host != NULL; host = nexthost)
{
nexthost = host->next;
- if (tf.gethostbyname ||
- string_is_ip_address(host->name, NULL) > 0)
+ if (tf.gethostbyname ||
+ string_is_ip_address(host->name, NULL) != 0)
(void)host_find_byname(host, NULL, &canonical_name, TRUE);
else
{
}
}
- /* Can only do a callout if we have at least one host! If the callout
+ /* Can only do a callout if we have at least one host! If the callout
fails, it will have set ${sender,recipient}_verify_failure. */
if (host_list != NULL)
}
}
}
-
+
/* Otherwise, any failure is a routing failure */
-
- else *failure_ptr = US"route";
+
+ else *failure_ptr = US"route";
/* A router may return REROUTED if it has set up a child address as a result
of a change of domain name (typically from widening). In this case we always
want to continue to verify the new child. */
if (rc == REROUTED) continue;
-
+
/* Handle hard failures */
if (rc == FAIL)
generated address. */
if (!full_info && /* Stop if short info wanted AND */
- (addr_new == NULL || /* No new address OR */
- addr_new->next != NULL || /* More than one new address OR */
- testflag(addr_new, af_pfr))) /* New address is pfr */
+ (((addr_new == NULL || /* No new address OR */
+ addr_new->next != NULL || /* More than one new address OR */
+ testflag(addr_new, af_pfr))) /* New address is pfr */
+ || /* OR */
+ (addr_new != NULL && /* At least one new address AND */
+ success_on_redirect))) /* success_on_redirect is set */
{
if (f != NULL) fprintf(f, "%s %s\n", address,
address_test_mode? "is deliverable" : "verified");
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)
{
addr_list = addr->next;
fprintf(f, "%s", CS addr->address);
+#ifdef EXPERIMENTAL_SRS
+ 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);
}
}
-/* Will be DEFER or FAIL if any one address has, only for full_info (which is
+/* Will be DEFER or FAIL if any one address has, only for full_info (which is
the -bv or -bt case). */
-return yield;
+return yield;
}
{
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 *
log_msgptr points to where to put a log error message
callout timeout for callout check (passed to verify_address())
callout_overall overall callout timeout (ditto)
- callout_connect connect callout timeout (ditto)
+ callout_connect connect callout timeout (ditto)
se_mailfrom mailfrom for verify; NULL => ""
pm_mailfrom sender for pm callout check (passed to verify_address())
options callout options (passed to verify_address())
+ verrno where to put the address basic_errno
If log_msgptr is set to something without setting user_msgptr, the caller
normally uses log_msgptr for both things.
int
verify_check_header_address(uschar **user_msgptr, uschar **log_msgptr,
- int callout, int callout_overall, int callout_connect, uschar *se_mailfrom,
- uschar *pm_mailfrom, int options)
+ int callout, int callout_overall, int callout_connect, uschar *se_mailfrom,
+ uschar *pm_mailfrom, int options, int *verrno)
{
static int header_types[] = { htype_sender, htype_reply_to, htype_from };
int yield = FAIL;
{
vaddr = deliver_make_addr(address, FALSE);
new_ok = verify_address(vaddr, NULL, options | vopt_fake_sender,
- callout, callout_overall, callout_connect, se_mailfrom,
+ callout, callout_overall, callout_connect, se_mailfrom,
pm_mailfrom, NULL);
}
}
last of these will be returned to the user if all three fail. We do not
set a log message - the generic one below will be used. */
- if (new_ok != OK && smtp_return_error_details)
+ if (new_ok != OK)
{
- *user_msgptr = string_sprintf("Rejected after DATA: "
- "could not verify \"%.*s\" header address\n%s: %s",
- endname - h->text, h->text, vaddr->address, vaddr->message);
+ *verrno = vaddr->basic_errno;
+ if (smtp_return_error_details)
+ {
+ *user_msgptr = string_sprintf("Rejected after DATA: "
+ "could not verify \"%.*s\" header address\n%s: %s",
+ endname - h->text, h->text, vaddr->address, vaddr->message);
+ }
}
/* Success or defer */
DEBUG(D_ident) debug_printf("sender_ident = %s\n", sender_ident);
END_OFF:
-close(sock);
+(void)close(sock);
return;
}
error for error message when returning ERROR
The block contains:
- host_name the host name or NULL, implying use sender_host_name and
- sender_host_aliases, looking them up if required
+ host_name (a) the host name, or
+ (b) NULL, implying use sender_host_name and
+ sender_host_aliases, looking them up if required, or
+ (c) the empty string, meaning that only IP address matches
+ are permitted
host_address the host address
host_ipv4 the IPv4 address taken from an IPv6 one
Returns: OK matched
FAIL did not match
DEFER lookup deferred
- ERROR failed to find the host name or IP address
- unknown lookup type specified
+ ERROR (a) failed to find the host name or IP address, or
+ (b) unknown lookup type specified, or
+ (c) host name encountered when only IP addresses are
+ being matched
*/
-static int
+int
check_host(void *arg, uschar *ss, uschar **valueptr, uschar **error)
{
check_host_block *cb = (check_host_block *)arg;
+int mlen = -1;
int maskoffset;
+BOOL iplookup = FALSE;
BOOL isquery = FALSE;
-uschar *semicolon, *t;
+BOOL isiponly = cb->host_name != NULL && cb->host_name[0] == 0;
+uschar *t;
+uschar *semicolon;
uschar **aliases;
/* Optimize for the special case when the pattern is "*". */
if (cb->host_address[0] == 0) return (*ss == 0)? OK : FAIL;
if (*ss == 0) return FAIL;
-/* If the pattern is precisely "@" then match against the primary host name;
-if it's "@[]" match against the local host's IP addresses. */
+/* If the pattern is precisely "@" then match against the primary host name,
+provided that host name matching is permitted; if it's "@[]" match against the
+local host's IP addresses. */
if (*ss == '@')
{
- if (ss[1] == 0) ss = primary_hostname;
+ if (ss[1] == 0)
+ {
+ if (isiponly) return ERROR;
+ ss = primary_hostname;
+ }
else if (Ustrcmp(ss, "@[]") == 0)
{
ip_address_item *ip;
/* 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);
-/* 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. */
+/* 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, even if there is no "net-". */
-if (Ustrncmp(ss, "net", 3) == 0 && (semicolon = Ustrchr(ss, ';')) != NULL)
+if (isiponly)
{
- int mlen = 0;
- for (t = ss + 3; isdigit(*t); t++) mlen = mlen * 10 + *t - '0';
- if (*t++ == '-')
- {
- int insize;
- int search_type;
- int incoming[4];
- void *handle;
- uschar *filename, *key, *result;
- uschar buffer[64];
+ iplookup = semicolon != NULL;
+ }
- /* If no mask was supplied, set a negative value */
+/* 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. 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 (mlen == 0 && t == ss+4) mlen = -1;
+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;
- /* Find the search type */
+/* Do the IP address lookup if that is indeed what we have */
- search_type = search_findtype(t, semicolon - t);
+if (iplookup)
+ {
+ int insize;
+ int search_type;
+ int incoming[4];
+ void *handle;
+ uschar *filename, *key, *result;
+ uschar buffer[64];
- if (search_type < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s",
- search_error_message);
+ /* Find the search type */
- /* Adjust parameters for the type of lookup. For a query-style
- lookup, there is no file name, and the "key" is just the query. For
- a single-key lookup, the key is the current IP address, masked
- appropriately, and reconverted to text form, with the mask appended.
- For IPv6 addresses, specify dot separators instead of colons. */
+ search_type = search_findtype(t, semicolon - t);
- if (mac_islookup(search_type, lookup_querystyle))
- {
- filename = NULL;
- key = semicolon + 1;
- }
- else
- {
- insize = host_aton(cb->host_address, incoming);
- host_mask(insize, incoming, mlen);
- (void)host_nmtoa(insize, incoming, mlen, buffer, '.');
- key = buffer;
- filename = semicolon + 1;
- }
+ if (search_type < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s",
+ search_error_message);
- /* Now do the actual lookup; note that there is no search_close() because
- of the caching arrangements. */
+ /* Adjust parameters for the type of lookup. For a query-style lookup, there
+ is no file name, and the "key" is just the query. For query-style with a file
+ name, we have to fish the file off the start of the query. For a single-key
+ lookup, the key is the current IP address, masked appropriately, and
+ reconverted to text form, with the mask appended. For IPv6 addresses, specify
+ dot separators instead of colons. */
- handle = search_open(filename, search_type, 0, NULL, NULL);
- if (handle == NULL) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s",
- search_error_message);
- result = search_find(handle, filename, key, -1, NULL, 0, 0, NULL);
- if (valueptr != NULL) *valueptr = result;
- return (result != NULL)? OK : search_find_defer? DEFER: FAIL;
+ if (mac_islookup(search_type, lookup_absfilequery))
+ {
+ filename = semicolon + 1;
+ key = filename;
+ while (*key != 0 && !isspace(*key)) key++;
+ filename = string_copyn(filename, key - filename);
+ while (isspace(*key)) key++;
}
+ else if (mac_islookup(search_type, lookup_querystyle))
+ {
+ filename = NULL;
+ key = semicolon + 1;
+ }
+ else
+ {
+ insize = host_aton(cb->host_address, incoming);
+ host_mask(insize, incoming, mlen);
+ (void)host_nmtoa(insize, incoming, mlen, buffer, '.');
+ key = buffer;
+ filename = semicolon + 1;
+ }
+
+ /* Now do the actual lookup; note that there is no search_close() because
+ of the caching arrangements. */
+
+ handle = search_open(filename, search_type, 0, NULL, NULL);
+ if (handle == NULL) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s",
+ search_error_message);
+ result = search_find(handle, filename, key, -1, NULL, 0, 0, NULL);
+ if (valueptr != NULL) *valueptr = result;
+ return (result != NULL)? OK : search_find_defer? DEFER: FAIL;
}
/* The pattern is not an IP address or network reference of any kind. That is,
-it is a host name pattern. Check the characters of the pattern to see if they
-comprise only letters, digits, full stops, and hyphens (the constituents of
-domain names). Allow underscores, as they are all too commonly found. Sigh.
-Also, if allow_utf8_domains is set, allow top-bit characters. */
+it is a host name pattern. If this is an IP only match, there's an error in the
+host list. */
+
+if (isiponly)
+ {
+ *error = US"cannot match host name in match_ip list";
+ return ERROR;
+ }
+
+/* Check the characters of the pattern to see if they comprise only letters,
+digits, full stops, and hyphens (the constituents of domain names). Allow
+underscores, as they are all too commonly found. Sigh. Also, if
+allow_utf8_domains is set, allow top-bit characters. */
for (t = ss; *t != 0; t++)
if (!isalnum(*t) && *t != '.' && *t != '-' && *t != '_' &&
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;
}
search_error_message, ss);
return DEFER;
}
- isquery = mac_islookup(id, lookup_querystyle);
+ isquery = mac_islookup(id, lookup_querystyle|lookup_absfilequery);
}
if (isquery)
cb.host_ipv4 = (Ustrncmp(host_address, "::ffff:", 7) == 0)?
host_address + 7 : host_address;
-/* During the running of the check, put the IP address into $host_address. In
-the case of calls from the smtp transport, it will already be there. However,
-in other calls (e.g. when testing ignore_target_hosts), it won't. Just to be on
+/* During the running of the check, put the IP address into $host_address. In
+the case of calls from the smtp transport, it will already be there. However,
+in other calls (e.g. when testing ignore_target_hosts), it won't. Just to be on
the safe side, any existing setting is preserved, though as I write this
(November 2004) I can't see any cases where it is actually needed. */
check_host, /* function for testing */
&cb, /* argument for function */
MCL_HOST, /* type of check */
- (host_address == sender_host_address)?
+ (host_address == sender_host_address)?
US"host" : host_address, /* text for debugging */
valueptr); /* where to pass back data */
deliver_host_address = save_host_address;
-return rc;
+return rc;
}
Arguments:
domain the outer dnsbl domain (for debug message)
- keydomain the current keydomain (for debug message)
+ keydomain the current keydomain (for debug message)
query the domain to be looked up
- iplist the list of matching IP addresses
- bitmask true if bitmask matching is wanted
- invert_result true if result to be inverted
- defer_return what to return for a defer
+ iplist the list of matching IP addresses
+ bitmask true if bitmask matching is wanted
+ invert_result true if result to be inverted
+ defer_return what to return for a defer
Returns: OK if lookup succeeded
FAIL if not
*/
static int
-one_check_dnsbl(uschar *domain, uschar *keydomain, uschar *query,
+one_check_dnsbl(uschar *domain, uschar *keydomain, uschar *query,
uschar *iplist, BOOL bitmask, BOOL invert_result, int defer_return)
-{
+{
dns_answer dnsa;
dns_scan dnss;
tree_node *t;
debug_printf("=> there was %s match for %c%s\n",
invert_result? "an exclude":"no", bitmask? '&' : '=', iplist);
}
- return FAIL;
+ return FAIL;
}
}
}
}
- /* If there is no key string, construct the query by adding the domain name
+ /* If there is no key string, construct the query by adding the domain name
onto the inverted host address, and perform a single DNS lookup. */
-
+
if (key == NULL)
{
if (sender_host_address == NULL) return FAIL; /* can never match */
if (revadd[0] == 0) invert_address(revadd, sender_host_address);
frc = string_format(query, sizeof(query), "%s%s", revadd, domain);
-
+
if (!frc)
{
log_write(0, LOG_MAIN|LOG_PANIC, "dnslist query is too long "
"(ignored): %s...", query);
continue;
}
-
- rc = one_check_dnsbl(domain, sender_host_address, query, iplist, bitmask,
+
+ rc = one_check_dnsbl(domain, sender_host_address, query, iplist, bitmask,
invert_result, defer_return);
-
+
if (rc == OK)
{
dnslist_domain = string_copy(domain);
- HDEBUG(D_dnsbl) debug_printf("=> that means %s is listed at %s\n",
+ HDEBUG(D_dnsbl) debug_printf("=> that means %s is listed at %s\n",
sender_host_address, domain);
}
-
+
if (rc != FAIL) return rc; /* OK or DEFER */
}
-
- /* If there is a key string, it can be a list of domains or IP addresses to
+
+ /* If there is a key string, it can be a list of domains or IP addresses to
be concatenated with the main domain. */
-
+
else
{
int keysep = 0;
- BOOL defer = FALSE;
- uschar *keydomain;
+ BOOL defer = FALSE;
+ uschar *keydomain;
uschar keybuffer[256];
-
- while ((keydomain = string_nextinlist(&key, &keysep, keybuffer,
+
+ 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);
- frc = string_format(query, sizeof(query), "%s%s", keyrevadd, domain);
+ frc = string_format(query, sizeof(query), "%s%s", keyrevadd, domain);
}
else
- {
+ {
frc = string_format(query, sizeof(query), "%s.%s", keydomain, domain);
}
"(ignored): %s...", query);
continue;
}
-
- rc = one_check_dnsbl(domain, keydomain, query, iplist, bitmask,
+
+ rc = one_check_dnsbl(domain, keydomain, query, iplist, bitmask,
invert_result, defer_return);
-
+
if (rc == OK)
{
dnslist_domain = string_copy(domain);
- HDEBUG(D_dnsbl) debug_printf("=> that means %s is listed at %s\n",
+ HDEBUG(D_dnsbl) debug_printf("=> that means %s is listed at %s\n",
keydomain, domain);
- return OK;
+ return OK;
}
-
+
/* If the lookup deferred, remember this fact. We keep trying the rest
of the list to see if we get a useful result, and if we don't, we return
DEFER at the end. */
} /* continue with next keystring domain/address */
if (defer) return DEFER;
- }
+ }
} /* continue with next dnsdb outer domain */
return FAIL;