freebsd-nq/contrib/ldns/resolver.c
Dag-Erling Smørgrav 986ba33c7a Upgrade LDNS to 1.7.0.
I've been holding back on this because 1.7.0 requires OpenSSL 1.1.0 or
newer for full DANE support.  But we can't wait forever, and nothing in
base uses DANE anyway, so here we go.
2018-05-12 12:00:18 +00:00

1593 lines
36 KiB
C

/*
* resolver.c
*
* resolver implementation
*
* a Net::DNS like library for C
*
* (c) NLnet Labs, 2004-2006
*
* See the file LICENSE for the license
*/
#include <ldns/config.h>
#include <ldns/ldns.h>
#include <strings.h>
/* Access function for reading
* and setting the different Resolver
* options */
/* read */
uint16_t
ldns_resolver_port(const ldns_resolver *r)
{
return r->_port;
}
ldns_rdf *
ldns_resolver_source(const ldns_resolver *r)
{
return r->_source;
}
uint16_t
ldns_resolver_edns_udp_size(const ldns_resolver *r)
{
return r->_edns_udp_size;
}
uint8_t
ldns_resolver_retry(const ldns_resolver *r)
{
return r->_retry;
}
uint8_t
ldns_resolver_retrans(const ldns_resolver *r)
{
return r->_retrans;
}
bool
ldns_resolver_fallback(const ldns_resolver *r)
{
return r->_fallback;
}
uint8_t
ldns_resolver_ip6(const ldns_resolver *r)
{
return r->_ip6;
}
bool
ldns_resolver_recursive(const ldns_resolver *r)
{
return r->_recursive;
}
bool
ldns_resolver_debug(const ldns_resolver *r)
{
return r->_debug;
}
bool
ldns_resolver_dnsrch(const ldns_resolver *r)
{
return r->_dnsrch;
}
bool
ldns_resolver_fail(const ldns_resolver *r)
{
return r->_fail;
}
bool
ldns_resolver_defnames(const ldns_resolver *r)
{
return r->_defnames;
}
ldns_rdf *
ldns_resolver_domain(const ldns_resolver *r)
{
return r->_domain;
}
ldns_rdf **
ldns_resolver_searchlist(const ldns_resolver *r)
{
return r->_searchlist;
}
ldns_rdf **
ldns_resolver_nameservers(const ldns_resolver *r)
{
return r->_nameservers;
}
size_t
ldns_resolver_nameserver_count(const ldns_resolver *r)
{
return r->_nameserver_count;
}
bool
ldns_resolver_dnssec(const ldns_resolver *r)
{
return r->_dnssec;
}
bool
ldns_resolver_dnssec_cd(const ldns_resolver *r)
{
return r->_dnssec_cd;
}
ldns_rr_list *
ldns_resolver_dnssec_anchors(const ldns_resolver *r)
{
return r->_dnssec_anchors;
}
bool
ldns_resolver_trusted_key(const ldns_resolver *r, ldns_rr_list * keys, ldns_rr_list * trusted_keys)
{
size_t i;
bool result = false;
ldns_rr_list * trust_anchors;
ldns_rr * cur_rr;
if (!r || !keys) { return false; }
trust_anchors = ldns_resolver_dnssec_anchors(r);
if (!trust_anchors) { return false; }
for (i = 0; i < ldns_rr_list_rr_count(keys); i++) {
cur_rr = ldns_rr_list_rr(keys, i);
if (ldns_rr_list_contains_rr(trust_anchors, cur_rr)) {
if (trusted_keys) { ldns_rr_list_push_rr(trusted_keys, cur_rr); }
result = true;
}
}
return result;
}
bool
ldns_resolver_igntc(const ldns_resolver *r)
{
return r->_igntc;
}
bool
ldns_resolver_usevc(const ldns_resolver *r)
{
return r->_usevc;
}
size_t *
ldns_resolver_rtt(const ldns_resolver *r)
{
return r->_rtt;
}
size_t
ldns_resolver_nameserver_rtt(const ldns_resolver *r, size_t pos)
{
size_t *rtt;
assert(r != NULL);
rtt = ldns_resolver_rtt(r);
if (pos >= ldns_resolver_nameserver_count(r)) {
/* error ?*/
return 0;
} else {
return rtt[pos];
}
}
struct timeval
ldns_resolver_timeout(const ldns_resolver *r)
{
return r->_timeout;
}
const char *
ldns_resolver_tsig_keyname(const ldns_resolver *r)
{
return r->_tsig_keyname;
}
const char *
ldns_resolver_tsig_algorithm(const ldns_resolver *r)
{
return r->_tsig_algorithm;
}
const char *
ldns_resolver_tsig_keydata(const ldns_resolver *r)
{
return r->_tsig_keydata;
}
bool
ldns_resolver_random(const ldns_resolver *r)
{
return r->_random;
}
size_t
ldns_resolver_searchlist_count(const ldns_resolver *r)
{
return r->_searchlist_count;
}
/* write */
void
ldns_resolver_set_port(ldns_resolver *r, uint16_t p)
{
r->_port = p;
}
void
ldns_resolver_set_source(ldns_resolver *r, ldns_rdf *s)
{
r->_source = s;
}
ldns_rdf *
ldns_resolver_pop_nameserver(ldns_resolver *r)
{
ldns_rdf **nameservers;
ldns_rdf *pop;
size_t ns_count;
size_t *rtt;
assert(r != NULL);
ns_count = ldns_resolver_nameserver_count(r);
nameservers = ldns_resolver_nameservers(r);
rtt = ldns_resolver_rtt(r);
if (ns_count == 0 || !nameservers) {
return NULL;
}
pop = nameservers[ns_count - 1];
if (ns_count == 1) {
LDNS_FREE(nameservers);
LDNS_FREE(rtt);
ldns_resolver_set_nameservers(r, NULL);
ldns_resolver_set_rtt(r, NULL);
} else {
nameservers = LDNS_XREALLOC(nameservers, ldns_rdf *,
(ns_count - 1));
rtt = LDNS_XREALLOC(rtt, size_t, (ns_count - 1));
ldns_resolver_set_nameservers(r, nameservers);
ldns_resolver_set_rtt(r, rtt);
}
/* decr the count */
ldns_resolver_dec_nameserver_count(r);
return pop;
}
ldns_status
ldns_resolver_push_nameserver(ldns_resolver *r, const ldns_rdf *n)
{
ldns_rdf **nameservers;
size_t ns_count;
size_t *rtt;
if (ldns_rdf_get_type(n) != LDNS_RDF_TYPE_A &&
ldns_rdf_get_type(n) != LDNS_RDF_TYPE_AAAA) {
return LDNS_STATUS_ERR;
}
ns_count = ldns_resolver_nameserver_count(r);
nameservers = ldns_resolver_nameservers(r);
rtt = ldns_resolver_rtt(r);
/* make room for the next one */
if (ns_count == 0) {
nameservers = LDNS_XMALLOC(ldns_rdf *, 1);
} else {
nameservers = LDNS_XREALLOC(nameservers, ldns_rdf *, (ns_count + 1));
}
if(!nameservers)
return LDNS_STATUS_MEM_ERR;
/* set the new value in the resolver */
ldns_resolver_set_nameservers(r, nameservers);
/* don't forget the rtt */
if (ns_count == 0) {
rtt = LDNS_XMALLOC(size_t, 1);
} else {
rtt = LDNS_XREALLOC(rtt, size_t, (ns_count + 1));
}
if(!rtt)
return LDNS_STATUS_MEM_ERR;
/* slide n in its slot. */
/* we clone it here, because then we can free the original
* rr's where it stood */
nameservers[ns_count] = ldns_rdf_clone(n);
rtt[ns_count] = LDNS_RESOLV_RTT_MIN;
ldns_resolver_incr_nameserver_count(r);
ldns_resolver_set_rtt(r, rtt);
return LDNS_STATUS_OK;
}
ldns_status
ldns_resolver_push_nameserver_rr(ldns_resolver *r, const ldns_rr *rr)
{
ldns_rdf *address;
if ((!rr) || (ldns_rr_get_type(rr) != LDNS_RR_TYPE_A &&
ldns_rr_get_type(rr) != LDNS_RR_TYPE_AAAA)) {
return LDNS_STATUS_ERR;
}
address = ldns_rr_rdf(rr, 0); /* extract the ip number */
if (address) {
return ldns_resolver_push_nameserver(r, address);
} else {
return LDNS_STATUS_ERR;
}
}
ldns_status
ldns_resolver_push_nameserver_rr_list(ldns_resolver *r, const ldns_rr_list *rrlist)
{
ldns_rr *rr;
ldns_status stat;
size_t i;
stat = LDNS_STATUS_OK;
if (rrlist) {
for(i = 0; i < ldns_rr_list_rr_count(rrlist); i++) {
rr = ldns_rr_list_rr(rrlist, i);
if (ldns_resolver_push_nameserver_rr(r, rr) != LDNS_STATUS_OK) {
stat = LDNS_STATUS_ERR;
break;
}
}
return stat;
} else {
return LDNS_STATUS_ERR;
}
}
void
ldns_resolver_set_edns_udp_size(ldns_resolver *r, uint16_t s)
{
r->_edns_udp_size = s;
}
void
ldns_resolver_set_recursive(ldns_resolver *r, bool re)
{
r->_recursive = re;
}
void
ldns_resolver_set_dnssec(ldns_resolver *r, bool d)
{
r->_dnssec = d;
}
void
ldns_resolver_set_dnssec_cd(ldns_resolver *r, bool d)
{
r->_dnssec_cd = d;
}
void
ldns_resolver_set_dnssec_anchors(ldns_resolver *r, ldns_rr_list * l)
{
r->_dnssec_anchors = l;
}
ldns_status
ldns_resolver_push_dnssec_anchor(ldns_resolver *r, ldns_rr *rr)
{
ldns_rr_list * trust_anchors;
if ((!rr) || (ldns_rr_get_type(rr) != LDNS_RR_TYPE_DNSKEY &&
ldns_rr_get_type(rr) != LDNS_RR_TYPE_DS)) {
return LDNS_STATUS_ERR;
}
if (!(trust_anchors = ldns_resolver_dnssec_anchors(r))) { /* Initialize */
trust_anchors = ldns_rr_list_new();
ldns_resolver_set_dnssec_anchors(r, trust_anchors);
}
return (ldns_rr_list_push_rr(trust_anchors, ldns_rr_clone(rr))) ? LDNS_STATUS_OK : LDNS_STATUS_ERR;
}
void
ldns_resolver_set_igntc(ldns_resolver *r, bool i)
{
r->_igntc = i;
}
void
ldns_resolver_set_usevc(ldns_resolver *r, bool vc)
{
r->_usevc = vc;
}
void
ldns_resolver_set_debug(ldns_resolver *r, bool d)
{
r->_debug = d;
}
void
ldns_resolver_set_ip6(ldns_resolver *r, uint8_t ip6)
{
r->_ip6 = ip6;
}
void
ldns_resolver_set_fail(ldns_resolver *r, bool f)
{
r->_fail =f;
}
static void
ldns_resolver_set_searchlist_count(ldns_resolver *r, size_t c)
{
r->_searchlist_count = c;
}
void
ldns_resolver_set_nameserver_count(ldns_resolver *r, size_t c)
{
r->_nameserver_count = c;
}
void
ldns_resolver_set_dnsrch(ldns_resolver *r, bool d)
{
r->_dnsrch = d;
}
void
ldns_resolver_set_retry(ldns_resolver *r, uint8_t retry)
{
r->_retry = retry;
}
void
ldns_resolver_set_retrans(ldns_resolver *r, uint8_t retrans)
{
r->_retrans = retrans;
}
void
ldns_resolver_set_fallback(ldns_resolver *r, bool fallback)
{
r->_fallback = fallback;
}
void
ldns_resolver_set_nameservers(ldns_resolver *r, ldns_rdf **n)
{
r->_nameservers = n;
}
void
ldns_resolver_set_defnames(ldns_resolver *r, bool d)
{
r->_defnames = d;
}
void
ldns_resolver_set_rtt(ldns_resolver *r, size_t *rtt)
{
r->_rtt = rtt;
}
void
ldns_resolver_set_nameserver_rtt(ldns_resolver *r, size_t pos, size_t value)
{
size_t *rtt;
assert(r != NULL);
rtt = ldns_resolver_rtt(r);
if (pos >= ldns_resolver_nameserver_count(r)) {
/* error ?*/
} else {
rtt[pos] = value;
}
}
void
ldns_resolver_incr_nameserver_count(ldns_resolver *r)
{
size_t c;
c = ldns_resolver_nameserver_count(r);
ldns_resolver_set_nameserver_count(r, ++c);
}
void
ldns_resolver_dec_nameserver_count(ldns_resolver *r)
{
size_t c;
c = ldns_resolver_nameserver_count(r);
if (c == 0) {
return;
} else {
ldns_resolver_set_nameserver_count(r, --c);
}
}
void
ldns_resolver_set_domain(ldns_resolver *r, ldns_rdf *d)
{
r->_domain = d;
}
void
ldns_resolver_set_timeout(ldns_resolver *r, struct timeval timeout)
{
r->_timeout.tv_sec = timeout.tv_sec;
r->_timeout.tv_usec = timeout.tv_usec;
}
void
ldns_resolver_push_searchlist(ldns_resolver *r, ldns_rdf *d)
{
ldns_rdf **searchlist;
size_t list_count;
if (ldns_rdf_get_type(d) != LDNS_RDF_TYPE_DNAME) {
return;
}
list_count = ldns_resolver_searchlist_count(r);
searchlist = ldns_resolver_searchlist(r);
searchlist = LDNS_XREALLOC(searchlist, ldns_rdf *, (list_count + 1));
if (searchlist) {
r->_searchlist = searchlist;
searchlist[list_count] = ldns_rdf_clone(d);
ldns_resolver_set_searchlist_count(r, list_count + 1);
} /* no way to report mem err */
}
void
ldns_resolver_set_tsig_keyname(ldns_resolver *r, const char *tsig_keyname)
{
LDNS_FREE(r->_tsig_keyname);
r->_tsig_keyname = strdup(tsig_keyname);
}
void
ldns_resolver_set_tsig_algorithm(ldns_resolver *r, const char *tsig_algorithm)
{
LDNS_FREE(r->_tsig_algorithm);
r->_tsig_algorithm = strdup(tsig_algorithm);
}
void
ldns_resolver_set_tsig_keydata(ldns_resolver *r, const char *tsig_keydata)
{
LDNS_FREE(r->_tsig_keydata);
r->_tsig_keydata = strdup(tsig_keydata);
}
void
ldns_resolver_set_random(ldns_resolver *r, bool b)
{
r->_random = b;
}
/* more sophisticated functions */
ldns_resolver *
ldns_resolver_new(void)
{
ldns_resolver *r;
r = LDNS_MALLOC(ldns_resolver);
if (!r) {
return NULL;
}
r->_searchlist = NULL;
r->_nameservers = NULL;
r->_rtt = NULL;
/* defaults are filled out */
ldns_resolver_set_searchlist_count(r, 0);
ldns_resolver_set_nameserver_count(r, 0);
ldns_resolver_set_usevc(r, 0);
ldns_resolver_set_port(r, LDNS_PORT);
ldns_resolver_set_domain(r, NULL);
ldns_resolver_set_defnames(r, false);
ldns_resolver_set_retry(r, 3);
ldns_resolver_set_retrans(r, 2);
ldns_resolver_set_fallback(r, true);
ldns_resolver_set_fail(r, false);
ldns_resolver_set_edns_udp_size(r, 0);
ldns_resolver_set_dnssec(r, false);
ldns_resolver_set_dnssec_cd(r, false);
ldns_resolver_set_dnssec_anchors(r, NULL);
ldns_resolver_set_ip6(r, LDNS_RESOLV_INETANY);
ldns_resolver_set_igntc(r, false);
ldns_resolver_set_recursive(r, false);
ldns_resolver_set_dnsrch(r, true);
ldns_resolver_set_source(r, NULL);
ldns_resolver_set_ixfr_serial(r, 0);
/* randomize the nameserver to be queried
* when there are multiple
*/
ldns_resolver_set_random(r, true);
ldns_resolver_set_debug(r, 0);
r->_timeout.tv_sec = LDNS_DEFAULT_TIMEOUT_SEC;
r->_timeout.tv_usec = LDNS_DEFAULT_TIMEOUT_USEC;
r->_socket = -1;
r->_axfr_soa_count = 0;
r->_axfr_i = 0;
r->_cur_axfr_pkt = NULL;
r->_tsig_keyname = NULL;
r->_tsig_keydata = NULL;
r->_tsig_algorithm = NULL;
return r;
}
ldns_resolver *
ldns_resolver_clone(ldns_resolver *src)
{
ldns_resolver *dst;
size_t i;
assert(src != NULL);
if (!(dst = LDNS_MALLOC(ldns_resolver))) return NULL;
(void) memcpy(dst, src, sizeof(ldns_resolver));
if (dst->_searchlist_count == 0)
dst->_searchlist = NULL;
else {
if (!(dst->_searchlist =
LDNS_XMALLOC(ldns_rdf *, dst->_searchlist_count)))
goto error;
for (i = 0; i < dst->_searchlist_count; i++)
if (!(dst->_searchlist[i] =
ldns_rdf_clone(src->_searchlist[i]))) {
dst->_searchlist_count = i;
goto error_searchlist;
}
}
if (dst->_nameserver_count == 0) {
dst->_nameservers = NULL;
dst->_rtt = NULL;
} else {
if (!(dst->_nameservers =
LDNS_XMALLOC(ldns_rdf *, dst->_nameserver_count)))
goto error_searchlist;
for (i = 0; i < dst->_nameserver_count; i++)
if (!(dst->_nameservers[i] =
ldns_rdf_clone(src->_nameservers[i]))) {
dst->_nameserver_count = i;
goto error_nameservers;
}
if (!(dst->_rtt =
LDNS_XMALLOC(size_t, dst->_nameserver_count)))
goto error_nameservers;
(void) memcpy(dst->_rtt, src->_rtt,
sizeof(size_t) * dst->_nameserver_count);
}
if (dst->_domain && (!(dst->_domain = ldns_rdf_clone(src->_domain))))
goto error_rtt;
if (dst->_tsig_keyname &&
(!(dst->_tsig_keyname = strdup(src->_tsig_keyname))))
goto error_domain;
if (dst->_tsig_keydata &&
(!(dst->_tsig_keydata = strdup(src->_tsig_keydata))))
goto error_tsig_keyname;
if (dst->_tsig_algorithm &&
(!(dst->_tsig_algorithm = strdup(src->_tsig_algorithm))))
goto error_tsig_keydata;
if (dst->_cur_axfr_pkt &&
(!(dst->_cur_axfr_pkt = ldns_pkt_clone(src->_cur_axfr_pkt))))
goto error_tsig_algorithm;
if (dst->_dnssec_anchors &&
(!(dst->_dnssec_anchors=ldns_rr_list_clone(src->_dnssec_anchors))))
goto error_cur_axfr_pkt;
return dst;
error_cur_axfr_pkt:
ldns_pkt_free(dst->_cur_axfr_pkt);
error_tsig_algorithm:
LDNS_FREE(dst->_tsig_algorithm);
error_tsig_keydata:
LDNS_FREE(dst->_tsig_keydata);
error_tsig_keyname:
LDNS_FREE(dst->_tsig_keyname);
error_domain:
ldns_rdf_deep_free(dst->_domain);
error_rtt:
LDNS_FREE(dst->_rtt);
error_nameservers:
for (i = 0; i < dst->_nameserver_count; i++)
ldns_rdf_deep_free(dst->_nameservers[i]);
LDNS_FREE(dst->_nameservers);
error_searchlist:
for (i = 0; i < dst->_searchlist_count; i++)
ldns_rdf_deep_free(dst->_searchlist[i]);
LDNS_FREE(dst->_searchlist);
error:
LDNS_FREE(dst);
return NULL;
}
ldns_status
ldns_resolver_new_frm_fp(ldns_resolver **res, FILE *fp)
{
return ldns_resolver_new_frm_fp_l(res, fp, NULL);
}
ldns_status
ldns_resolver_new_frm_fp_l(ldns_resolver **res, FILE *fp, int *line_nr)
{
ldns_resolver *r;
const char *keyword[LDNS_RESOLV_KEYWORDS];
char word[LDNS_MAX_LINELEN + 1];
int8_t expect;
uint8_t i;
ldns_rdf *tmp;
#ifdef HAVE_SSL
ldns_rr *tmp_rr;
#endif
ssize_t gtr, bgtr;
ldns_buffer *b;
int lnr = 0, oldline;
FILE* myfp = fp;
if(!line_nr) line_nr = &lnr;
if(!fp) {
myfp = fopen("/etc/resolv.conf", "r");
if(!myfp)
return LDNS_STATUS_FILE_ERR;
}
/* do this better
* expect =
* 0: keyword
* 1: default domain dname
* 2: NS aaaa or a record
*/
/* recognized keywords */
keyword[LDNS_RESOLV_NAMESERVER] = "nameserver";
keyword[LDNS_RESOLV_DEFDOMAIN] = "domain";
keyword[LDNS_RESOLV_SEARCH] = "search";
/* these two are read but not used atm TODO */
keyword[LDNS_RESOLV_SORTLIST] = "sortlist";
keyword[LDNS_RESOLV_OPTIONS] = "options";
keyword[LDNS_RESOLV_ANCHOR] = "anchor";
expect = LDNS_RESOLV_KEYWORD;
r = ldns_resolver_new();
if (!r) {
if(!fp) fclose(myfp);
return LDNS_STATUS_MEM_ERR;
}
gtr = 1;
word[0] = 0;
oldline = *line_nr;
expect = LDNS_RESOLV_KEYWORD;
while (gtr > 0) {
/* check comments */
if (word[0] == '#') {
word[0]='x';
if(oldline == *line_nr) {
/* skip until end of line */
int c;
do {
c = fgetc(myfp);
} while(c != EOF && c != '\n');
if(c=='\n') (*line_nr)++;
}
/* and read next to prepare for further parsing */
oldline = *line_nr;
continue;
}
oldline = *line_nr;
switch(expect) {
case LDNS_RESOLV_KEYWORD:
/* keyword */
gtr = ldns_fget_token_l(myfp, word, LDNS_PARSE_NORMAL, 0, line_nr);
if (gtr != 0) {
if(word[0] == '#') continue;
for(i = 0; i < LDNS_RESOLV_KEYWORDS; i++) {
if (strcasecmp(keyword[i], word) == 0) {
/* chosen the keyword and
* expect values carefully
*/
expect = i;
break;
}
}
/* no keyword recognized */
if (expect == LDNS_RESOLV_KEYWORD) {
/* skip line */
/*
ldns_resolver_deep_free(r);
if(!fp) fclose(myfp);
return LDNS_STATUS_SYNTAX_KEYWORD_ERR;
*/
}
}
break;
case LDNS_RESOLV_DEFDOMAIN:
/* default domain dname */
gtr = ldns_fget_token_l(myfp, word, LDNS_PARSE_NORMAL, 0, line_nr);
if (gtr == 0) {
if(!fp) fclose(myfp);
return LDNS_STATUS_SYNTAX_MISSING_VALUE_ERR;
}
if(word[0] == '#') {
expect = LDNS_RESOLV_KEYWORD;
continue;
}
tmp = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_DNAME, word);
if (!tmp) {
ldns_resolver_deep_free(r);
if(!fp) fclose(myfp);
return LDNS_STATUS_SYNTAX_DNAME_ERR;
}
/* DOn't free, because we copy the pointer */
ldns_resolver_set_domain(r, tmp);
expect = LDNS_RESOLV_KEYWORD;
break;
case LDNS_RESOLV_NAMESERVER:
/* NS aaaa or a record */
gtr = ldns_fget_token_l(myfp, word, LDNS_PARSE_NORMAL, 0, line_nr);
if (gtr == 0) {
if(!fp) fclose(myfp);
return LDNS_STATUS_SYNTAX_MISSING_VALUE_ERR;
}
if(word[0] == '#') {
expect = LDNS_RESOLV_KEYWORD;
continue;
}
if(strchr(word, '%')) {
/* snip off interface labels,
* fe80::222:19ff:fe31:4222%eth0 */
strchr(word, '%')[0]=0;
}
tmp = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_AAAA, word);
if (!tmp) {
/* try ip4 */
tmp = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_A, word);
}
/* could not parse it, exit */
if (!tmp) {
ldns_resolver_deep_free(r);
if(!fp) fclose(myfp);
return LDNS_STATUS_SYNTAX_ERR;
}
(void)ldns_resolver_push_nameserver(r, tmp);
ldns_rdf_deep_free(tmp);
expect = LDNS_RESOLV_KEYWORD;
break;
case LDNS_RESOLV_SEARCH:
/* search list domain dname */
gtr = ldns_fget_token_l(myfp, word, LDNS_PARSE_SKIP_SPACE, 0, line_nr);
b = LDNS_MALLOC(ldns_buffer);
if(!b) {
ldns_resolver_deep_free(r);
if(!fp) fclose(myfp);
return LDNS_STATUS_MEM_ERR;
}
ldns_buffer_new_frm_data(b, word, (size_t) gtr);
if(ldns_buffer_status(b) != LDNS_STATUS_OK) {
LDNS_FREE(b);
ldns_resolver_deep_free(r);
if(!fp) fclose(myfp);
return LDNS_STATUS_MEM_ERR;
}
bgtr = ldns_bget_token(b, word, LDNS_PARSE_NORMAL, (size_t) gtr + 1);
while (bgtr > 0) {
gtr -= bgtr;
if(word[0] == '#') {
expect = LDNS_RESOLV_KEYWORD;
break;
}
tmp = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_DNAME, word);
if (!tmp) {
ldns_resolver_deep_free(r);
ldns_buffer_free(b);
if(!fp) fclose(myfp);
return LDNS_STATUS_SYNTAX_DNAME_ERR;
}
ldns_resolver_push_searchlist(r, tmp);
ldns_rdf_deep_free(tmp);
bgtr = ldns_bget_token(b, word, LDNS_PARSE_NORMAL,
(size_t) gtr + 1);
}
ldns_buffer_free(b);
if (expect != LDNS_RESOLV_KEYWORD) {
gtr = 1;
expect = LDNS_RESOLV_KEYWORD;
}
break;
case LDNS_RESOLV_SORTLIST:
gtr = ldns_fget_token_l(myfp, word, LDNS_PARSE_SKIP_SPACE, 0, line_nr);
/* sortlist not implemented atm */
expect = LDNS_RESOLV_KEYWORD;
break;
case LDNS_RESOLV_OPTIONS:
gtr = ldns_fget_token_l(myfp, word, LDNS_PARSE_SKIP_SPACE, 0, line_nr);
/* options not implemented atm */
expect = LDNS_RESOLV_KEYWORD;
break;
case LDNS_RESOLV_ANCHOR:
/* a file containing a DNSSEC trust anchor */
gtr = ldns_fget_token_l(myfp, word, LDNS_PARSE_NORMAL, 0, line_nr);
if (gtr == 0) {
ldns_resolver_deep_free(r);
if(!fp) fclose(myfp);
return LDNS_STATUS_SYNTAX_MISSING_VALUE_ERR;
}
if(word[0] == '#') {
expect = LDNS_RESOLV_KEYWORD;
continue;
}
#ifdef HAVE_SSL
tmp_rr = ldns_read_anchor_file(word);
(void) ldns_resolver_push_dnssec_anchor(r, tmp_rr);
ldns_rr_free(tmp_rr);
#endif
expect = LDNS_RESOLV_KEYWORD;
break;
}
}
if(!fp)
fclose(myfp);
if (res) {
*res = r;
return LDNS_STATUS_OK;
} else {
ldns_resolver_deep_free(r);
return LDNS_STATUS_NULL;
}
}
ldns_status
ldns_resolver_new_frm_file(ldns_resolver **res, const char *filename)
{
ldns_resolver *r;
FILE *fp;
ldns_status s;
if (!filename) {
fp = fopen(LDNS_RESOLV_CONF, "r");
} else {
fp = fopen(filename, "r");
}
if (!fp) {
return LDNS_STATUS_FILE_ERR;
}
s = ldns_resolver_new_frm_fp(&r, fp);
fclose(fp);
if (s == LDNS_STATUS_OK) {
if (res) {
*res = r;
return LDNS_STATUS_OK;
} else {
ldns_resolver_free(r);
return LDNS_STATUS_NULL;
}
}
return s;
}
void
ldns_resolver_free(ldns_resolver *res)
{
LDNS_FREE(res);
}
void
ldns_resolver_deep_free(ldns_resolver *res)
{
size_t i;
if (res) {
close_socket(res->_socket);
if (res->_searchlist) {
for (i = 0; i < ldns_resolver_searchlist_count(res); i++) {
ldns_rdf_deep_free(res->_searchlist[i]);
}
LDNS_FREE(res->_searchlist);
}
if (res->_nameservers) {
for (i = 0; i < res->_nameserver_count; i++) {
ldns_rdf_deep_free(res->_nameservers[i]);
}
LDNS_FREE(res->_nameservers);
}
if (ldns_resolver_domain(res)) {
ldns_rdf_deep_free(ldns_resolver_domain(res));
}
if (res->_tsig_keyname) {
LDNS_FREE(res->_tsig_keyname);
}
if (res->_tsig_keydata) {
LDNS_FREE(res->_tsig_keydata);
}
if (res->_tsig_algorithm) {
LDNS_FREE(res->_tsig_algorithm);
}
if (res->_cur_axfr_pkt) {
ldns_pkt_free(res->_cur_axfr_pkt);
}
if (res->_rtt) {
LDNS_FREE(res->_rtt);
}
if (res->_dnssec_anchors) {
ldns_rr_list_deep_free(res->_dnssec_anchors);
}
LDNS_FREE(res);
}
}
ldns_status
ldns_resolver_search_status(ldns_pkt** pkt,
ldns_resolver *r, const ldns_rdf *name,
ldns_rr_type t, ldns_rr_class c, uint16_t flags)
{
ldns_rdf *new_name;
ldns_rdf **search_list;
size_t i;
ldns_status s = LDNS_STATUS_OK;
ldns_rdf root_dname = { 1, LDNS_RDF_TYPE_DNAME, (void *)"" };
if (ldns_dname_absolute(name)) {
/* query as-is */
return ldns_resolver_query_status(pkt, r, name, t, c, flags);
} else if (ldns_resolver_dnsrch(r)) {
search_list = ldns_resolver_searchlist(r);
for (i = 0; i <= ldns_resolver_searchlist_count(r); i++) {
if (i == ldns_resolver_searchlist_count(r)) {
new_name = ldns_dname_cat_clone(name,
&root_dname);
} else {
new_name = ldns_dname_cat_clone(name,
search_list[i]);
}
s = ldns_resolver_query_status(pkt, r,
new_name, t, c, flags);
ldns_rdf_free(new_name);
if (pkt && *pkt) {
if (s == LDNS_STATUS_OK &&
ldns_pkt_get_rcode(*pkt) ==
LDNS_RCODE_NOERROR) {
return LDNS_STATUS_OK;
}
ldns_pkt_free(*pkt);
*pkt = NULL;
}
}
}
return s;
}
ldns_pkt *
ldns_resolver_search(const ldns_resolver *r,const ldns_rdf *name,
ldns_rr_type t, ldns_rr_class c, uint16_t flags)
{
ldns_pkt* pkt = NULL;
if (ldns_resolver_search_status(&pkt, (ldns_resolver *)r,
name, t, c, flags) != LDNS_STATUS_OK) {
ldns_pkt_free(pkt);
}
return pkt;
}
ldns_status
ldns_resolver_query_status(ldns_pkt** pkt,
ldns_resolver *r, const ldns_rdf *name,
ldns_rr_type t, ldns_rr_class c, uint16_t flags)
{
ldns_rdf *newname;
ldns_status status;
if (!ldns_resolver_defnames(r) || !ldns_resolver_domain(r)) {
return ldns_resolver_send(pkt, r, name, t, c, flags);
}
newname = ldns_dname_cat_clone(name, ldns_resolver_domain(r));
if (!newname) {
return LDNS_STATUS_MEM_ERR;
}
status = ldns_resolver_send(pkt, r, newname, t, c, flags);
ldns_rdf_free(newname);
return status;
}
ldns_pkt *
ldns_resolver_query(const ldns_resolver *r, const ldns_rdf *name,
ldns_rr_type t, ldns_rr_class c, uint16_t flags)
{
ldns_pkt* pkt = NULL;
if (ldns_resolver_query_status(&pkt, (ldns_resolver *)r,
name, t, c, flags) != LDNS_STATUS_OK) {
ldns_pkt_free(pkt);
}
return pkt;
}
static size_t *
ldns_resolver_backup_rtt(ldns_resolver *r)
{
size_t *new_rtt;
size_t *old_rtt = ldns_resolver_rtt(r);
if (old_rtt && ldns_resolver_nameserver_count(r)) {
new_rtt = LDNS_XMALLOC(size_t
, ldns_resolver_nameserver_count(r));
memcpy(new_rtt, old_rtt, sizeof(size_t)
* ldns_resolver_nameserver_count(r));
ldns_resolver_set_rtt(r, new_rtt);
return old_rtt;
}
return NULL;
}
static void
ldns_resolver_restore_rtt(ldns_resolver *r, size_t *old_rtt)
{
size_t *cur_rtt = ldns_resolver_rtt(r);
if (cur_rtt) {
LDNS_FREE(cur_rtt);
}
ldns_resolver_set_rtt(r, old_rtt);
}
ldns_status
ldns_resolver_send_pkt(ldns_pkt **answer, ldns_resolver *r,
ldns_pkt *query_pkt)
{
ldns_pkt *answer_pkt = NULL;
ldns_status stat = LDNS_STATUS_OK;
size_t *rtt;
stat = ldns_send(&answer_pkt, (ldns_resolver *)r, query_pkt);
if (stat != LDNS_STATUS_OK) {
if(answer_pkt) {
ldns_pkt_free(answer_pkt);
answer_pkt = NULL;
}
} else {
/* if tc=1 fall back to EDNS and/or TCP */
/* check for tcp first (otherwise we don't care about tc=1) */
if (!ldns_resolver_usevc(r) && ldns_resolver_fallback(r)) {
if (ldns_pkt_tc(answer_pkt)) {
/* was EDNS0 set? */
if (ldns_pkt_edns_udp_size(query_pkt) == 0) {
ldns_pkt_set_edns_udp_size(query_pkt
, 4096);
ldns_pkt_free(answer_pkt);
answer_pkt = NULL;
/* Nameservers should not become
* unreachable because fragments are
* dropped (network error). We might
* still have success with TCP.
* Therefore maintain reachability
* statuses of the nameservers by
* backup and restore the rtt list.
*/
rtt = ldns_resolver_backup_rtt(r);
stat = ldns_send(&answer_pkt, r
, query_pkt);
ldns_resolver_restore_rtt(r, rtt);
}
/* either way, if it is still truncated, use TCP */
if (stat != LDNS_STATUS_OK ||
ldns_pkt_tc(answer_pkt)) {
ldns_resolver_set_usevc(r, true);
ldns_pkt_free(answer_pkt);
stat = ldns_send(&answer_pkt, r, query_pkt);
ldns_resolver_set_usevc(r, false);
}
}
}
}
if (answer) {
*answer = answer_pkt;
}
return stat;
}
ldns_status
ldns_resolver_prepare_query_pkt(ldns_pkt **query_pkt, ldns_resolver *r,
const ldns_rdf *name, ldns_rr_type t,
ldns_rr_class c, uint16_t flags)
{
struct timeval now;
ldns_rr* soa = NULL;
/* prepare a question pkt from the parameters
* and then send this */
if (t == LDNS_RR_TYPE_IXFR) {
ldns_rdf *owner_rdf;
ldns_rdf *mname_rdf;
ldns_rdf *rname_rdf;
ldns_rdf *serial_rdf;
ldns_rdf *refresh_rdf;
ldns_rdf *retry_rdf;
ldns_rdf *expire_rdf;
ldns_rdf *minimum_rdf;
soa = ldns_rr_new();
if (!soa) {
return LDNS_STATUS_ERR;
}
owner_rdf = ldns_rdf_clone(name);
if (!owner_rdf) {
ldns_rr_free(soa);
return LDNS_STATUS_ERR;
}
ldns_rr_set_owner(soa, owner_rdf);
ldns_rr_set_type(soa, LDNS_RR_TYPE_SOA);
ldns_rr_set_class(soa, c);
ldns_rr_set_question(soa, false);
if (ldns_str2rdf_dname(&mname_rdf, ".") != LDNS_STATUS_OK) {
ldns_rr_free(soa);
return LDNS_STATUS_ERR;
} else ldns_rr_push_rdf(soa, mname_rdf);
if (ldns_str2rdf_dname(&rname_rdf, ".") != LDNS_STATUS_OK) {
ldns_rr_free(soa);
return LDNS_STATUS_ERR;
} else ldns_rr_push_rdf(soa, rname_rdf);
serial_rdf = ldns_native2rdf_int32(LDNS_RDF_TYPE_INT32, ldns_resolver_get_ixfr_serial(r));
if (!serial_rdf) {
ldns_rr_free(soa);
return LDNS_STATUS_ERR;
} else ldns_rr_push_rdf(soa, serial_rdf);
refresh_rdf = ldns_native2rdf_int32(LDNS_RDF_TYPE_INT32, 0);
if (!refresh_rdf) {
ldns_rr_free(soa);
return LDNS_STATUS_ERR;
} else ldns_rr_push_rdf(soa, refresh_rdf);
retry_rdf = ldns_native2rdf_int32(LDNS_RDF_TYPE_INT32, 0);
if (!retry_rdf) {
ldns_rr_free(soa);
return LDNS_STATUS_ERR;
} else ldns_rr_push_rdf(soa, retry_rdf);
expire_rdf = ldns_native2rdf_int32(LDNS_RDF_TYPE_INT32, 0);
if (!expire_rdf) {
ldns_rr_free(soa);
return LDNS_STATUS_ERR;
} else ldns_rr_push_rdf(soa, expire_rdf);
minimum_rdf = ldns_native2rdf_int32(LDNS_RDF_TYPE_INT32, 0);
if (!minimum_rdf) {
ldns_rr_free(soa);
return LDNS_STATUS_ERR;
} else ldns_rr_push_rdf(soa, minimum_rdf);
*query_pkt = ldns_pkt_ixfr_request_new(ldns_rdf_clone(name),
c, flags, soa);
} else {
*query_pkt = ldns_pkt_query_new(ldns_rdf_clone(name), t, c, flags);
}
if (!*query_pkt) {
ldns_rr_free(soa);
return LDNS_STATUS_ERR;
}
/* set DO bit if necessary */
if (ldns_resolver_dnssec(r)) {
if (ldns_resolver_edns_udp_size(r) == 0) {
ldns_resolver_set_edns_udp_size(r, 4096);
}
ldns_pkt_set_edns_do(*query_pkt, true);
if (ldns_resolver_dnssec_cd(r) || (flags & LDNS_CD)) {
ldns_pkt_set_cd(*query_pkt, true);
}
}
/* transfer the udp_edns_size from the resolver to the packet */
if (ldns_resolver_edns_udp_size(r) != 0) {
ldns_pkt_set_edns_udp_size(*query_pkt, ldns_resolver_edns_udp_size(r));
}
/* set the timestamp */
now.tv_sec = time(NULL);
now.tv_usec = 0;
ldns_pkt_set_timestamp(*query_pkt, now);
if (ldns_resolver_debug(r)) {
ldns_pkt_print(stdout, *query_pkt);
}
/* only set the id if it is not set yet */
if (ldns_pkt_id(*query_pkt) == 0) {
ldns_pkt_set_random_id(*query_pkt);
}
return LDNS_STATUS_OK;
}
ldns_status
ldns_resolver_send(ldns_pkt **answer, ldns_resolver *r, const ldns_rdf *name,
ldns_rr_type t, ldns_rr_class c, uint16_t flags)
{
ldns_pkt *query_pkt;
ldns_pkt *answer_pkt;
ldns_status status;
assert(r != NULL);
assert(name != NULL);
answer_pkt = NULL;
/* do all the preprocessing here, then fire of an query to
* the network */
if (0 == t) {
t= LDNS_RR_TYPE_A;
}
if (0 == c) {
c= LDNS_RR_CLASS_IN;
}
if (0 == ldns_resolver_nameserver_count(r)) {
return LDNS_STATUS_RES_NO_NS;
}
if (ldns_rdf_get_type(name) != LDNS_RDF_TYPE_DNAME) {
return LDNS_STATUS_RES_QUERY;
}
status = ldns_resolver_prepare_query_pkt(&query_pkt, r, name,
t, c, flags);
if (status != LDNS_STATUS_OK) {
return status;
}
/* if tsig values are set, tsign it */
/* TODO: make last 3 arguments optional too? maybe make complete
rr instead of separate values in resolver (and packet)
Jelte
should this go in pkt_prepare?
*/
if (ldns_resolver_tsig_keyname(r) && ldns_resolver_tsig_keydata(r)) {
#ifdef HAVE_SSL
status = ldns_pkt_tsig_sign(query_pkt,
ldns_resolver_tsig_keyname(r),
ldns_resolver_tsig_keydata(r),
300, ldns_resolver_tsig_algorithm(r), NULL);
if (status != LDNS_STATUS_OK) {
ldns_pkt_free(query_pkt);
return LDNS_STATUS_CRYPTO_TSIG_ERR;
}
#else
ldns_pkt_free(query_pkt);
return LDNS_STATUS_CRYPTO_TSIG_ERR;
#endif /* HAVE_SSL */
}
status = ldns_resolver_send_pkt(&answer_pkt, r, query_pkt);
ldns_pkt_free(query_pkt);
/* allows answer to be NULL when not interested in return value */
if (answer) {
*answer = answer_pkt;
}
return status;
}
ldns_rr *
ldns_axfr_next(ldns_resolver *resolver)
{
ldns_rr *cur_rr;
uint8_t *packet_wire;
size_t packet_wire_size;
ldns_status status;
/* check if start() has been called */
if (!resolver || resolver->_socket == -1) {
return NULL;
}
if (resolver->_cur_axfr_pkt) {
if (resolver->_axfr_i == ldns_pkt_ancount(resolver->_cur_axfr_pkt)) {
ldns_pkt_free(resolver->_cur_axfr_pkt);
resolver->_cur_axfr_pkt = NULL;
return ldns_axfr_next(resolver);
}
cur_rr = ldns_rr_clone(ldns_rr_list_rr(
ldns_pkt_answer(resolver->_cur_axfr_pkt),
resolver->_axfr_i));
resolver->_axfr_i++;
if (ldns_rr_get_type(cur_rr) == LDNS_RR_TYPE_SOA) {
resolver->_axfr_soa_count++;
if (resolver->_axfr_soa_count >= 2) {
close_socket(resolver->_socket);
ldns_pkt_free(resolver->_cur_axfr_pkt);
resolver->_cur_axfr_pkt = NULL;
}
}
return cur_rr;
} else {
packet_wire = ldns_tcp_read_wire_timeout(resolver->_socket, &packet_wire_size, resolver->_timeout);
if(!packet_wire)
return NULL;
status = ldns_wire2pkt(&resolver->_cur_axfr_pkt, packet_wire,
packet_wire_size);
LDNS_FREE(packet_wire);
resolver->_axfr_i = 0;
if (status != LDNS_STATUS_OK) {
/* TODO: make status return type of this function (...api change) */
#ifdef STDERR_MSGS
fprintf(stderr, "Error parsing rr during AXFR: %s\n", ldns_get_errorstr_by_id(status));
#endif
/* we must now also close the socket, otherwise subsequent uses of the
same resolver structure will fail because the link is still open or
in an undefined state */
close_socket(resolver->_socket);
return NULL;
} else if (ldns_pkt_get_rcode(resolver->_cur_axfr_pkt) != 0) {
#ifdef STDERR_MSGS
ldns_lookup_table *rcode = ldns_lookup_by_id(
ldns_rcodes,(int) ldns_pkt_get_rcode(
resolver->_cur_axfr_pkt));
if (rcode) {
fprintf(stderr, "Error in AXFR: %s\n",
rcode->name);
} else {
fprintf(stderr, "Error in AXFR: %d\n",
(int) ldns_pkt_get_rcode(
resolver->_cur_axfr_pkt));
}
#endif
/* we must now also close the socket, otherwise subsequent uses of the
same resolver structure will fail because the link is still open or
in an undefined state */
close_socket(resolver->_socket);
return NULL;
} else {
return ldns_axfr_next(resolver);
}
}
}
/* this function is needed to abort a transfer that is in progress;
* without it an aborted transfer will lead to the AXFR code in the
* library staying in an indetermined state because the socket for the
* AXFR is never closed
*/
void
ldns_axfr_abort(ldns_resolver *resolver)
{
/* Only abort if an actual AXFR is in progress */
if (resolver->_socket != 0)
{
#ifndef USE_WINSOCK
close(resolver->_socket);
#else
closesocket(resolver->_socket);
#endif
resolver->_socket = 0;
}
}
bool
ldns_axfr_complete(const ldns_resolver *res)
{
/* complete when soa count is 2? */
return res->_axfr_soa_count == 2;
}
ldns_pkt *
ldns_axfr_last_pkt(const ldns_resolver *res)
{
return res->_cur_axfr_pkt;
}
void
ldns_resolver_set_ixfr_serial(ldns_resolver *r, uint32_t serial)
{
r->_serial = serial;
}
uint32_t
ldns_resolver_get_ixfr_serial(const ldns_resolver *res)
{
return res->_serial;
}
/* random isn't really that good */
void
ldns_resolver_nameservers_randomize(ldns_resolver *r)
{
uint16_t i, j;
ldns_rdf **ns, *tmpns;
size_t *rtt, tmprtt;
/* should I check for ldns_resolver_random?? */
assert(r != NULL);
ns = ldns_resolver_nameservers(r);
rtt = ldns_resolver_rtt(r);
for (i = 0; i < ldns_resolver_nameserver_count(r); i++) {
j = ldns_get_random() % ldns_resolver_nameserver_count(r);
tmpns = ns[i];
ns[i] = ns[j];
ns[j] = tmpns;
tmprtt = rtt[i];
rtt[i] = rtt[j];
rtt[j] = tmprtt;
}
ldns_resolver_set_nameservers(r, ns);
}