* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2012 */
+/* Copyright (c) University of Cambridge 1995 - 2016 */
/* See the file NOTICE for conditions of use and distribution. */
/* Functions for finding hosts, either by gethostbyname(), gethostbyaddr(), or
*************************************************/
/* 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
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)
}
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 **));
+ alist = store_get((count + 1) * sizeof(char *));
adds = store_get(count *alen);
yield->h_name = CS name;
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++)
domain. Sigh. */
address = string_sprintf("[%s]:%d", sender_host_address, sender_host_port);
-if ((log_extra_selector & LX_incoming_port) == 0 || sender_host_port <= 0)
+if (!LOGGING(incoming_port) || sender_host_port <= 0)
*(Ustrrchr(address, ':')) = 0;
/* If there's no EHLO/HELO data, we can't show it. */
sender_fullhost = (sender_helo_name == NULL)? address :
string_sprintf("(%s) %s", sender_helo_name, address);
- sender_rcvhost = string_cat(NULL, &size, &ptr, address, adlen);
+ sender_rcvhost = string_catn(NULL, &size, &ptr, address, adlen);
if (sender_ident != NULL || show_helo || portptr != NULL)
{
int firstptr;
- sender_rcvhost = string_cat(sender_rcvhost, &size, &ptr, US" (", 2);
+ sender_rcvhost = string_catn(sender_rcvhost, &size, &ptr, US" (", 2);
firstptr = ptr;
if (portptr != NULL)
sender_rcvhost = string_append(sender_rcvhost, &size, &ptr, 2,
(firstptr == ptr)? US"ident=" : US" ident=", sender_ident);
- sender_rcvhost = string_cat(sender_rcvhost, &size, &ptr, US")", 1);
+ sender_rcvhost = string_catn(sender_rcvhost, &size, &ptr, US")", 1);
}
sender_rcvhost[ptr] = 0; /* string_cat() always leaves room */
{
uschar *flag = useflag? US"H=" : US"";
uschar *iface = US"";
- if ((log_extra_selector & LX_incoming_interface) != 0 &&
- interface_address != NULL)
+ if (LOGGING(incoming_interface) && interface_address != NULL)
iface = string_sprintf(" I=[%s]:%d", interface_address, interface_port);
if (sender_ident == NULL)
(void)string_format(big_buffer, big_buffer_size, "%s%s%s",
/* Handle IPv4 address */
(void)sscanf(CS address, "%d.%d.%d.%d", x, x+1, x+2, x+3);
-bin[v4offset] = (x[0] << 24) + (x[1] << 16) + (x[2] << 8) + x[3];
+bin[v4offset] = ((uint)x[0] << 24) + (x[1] << 16) + (x[2] << 8) + x[3];
return v4offset+1;
}
if (mask == 0) wordmask = 0;
else if (mask < 32)
{
- wordmask = (-1) << (32 - mask);
+ wordmask = (uint)(-1) << (32 - mask);
mask = 0;
}
else
{
j = binary[0];
for (i = 24; i >= 0; i -= 8)
- {
- sprintf(CS tt, "%d.", (j >> i) & 255);
- while (*tt) tt++;
- }
+ tt += sprintf(CS tt, "%d.", (j >> i) & 255);
}
else
- {
for (i = 0; i < 4; i++)
{
j = binary[i];
- sprintf(CS tt, "%04x%c%04x%c", (j >> 16) & 0xffff, sep, j & 0xffff, sep);
- while (*tt) tt++;
+ tt += sprintf(CS tt, "%04x%c%04x%c", (j >> 16) & 0xffff, sep, j & 0xffff, sep);
}
- }
tt--; /* lose final separator */
}
+/* Like host_nmtoa() but: ipv6-only, canonical output, no mask
+
+Arguments:
+ binary points to the ints
+ buffer big enough to hold the result
+
+Returns: the number of characters placed in buffer, not counting
+ the final nul.
+*/
+
+int
+ipv6_nmtoa(int * binary, uschar * buffer)
+{
+int i, j, k;
+uschar * c = buffer;
+uschar * d = NULL; /* shut insufficiently "clever" compiler up */
+
+for (i = 0; i < 4; i++)
+ { /* expand to text */
+ j = binary[i];
+ c += sprintf(CS c, "%x:%x:", (j >> 16) & 0xffff, j & 0xffff);
+ }
+
+for (c = buffer, k = -1, i = 0; i < 8; i++)
+ { /* find longest 0-group sequence */
+ if (*c == '0') /* must be "0:" */
+ {
+ uschar * s = c;
+ j = i;
+ while (c[2] == '0') i++, c += 2;
+ if (i-j > k)
+ {
+ k = i-j; /* length of sequence */
+ d = s; /* start of sequence */
+ }
+ }
+ while (*++c != ':') ;
+ c++;
+ }
+
+c[-1] = '\0'; /* drop trailing colon */
+
+/* debug_printf("%s: D k %d <%s> <%s>\n", __FUNCTION__, k, d, d + 2*(k+1)); */
+if (k >= 0)
+ { /* collapse */
+ c = d + 2*(k+1);
+ if (d == buffer) c--; /* need extra colon */
+ *d++ = ':'; /* 1st 0 */
+ while ((*d++ = *c++)) ;
+ }
+else
+ d = c;
+
+return d - buffer;
+}
+
+
/*************************************************
* Check port for tls_on_connect *
if (mlen == 0) mask = 0;
else if (mlen < 32)
{
- mask = (-1) << (32 - mlen);
+ mask = (uint)(-1) << (32 - mlen);
mlen = 0;
}
else
uschar *s, *t;
struct hostent *hosts;
struct in_addr addr;
-unsigned long time_msec;
+unsigned long time_msec = 0; /* init to quieten dumb static analysis */
if (slow_lookup_log) time_msec = get_time_in_ms();
/* Do lookups directly in the DNS or via gethostbyaddr() (or equivalent), in
the order specified by the host_lookup_order option. */
-while ((ordername = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))
- != NULL)
+while ((ordername = string_nextinlist(&list, &sep, buffer, sizeof(buffer))))
{
if (strcmpic(ordername, US"bydns") == 0)
{
int count = 0;
int old_pool = store_pool;
- /* Ideally we'd check DNSSEC both forward and reverse, but we use the
- gethost* routines for forward, so can't do that unless/until we rewrite. */
sender_host_dnssec = dns_is_secure(&dnsa);
DEBUG(D_dns)
debug_printf("Reverse DNS security status: %s\n",
store_pool = POOL_PERM; /* Save names in permanent storage */
for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
- rr != NULL;
+ rr;
rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
- {
- if (rr->type == T_PTR) count++;
- }
+ if (rr->type == T_PTR)
+ count++;
/* Get store for the list of aliases. For compatibility with
gethostbyaddr, we make an empty list if there are none. */
/* Re-scan and extract the names */
for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
- rr != NULL;
+ rr;
rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
{
uschar *s = NULL;
"empty name: treated as non-existent host name\n");
continue;
}
- if (sender_host_name == NULL) sender_host_name = s;
- else *aptr++ = s;
+ if (!sender_host_name) sender_host_name = s;
+ else *aptr++ = s;
while (*s != 0) { *s = tolower(*s); s++; }
}
int rc;
BOOL ok = FALSE;
host_item h;
+ dnssec_domains d;
+
h.next = NULL;
h.name = hname;
h.mx = MX_NONE;
h.address = NULL;
+ d.request = sender_host_dnssec ? US"*" : NULL;;
+ d.require = NULL;
- /* When called with the last argument FALSE, host_find_byname() won't return
- HOST_FOUND_LOCAL. If the incoming address is an IPv4 address expressed in
- IPv6 format, we must compare the IPv4 part to any IPv4 addresses. */
-
- if ((rc = host_find_byname(&h, NULL, 0, NULL, FALSE)) == HOST_FOUND)
+ if ( (rc = host_find_bydns(&h, NULL, HOST_FIND_BY_A,
+ NULL, NULL, NULL, &d, NULL, NULL)) == HOST_FOUND
+ || rc == HOST_FOUND_LOCAL
+ )
{
host_item *hh;
HDEBUG(D_host_lookup) debug_printf("checking addresses for %s\n", hname);
+
+ /* If the forward lookup was not secure we cancel the is-secure variable */
+
+ DEBUG(D_dns) debug_printf("Forward DNS security status: %s\n",
+ h.dnssec == DS_YES ? "DNSSEC verified (AD)" : "unverified");
+ if (h.dnssec != DS_YES) sender_host_dnssec = FALSE;
+
for (hh = &h; hh != NULL; hh = hh->next)
- {
if (host_is_in_net(hh->address, sender_host_address, 0))
{
HDEBUG(D_host_lookup) debug_printf(" %s OK\n", hh->address);
break;
}
else
- {
HDEBUG(D_host_lookup) debug_printf(" %s\n", hh->address);
- }
- }
+
if (!ok) HDEBUG(D_host_lookup)
debug_printf("no IP address for %s matched %s\n", hname,
sender_host_address);
return DEFER;
}
else
- {
HDEBUG(D_host_lookup) debug_printf("no IP addresses found for %s\n", hname);
- }
/* If this name is no good, and it's the sender name, set it null pro tem;
if it's an alias, just remove it from the list. */
int af;
#endif
-/* If we are in the test harness, a name ending in .test.again.dns always
-forces a temporary error response, unless the name is in
-dns_again_means_nonexist. */
-
-if (running_in_test_harness)
- {
- const uschar *endname = host->name + Ustrlen(host->name);
- if (Ustrcmp(endname - 14, "test.again.dns") == 0) goto RETURN_AGAIN;
- }
-
/* Make sure DNS options are set as required. This appears to be necessary in
some circumstances when the get..byname() function actually calls the DNS. */
dns_init((flags & HOST_FIND_QUALIFY_SINGLE) != 0,
(flags & HOST_FIND_SEARCH_PARENTS) != 0,
- FALSE); /*XXX dnssec? */
+ FALSE); /* Cannot retrieve dnssec status so do not request */
/* In an IPv6 world, unless IPv6 has been disabled, we need to scan for both
kinds of address, so go round the loop twice. Note that we have ensured that
BOOL ipv4_addr;
int error_num = 0;
struct hostent *hostdata;
- unsigned long time_msec;
+ unsigned long time_msec = 0; /* compiler quietening */
#ifdef STAND_ALONE
printf("Looking up: %s\n", host->name);
}
#endif /* HAVE_IPV6 */
- if (slow_lookup_log
+ if ( slow_lookup_log
&& (time_msec = get_time_in_ms() - time_msec) > slow_lookup_log)
log_long_lookup(US"name", host->name, time_msec);
fully_qualified_name if not NULL, return fully qualified name here if
the contents are different (i.e. it must be preset
to something)
- dnnssec_require if TRUE check the DNS result AD bit
+ dnssec_request if TRUE request the AD bit
+ dnssec_require if TRUE require the AD bit
Returns: HOST_FIND_FAILED couldn't find A record
HOST_FIND_AGAIN try again later
return HOST_FOUND;
}
-/* On an IPv6 system, unless IPv6 is disabled, go round the loop up to three
-times, looking for A6 and AAAA records the first two times. However, unless
+/* On an IPv6 system, unless IPv6 is disabled, go round the loop up to twice,
+looking for AAAA records the first time. However, unless
doing standalone testing, we force an IPv4 lookup if the domain matches
-dns_ipv4_lookup is set. Since A6 records look like being abandoned, support
-them only if explicitly configured to do so. On an IPv4 system, go round the
+dns_ipv4_lookup is set. On an IPv4 system, go round the
loop once only, looking only for A records. */
#if HAVE_IPV6
for (; i >= 0; i--)
{
- static int types[] = { T_A, T_AAAA, T_A6 };
+ static int types[] = { T_A, T_AAAA };
int type = types[i];
int randoffset = (i == 0)? 500 : 0; /* Ensures v6 sorts before v4 */
dns_answer dnsa;
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
+ DEBUG(D_dns)
+ if ( (dnssec_request || dnssec_require)
+ && !dns_is_secure(&dnsa)
+ && dns_is_aa(&dnsa)
+ )
+ debug_printf("DNS lookup of %.256s (A/AAAA) requested AD, but got AA\n", host->name);
+
+ /* We want to return HOST_FIND_AGAIN if one of the A or AAAA lookups
fails or times out, but not if another one succeeds. (In the early
IPv6 days there are name servers that always fail on AAAA, but are happy
to give out an A record. We want to proceed with that A record.) */
{
if (i == 0) /* Just tried for an A record, i.e. end of loop */
{
- if (host->address != NULL) return HOST_FOUND; /* A6 or AAAA was found */
+ if (host->address != NULL) return HOST_FOUND; /* AAAA was found */
if (rc == DNS_AGAIN || rc == DNS_FAIL || v6_find_again)
return HOST_FIND_AGAIN;
return HOST_FIND_FAILED; /* DNS_NOMATCH or DNS_NODATA */
}
- /* Tried for an A6 or AAAA record: remember if this was a temporary
+ /* Tried for an AAAA record: remember if this was a temporary
error, and look for the next record type. */
if (rc != DNS_NOMATCH && rc != DNS_NODATA) v6_find_again = TRUE;
{
log_write(L_host_lookup_failed, LOG_MAIN,
"dnssec fail on %s for %.256s",
- i>1 ? "A6" : i>0 ? "AAAA" : "A", host->name);
+ i>0 ? "AAAA" : "A", host->name);
continue;
}
if (host->dnssec == DS_YES) /* set in host_find_bydns() */
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)
{
- /* dns_address *da = dns_address_from_rr(&dnsa, rr); */
-
- dns_address *da;
- da = dns_address_from_rr(&dnsa, rr);
+ dns_address *da = dns_address_from_rr(&dnsa, rr);
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 &&
}
}
-/* Control gets here only if the third lookup (the A record) succeeded.
+/* Control gets here only if the econdookup (the A record) succeeded.
However, the address may not be filled in if it was ignored. */
-return (host->address == NULL)? HOST_IGNORED : HOST_FOUND;
+return host->address ? HOST_FOUND : HOST_IGNORED;
}
srv_service when SRV used, the service name
srv_fail_domains DNS errors for these domains => assume nonexist
mx_fail_domains DNS errors for these domains => assume nonexist
- dnssec_request_domains => make dnssec request
- dnssec_require_domains => ditto and nonexist failures
+ dnssec_d.request => make dnssec request: domainlist
+ dnssec_d.require => ditto and nonexist failures
fully_qualified_name if not NULL, return fully-qualified name
removed set TRUE if local host was removed from the list
int
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,
+ const dnssec_domains *dnssec_d,
const uschar **fully_qualified_name, BOOL *removed)
{
host_item *h, *last;
int yield;
dns_answer dnsa;
dns_scan dnss;
-BOOL dnssec_require = match_isinlist(host->name, CUSS &dnssec_require_domains,
+BOOL dnssec_require = dnssec_d
+ && match_isinlist(host->name, CUSS &dnssec_d->require,
0, NULL, NULL, MCL_DOMAIN, TRUE, NULL) == OK;
BOOL dnssec_request = dnssec_require
- || match_isinlist(host->name, CUSS &dnssec_request_domains,
- 0, NULL, NULL, MCL_DOMAIN, TRUE, NULL) == OK;
+ || ( dnssec_d
+ && match_isinlist(host->name, CUSS &dnssec_d->request,
+ 0, NULL, NULL, MCL_DOMAIN, TRUE, NULL) == OK);
dnssec_status_t dnssec;
/* Set the default fully qualified name to the incoming name, initialize the
if (fully_qualified_name != NULL) *fully_qualified_name = host->name;
dns_init((whichrrs & HOST_FIND_QUALIFY_SINGLE) != 0,
(whichrrs & HOST_FIND_SEARCH_PARENTS) != 0,
- dnssec_request
- );
+ dnssec_request);
host_find_failed_syntax = FALSE;
/* First, if requested, look for SRV records. The service name is given; we
-assume TCP progocol. DNS domain names are constrained to a maximum of 256
+assume TCP protocol. DNS domain names are constrained to a maximum of 256
characters, so the code below should be safe. */
if ((whichrrs & HOST_FIND_BY_SRV) != 0)
lookup_dnssec_authenticated = NULL;
rc = dns_lookup_timerwrap(&dnsa, buffer, ind_type, CUSS &temp_fully_qualified_name);
+ DEBUG(D_dns)
+ if ((dnssec_request || dnssec_require)
+ & !dns_is_secure(&dnsa)
+ & dns_is_aa(&dnsa))
+ debug_printf("DNS lookup of %.256s (SRV) requested AD, but got AA\n", host->name);
+
if (dnssec_request)
{
if (dns_is_secure(&dnsa))
lookup_dnssec_authenticated = NULL;
rc = dns_lookup_timerwrap(&dnsa, host->name, ind_type, fully_qualified_name);
+ DEBUG(D_dns)
+ if ((dnssec_request || dnssec_require)
+ & !dns_is_secure(&dnsa)
+ & dns_is_aa(&dnsa))
+ debug_printf("DNS lookup of %.256s (MX) requested AD, but got AA\n", host->name);
+
if (dnssec_request)
{
if (dns_is_secure(&dnsa))
- {
+ {
DEBUG(D_host_lookup) debug_printf("%s MX DNSSEC\n", host->name);
dnssec = DS_YES; lookup_dnssec_authenticated = US"yes";
}
NEXT_MX_RR: continue;
}
+if (!last) /* No rr of correct type; give up */
+ {
+ yield = HOST_FIND_FAILED;
+ goto out;
+ }
+
/* If the list of hosts was obtained from SRV records, there are two things to
do. First, if there is only one host, and it's name is ".", it means there is
no SMTP service at this domain. Otherwise, we have to sort the hosts of equal
debug_printf(" %s P=%d W=%d\n", h->name, h->mx, h->sort_key % 1000);
}
- for (pptr = &host, h = host; h != last; pptr = &(h->next), h = h->next)
+ for (pptr = &host, h = host; h != last; pptr = &h->next, h = h->next)
{
int sum = 0;
host_item *hh;
for (h = host; h != last->next; h = h->next)
{
- if (h->address != NULL) continue; /* Inserted by a multihomed host */
+ if (h->address) continue; /* Inserted by a multihomed host */
+
rc = set_address_from_dns(h, &last, ignore_target_hosts, allow_mx_to_ip,
NULL, dnssec_request, dnssec_require);
if (rc != HOST_FOUND)
h->why = hwhy_deferred;
}
else
- h->why = (rc == HOST_IGNORED)? hwhy_ignored : hwhy_failed;
+ h->why = rc == HOST_IGNORED ? hwhy_ignored : hwhy_failed;
}
}
else
{
int flags = whichrrs;
+ dnssec_domains d;
h.name = buffer;
h.next = NULL;
if (qualify_single) flags |= HOST_FIND_QUALIFY_SINGLE;
if (search_parents) flags |= HOST_FIND_SEARCH_PARENTS;
+ d.request = request_dnssec ? &h.name : NULL;
+ d.require = require_dnssec ? &h.name : NULL;
+
rc = byname
? host_find_byname(&h, NULL, flags, &fully_qualified_name, TRUE)
: host_find_bydns(&h, NULL, flags, US"smtp", NULL, NULL,
- request_dnssec ? &h.name : NULL,
- require_dnssec ? &h.name : NULL,
- &fully_qualified_name, NULL);
+ &d, &fully_qualified_name, NULL);
if (rc == HOST_FIND_FAILED) printf("Failed\n");
else if (rc == HOST_FIND_AGAIN) printf("Again\n");