summaryrefslogtreecommitdiff
path: root/src/sm_resolve.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sm_resolve.c')
-rw-r--r--src/sm_resolve.c1340
1 files changed, 1224 insertions, 116 deletions
diff --git a/src/sm_resolve.c b/src/sm_resolve.c
index 8ec2cb68e6bfc..79e4168715a39 100644
--- a/src/sm_resolve.c
+++ b/src/sm_resolve.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000-2004, 2010 Proofpoint, Inc. and its suppliers.
+ * Copyright (c) 2000-2004, 2010, 2015, 2020 Proofpoint, Inc. and its suppliers.
* All rights reserved.
*
* By using this file, you agree to the terms and conditions set
@@ -42,14 +42,17 @@
*/
#include <sendmail.h>
-#if DNSMAP
+#if DNSMAP || DANE
# if NAMED_BIND
# if NETINET
# include <netinet/in_systm.h>
# include <netinet/ip.h>
-# endif /* NETINET */
+# endif
+# define _DEFINE_SMR_GLOBALS 1
# include "sm_resolve.h"
+#include <arpa/inet.h>
+
SM_RCSID("$Id: sm_resolve.c,v 8.40 2013-11-22 20:51:56 ca Exp $")
static struct stot
@@ -60,10 +63,10 @@ static struct stot
{
# if NETINET
{ "A", T_A },
-# endif /* NETINET */
+# endif
# if NETINET6
{ "AAAA", T_AAAA },
-# endif /* NETINET6 */
+# endif
{ "NS", T_NS },
{ "CNAME", T_CNAME },
{ "PTR", T_PTR },
@@ -71,10 +74,24 @@ static struct stot
{ "TXT", T_TXT },
{ "AFSDB", T_AFSDB },
{ "SRV", T_SRV },
+# ifdef T_DS
+ { "DS", T_DS },
+# endif
+ { "RRSIG", T_RRSIG },
+# ifdef T_NSEC
+ { "NSEC", T_NSEC },
+# endif
+# ifdef T_DNSKEY
+ { "DNSKEY", T_DNSKEY },
+# endif
+ { "TLSA", T_TLSA },
{ NULL, 0 }
};
-static DNS_REPLY_T *parse_dns_reply __P((unsigned char *, int));
+static DNS_REPLY_T *parse_dns_reply __P((unsigned char *, int, unsigned int));
+# if DNSSEC_TEST && defined(T_TLSA)
+static char *hex2bin __P((const char *, int));
+# endif
/*
** DNS_STRING_TO_TYPE -- convert resource record name into type
@@ -126,21 +143,23 @@ dns_type_to_string(type)
** DNS_FREE_DATA -- free all components of a DNS_REPLY_T
**
** Parameters:
-** r -- pointer to DNS_REPLY_T
+** dr -- pointer to DNS_REPLY_T
**
** Returns:
** none.
*/
void
-dns_free_data(r)
- DNS_REPLY_T *r;
+dns_free_data(dr)
+ DNS_REPLY_T *dr;
{
RESOURCE_RECORD_T *rr;
- if (r->dns_r_q.dns_q_domain != NULL)
- sm_free(r->dns_r_q.dns_q_domain);
- for (rr = r->dns_r_head; rr != NULL; )
+ if (dr == NULL)
+ return;
+ if (dr->dns_r_q.dns_q_domain != NULL)
+ sm_free(dr->dns_r_q.dns_q_domain);
+ for (rr = dr->dns_r_head; rr != NULL; )
{
RESOURCE_RECORD_T *tmp = rr;
@@ -151,7 +170,59 @@ dns_free_data(r)
rr = rr->rr_next;
sm_free(tmp);
}
- sm_free(r);
+ sm_free(dr);
+}
+
+/*
+** BIN2HEX -- convert binary TLSA RR to hex string
+**
+** Parameters:
+** tlsa -- pointer to result (allocated here)
+** p -- binary data (TLSA RR)
+** size -- length of p
+** min_size -- minimum expected size
+**
+** Returns:
+** >0: length of string (*tlsa)
+** -1: error
+*/
+
+static int bin2hex __P((char **, unsigned char *, int, int));
+
+static int
+bin2hex(tlsa, p, size, min_size)
+ char **tlsa;
+ unsigned char *p;
+ int size;
+ int min_size;
+{
+ int i, pos, txtlen;
+
+ txtlen = size * 3;
+ if (txtlen <= size || size < min_size)
+ {
+ if (LogLevel > 5)
+ sm_syslog(LOG_WARNING, NOQID,
+ "ERROR: bin2hex: size %d wrong", size);
+ return -1;
+ }
+ *tlsa = (char *) sm_malloc(txtlen);
+ if (*tlsa == NULL)
+ {
+ if (tTd(8, 17))
+ sm_dprintf("len=%d, rr_data=NULL\n", txtlen);
+ return -1;
+ }
+ snprintf(*tlsa, txtlen,
+ "%02X %02X %02X", p[0], p[1], p[2]);
+ pos = strlen(*tlsa);
+
+ /* why isn't there a print function like strlcat? */
+ for (i = 3; i < size && pos < txtlen; i++, pos += 3)
+ snprintf(*tlsa + pos, txtlen - pos, "%c%02X",
+ (i == 3) ? ' ' : ':', p[i]);
+
+ return i;
}
/*
@@ -160,54 +231,72 @@ dns_free_data(r)
** Parameters:
** data -- pointer to dns data
** len -- len of data
+** flags -- flags (RR_*)
**
** Returns:
** pointer to DNS_REPLY_T if succeeded.
** NULL otherwise.
+**
+** Note:
+** use dns_free_data() to free() the result when no longer needed.
*/
static DNS_REPLY_T *
-parse_dns_reply(data, len)
+parse_dns_reply(data, len, flags)
unsigned char *data;
int len;
+ unsigned int flags;
{
unsigned char *p;
unsigned short ans_cnt, ui;
int status;
size_t l;
char host[MAXHOSTNAMELEN];
- DNS_REPLY_T *r;
+ DNS_REPLY_T *dr;
RESOURCE_RECORD_T **rr;
- r = (DNS_REPLY_T *) sm_malloc(sizeof(*r));
- if (r == NULL)
+ if (tTd(8, 90))
+ {
+ FILE *fp;
+
+ fp = fopen("dns.buffer", "w");
+ if (fp != NULL)
+ {
+ fwrite(data, 1, len, fp);
+ fclose(fp);
+ fp = NULL;
+ }
+ else
+ sm_dprintf("parse_dns_reply: fp=%p, e=%d\n",
+ (void *)fp, errno);
+ }
+
+ dr = (DNS_REPLY_T *) sm_malloc(sizeof(*dr));
+ if (dr == NULL)
return NULL;
- memset(r, 0, sizeof(*r));
+ memset(dr, 0, sizeof(*dr));
p = data;
/* doesn't work on Crays? */
- memcpy(&r->dns_r_h, p, sizeof(r->dns_r_h));
- p += sizeof(r->dns_r_h);
+ memcpy(&dr->dns_r_h, p, sizeof(dr->dns_r_h));
+ p += sizeof(dr->dns_r_h);
status = dn_expand(data, data + len, p, host, sizeof(host));
if (status < 0)
- {
- dns_free_data(r);
- return NULL;
- }
- r->dns_r_q.dns_q_domain = sm_strdup(host);
- if (r->dns_r_q.dns_q_domain == NULL)
- {
- dns_free_data(r);
- return NULL;
- }
+ goto error;
+ dr->dns_r_q.dns_q_domain = sm_strdup(host);
+ if (dr->dns_r_q.dns_q_domain == NULL)
+ goto error;
- ans_cnt = ntohs((unsigned short) r->dns_r_h.ancount);
+ ans_cnt = ntohs((unsigned short) dr->dns_r_h.ancount);
+ if (tTd(8, 17))
+ sm_dprintf("parse_dns_reply: ac=%d, ad=%d\n", ans_cnt,
+ dr->dns_r_h.ad);
p += status;
- GETSHORT(r->dns_r_q.dns_q_type, p);
- GETSHORT(r->dns_r_q.dns_q_class, p);
- rr = &r->dns_r_head;
+ GETSHORT(dr->dns_r_q.dns_q_type, p);
+ GETSHORT(dr->dns_r_q.dns_q_class, p);
+ rr = &dr->dns_r_head;
ui = 0;
while (p < data + len && ui < ans_cnt)
{
@@ -215,10 +304,7 @@ parse_dns_reply(data, len)
status = dn_expand(data, data + len, p, host, sizeof(host));
if (status < 0)
- {
- dns_free_data(r);
- return NULL;
- }
+ goto error;
++ui;
p += status;
GETSHORT(type, p);
@@ -236,22 +322,15 @@ parse_dns_reply(data, len)
sm_syslog(LOG_WARNING, NOQID,
"ERROR: DNS RDLENGTH=%d > data len=%d",
size, len - (int)(p - data));
- dns_free_data(r);
- return NULL;
+ goto error;
}
*rr = (RESOURCE_RECORD_T *) sm_malloc(sizeof(**rr));
if (*rr == NULL)
- {
- dns_free_data(r);
- return NULL;
- }
+ goto error;
memset(*rr, 0, sizeof(**rr));
(*rr)->rr_domain = sm_strdup(host);
if ((*rr)->rr_domain == NULL)
- {
- dns_free_data(r);
- return NULL;
- }
+ goto error;
(*rr)->rr_type = type;
(*rr)->rr_class = class;
(*rr)->rr_ttl = ttl;
@@ -264,16 +343,13 @@ parse_dns_reply(data, len)
status = dn_expand(data, data + len, p, host,
sizeof(host));
if (status < 0)
- {
- dns_free_data(r);
- return NULL;
- }
+ goto error;
+ if (tTd(8, 50))
+ sm_dprintf("parse_dns_reply: type=%s, host=%s\n",
+ dns_type_to_string(type), host);
(*rr)->rr_u.rr_txt = sm_strdup(host);
if ((*rr)->rr_u.rr_txt == NULL)
- {
- dns_free_data(r);
- return NULL;
- }
+ goto error;
break;
case T_MX:
@@ -281,39 +357,30 @@ parse_dns_reply(data, len)
status = dn_expand(data, data + len, p + 2, host,
sizeof(host));
if (status < 0)
- {
- dns_free_data(r);
- return NULL;
- }
+ goto error;
l = strlen(host) + 1;
(*rr)->rr_u.rr_mx = (MX_RECORD_T *)
sm_malloc(sizeof(*((*rr)->rr_u.rr_mx)) + l);
if ((*rr)->rr_u.rr_mx == NULL)
- {
- dns_free_data(r);
- return NULL;
- }
+ goto error;
(*rr)->rr_u.rr_mx->mx_r_preference = (p[0] << 8) | p[1];
(void) sm_strlcpy((*rr)->rr_u.rr_mx->mx_r_domain,
host, l);
+ if (tTd(8, 50))
+ sm_dprintf("mx=%s, pref=%d\n", host,
+ (*rr)->rr_u.rr_mx->mx_r_preference);
break;
case T_SRV:
status = dn_expand(data, data + len, p + 6, host,
sizeof(host));
if (status < 0)
- {
- dns_free_data(r);
- return NULL;
- }
+ goto error;
l = strlen(host) + 1;
(*rr)->rr_u.rr_srv = (SRV_RECORDT_T*)
sm_malloc(sizeof(*((*rr)->rr_u.rr_srv)) + l);
if ((*rr)->rr_u.rr_srv == NULL)
- {
- dns_free_data(r);
- return NULL;
- }
+ goto error;
(*rr)->rr_u.rr_srv->srv_r_priority = (p[0] << 8) | p[1];
(*rr)->rr_u.rr_srv->srv_r_weight = (p[2] << 8) | p[3];
(*rr)->rr_u.rr_srv->srv_r_port = (p[4] << 8) | p[5];
@@ -340,38 +407,850 @@ parse_dns_reply(data, len)
sm_syslog(LOG_WARNING, NOQID,
"ERROR: DNS TXT record size=%d <= text len=%d",
size, txtlen);
- dns_free_data(r);
- return NULL;
+ goto error;
}
(*rr)->rr_u.rr_txt = (char *) sm_malloc(txtlen + 1);
if ((*rr)->rr_u.rr_txt == NULL)
- {
- dns_free_data(r);
- return NULL;
- }
+ goto error;
(void) sm_strlcpy((*rr)->rr_u.rr_txt, (char*) p + 1,
txtlen + 1);
break;
+# ifdef T_TLSA
+ case T_TLSA:
+ if (tTd(8, 61))
+ sm_dprintf("parse_dns_reply: TLSA, size=%d, flags=%X\n",
+ size, flags);
+ if ((flags & RR_AS_TEXT) != 0)
+ {
+ txtlen = bin2hex((char **)&((*rr)->rr_u.rr_data),
+ p, size, 4);
+ if (txtlen <= 0)
+ goto error;
+ break;
+ }
+ /* FALLTHROUGH */
+ /* return "raw" data for caller to use as it pleases */
+# endif /* T_TLSA */
+
default:
(*rr)->rr_u.rr_data = (unsigned char*) sm_malloc(size);
if ((*rr)->rr_u.rr_data == NULL)
+ goto error;
+ (void) memcpy((*rr)->rr_u.rr_data, p, size);
+ if (tTd(8, 61) && type == T_A)
{
- dns_free_data(r);
- return NULL;
+ SOCKADDR addr;
+
+ (void) memcpy((void *)&addr.sin.sin_addr.s_addr, p, size);
+ sm_dprintf("parse_dns_reply: IPv4=%s\n",
+ inet_ntoa(addr.sin.sin_addr));
}
- (void) memcpy((*rr)->rr_u.rr_data, p, size);
break;
}
p += size;
rr = &(*rr)->rr_next;
}
*rr = NULL;
- return r;
+ return dr;
+
+ error:
+ dns_free_data(dr);
+ return NULL;
+}
+
+# if DNSSEC_TEST
+
+#include <arpa/nameser.h>
+
+static int gen_dns_reply __P((unsigned char *, int, unsigned char *,
+ const char *, int, const char *, int, int, int, int,
+ const char *, int, int, int));
+static int dnscrtrr __P((const char *, const char *, int, char *, int,
+ unsigned int, int *, int *, unsigned char *, int, unsigned char *));
+
+/*
+** HERRNO2TXT -- return error text for h_errno
+**
+** Parameters:
+** e -- h_errno
+**
+** Returns:
+** DNS error text if available
+*/
+
+const char *
+herrno2txt(e)
+ int e;
+{
+ switch (e)
+ {
+ case NETDB_INTERNAL:
+ return "see errno";
+ case NETDB_SUCCESS:
+ return "OK";
+ case HOST_NOT_FOUND:
+ return "HOST_NOT_FOUND";
+ case TRY_AGAIN:
+ return "TRY_AGAIN";
+ case NO_RECOVERY:
+ return "NO_RECOVERY";
+ case NO_DATA:
+ return "NO_DATA";
+ }
+ return "bogus h_errno";
}
/*
-** DNS_LOOKUP_INT -- perform dns map lookup (internal helper routine)
+** GEN_DNS_REPLY -- generate DNS reply data.
+**
+** Parameters:
+** buf -- buffer to which DNS data is written
+** buflen -- length of buffer
+** bufpos -- position in buffer where DNS RRs are appended
+** query -- name of query
+** qtype -- resource record type of query
+** domain -- name of domain which has been "found"
+** class -- resource record class
+** type -- resource record type
+** ttl -- TTL
+** size -- size of data
+** data -- data
+** txtlen -- length of text
+** pref -- MX preference
+** ad -- ad flag
+**
+** Returns:
+** >0 length of buffer that has been used.
+** <0 error
+*/
+
+static int
+gen_dns_reply(buf, buflen, bufpos, query, qtype, domain, class, type, ttl, size, data, txtlen, pref, ad)
+ unsigned char *buf;
+ int buflen;
+ unsigned char *bufpos;
+ const char *query;
+ int qtype;
+ const char *domain;
+ int class;
+ int type;
+ int ttl;
+ int size;
+ const char *data;
+ int txtlen;
+ int pref;
+ int ad;
+{
+ unsigned short ans_cnt;
+ HEADER *hp;
+ unsigned char *cp, *ep;
+ int n;
+ static unsigned char *dnptrs[20], **dpp, **lastdnptr;
+
+ SM_REQUIRE(NULL != buf);
+ SM_REQUIRE(buflen >= HFIXEDSZ);
+ SM_REQUIRE(query != NULL);
+ hp = (HEADER *) buf;
+ ep = buf + buflen;
+ cp = buf + HFIXEDSZ;
+
+ if (bufpos != NULL)
+ cp = bufpos;
+ else
+ {
+ sm_dprintf("gen_dns_reply: query=%s, domain=%s, type=%s, size=%d, ad=%d\n",
+ query, domain, dns_type_to_string(type), size, ad);
+ dpp = dnptrs;
+ *dpp++ = buf;
+ *dpp++ = NULL;
+ lastdnptr = dnptrs + sizeof dnptrs / sizeof dnptrs[0];
+
+ memset(buf, 0, HFIXEDSZ);
+ hp->id = 0xdead; /* HACK */
+ hp->qr = 1;
+ hp->opcode = QUERY;
+ hp->rd = 0; /* recursion desired? */
+ hp->rcode = 0; /* !!! */
+ /* hp->aa = ?; * !!! */
+ /* hp->tc = ?; * !!! */
+ /* hp->ra = ?; * !!! */
+ hp->qdcount = htons(1);
+ hp->ancount = 0;
+
+ n = dn_comp(query, cp, ep - cp - QFIXEDSZ, dnptrs, lastdnptr);
+ if (n < 0)
+ return n;
+ cp += n;
+ PUTSHORT(qtype, cp);
+ PUTSHORT(class, cp);
+ }
+ hp->ad = ad;
+
+ if (ep - cp < QFIXEDSZ)
+ return (-1);
+ n = dn_comp(domain, cp, ep - cp - QFIXEDSZ, dnptrs, lastdnptr);
+ if (n < 0)
+ return n;
+ cp += n;
+ PUTSHORT(type, cp);
+ PUTSHORT(class, cp);
+ PUTLONG(ttl, cp);
+
+ ans_cnt = ntohs((unsigned short) hp->ancount);
+ ++ans_cnt;
+ hp->ancount = htons((unsigned short) ans_cnt);
+
+ switch (type)
+ {
+ case T_MX:
+ n = dn_comp(data, cp + 4, ep - cp - QFIXEDSZ, dnptrs, lastdnptr);
+ if (n < 0)
+ return n;
+ PUTSHORT(n + 2, cp);
+ PUTSHORT(pref, cp);
+ cp += n;
+ break;
+
+ case T_TXT:
+ if (txtlen >= size)
+ return -1;
+ PUTSHORT(txtlen, cp);
+ (void) sm_strlcpy((char *)cp, data, txtlen + 1);
+ cp += txtlen;
+ break;
+
+ case T_CNAME:
+ n = dn_comp(data, cp + 2, ep - cp - QFIXEDSZ, dnptrs, lastdnptr);
+ if (n < 0)
+ return n;
+ PUTSHORT(n, cp);
+ cp += n;
+ break;
+
+# if defined(T_TLSA)
+ case T_TLSA:
+ {
+ char *tlsa;
+
+ tlsa = hex2bin(data, size);
+ if (tlsa == NULL)
+ return (-1);
+ n = size / 2;
+ PUTSHORT(n, cp);
+ (void) memcpy(cp, tlsa, n);
+ cp += n;
+ }
+ break;
+# endif /* T_TLSA */
+
+ default:
+ PUTSHORT(size, cp);
+ (void) memcpy(cp, data, size);
+ cp += size;
+ break;
+ }
+
+ return (cp - buf);
+}
+
+/*
+** SETHERRNOFROMSTRING -- set h_errno based on text
+**
+** Parameters:
+** str -- string which might contain h_errno text
+** prc -- pointer to rcode (EX_*)
+**
+** Returns:
+** h_errno if found
+** 0 otherwise
+*/
+
+int
+setherrnofromstring(str, prc)
+ const char *str;
+ int *prc;
+{
+ SM_SET_H_ERRNO(0);
+ if (str == NULL || *str == '\0')
+ return 0;
+ if (strstr(str, "herrno:") == NULL)
+ return 0;
+ if (prc != NULL)
+ *prc = EX_NOHOST;
+ if (strstr(str, "host_not_found"))
+ SM_SET_H_ERRNO(HOST_NOT_FOUND);
+ else if (strstr(str, "try_again"))
+ {
+ SM_SET_H_ERRNO(TRY_AGAIN);
+ if (prc != NULL)
+ *prc = EX_TEMPFAIL;
+ }
+ else if (strstr(str, "no_recovery"))
+ SM_SET_H_ERRNO(NO_RECOVERY);
+ else if (strstr(str, "no_data"))
+ SM_SET_H_ERRNO(NO_DATA);
+ else
+ SM_SET_H_ERRNO(NETDB_INTERNAL);
+ return h_errno;
+}
+
+/*
+** GETTTLFROMSTRING -- extract ttl from a string
+**
+** Parameters:
+** str -- string which might contain ttl
+**
+** Returns:
+** ttl if found
+** 0 otherwise
+*/
+
+int
+getttlfromstring(str)
+ const char *str;
+{
+ if (str == NULL || *str == '\0')
+ return 0;
+#define TTL_PRE "ttl="
+ if (strstr(str, TTL_PRE) == NULL)
+ return 0;
+ return strtoul(str + strlen(TTL_PRE), NULL, 10);
+}
+
+/*
+** NSPORTIP -- parse port@IPv4 and set NS accordingly
+**
+** Parameters:
+** p -- port@Ipv4
+**
+** Returns:
+** <0: error
+** >0: ok
+**
+** Side Effects:
+** sets NS for DNS lookups
+*/
+
+/*
+** There should be a generic function for this...
+** milter_open(), socket_map_open(), others?
+*/
+
+int
+nsportip(p)
+ char *p;
+{
+ char *h;
+ int r;
+ unsigned short port;
+ struct in_addr nsip;
+
+ if (p == NULL || *p == '\0')
+ return -1;
+
+ port = 0;
+ while (SM_ISSPACE(*p))
+ p++;
+ if (*p == '\0')
+ return -1;
+ h = strchr(p, '@');
+ if (h != NULL)
+ {
+ *h = '\0';
+ if (isascii(*p) && isdigit(*p))
+ port = atoi(p);
+ *h = '@';
+ p = h + 1;
+ }
+ h = strchr(p, ' ');
+ if (h != NULL)
+ *h = '\0';
+ r = inet_pton(AF_INET, p, &nsip);
+ if (r > 0)
+ {
+ if ((_res.options & RES_INIT) == 0)
+ (void) res_init();
+ dns_setns(&nsip, port);
+ }
+ if (h != NULL)
+ *h = ' ';
+ return r > 0 ? 0 : -1;
+}
+
+/*
+** DNS_SETNS -- set one NS in resolver context
+**
+** Parameters:
+** ns -- (IPv4 address of) nameserver
+** port -- nameserver port
+**
+** Returns:
+** None.
+*/
+
+void
+dns_setns(ns, port)
+ struct in_addr *ns;
+ unsigned int port;
+{
+ _res.nsaddr_list[0].sin_family = AF_INET;
+ _res.nsaddr_list[0].sin_addr = *ns;
+ if (port != 0)
+ _res.nsaddr_list[0].sin_port = htons(port);
+ _res.nscount = 1;
+ if (tTd(8, 61))
+ sm_dprintf("dns_setns(%s,%u)\n", inet_ntoa(*ns), port);
+}
+
+# if defined(T_TLSA)
+/*
+** HEX2BIN -- convert hex string to binary TLSA RR
+**
+** Parameters:
+** p -- hex representation of TLSA RR
+** size -- length of p
+**
+** Returns:
+** pointer to binary TLSA RR
+** NULL: error
+*/
+
+static char *
+hex2bin(p, size)
+ const char *p;
+ int size;
+{
+ int i, pos, txtlen;
+ char *tlsa;
+
+ txtlen = size / 2;
+ if (txtlen * 2 == size)
+ {
+ if (LogLevel > 5)
+ sm_syslog(LOG_WARNING, NOQID,
+ "ERROR: hex2bin: size %d wrong", size);
+ return NULL;
+ }
+ tlsa = sm_malloc(txtlen + 1);
+ if (tlsa == NULL)
+ {
+ if (tTd(8, 17))
+ sm_dprintf("len=%d, tlsa=NULL\n", txtlen);
+ return NULL;
+ }
+
+#define CHAR2INT(c) (((c) <= '9') ? ((c) - '0') : (toupper(c) - 'A' + 10))
+ for (i = 0, pos = 0; i + 1 < size && pos < txtlen; i += 2, pos++)
+ tlsa[pos] = CHAR2INT(p[i]) * 16 + CHAR2INT(p[i+1]);
+
+ return tlsa;
+}
+# endif /* T_TLSA */
+
+const char *
+rr_type2tag(rr_type)
+ int rr_type;
+{
+ switch (rr_type)
+ {
+ case T_A:
+ return "ipv4";
+# if NETINET6
+ case T_AAAA:
+ return "ipv6";
+# endif
+ case T_CNAME:
+ return "cname";
+ case T_MX:
+ return "mx";
+# ifdef T_TLSA
+ case T_TLSA:
+ return "tlsa";
+# endif
+ }
+ return NULL;
+}
+
+/*
+** DNSCRTRR -- create DNS RR
+**
+** Parameters:
+** domain -- original query domain
+** query -- name of query
+** qtype -- resource record type of query
+** value -- (list of) data to set
+** rr_type -- resource record type
+** flags -- flags how to handle various lookups
+** herr -- (pointer to) h_errno (output if non-NULL)
+** adp -- (pointer to) ad flag
+** answer -- buffer for RRs
+** anslen -- size of answer
+** anspos -- current position in answer
+**
+** Returns:
+** >0: length of data in answer
+** <0: error, check *herr
+*/
+
+static int
+dnscrtrr(domain, query, qtype, value, rr_type, flags, herr, adp, answer, anslen, anspos)
+ const char *domain;
+ const char *query;
+ int qtype;
+ char *value;
+ int rr_type;
+ unsigned int flags;
+ int *herr;
+ int *adp;
+ unsigned char *answer;
+ int anslen;
+ unsigned char *anspos;
+{
+ SOCKADDR addr;
+ int ttl, ad, rlen;
+ char *p, *token;
+ char data[IN6ADDRSZ];
+ char rhs[MAXLINE];
+
+ rlen = -1;
+ if (NULL == value || '\0' == *value)
+ return rlen;
+ SM_REQUIRE(adp != NULL);
+ (void) sm_strlcpy(rhs, value, sizeof(rhs));
+ p = rhs;
+ if (setherrnofromstring(p, NULL) != 0)
+ {
+ if (herr != NULL)
+ *herr = h_errno;
+ if (tTd(8, 16))
+ sm_dprintf("dnscrtrr rhs=%s h_errno=%d (%s)\n",
+ p, h_errno, herrno2txt(h_errno));
+ return rlen;
+ }
+
+ ttl = 0;
+ ad = 0;
+ for (token = p; token != NULL && *token != '\0'; token = p)
+ {
+ rlen = 0;
+ while (p != NULL && *p != '\0' && !SM_ISSPACE(*p))
+ ++p;
+ if (SM_ISSPACE(*p))
+ *p++ = '\0';
+ sm_dprintf("dnscrtrr: token=%s\n", token);
+ if (strcmp(token, "ad") == 0)
+ {
+ bool adflag;
+
+ adflag = (_res.options & RES_USE_DNSSEC) != 0;
+
+ /* maybe print this only for the final RR? */
+ if (tTd(8, 61))
+ sm_dprintf("dnscrtrr: ad=1, adp=%d, adflag=%d\n",
+ *adp, adflag);
+ if (*adp != 0 && adflag)
+ {
+ *adp = 1;
+ ad = 1;
+ }
+ continue;
+ }
+ if (ttl == 0 && (ttl = getttlfromstring(token)) > 0)
+ {
+ if (tTd(8, 61))
+ sm_dprintf("dnscrtrr: ttl=%d\n", ttl);
+ continue;
+ }
+
+ if (rr_type == T_A)
+ {
+ addr.sin.sin_addr.s_addr = inet_addr(token);
+ (void) memmove(data, (void *)&addr.sin.sin_addr.s_addr,
+ INADDRSZ);
+ rlen = gen_dns_reply(answer, anslen, anspos,
+ query, qtype, domain, C_IN, rr_type, ttl,
+ INADDRSZ, data, 0, 0, ad);
+ }
+
+# if NETINET6
+ if (rr_type == T_AAAA)
+ {
+ anynet_pton(AF_INET6, token, &addr.sin6.sin6_addr);
+ memmove(data, (void *)&addr.sin6.sin6_addr, IN6ADDRSZ);
+ rlen = gen_dns_reply(answer, anslen, anspos,
+ query, qtype, domain, C_IN, rr_type, ttl,
+ IN6ADDRSZ, data, 0, 0, ad);
+ }
+# endif /* NETINET6 */
+
+ if (rr_type == T_MX)
+ {
+ char *endptr;
+ int pref;
+
+ pref = (int) strtoul(token, &endptr, 10);
+ if (endptr == NULL || *endptr != ':')
+ goto error;
+ token = endptr + 1;
+ rlen = gen_dns_reply(answer, anslen, anspos,
+ query, qtype, domain, C_IN, rr_type, ttl,
+ strlen(token) + 1, token, 0, pref, ad);
+ if (tTd(8, 50))
+ sm_dprintf("dnscrtrr: mx=%s, pref=%d\n",
+ token, pref);
+ }
+
+# ifdef T_TLSA
+ if (rr_type == T_TLSA)
+ rlen = gen_dns_reply(answer, anslen, anspos,
+ query, qtype, domain, C_IN, rr_type, ttl,
+ strlen(token) + 1, token, 0, 0, ad);
+# endif
+
+ if (rr_type == T_CNAME)
+ rlen = gen_dns_reply(answer, anslen, anspos,
+ query, qtype, domain, C_IN, rr_type, ttl,
+ strlen(token), token, 0, 0, ad);
+ if (rlen < 0)
+ goto error;
+ if (rlen > 0)
+ anspos = answer + rlen;
+ }
+
+ if (ad != 1)
+ *adp = 0;
+
+ return rlen;
+
+ error:
+ if (herr != NULL && 0 == *herr)
+ *herr = NO_RECOVERY;
+ return -1;
+}
+
+/*
+** TSTDNS_SEARCH -- replacement for res_search() for testing
+**
+** Parameters:
+** domain -- query domain
+** class -- class
+** type -- resource record type
+** answer -- buffer for RRs
+** anslen -- size of answer
+**
+** Returns:
+** >0: length of data in answer
+** <0: error, check h_errno
+*/
+
+int
+tstdns_search(domain, class, type, answer, anslen)
+ const char *domain;
+ int class;
+ int type;
+ unsigned char *answer;
+ int anslen;
+{
+ int rlen, ad, maprcode, cnt, flags, herr;
+ bool found_cname;
+ const char *query;
+ char *p;
+ const char *tag;
+ char *av[2];
+ STAB *map;
+ char key[MAXNAME + 16];
+ char rhs[MAXLINE];
+ unsigned char *anspos;
+
+ rlen = -1;
+ herr = 0;
+ if (class != C_IN)
+ goto error;
+ if (NULL == domain || '\0' == *domain)
+ goto error;
+ tag = rr_type2tag(type);
+ if (tag == NULL)
+ goto error;
+ maprcode = EX_OK;
+ ad = -1;
+ flags = 0;
+ query = domain;
+ anspos = NULL;
+
+ map = stab("access", ST_MAP, ST_FIND);
+ if (NULL == map)
+ {
+ sm_dprintf("access map not found\n");
+ goto error;
+ }
+ if (!bitset(MF_OPEN, map->s_map.map_mflags) &&
+ !openmap(&(map->s_map)))
+ {
+ sm_dprintf("access map open failed\n");
+ goto error;
+ }
+
+/*
+** Look up tag:domain, if not found and domain does not end with a dot
+** (and the proper debug level is selected), also try with trailing dot.
+*/
+
+#define SM_LOOKUP2(tag) \
+ do { \
+ int len; \
+ \
+ len = strlen(domain); \
+ av[0] = key; \
+ av[1] = NULL; \
+ snprintf(key, sizeof(key), "%s:%s", tag, domain); \
+ p = (*map->s_map.map_class->map_lookup)(&map->s_map, key, av, \
+ &maprcode); \
+ if (p != NULL) \
+ break; \
+ if (!tTd(8, 112) || (len > 0 && '.' == domain[len - 1])) \
+ break; \
+ snprintf(key, sizeof(key), "%s:%s.", tag, domain); \
+ p = (*map->s_map.map_class->map_lookup)(&map->s_map, key, av, \
+ &maprcode); \
+ } while (0)
+
+ cnt = 0;
+ found_cname = false;
+ while (cnt < 6)
+ {
+ char *last;
+
+ /* Should this try with/without trailing dot? */
+ SM_LOOKUP2(tag);
+ if (p != NULL)
+ {
+ sm_dprintf("access map lookup key=%s, value=%s\n", key,
+ p);
+ break;
+ }
+ if (NULL == p && (flags & RR_NO_CNAME) == 0)
+ {
+ sm_dprintf("access map lookup failed key=%s, try cname\n",
+ key);
+ SM_LOOKUP2("cname");
+ if (p != NULL)
+ {
+ sm_dprintf("cname lookup key=%s, value=%s, ad=%d\n",
+ key, p, ad);
+ rlen = dnscrtrr(domain, query, type, p, T_CNAME,
+ flags, &herr, &ad, answer,
+ anslen, anspos);
+ if (rlen < 0)
+ goto error;
+ if (rlen > 0)
+ anspos = answer + rlen;
+ found_cname = true;
+ }
+ }
+ if (NULL == p)
+ break;
+
+ (void) sm_strlcpy(rhs, p, sizeof(rhs));
+ p = rhs;
+
+ /* skip (leading) ad/ttl: look for last ' ' */
+ if ((last = strrchr(p, ' ')) != NULL && last[1] != '\0')
+ domain = last + 1;
+ else
+ domain = p;
+ ++cnt;
+ }
+ if (NULL == p)
+ {
+ int t;
+ char *tags[] = { "ipv4", "mx", "tlsa",
+# if NETINET6
+ "ipv6",
+# endif
+ NULL
+ };
+
+ for (t = 0; tags[t] != NULL; t++)
+ {
+ if (strcmp(tag, tags[t]) == 0)
+ continue;
+ SM_LOOKUP2(tags[t]);
+ if (p != NULL)
+ {
+ sm_dprintf("access map lookup failed key=%s:%s, but found key=%s\n",
+ tag, domain, key);
+ herr = NO_DATA;
+ goto error;
+ }
+ }
+ sm_dprintf("access map lookup failed key=%s\n", key);
+ herr = HOST_NOT_FOUND;
+ goto error;
+ }
+ if (found_cname && (flags & RR_ONLY_CNAME) != 0)
+ return rlen;
+ rlen = dnscrtrr(domain, query, type, p, type, flags, &herr, &ad,
+ answer, anslen, anspos);
+ if (rlen < 0)
+ goto error;
+ return rlen;
+
+ error:
+ if (0 == herr)
+ herr = NO_RECOVERY;
+ SM_SET_H_ERRNO(herr);
+ sm_dprintf("rlen=%d, herr=%d\n", rlen, herr);
+ return -1;
+}
+
+/*
+** TSTDNS_QUERYDOMAIN -- replacement for res_querydomain() for testing
+**
+** Parameters:
+** name -- query name
+** domain -- query domain
+** class -- class
+** type -- resource record type
+** answer -- buffer for RRs
+** anslen -- size of answer
+**
+** Returns:
+** >0: length of data in answer
+** <0: error, check h_errno
+*/
+
+int
+tstdns_querydomain(name, domain, class, type, answer, anslen)
+ const char *name;
+ const char *domain;
+ int class;
+ int type;
+ unsigned char *answer;
+ int anslen;
+{
+ char query[MAXNAME];
+ int len;
+
+ if (NULL == name)
+ goto error;
+ if (NULL == domain || '\0' == *domain)
+ return tstdns_search(name, class, type, answer, anslen);
+
+ len = snprintf(query, sizeof(query), "%s.%s", name, domain);
+ if (len >= (int)sizeof(query))
+ goto error;
+ return tstdns_search(query, class, type, answer, anslen);
+
+ error:
+ SM_SET_H_ERRNO(NO_RECOVERY);
+ return -1;
+}
+
+# endif /* DNSSEC_TEST */
+
+/*
+** DNS_LOOKUP_INT -- perform DNS lookup
**
** Parameters:
** domain -- name to lookup
@@ -379,6 +1258,10 @@ parse_dns_reply(data, len)
** rr_type -- resource record type
** retrans -- retransmission timeout
** retry -- number of retries
+** options -- DNS resolver options
+** flags -- currently only passed to parse_dns_reply()
+** err -- (pointer to) errno (output if non-NULL)
+** herr -- (pointer to) h_errno (output if non-NULL)
**
** Returns:
** result of lookup if succeeded.
@@ -386,33 +1269,55 @@ parse_dns_reply(data, len)
*/
DNS_REPLY_T *
-dns_lookup_int(domain, rr_class, rr_type, retrans, retry)
+dns_lookup_int(domain, rr_class, rr_type, retrans, retry, options, flags, err, herr)
const char *domain;
int rr_class;
int rr_type;
time_t retrans;
int retry;
+ unsigned int options;
+ unsigned int flags;
+ int *err;
+ int *herr;
{
int len;
unsigned long old_options = 0;
time_t save_retrans = 0;
int save_retry = 0;
- DNS_REPLY_T *r = NULL;
+ DNS_REPLY_T *dr = NULL;
querybuf reply_buf;
unsigned char *reply;
+ int (*resfunc) __P((const char *, int, int, u_char *, int));
-#define SMRBSIZE sizeof(reply_buf)
-#ifndef IP_MAXPACKET
-# define IP_MAXPACKET 65535
-#endif
+# define SMRBSIZE ((int) sizeof(reply_buf))
+# ifndef IP_MAXPACKET
+# define IP_MAXPACKET 65535
+# endif
+ resfunc = res_search;
+# if DNSSEC_TEST
+ if (tTd(8, 110))
+ resfunc = tstdns_search;
+# endif
+
+ old_options = _res.options;
+ _res.options |= options;
+ if (err != NULL)
+ *err = 0;
+ if (herr != NULL)
+ *herr = 0;
if (tTd(8, 16))
{
- old_options = _res.options;
_res.options |= RES_DEBUG;
- sm_dprintf("dns_lookup(%s, %d, %s)\n", domain,
- rr_class, dns_type_to_string(rr_type));
+ sm_dprintf("dns_lookup_int(%s, %d, %s, %x)\n", domain,
+ rr_class, dns_type_to_string(rr_type), options);
}
+# if DNSSEC_TEST
+ if (tTd(8, 15))
+ sm_dprintf("NS=%s, port=%d\n",
+ inet_ntoa(_res.nsaddr_list[0].sin_addr),
+ ntohs(_res.nsaddr_list[0].sin_port));
+# endif
if (retrans > 0)
{
save_retrans = _res.retrans;
@@ -426,38 +1331,61 @@ dns_lookup_int(domain, rr_class, rr_type, retrans, retry)
errno = 0;
SM_SET_H_ERRNO(0);
reply = (unsigned char *)&reply_buf;
- len = res_search(domain, rr_class, rr_type, reply, SMRBSIZE);
+ len = (*resfunc)(domain, rr_class, rr_type, reply, SMRBSIZE);
if (len >= SMRBSIZE)
{
if (len >= IP_MAXPACKET)
{
if (tTd(8, 4))
sm_dprintf("dns_lookup: domain=%s, length=%d, default_size=%d, max=%d, status=response too long\n",
- domain, len, (int) SMRBSIZE,
- IP_MAXPACKET);
+ domain, len, SMRBSIZE, IP_MAXPACKET);
}
else
{
if (tTd(8, 6))
sm_dprintf("dns_lookup: domain=%s, length=%d, default_size=%d, max=%d, status=response longer than default size, resizing\n",
- domain, len, (int) SMRBSIZE,
- IP_MAXPACKET);
+ domain, len, SMRBSIZE, IP_MAXPACKET);
reply = (unsigned char *)sm_malloc(IP_MAXPACKET);
if (reply == NULL)
SM_SET_H_ERRNO(TRY_AGAIN);
else
- len = res_search(domain, rr_class, rr_type,
+ {
+ SM_SET_H_ERRNO(0);
+ len = (*resfunc)(domain, rr_class, rr_type,
reply, IP_MAXPACKET);
+ }
}
}
- if (tTd(8, 16))
+ _res.options = old_options;
+ if (len < 0)
+ {
+ if (err != NULL)
+ *err = errno;
+ if (herr != NULL)
+ *herr = h_errno;
+ if (tTd(8, 16))
+ {
+ sm_dprintf("dns_lookup_int(%s, %d, %s, %x)=%d, errno=%d, h_errno=%d"
+# if DNSSEC_TEST
+ " (%s)"
+# endif
+ "\n",
+ domain, rr_class, dns_type_to_string(rr_type),
+ options, len, errno, h_errno
+# if DNSSEC_TEST
+ , herrno2txt(h_errno)
+# endif
+ );
+ }
+ }
+ else if (tTd(8, 16))
{
- _res.options = old_options;
- sm_dprintf("dns_lookup(%s, %d, %s) --> %d\n",
- domain, rr_class, dns_type_to_string(rr_type), len);
+ sm_dprintf("dns_lookup_int(%s, %d, %s, %x)=%d\n",
+ domain, rr_class, dns_type_to_string(rr_type),
+ options, len);
}
if (len >= 0 && len < IP_MAXPACKET && reply != NULL)
- r = parse_dns_reply(reply, len);
+ dr = parse_dns_reply(reply, len, flags);
if (reply != (unsigned char *)&reply_buf && reply != NULL)
{
sm_free(reply);
@@ -467,29 +1395,209 @@ dns_lookup_int(domain, rr_class, rr_type, retrans, retry)
_res.retrans = save_retrans;
if (retry > 0)
_res.retry = save_retry;
- return r;
+ return dr;
}
-# if 0
+/*
+** DNS_LOOKUP_MAP -- perform DNS map lookup
+**
+** Parameters:
+** domain -- name to lookup
+** rr_class -- resource record class
+** rr_type -- resource record type
+** retrans -- retransmission timeout
+** retry -- number of retries
+** options -- DNS resolver options
+**
+** Returns:
+** result of lookup if succeeded.
+** NULL otherwise.
+*/
+
DNS_REPLY_T *
-dns_lookup(domain, type_name, retrans, retry)
+dns_lookup_map(domain, rr_class, rr_type, retrans, retry, options)
const char *domain;
- const char *type_name;
+ int rr_class;
+ int rr_type;
time_t retrans;
int retry;
+ unsigned int options;
{
- int type;
+ return dns_lookup_int(domain, rr_class, rr_type, retrans, retry,
+ options, RR_AS_TEXT, NULL, NULL);
+}
- type = dns_string_to_type(type_name);
- if (type == -1)
+# if DANE
+/*
+** DNS2HE -- convert DNS_REPLY_T list to hostent struct
+**
+** Parameters:
+** dr -- DNS lookup result
+** family -- address family
+**
+** Returns:
+** hostent struct if succeeded.
+** NULL otherwise.
+**
+** Note:
+** this returns a pointer to a static struct!
+*/
+
+struct hostent *
+dns2he(dr, family)
+ DNS_REPLY_T *dr;
+ int family;
+{
+# define SM_MAX_ADDRS 256
+ static struct hostent he;
+ static char *he_aliases[1];
+ static char *he_addr_list[SM_MAX_ADDRS];
+# ifdef IN6ADDRSZ
+# define IN_ADDRSZ IN6ADDRSZ
+# else
+# define IN_ADDRSZ INADDRSZ
+# endif
+ static char he_addrs[SM_MAX_ADDRS * IN_ADDRSZ];
+ static char he_name[MAXNAME];
+ static bool he_init = false;
+ struct hostent *h;
+ struct in_addr ia;
+ int i;
+ size_t sz;
+# if NETINET6 && DNSSEC_TEST
+ struct in6_addr ia6;
+ char buf6[INET6_ADDRSTRLEN];
+# endif
+ RESOURCE_RECORD_T *rr;
+
+ if (dr == NULL)
+ return NULL;
+
+ h = &he;
+ if (!he_init)
{
- if (tTd(8, 16))
- sm_dprintf("dns_lookup: unknown resource type: `%s'\n",
- type_name);
+ he_aliases[0] = NULL;
+ he.h_aliases = he_aliases;
+ he.h_addr_list = he_addr_list;
+ he.h_name = he_name;
+ he_init = true;
+ }
+ h->h_addrtype = family;
+
+ if (tTd(8, 17))
+ sm_dprintf("dns2he: ad=%d\n", dr->dns_r_h.ad);
+
+ /* do we want/need to copy the name? */
+ rr = dr->dns_r_head;
+ if (rr != NULL && rr->rr_domain != NULL)
+ sm_strlcpy(h->h_name, rr->rr_domain, sizeof(he_name));
+ else
+ h->h_name[0] = '\0';
+
+ sz = 0;
+# if NETINET
+ if (family == AF_INET)
+ sz = INADDRSZ;
+# endif
+# if NETINET6
+ if (family == AF_INET6)
+ sz = IN6ADDRSZ;
+# endif
+ if (sz == 0)
return NULL;
+ h->h_length = sz;
+
+ for (rr = dr->dns_r_head, i = 0; rr != NULL && i < SM_MAX_ADDRS - 1;
+ rr = rr->rr_next)
+ {
+ h->h_addr_list[i] = he_addrs + i * h->h_length;
+ switch (rr->rr_type)
+ {
+# if NETINET
+ case T_A:
+ if (family != AF_INET)
+ continue;
+ memmove(h->h_addr_list[i], rr->rr_u.rr_a, INADDRSZ);
+ ++i;
+ break;
+# endif /* NETINET */
+# if NETINET6
+ case T_AAAA:
+ if (family != AF_INET6)
+ continue;
+ memmove(h->h_addr_list[i], rr->rr_u.rr_aaaa, IN6ADDRSZ);
+ ++i;
+ break;
+# endif /* NETINET6 */
+ case T_CNAME:
+# if DNSSEC_TEST
+ if (tTd(8, 16))
+ sm_dprintf("dns2he: cname: %s ttl=%d\n",
+ rr->rr_u.rr_txt, rr->rr_ttl);
+# endif
+ break;
+ case T_MX:
+# if DNSSEC_TEST
+ if (tTd(8, 16))
+ sm_dprintf("dns2he: mx: %d %s ttl=%d\n",
+ rr->rr_u.rr_mx->mx_r_preference,
+ rr->rr_u.rr_mx->mx_r_domain,
+ rr->rr_ttl);
+# endif
+ break;
+
+# if defined(T_TLSA)
+ case T_TLSA:
+# if DNSSEC_TEST
+ if (tTd(8, 16))
+ {
+ char *tlsa;
+ int len;
+
+ len = bin2hex(&tlsa, rr->rr_u.rr_data,
+ rr->rr_size, 4);
+ if (len > 0)
+ sm_dprintf("dns2he: tlsa: %s ttl=%d\n",
+ tlsa, rr->rr_ttl);
+ }
+# endif
+ break;
+# endif /* T_TLSA */
+ }
+ }
+
+ /* complain if list is too long! */
+ SM_ASSERT(i < SM_MAX_ADDRS);
+ h->h_addr_list[i] = NULL;
+
+# if DNSSEC_TEST
+ if (tTd(8, 16))
+ {
+ for (i = 0; h->h_addr_list[i] != NULL && i < SM_MAX_ADDRS; i++)
+ {
+ char *addr;
+
+ addr = NULL;
+# if NETINET6
+ if (h->h_addrtype == AF_INET6)
+ {
+ memmove(&ia6, h->h_addr_list[i], IN6ADDRSZ);
+ addr = anynet_ntop(&ia6, buf6, sizeof(buf6));
+ }
+ else
+# endif /* NETINET6 */
+ /* "else" in #if code above */
+ {
+ memmove(&ia, h->h_addr_list[i], INADDRSZ);
+ addr = (char *) inet_ntoa(ia);
+ }
+ if (addr != NULL)
+ sm_dprintf("dns2he: addr[%d]: %s\n", i, addr);
+ }
}
- return dns_lookup_int(domain, C_IN, type, retrans, retry);
+# endif /* DNSSEC_TEST */
+ return h;
}
-# endif /* 0 */
+# endif /* DANE */
# endif /* NAMED_BIND */
-#endif /* DNSMAP */
+#endif /* DNSMAP || DANE */