*************************************************/
/* Copyright (c) University of Cambridge 1995 - 2018 */
+/* Copyright (c) The Exim Maintainers 2020 */
/* See the file NOTICE for conditions of use and distribution. */
/* Functions for finding hosts, either by gethostbyname(), gethostbyaddr(), or
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);
+ log_long_lookup(dns_text_type(type), name, time_msec);
return retval;
}
uschar *adds;
uschar **alist;
struct hostent *yield;
-dns_answer dnsa;
+dns_answer * dnsa = store_get_dns_answer();
dns_scan dnss;
DEBUG(D_host_lookup)
debug_printf("using host_fake_gethostbyname for %s (%s)\n", name,
- (af == AF_INET)? "IPv4" : "IPv6");
+ af == AF_INET ? "IPv4" : "IPv6");
/* Handle unqualified "localhost" */
if (Ustrcmp(name, "localhost") == 0)
- lname = (af == AF_INET)? US"127.0.0.1" : US"::1";
+ lname = af == AF_INET ? US"127.0.0.1" : US"::1";
/* Handle a literal IP address */
if ((ipa = string_is_ip_address(lname, NULL)) != 0)
- {
- if ((ipa == 4 && af == AF_INET) ||
- (ipa == 6 && af == AF_INET6))
+ if ( ipa == 4 && af == AF_INET
+ || ipa == 6 && af == AF_INET6)
{
int x[4];
- yield = store_get(sizeof(struct hostent));
- alist = store_get(2 * sizeof(char *));
- adds = store_get(alen);
+ yield = store_get(sizeof(struct hostent), FALSE);
+ alist = store_get(2 * sizeof(char *), FALSE);
+ adds = store_get(alen, FALSE);
yield->h_name = CS name;
yield->h_aliases = NULL;
yield->h_addrtype = af;
*error_num = HOST_NOT_FOUND;
return NULL;
}
- }
/* Handle a host name */
else
{
- int type = (af == AF_INET)? T_A:T_AAAA;
- int rc = dns_lookup_timerwrap(&dnsa, lname, type, NULL);
+ int type = af == AF_INET ? T_A:T_AAAA;
+ int rc = dns_lookup_timerwrap(dnsa, lname, type, NULL);
int count = 0;
lookup_dnssec_authenticated = NULL;
case DNS_FAIL: *error_num = NO_RECOVERY; return NULL;
}
- for (dns_record * rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
+ for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
rr;
- rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) if (rr->type == type)
+ rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) if (rr->type == type)
count++;
- yield = store_get(sizeof(struct hostent));
- alist = store_get((count + 1) * sizeof(char *));
- adds = store_get(count *alen);
+ yield = store_get(sizeof(struct hostent), FALSE);
+ alist = store_get((count + 1) * sizeof(char *), FALSE);
+ adds = store_get(count *alen, FALSE);
yield->h_name = CS name;
yield->h_aliases = NULL;
yield->h_length = alen;
yield->h_addr_list = CSS alist;
- for (dns_record * rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
+ for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
rr;
- rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) if (rr->type == type)
+ rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) if (rr->type == type)
{
int x[4];
dns_address *da;
- if (!(da = dns_address_from_rr(&dnsa, rr))) break;
+ if (!(da = dns_address_from_rr(dnsa, rr))) break;
*alist++ = adds;
for (int n = host_aton(da->address, x), i = 0; i < n; i++)
{
continue;
}
- h = store_get(sizeof(host_item));
+ h = store_get(sizeof(host_item), FALSE);
h->name = name;
h->address = NULL;
h->port = PORT_NONE;
host_build_sender_fullhost(void)
{
BOOL show_helo = TRUE;
-uschar * address, * fullhost, * rcvhost, * reset_point;
+uschar * address, * fullhost, * rcvhost;
+rmark reset_point;
int len;
if (!sender_host_address) return;
-reset_point = store_get(0);
+reset_point = store_mark();
/* Set up address, with or without the port. After discussion, it seems that
the only format that doesn't cause trouble is [aaaa]:pppp. However, we can't
}
}
-if (sender_fullhost) store_free(sender_fullhost);
-sender_fullhost = string_copy_malloc(fullhost);
-if (sender_rcvhost) store_free(sender_rcvhost);
-sender_rcvhost = string_copy_malloc(rcvhost);
+sender_fullhost = string_copy_perm(fullhost, TRUE);
+sender_rcvhost = string_copy_perm(rcvhost, TRUE);
store_reset(reset_point);
ident set, no host => U=ident
ident set, host set => H=sender_fullhost U=ident
+Use taint-unchecked routines on the assumption we'll never expand the results.
+
Arguments:
useflag TRUE if first item to be flagged (H= or U=); if there are two
items, the second is always flagged
host_and_ident(BOOL useflag)
{
if (!sender_fullhost)
- (void)string_format(big_buffer, big_buffer_size, "%s%s", useflag ? "U=" : "",
+ string_format_nt(big_buffer, big_buffer_size, "%s%s", useflag ? "U=" : "",
sender_ident ? sender_ident : US"unknown");
else
{
if (LOGGING(incoming_interface) && interface_address)
iface = string_sprintf(" I=[%s]:%d", interface_address, interface_port);
if (sender_ident)
- (void)string_format(big_buffer, big_buffer_size, "%s%s%s U=%s",
+ string_format_nt(big_buffer, big_buffer_size, "%s%s%s U=%s",
flag, sender_fullhost, iface, sender_ident);
else
- (void)string_format(big_buffer, big_buffer_size, "%s%s%s",
+ string_format_nt(big_buffer, big_buffer_size, "%s%s%s",
flag, sender_fullhost, iface);
}
return big_buffer;
int sep = 0;
uschar *s;
ip_address_item * yield = NULL, * last = NULL, * next;
+BOOL taint = is_tainted(list);
while ((s = string_nextinlist(&list, &sep, NULL, 0)))
{
address above. The field in the ip_address_item is large enough to hold an
IPv6 address. */
- next = store_get(sizeof(ip_address_item));
+ next = store_get(sizeof(ip_address_item), taint);
next->next = NULL;
Ustrcpy(next->address, s);
next->port = port;
next->v6_include_v4 = FALSE;
+ next->log = NULL;
if (!yield)
yield = last = next;
ip_address_item *ipa2;
for (ipa2 = list; ipa2; ipa2 = ipa2->next)
if (Ustrcmp(ipa2->address, ipa->address) == 0) return list;
-ipa2 = store_get_perm(sizeof(ip_address_item));
+ipa2 = store_get_perm(sizeof(ip_address_item), FALSE);
*ipa2 = *ipa;
ipa2->next = list;
return ipa2;
if (local_interface_data == NULL)
{
- void *reset_item = store_get(0);
+ void *reset_item = store_mark();
ip_address_item *dlist = host_build_ifacelist(CUS local_interfaces,
US"local_interfaces");
ip_address_item *xlist = host_build_ifacelist(CUS extra_local_interfaces,
/* If there is no buffer, put the string into some new store. */
-if (buffer == NULL) return string_copy(yield);
+if (!buffer) buffer = store_get(46, FALSE);
/* Callers of this function with a non-NULL buffer must ensure that it is
large enough to hold an IPv6 address, namely, at least 46 bytes. That's what
-makes this use of strcpy() OK. */
+makes this use of strcpy() OK.
+If the library returned apparently an apparently tainted string, clean it;
+we trust IP addresses. */
-Ustrcpy(buffer, yield);
+string_format_nt(buffer, 46, "%s", yield);
return buffer;
}
static int
host_name_lookup_byaddr(void)
{
-int len;
-uschar *s, *t;
-struct hostent *hosts;
+struct hostent * hosts;
struct in_addr addr;
unsigned long time_msec = 0; /* init to quieten dumb static analysis */
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);
+ log_long_lookup(US"gethostbyaddr", sender_host_address, time_msec);
/* Failed to look up the host. */
-if (hosts == NULL)
+if (!hosts)
{
HDEBUG(D_host_lookup) debug_printf("IP address lookup failed: h_errno=%d\n",
h_errno);
treat this as non-existent. In some operating systems, this is returned as an
empty string; in others as a single dot. */
-if (hosts->h_name == NULL || hosts->h_name[0] == 0 || hosts->h_name[0] == '.')
+if (!hosts->h_name || !hosts->h_name[0] || hosts->h_name[0] == '.')
{
HDEBUG(D_host_lookup) debug_printf("IP address lookup yielded an empty name: "
"treated as non-existent host name\n");
/* Copy and lowercase the name, which is in static storage in many systems.
Put it in permanent memory. */
-s = US hosts->h_name;
-len = Ustrlen(s) + 1;
-t = sender_host_name = store_get_perm(len);
-while (*s != 0) *t++ = tolower(*s++);
-*t = 0;
+ {
+ int old_pool = store_pool;
+ store_pool = POOL_TAINT_PERM; /* names are tainted */
-/* If the host has aliases, build a copy of the alias list */
+ sender_host_name = string_copylc(US hosts->h_name);
-if (hosts->h_aliases)
- {
- int count = 1;
- uschar **ptr;
- for (uschar ** aliases = USS hosts->h_aliases; *aliases; aliases++) count++;
- ptr = sender_host_aliases = store_get_perm(count * sizeof(uschar *));
- for (uschar ** aliases = USS hosts->h_aliases; *aliases; aliases++)
- {
- uschar *s = *aliases;
- int len = Ustrlen(s) + 1;
- uschar *t = *ptr++ = store_get_perm(len);
- while (*s != 0) *t++ = tolower(*s++);
- *t = 0;
- }
- *ptr = NULL;
+ /* If the host has aliases, build a copy of the alias list */
+
+ if (hosts->h_aliases)
+ {
+ int count = 1;
+ uschar **ptr;
+
+ for (uschar ** aliases = USS hosts->h_aliases; *aliases; aliases++) count++;
+ store_pool = POOL_PERM;
+ ptr = sender_host_aliases = store_get(count * sizeof(uschar *), FALSE);
+ store_pool = POOL_TAINT_PERM;
+
+ for (uschar ** aliases = USS hosts->h_aliases; *aliases; aliases++)
+ *ptr++ = string_copylc(*aliases);
+ *ptr = NULL;
+ }
+ store_pool = old_pool;
}
return OK;
int sep = 0;
uschar *save_hostname;
uschar **aliases;
-uschar buffer[256];
uschar *ordername;
const uschar *list = host_lookup_order;
-dns_answer dnsa;
+dns_answer * dnsa = store_get_dns_answer();
dns_scan dnss;
sender_host_dnssec = host_lookup_deferred = host_lookup_failed = FALSE;
/* 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))))
+while ((ordername = string_nextinlist(&list, &sep, NULL, 0)))
{
if (strcmpic(ordername, US"bydns") == 0)
{
+ uschar * name = dns_build_reverse(sender_host_address);
+
dns_init(FALSE, FALSE, FALSE); /* dnssec ctrl by dns_dnssec_ok glbl */
- dns_build_reverse(sender_host_address, buffer);
- rc = dns_lookup_timerwrap(&dnsa, buffer, T_PTR, NULL);
+ rc = dns_lookup_timerwrap(dnsa, name, 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
int count = 0;
int old_pool = store_pool;
- sender_host_dnssec = dns_is_secure(&dnsa);
+ sender_host_dnssec = dns_is_secure(dnsa);
DEBUG(D_dns)
debug_printf("Reverse DNS security status: %s\n",
sender_host_dnssec ? "DNSSEC verified (AD)" : "unverified");
store_pool = POOL_PERM; /* Save names in permanent storage */
- for (dns_record * rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
+ for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
rr;
- rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) if (rr->type == T_PTR)
+ rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) 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. */
- aptr = sender_host_aliases = store_get(count * sizeof(uschar *));
+ aptr = sender_host_aliases = store_get(count * sizeof(uschar *), FALSE);
/* Re-scan and extract the names */
- for (dns_record * rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
+ for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
rr;
- rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) if (rr->type == T_PTR)
+ rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) if (rr->type == T_PTR)
{
- uschar * s = store_get(ssize);
+ uschar * s = store_get(ssize, TRUE); /* names are tainted */
/* If an overlong response was received, the data will have been
truncated and dn_expand may fail. */
- if (dn_expand(dnsa.answer, dnsa.answer + dnsa.answerlen,
+ if (dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen,
US (rr->data), (DN_EXPAND_ARG4_TYPE)(s), ssize) < 0)
{
log_write(0, LOG_MAIN, "host name alias list truncated for %s",
break;
}
- store_reset(s + Ustrlen(s) + 1);
- if (s[0] == 0)
+ store_release_above(s + Ustrlen(s) + 1);
+ if (!s[0])
{
HDEBUG(D_host_lookup) debug_printf("IP address lookup yielded an "
"empty name: treated as non-existent host name\n");
}
if (!sender_host_name) sender_host_name = s;
else *aptr++ = s;
- while (*s != 0) { *s = tolower(*s); s++; }
+ while (*s) { *s = tolower(*s); s++; }
}
*aptr = NULL; /* End of alias list */
store_pool = old_pool; /* Reset store pool */
- /* If we've found a names, break out of the "order" loop */
+ /* If we've found a name, break out of the "order" loop */
- if (sender_host_name != NULL) break;
+ if (sender_host_name) break;
}
/* If the DNS lookup deferred, we must also defer. */
int af;
#endif
+#ifndef DISABLE_TLS
+/* Copy the host name at this point to the value which is used for
+TLS certificate name checking, before anything modifies it. */
+
+host->certname = host->name;
+#endif
+
/* 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. */
if ( slow_lookup_log
&& (time_msec = get_time_in_ms() - time_msec) > slow_lookup_log)
- log_long_lookup(US"name", host->name, time_msec);
+ log_long_lookup(US"gethostbyname", host->name, time_msec);
- if (hostdata == NULL)
+ if (!hostdata)
{
uschar *error;
switch (error_num)
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
+ #if NO_DATA != NO_ADDRESS
case NO_ADDRESS: error = US"NO_ADDRESS"; break;
- #endif
+ #endif
default: error = US"?"; break;
}
DEBUG(D_host_lookup) debug_printf("%s returned %d (%s)\n",
+ f.running_in_test_harness ? "host_fake_gethostbyname" :
#if HAVE_IPV6
#if HAVE_GETIPNODEBYNAME
- (af == AF_INET6)? "getipnodebyname(af=inet6)" : "getipnodebyname(af=inet)",
+ af == AF_INET6 ? "getipnodebyname(af=inet6)" : "getipnodebyname(af=inet)",
#else
- (af == AF_INET6)? "gethostbyname2(af=inet6)" : "gethostbyname2(af=inet)",
+ af == AF_INET6 ? "gethostbyname2(af=inet6)" : "gethostbyname2(af=inet)",
#endif
#else
"gethostbyname",
else
{
- host_item *next = store_get(sizeof(host_item));
+ host_item *next = store_get(sizeof(host_item), FALSE);
next->name = host->name;
+#ifndef DISABLE_TLS
+ next->certname = host->certname;
+#endif
next->mx = host->mx;
next->address = text_address;
next->port = PORT_NONE;
NULL. If temp_error is set, at least one of the lookups gave a temporary error,
so we pass that back. */
-if (host->address == NULL)
+if (!host->address)
{
uschar *msg =
#ifndef STAND_ALONE
- (message_id[0] == 0 && smtp_in != NULL)?
- string_sprintf("no IP address found for host %s (during %s)", host->name,
+ message_id[0] == 0 && smtp_in
+ ? string_sprintf("no IP address found for host %s (during %s)", host->name,
smtp_get_connection_info()) :
#endif
string_sprintf("no IP address found for host %s", host->name);
BOOL dnssec_fail = FALSE;
int i;
+#ifndef DISABLE_TLS
+/* Copy the host name at this point to the value which is used for
+TLS certificate name checking, before any CNAME-following modifies it. */
+
+host->certname = host->name;
+#endif
+
/* If allow_ip is set, a name which is an IP address returns that value
as its address. This is used for MX records when allow_mx_to_ip is set, for
those sites that feel they have to flaunt the RFC rules. */
int type = types[i];
int randoffset = i == (whichrrs & HOST_FIND_IPV4_FIRST ? 1 : 0)
? 500 : 0; /* Ensures v6/4 sort order */
- dns_answer dnsa;
+ dns_answer * dnsa = store_get_dns_answer();
dns_scan dnss;
- int rc = dns_lookup_timerwrap(&dnsa, host->name, type, fully_qualified_name);
+ 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";
+ : dns_is_secure(dnsa) ? US"yes" : US"no";
DEBUG(D_dns)
if ( (dnssec_request || dnssec_require)
- && !dns_is_secure(&dnsa)
- && dns_is_aa(&dnsa)
+ && !dns_is_secure(dnsa)
+ && dns_is_aa(dnsa)
)
debug_printf("DNS lookup of %.256s (A/AAAA) requested AD, but got AA\n", host->name);
if (dnssec_request)
{
- if (dns_is_secure(&dnsa))
+ 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() */
fully_qualified_name = NULL;
- for (dns_record * rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
+ for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
rr;
- rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) if (rr->type == type)
+ rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) if (rr->type == type)
{
- dns_address * da = dns_address_from_rr(&dnsa, rr);
+ dns_address * da = dns_address_from_rr(dnsa, rr);
DEBUG(D_host_lookup)
if (!da) debug_printf("no addresses extracted from A6 RR for %s\n",
/* Not a duplicate */
new_sort_key = host->mx * 1000 + random_number(500) + randoffset;
- next = store_get(sizeof(host_item));
+ next = store_get(sizeof(host_item), FALSE);
/* New address goes first: insert the new block after the first one
(so as not to disturb the original pointer) but put the new address
int rc = DNS_FAIL;
int ind_type = 0;
int yield;
-dns_answer dnsa;
+dns_answer * dnsa = store_get_dns_answer();
dns_scan dnss;
BOOL dnssec_require = dnssec_d
&& match_isinlist(host->name, CUSS &dnssec_d->require,
dnssec = DS_UNK;
lookup_dnssec_authenticated = NULL;
- rc = dns_lookup_timerwrap(&dnsa, temp_fully_qualified_name, ind_type,
+ rc = dns_lookup_timerwrap(dnsa, temp_fully_qualified_name, ind_type,
CUSS &temp_fully_qualified_name);
DEBUG(D_dns)
if ((dnssec_request || dnssec_require)
- && !dns_is_secure(&dnsa)
- && dns_is_aa(&dnsa))
+ && !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))
+ if (dns_is_secure(dnsa))
{ dnssec = DS_YES; lookup_dnssec_authenticated = US"yes"; }
else
{ dnssec = DS_NO; lookup_dnssec_authenticated = US"no"; }
/* On DNS failures, we give the "try again" error unless the domain is
listed as one for which we continue. */
- if (rc == DNS_SUCCEED && dnssec_require && !dns_is_secure(&dnsa))
+ if (rc == DNS_SUCCEED && dnssec_require && !dns_is_secure(dnsa))
{
log_write(L_host_lookup_failed, LOG_MAIN,
"dnssec fail on SRV for %.256s", host->name);
ind_type = T_MX;
dnssec = DS_UNK;
lookup_dnssec_authenticated = NULL;
- rc = dns_lookup_timerwrap(&dnsa, host->name, ind_type, fully_qualified_name);
+ 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))
+ && !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))
+ if (dns_is_secure(dnsa))
{
- DEBUG(D_host_lookup) debug_printf("%s MX DNSSEC\n", host->name);
+ DEBUG(D_host_lookup) debug_printf("%s (MX resp) DNSSEC\n", host->name);
dnssec = DS_YES; lookup_dnssec_authenticated = US"yes";
}
else
yield = HOST_FIND_FAILED; goto out;
case DNS_SUCCEED:
- if (!dnssec_require || dns_is_secure(&dnsa))
+ if (!dnssec_require || dns_is_secure(dnsa))
break;
DEBUG(D_host_lookup)
debug_printf("dnssec fail on MX for %.256s", host->name);
last = NULL; /* Indicates that not even the first item is filled yet */
-for (dns_record * rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
+for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
rr;
- rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) if (rr->type == ind_type)
+ rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) if (rr->type == ind_type)
{
int precedence, weight;
int port = PORT_NONE;
/* Get the name of the host pointed to. */
- (void)dn_expand(dnsa.answer, dnsa.answer + dnsa.answerlen, s,
+ (void)dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, s,
(DN_EXPAND_ARG4_TYPE)data, sizeof(data));
/* Check that we haven't already got this host on the chain; if we have,
/* Make a new host item and seek the correct insertion place */
{
int sort_key = precedence * 1000 + weight;
- host_item *next = store_get(sizeof(host_item));
+ host_item *next = store_get(sizeof(host_item), FALSE);
next->name = string_copy_dnsdomain(data);
next->address = NULL;
next->port = port;
return yield;
}
+
+
+
+#ifdef SUPPORT_DANE
+/* Lookup TLSA record for host/port.
+Return: OK success with dnssec; DANE mode
+ DEFER Do not use this host now, may retry later
+ FAIL_FORCED No TLSA record; DANE not usable
+ FAIL Do not use this connection
+*/
+
+int
+tlsa_lookup(const host_item * host, dns_answer * dnsa, BOOL dane_required)
+{
+uschar buffer[300];
+const uschar * fullname = buffer;
+int rc;
+BOOL sec;
+
+/* TLSA lookup string */
+(void)sprintf(CS buffer, "_%d._tcp.%.256s", host->port, host->name);
+
+rc = dns_lookup_timerwrap(dnsa, buffer, T_TLSA, &fullname);
+sec = dns_is_secure(dnsa);
+DEBUG(D_transport)
+ debug_printf("TLSA lookup ret %d %sDNSSEC\n", rc, sec ? "" : "not ");
+
+switch (rc)
+ {
+ case DNS_AGAIN:
+ return DEFER; /* just defer this TLS'd conn */
+
+ case DNS_SUCCEED:
+ if (sec)
+ {
+ DEBUG(D_transport)
+ {
+ dns_scan dnss;
+ for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
+ rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
+ if (rr->type == T_TLSA && rr->size > 3)
+ {
+ uint16_t payload_length = rr->size - 3;
+ uschar s[MAX_TLSA_EXPANDED_SIZE], * sp = s, * p = US rr->data;
+
+ sp += sprintf(CS sp, "%d ", *p++); /* usage */
+ sp += sprintf(CS sp, "%d ", *p++); /* selector */
+ sp += sprintf(CS sp, "%d ", *p++); /* matchtype */
+ while (payload_length-- > 0 && sp-s < (MAX_TLSA_EXPANDED_SIZE - 4))
+ sp += sprintf(CS sp, "%02x", *p++);
+
+ debug_printf(" %s\n", s);
+ }
+ }
+ return OK;
+ }
+ log_write(0, LOG_MAIN,
+ "DANE error: TLSA lookup for %s not DNSSEC", host->name);
+ /*FALLTRHOUGH*/
+
+ case DNS_NODATA: /* no TLSA RR for this lookup */
+ case DNS_NOMATCH: /* no records at all for this lookup */
+ return dane_required ? FAIL : FAIL_FORCED;
+
+ default:
+ case DNS_FAIL:
+ return dane_required ? FAIL : DEFER;
+ }
+}
+#endif /*SUPPORT_DANE*/
+
+
+
/*************************************************
**************************************************
* Stand-alone test program *