return (unsigned int)(random_seed >> 16) % limit;
}
+/*************************************************
+* Wrappers for logging lookup times *
+*************************************************/
+
+/* When the 'slow_lookup_log' variable is enabled, these wrappers will
+write to the log file all (potential) dns lookups that take more than
+slow_lookup_log milliseconds
+*/
+
+static void
+log_long_lookup(const uschar * type, const uschar * data, unsigned long msec)
+{
+log_write(0, LOG_MAIN, "Long %s lookup for '%s': %lu msec",
+ type, data, msec);
+}
+
+
+/* returns the current system epoch time in milliseconds. */
+static unsigned long
+get_time_in_ms()
+{
+struct timeval tmp_time;
+unsigned long seconds, microseconds;
+
+gettimeofday(&tmp_time, NULL);
+seconds = (unsigned long) tmp_time.tv_sec;
+microseconds = (unsigned long) tmp_time.tv_usec;
+return seconds*1000 + microseconds/1000;
+}
+
+
+static int
+dns_lookup_timerwrap(dns_answer *dnsa, const uschar *name, int type,
+ const uschar **fully_qualified_name)
+{
+int retval;
+unsigned long time_msec;
+
+if (!slow_lookup_log)
+ return dns_lookup(dnsa, name, type, fully_qualified_name);
+
+time_msec = get_time_in_ms();
+retval = dns_lookup(dnsa, name, type, fully_qualified_name);
+if ((time_msec = get_time_in_ms() - time_msec) > slow_lookup_log)
+ log_long_lookup(US"name", name, time_msec);
+return retval;
+}
/*************************************************
*************************************************/
/* This function is called instead of gethostbyname(), gethostbyname2(), or
-getipnodebyname() when running in the test harness. It recognizes the name
-"manyhome.test.ex" and generates a humungous number of IP addresses. It also
+getipnodebyname() when running in the test harness. . It also
recognizes an unqualified "localhost" and forces it to the appropriate loopback
address. IP addresses are treated as literals. For other names, it uses the DNS
to find the host name. In the test harness, this means it will access only the
*/
static struct hostent *
-host_fake_gethostbyname(uschar *name, int af, int *error_num)
+host_fake_gethostbyname(const uschar *name, int af, int *error_num)
{
#if HAVE_IPV6
int alen = (af == AF_INET)? sizeof(struct in_addr):sizeof(struct in6_addr);
#endif
int ipa;
-uschar *lname = name;
+const uschar *lname = name;
uschar *adds;
uschar **alist;
struct hostent *yield;
debug_printf("using host_fake_gethostbyname for %s (%s)\n", name,
(af == AF_INET)? "IPv4" : "IPv6");
-/* Handle the name that needs a vast number of IP addresses */
-
-if (Ustrcmp(name, "manyhome.test.ex") == 0 && af == AF_INET)
- {
- int i, j;
- yield = store_get(sizeof(struct hostent));
- alist = store_get(2049 * sizeof(char *));
- adds = store_get(2048 * alen);
- yield->h_name = CS name;
- yield->h_aliases = NULL;
- yield->h_addrtype = af;
- yield->h_length = alen;
- yield->h_addr_list = CSS alist;
- for (i = 104; i <= 111; i++)
- {
- for (j = 0; j <= 255; j++)
- {
- *alist++ = adds;
- *adds++ = 10;
- *adds++ = 250;
- *adds++ = i;
- *adds++ = j;
- }
- }
- *alist = NULL;
- return yield;
- }
-
/* Handle unqualified "localhost" */
if (Ustrcmp(name, "localhost") == 0)
else
{
int type = (af == AF_INET)? T_A:T_AAAA;
- int rc = dns_lookup(&dnsa, lname, type, NULL);
+ int rc = dns_lookup_timerwrap(&dnsa, lname, type, NULL);
int count = 0;
lookup_dnssec_authenticated = NULL;
}
for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
- rr != NULL;
+ rr;
rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
- {
- if (rr->type == type) count++;
- }
+ if (rr->type == type)
+ count++;
yield = store_get(sizeof(struct hostent));
alist = store_get((count + 1) * sizeof(char **));
yield->h_addr_list = CSS alist;
for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
- rr != NULL;
+ rr;
rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
{
int i, n;
int x[4];
dns_address *da;
if (rr->type != type) continue;
- da = dns_address_from_rr(&dnsa, rr);
+ if (!(da = dns_address_from_rr(&dnsa, rr))) break;
*alist++ = adds;
n = host_aton(da->address, x);
for (i = 0; i < n; i++)
*/
void
-host_build_hostlist(host_item **anchor, uschar *list, BOOL randomize)
+host_build_hostlist(host_item **anchor, const uschar *list, BOOL randomize)
{
int sep = 0;
int fake_mx = MX_NONE; /* This value is actually -1 */
uschar *name;
-uschar buffer[1024];
if (list == NULL) return;
if (randomize) fake_mx--; /* Start at -2 for randomizing */
*anchor = NULL;
-while ((name = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL)
+while ((name = string_nextinlist(&list, &sep, NULL, 0)) != NULL)
{
host_item *h;
}
h = store_get(sizeof(host_item));
- h->name = string_copy(name);
+ h->name = name;
h->address = NULL;
h->port = PORT_NONE;
h->mx = fake_mx;
int
host_item_get_port(host_item *h)
{
-uschar *p;
+const uschar *p;
int port, x;
int len = Ustrlen(h->name);
*/
ip_address_item *
-host_build_ifacelist(uschar *list, uschar *name)
+host_build_ifacelist(const uschar *list, uschar *name)
{
int sep = 0;
uschar *s;
if (local_interface_data == NULL)
{
void *reset_item = store_get(0);
- ip_address_item *dlist = host_build_ifacelist(local_interfaces,
+ ip_address_item *dlist = host_build_ifacelist(CUS local_interfaces,
US"local_interfaces");
- ip_address_item *xlist = host_build_ifacelist(extra_local_interfaces,
+ ip_address_item *xlist = host_build_ifacelist(CUS extra_local_interfaces,
US"extra_local_interfaces");
ip_address_item *ipa;
*/
int
-host_aton(uschar *address, int *bin)
+host_aton(const uschar *address, int *bin)
{
int x[4];
int v4offset = 0;
if (Ustrchr(address, ':') != NULL)
{
- uschar *p = address;
- uschar *component[8];
+ const uschar *p = address;
+ const uschar *component[8];
BOOL ipv4_ends = FALSE;
int ci = 0;
int nulloffset = 0;
{
int sep = 0;
uschar buffer[32];
-uschar *list = tls_in.on_connect_ports;
+const uschar *list = tls_in.on_connect_ports;
uschar *s;
+uschar *end;
if (tls_in.on_connect) return TRUE;
-while ((s = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL)
- {
- uschar *end;
- int lport = Ustrtol(s, &end, 10);
- if (*end != 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "tls_on_connect_ports "
- "contains \"%s\", which is not a port number: exim abandoned", s);
- if (lport == port) return TRUE;
- }
+while ((s = string_nextinlist(&list, &sep, buffer, sizeof(buffer))))
+ if (Ustrtol(s, &end, 10) == port)
+ return TRUE;
return FALSE;
}
*/
BOOL
-host_is_in_net(uschar *host, uschar *net, int maskoffset)
+host_is_in_net(const uschar *host, const uschar *net, int maskoffset)
{
int i;
int address[4];
if (hosts_treat_as_local != NULL)
{
int rc;
- uschar *save = deliver_domain;
+ const uschar *save = deliver_domain;
deliver_domain = h->name; /* set $domain */
- rc = match_isinlist(string_copylc(h->name), &hosts_treat_as_local, 0,
+ rc = match_isinlist(string_copylc(h->name), CUSS &hosts_treat_as_local, 0,
&domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL);
deliver_domain = save;
if (rc == OK) goto FOUND_LOCAL;
uschar *s, *t;
struct hostent *hosts;
struct in_addr addr;
+unsigned long time_msec;
+
+if (slow_lookup_log) time_msec = get_time_in_ms();
/* Lookup on IPv6 system */
hosts = gethostbyaddr(CS(&addr), sizeof(addr), AF_INET);
#endif
+if ( slow_lookup_log
+ && (time_msec = get_time_in_ms() - time_msec) > slow_lookup_log
+ )
+ log_long_lookup(US"name", sender_host_address, time_msec);
+
/* Failed to look up the host. */
if (hosts == NULL)
uschar **aliases;
uschar buffer[256];
uschar *ordername;
-uschar *list = host_lookup_order;
+const uschar *list = host_lookup_order;
dns_record *rr;
dns_answer dnsa;
dns_scan dnss;
{
dns_init(FALSE, FALSE, FALSE); /* dnssec ctrl by dns_dnssec_ok glbl */
dns_build_reverse(sender_host_address, buffer);
- rc = dns_lookup(&dnsa, buffer, T_PTR, NULL);
+ rc = dns_lookup_timerwrap(&dnsa, buffer, T_PTR, NULL);
/* The first record we come across is used for the name; others are
considered to be aliases. We have to scan twice, in order to find out the
HDEBUG(D_host_lookup)
{
uschar **aliases = sender_host_aliases;
- debug_printf("IP address lookup yielded %s\n", sender_host_name);
- while (*aliases != NULL) debug_printf(" alias %s\n", *aliases++);
+ debug_printf("IP address lookup yielded \"%s\"\n", sender_host_name);
+ while (*aliases != NULL) debug_printf(" alias \"%s\"\n", *aliases++);
}
/* We need to verify that a forward lookup on the name we found does indeed
*/
int
-host_find_byname(host_item *host, uschar *ignore_target_hosts, int flags,
- uschar **fully_qualified_name, BOOL local_host_check)
+host_find_byname(host_item *host, const uschar *ignore_target_hosts, int flags,
+ const uschar **fully_qualified_name, BOOL local_host_check)
{
int i, yield, times;
uschar **addrlist;
if (running_in_test_harness)
{
- uschar *endname = host->name + Ustrlen(host->name);
+ const uschar *endname = host->name + Ustrlen(host->name);
if (Ustrcmp(endname - 14, "test.again.dns") == 0) goto RETURN_AGAIN;
}
#else
if (disable_ipv6 ||
(dns_ipv4_lookup != NULL &&
- match_isinlist(host->name, &dns_ipv4_lookup, 0, NULL, NULL, MCL_DOMAIN,
- TRUE, NULL) == OK))
+ match_isinlist(host->name, CUSS &dns_ipv4_lookup, 0, NULL, NULL,
+ MCL_DOMAIN, TRUE, NULL) == OK))
#endif
{ af = AF_INET; times = 1; }
BOOL ipv4_addr;
int error_num = 0;
struct hostent *hostdata;
+ unsigned long time_msec;
#ifdef STAND_ALONE
printf("Looking up: %s\n", host->name);
#endif
+ if (slow_lookup_log) time_msec = get_time_in_ms();
+
#if HAVE_IPV6
if (running_in_test_harness)
hostdata = host_fake_gethostbyname(host->name, af, &error_num);
}
#endif /* HAVE_IPV6 */
+ if (slow_lookup_log
+ && (time_msec = get_time_in_ms() - time_msec) > slow_lookup_log)
+ log_long_lookup(US"name", host->name, time_msec);
+
if (hostdata == NULL)
{
uschar *error;
switch (error_num)
{
case HOST_NOT_FOUND: error = US"HOST_NOT_FOUND"; break;
- case TRY_AGAIN: error = US"TRY_AGAIN"; break;
- case NO_RECOVERY: error = US"NO_RECOVERY"; break;
- case NO_DATA: error = US"NO_DATA"; break;
+ case TRY_AGAIN: error = US"TRY_AGAIN"; break;
+ case NO_RECOVERY: error = US"NO_RECOVERY"; break;
+ case NO_DATA: error = US"NO_DATA"; break;
#if NO_DATA != NO_ADDRESS
- case NO_ADDRESS: error = US"NO_ADDRESS"; break;
+ case NO_ADDRESS: error = US"NO_ADDRESS"; break;
#endif
default: error = US"?"; break;
}
HDEBUG(D_host_lookup)
{
- host_item *h;
+ const host_item *h;
if (fully_qualified_name != NULL)
debug_printf("fully qualified name = %s\n", *fully_qualified_name);
debug_printf("%s looked up these IP addresses:\n",
{
#ifndef STAND_ALONE
int rc;
- uschar *save = deliver_domain;
+ const uschar *save = deliver_domain;
deliver_domain = host->name; /* set $domain */
- rc = match_isinlist(host->name, &dns_again_means_nonexist, 0, NULL, NULL,
+ rc = match_isinlist(host->name, CUSS &dns_again_means_nonexist, 0, NULL, NULL,
MCL_DOMAIN, TRUE, NULL);
deliver_domain = save;
if (rc == OK)
static int
set_address_from_dns(host_item *host, host_item **lastptr,
- uschar *ignore_target_hosts, BOOL allow_ip, uschar **fully_qualified_name,
- BOOL dnssec_requested, BOOL dnssec_require)
+ const uschar *ignore_target_hosts, BOOL allow_ip,
+ const uschar **fully_qualified_name,
+ BOOL dnssec_request, BOOL dnssec_require)
{
dns_record *rr;
host_item *thishostlast = NULL; /* Indicates not yet filled in anything */
#if HAVE_IPV6
#ifndef STAND_ALONE
if (disable_ipv6 || (dns_ipv4_lookup != NULL &&
- match_isinlist(host->name, &dns_ipv4_lookup, 0, NULL, NULL, MCL_DOMAIN,
- TRUE, NULL) == OK))
+ match_isinlist(host->name, CUSS &dns_ipv4_lookup, 0, NULL, NULL,
+ MCL_DOMAIN, TRUE, NULL) == OK))
i = 0; /* look up A records only */
else
#endif /* STAND_ALONE */
- #ifdef SUPPORT_A6
- i = 2; /* look up A6 and AAAA and A records */
- #else
i = 1; /* look up AAAA and A records */
- #endif /* SUPPORT_A6 */
/* The IPv4 world */
dns_answer dnsa;
dns_scan dnss;
- int rc = dns_lookup(&dnsa, host->name, type, fully_qualified_name);
- lookup_dnssec_authenticated = !dnssec_requested ? NULL
+ int rc = dns_lookup_timerwrap(&dnsa, host->name, type, fully_qualified_name);
+ lookup_dnssec_authenticated = !dnssec_request ? NULL
: dns_is_secure(&dnsa) ? US"yes" : US"no";
/* We want to return HOST_FIND_AGAIN if one of the A, A6, or AAAA lookups
if (rc != DNS_NOMATCH && rc != DNS_NODATA) v6_find_again = TRUE;
continue;
}
- if (dnssec_require && !dns_is_secure(&dnsa))
+
+ if (dnssec_request)
{
- log_write(L_host_lookup_failed, LOG_MAIN, "dnssec fail on %s for %.256s",
+ if (dns_is_secure(&dnsa))
+ {
+ DEBUG(D_host_lookup) debug_printf("%s A DNSSEC\n", host->name);
+ if (host->dnssec == DS_UNK) /* set in host_find_bydns() */
+ host->dnssec = DS_YES;
+ }
+ else
+ {
+ if (dnssec_require)
+ {
+ log_write(L_host_lookup_failed, LOG_MAIN,
+ "dnssec fail on %s for %.256s",
i>1 ? "A6" : i>0 ? "AAAA" : "A", host->name);
- continue;
+ continue;
+ }
+ if (host->dnssec == DS_YES) /* set in host_find_bydns() */
+ {
+ DEBUG(D_host_lookup) debug_printf("%s A cancel DNSSEC\n", host->name);
+ host->dnssec = DS_NO;
+ lookup_dnssec_authenticated = US"no";
+ }
+ }
}
/* Lookup succeeded: fill in the given host item with the first non-ignored
address found; create additional items for any others. A single A6 record
- may generate more than one address. */
+ may generate more than one address. The lookup had a chance to update the
+ fqdn; we do not want any later times round the loop to do so. */
+
+ fully_qualified_name = NULL;
for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
- rr != NULL;
+ rr;
rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
{
if (rr->type == type)
DEBUG(D_host_lookup)
{
- if (da == NULL)
- debug_printf("no addresses extracted from A6 RR for %s\n",
+ if (!da) debug_printf("no addresses extracted from A6 RR for %s\n",
host->name);
}
/* This loop runs only once for A and AAAA records, but may run
several times for an A6 record that generated multiple addresses. */
- for (; da != NULL; da = da->next)
+ for (; da; da = da->next)
{
#ifndef STAND_ALONE
if (ignore_target_hosts != NULL &&
*/
int
-host_find_bydns(host_item *host, uschar *ignore_target_hosts, int whichrrs,
+host_find_bydns(host_item *host, const uschar *ignore_target_hosts, int whichrrs,
uschar *srv_service, uschar *srv_fail_domains, uschar *mx_fail_domains,
uschar *dnssec_request_domains, uschar *dnssec_require_domains,
- uschar **fully_qualified_name, BOOL *removed)
+ const uschar **fully_qualified_name, BOOL *removed)
{
host_item *h, *last;
dns_record *rr;
int yield;
dns_answer dnsa;
dns_scan dnss;
-BOOL dnssec_require = match_isinlist(host->name, &dnssec_require_domains,
+BOOL dnssec_require = match_isinlist(host->name, CUSS &dnssec_require_domains,
0, NULL, NULL, MCL_DOMAIN, TRUE, NULL) == OK;
BOOL dnssec_request = dnssec_require
- || match_isinlist(host->name, &dnssec_request_domains,
+ || match_isinlist(host->name, CUSS &dnssec_request_domains,
0, NULL, NULL, MCL_DOMAIN, TRUE, NULL) == OK;
dnssec_status_t dnssec;
dnssec = DS_UNK;
lookup_dnssec_authenticated = NULL;
- rc = dns_lookup(&dnsa, buffer, ind_type, &temp_fully_qualified_name);
+ rc = dns_lookup_timerwrap(&dnsa, buffer, ind_type, CUSS &temp_fully_qualified_name);
if (dnssec_request)
{
if (rc == DNS_FAIL || rc == DNS_AGAIN)
{
#ifndef STAND_ALONE
- if (match_isinlist(host->name, &srv_fail_domains, 0, NULL, NULL, MCL_DOMAIN,
- TRUE, NULL) != OK)
+ if (match_isinlist(host->name, CUSS &srv_fail_domains, 0, NULL, NULL,
+ MCL_DOMAIN, TRUE, NULL) != OK)
#endif
{ yield = HOST_FIND_AGAIN; goto out; }
DEBUG(D_host_lookup) debug_printf("DNS_%s treated as DNS_NODATA "
ind_type = T_MX;
dnssec = DS_UNK;
lookup_dnssec_authenticated = NULL;
- rc = dns_lookup(&dnsa, host->name, ind_type, fully_qualified_name);
+ rc = dns_lookup_timerwrap(&dnsa, host->name, ind_type, fully_qualified_name);
if (dnssec_request)
{
if (dns_is_secure(&dnsa))
- { dnssec = DS_YES; lookup_dnssec_authenticated = US"yes"; }
+ {
+ DEBUG(D_host_lookup) debug_printf("%s MX DNSSEC\n", host->name);
+ dnssec = DS_YES; lookup_dnssec_authenticated = US"yes";
+ }
else
- { dnssec = DS_NO; lookup_dnssec_authenticated = US"no"; }
+ {
+ dnssec = DS_NO; lookup_dnssec_authenticated = US"no";
+ }
}
switch (rc)
log_write(L_host_lookup_failed, LOG_MAIN,
"dnssec fail on MX for %.256s", host->name);
rc = DNS_FAIL;
- /*FALLTRHOUGH*/
+ /*FALLTHROUGH*/
case DNS_FAIL:
case DNS_AGAIN:
#ifndef STAND_ALONE
- if (match_isinlist(host->name, &mx_fail_domains, 0, NULL, NULL, MCL_DOMAIN,
- TRUE, NULL) != OK)
+ if (match_isinlist(host->name, CUSS &mx_fail_domains, 0, NULL, NULL,
+ MCL_DOMAIN, TRUE, NULL) != OK)
#endif
{ yield = HOST_FIND_AGAIN; goto out; }
DEBUG(D_host_lookup) debug_printf("DNS_%s treated as DNS_NODATA "
last = host; /* End of local chainlet */
host->mx = MX_NONE;
host->port = PORT_NONE;
- dnssec = DS_UNK;
+ host->dnssec = DS_UNK;
lookup_dnssec_authenticated = NULL;
rc = set_address_from_dns(host, &last, ignore_target_hosts, FALSE,
fully_qualified_name, dnssec_request, dnssec_require);
- if (dnssec_request)
- {
- if (dns_is_secure(&dnsa))
- { dnssec = DS_YES; lookup_dnssec_authenticated = US"yes"; }
- else
- { dnssec = DS_NO; lookup_dnssec_authenticated = US"no"; }
- }
-
/* If one or more address records have been found, check that none of them
are local. Since we know the host items all have their IP addresses
inserted, host_scan_for_local_hosts() can only return HOST_FOUND or
yield);
for (h = host; h != last->next; h = h->next)
{
- debug_printf(" %s %s MX=%d ", h->name,
- (h->address == NULL)? US"<null>" : h->address, h->mx);
+ debug_printf(" %s %s MX=%d %s", h->name,
+ !h->address ? US"<null>" : h->address, h->mx,
+ h->dnssec == DS_YES ? US"DNSSEC " : US"");
if (h->port != PORT_NONE) debug_printf("port=%d ", h->port);
if (h->status >= hstatus_unusable) debug_printf("*");
debug_printf("\n");
return yield;
}
-
-
-
/*************************************************
**************************************************
* Stand-alone test program *