5b0945b570
Includes build infrastructure & config updates required for changes in 8.16.1 MFC after: 5 days
1604 lines
33 KiB
C
1604 lines
33 KiB
C
/*
|
|
* 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
|
|
* forth in the LICENSE file which can be found at the top level of
|
|
* the sendmail distribution.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan
|
|
* (Royal Institute of Technology, Stockholm, Sweden).
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* 3. Neither the name of the Institute nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sendmail.h>
|
|
#if DNSMAP || DANE
|
|
# if NAMED_BIND
|
|
# if NETINET
|
|
# include <netinet/in_systm.h>
|
|
# include <netinet/ip.h>
|
|
# 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
|
|
{
|
|
const char *st_name;
|
|
int st_type;
|
|
} stot[] =
|
|
{
|
|
# if NETINET
|
|
{ "A", T_A },
|
|
# endif
|
|
# if NETINET6
|
|
{ "AAAA", T_AAAA },
|
|
# endif
|
|
{ "NS", T_NS },
|
|
{ "CNAME", T_CNAME },
|
|
{ "PTR", T_PTR },
|
|
{ "MX", T_MX },
|
|
{ "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, 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
|
|
**
|
|
** Parameters:
|
|
** name -- name of resource record type
|
|
**
|
|
** Returns:
|
|
** type if succeeded.
|
|
** -1 otherwise.
|
|
*/
|
|
|
|
int
|
|
dns_string_to_type(name)
|
|
const char *name;
|
|
{
|
|
struct stot *p = stot;
|
|
|
|
for (p = stot; p->st_name != NULL; p++)
|
|
if (sm_strcasecmp(name, p->st_name) == 0)
|
|
return p->st_type;
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
** DNS_TYPE_TO_STRING -- convert resource record type into name
|
|
**
|
|
** Parameters:
|
|
** type -- resource record type
|
|
**
|
|
** Returns:
|
|
** name if succeeded.
|
|
** NULL otherwise.
|
|
*/
|
|
|
|
const char *
|
|
dns_type_to_string(type)
|
|
int type;
|
|
{
|
|
struct stot *p = stot;
|
|
|
|
for (p = stot; p->st_name != NULL; p++)
|
|
if (type == p->st_type)
|
|
return p->st_name;
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
** DNS_FREE_DATA -- free all components of a DNS_REPLY_T
|
|
**
|
|
** Parameters:
|
|
** dr -- pointer to DNS_REPLY_T
|
|
**
|
|
** Returns:
|
|
** none.
|
|
*/
|
|
|
|
void
|
|
dns_free_data(dr)
|
|
DNS_REPLY_T *dr;
|
|
{
|
|
RESOURCE_RECORD_T *rr;
|
|
|
|
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;
|
|
|
|
if (rr->rr_domain != NULL)
|
|
sm_free(rr->rr_domain);
|
|
if (rr->rr_u.rr_data != NULL)
|
|
sm_free(rr->rr_u.rr_data);
|
|
rr = rr->rr_next;
|
|
sm_free(tmp);
|
|
}
|
|
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;
|
|
}
|
|
|
|
/*
|
|
** PARSE_DNS_REPLY -- parse DNS reply data.
|
|
**
|
|
** 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, 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 *dr;
|
|
RESOURCE_RECORD_T **rr;
|
|
|
|
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(dr, 0, sizeof(*dr));
|
|
|
|
p = data;
|
|
|
|
/* doesn't work on Crays? */
|
|
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)
|
|
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) 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(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)
|
|
{
|
|
int type, class, ttl, size, txtlen;
|
|
|
|
status = dn_expand(data, data + len, p, host, sizeof(host));
|
|
if (status < 0)
|
|
goto error;
|
|
++ui;
|
|
p += status;
|
|
GETSHORT(type, p);
|
|
GETSHORT(class, p);
|
|
GETLONG(ttl, p);
|
|
GETSHORT(size, p);
|
|
if (p + size > data + len)
|
|
{
|
|
/*
|
|
** announced size of data exceeds length of
|
|
** data paket: someone is cheating.
|
|
*/
|
|
|
|
if (LogLevel > 5)
|
|
sm_syslog(LOG_WARNING, NOQID,
|
|
"ERROR: DNS RDLENGTH=%d > data len=%d",
|
|
size, len - (int)(p - data));
|
|
goto error;
|
|
}
|
|
*rr = (RESOURCE_RECORD_T *) sm_malloc(sizeof(**rr));
|
|
if (*rr == NULL)
|
|
goto error;
|
|
memset(*rr, 0, sizeof(**rr));
|
|
(*rr)->rr_domain = sm_strdup(host);
|
|
if ((*rr)->rr_domain == NULL)
|
|
goto error;
|
|
(*rr)->rr_type = type;
|
|
(*rr)->rr_class = class;
|
|
(*rr)->rr_ttl = ttl;
|
|
(*rr)->rr_size = size;
|
|
switch (type)
|
|
{
|
|
case T_NS:
|
|
case T_CNAME:
|
|
case T_PTR:
|
|
status = dn_expand(data, data + len, p, host,
|
|
sizeof(host));
|
|
if (status < 0)
|
|
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)
|
|
goto error;
|
|
break;
|
|
|
|
case T_MX:
|
|
case T_AFSDB:
|
|
status = dn_expand(data, data + len, p + 2, host,
|
|
sizeof(host));
|
|
if (status < 0)
|
|
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)
|
|
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)
|
|
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)
|
|
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];
|
|
(void) sm_strlcpy((*rr)->rr_u.rr_srv->srv_r_target,
|
|
host, l);
|
|
break;
|
|
|
|
case T_TXT:
|
|
|
|
/*
|
|
** The TXT record contains the length as
|
|
** leading byte, hence the value is restricted
|
|
** to 255, which is less than the maximum value
|
|
** of RDLENGTH (size). Nevertheless, txtlen
|
|
** must be less than size because the latter
|
|
** specifies the length of the entire TXT
|
|
** record.
|
|
*/
|
|
|
|
txtlen = *p;
|
|
if (txtlen >= size)
|
|
{
|
|
if (LogLevel > 5)
|
|
sm_syslog(LOG_WARNING, NOQID,
|
|
"ERROR: DNS TXT record size=%d <= text len=%d",
|
|
size, txtlen);
|
|
goto error;
|
|
}
|
|
(*rr)->rr_u.rr_txt = (char *) sm_malloc(txtlen + 1);
|
|
if ((*rr)->rr_u.rr_txt == 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)
|
|
{
|
|
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));
|
|
}
|
|
break;
|
|
}
|
|
p += size;
|
|
rr = &(*rr)->rr_next;
|
|
}
|
|
*rr = NULL;
|
|
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";
|
|
}
|
|
|
|
/*
|
|
** 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
|
|
** rr_class -- resource record class
|
|
** 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.
|
|
** NULL otherwise.
|
|
*/
|
|
|
|
DNS_REPLY_T *
|
|
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 *dr = NULL;
|
|
querybuf reply_buf;
|
|
unsigned char *reply;
|
|
int (*resfunc) __P((const char *, int, int, u_char *, int));
|
|
|
|
# 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))
|
|
{
|
|
_res.options |= RES_DEBUG;
|
|
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;
|
|
_res.retrans = retrans;
|
|
}
|
|
if (retry > 0)
|
|
{
|
|
save_retry = _res.retry;
|
|
_res.retry = retry;
|
|
}
|
|
errno = 0;
|
|
SM_SET_H_ERRNO(0);
|
|
reply = (unsigned char *)&reply_buf;
|
|
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, 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, SMRBSIZE, IP_MAXPACKET);
|
|
reply = (unsigned char *)sm_malloc(IP_MAXPACKET);
|
|
if (reply == NULL)
|
|
SM_SET_H_ERRNO(TRY_AGAIN);
|
|
else
|
|
{
|
|
SM_SET_H_ERRNO(0);
|
|
len = (*resfunc)(domain, rr_class, rr_type,
|
|
reply, IP_MAXPACKET);
|
|
}
|
|
}
|
|
}
|
|
_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))
|
|
{
|
|
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)
|
|
dr = parse_dns_reply(reply, len, flags);
|
|
if (reply != (unsigned char *)&reply_buf && reply != NULL)
|
|
{
|
|
sm_free(reply);
|
|
reply = NULL;
|
|
}
|
|
if (retrans > 0)
|
|
_res.retrans = save_retrans;
|
|
if (retry > 0)
|
|
_res.retry = save_retry;
|
|
return dr;
|
|
}
|
|
|
|
/*
|
|
** 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_map(domain, rr_class, rr_type, retrans, retry, options)
|
|
const char *domain;
|
|
int rr_class;
|
|
int rr_type;
|
|
time_t retrans;
|
|
int retry;
|
|
unsigned int options;
|
|
{
|
|
return dns_lookup_int(domain, rr_class, rr_type, retrans, retry,
|
|
options, RR_AS_TEXT, NULL, NULL);
|
|
}
|
|
|
|
# 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)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
# endif /* DNSSEC_TEST */
|
|
return h;
|
|
}
|
|
# endif /* DANE */
|
|
# endif /* NAMED_BIND */
|
|
#endif /* DNSMAP || DANE */
|