#include <sys/types.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <arpa/nameser.h>
#include <netdb.h>
#include <asr.h>
#include <errno.h>
#include <resolv.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "asr_private.h"
static int res_search_async_run(struct asr_query *, struct asr_result *);
static size_t domcat(const char *, const char *, char *, size_t);
struct asr_query *
res_search_async(const char *name, int class, int type, void *asr)
{
struct asr_ctx *ac;
struct asr_query *as;
DPRINT("asr: res_search_async(\"%s\", %i, %i)\n", name, class, type);
ac = _asr_use_resolver(asr);
as = _res_search_async_ctx(name, class, type, ac);
_asr_ctx_unref(ac);
return (as);
}
DEF_WEAK(res_search_async);
struct asr_query *
_res_search_async_ctx(const char *name, int class, int type, struct asr_ctx *ac)
{
struct asr_query *as;
DPRINT("asr: res_search_async_ctx(\"%s\", %i, %i)\n", name, class,
type);
if ((as = _asr_async_new(ac, ASR_SEARCH)) == NULL)
goto err;
as->as_run = res_search_async_run;
if ((as->as.search.name = strdup(name)) == NULL)
goto err;
as->as.search.class = class;
as->as.search.type = type;
return (as);
err:
if (as)
_asr_async_free(as);
return (NULL);
}
#define HERRNO_UNSET -2
static int
res_search_async_run(struct asr_query *as, struct asr_result *ar)
{
int r;
char fqdn[MAXDNAME];
next:
switch (as->as_state) {
case ASR_STATE_INIT:
if (as->as.search.name[0] == '\0') {
ar->ar_h_errno = NO_DATA;
async_set_state(as, ASR_STATE_HALT);
break;
}
as->as.search.saved_h_errno = HERRNO_UNSET;
async_set_state(as, ASR_STATE_NEXT_DOMAIN);
break;
case ASR_STATE_NEXT_DOMAIN:
as->as_dom_flags = 0;
r = _asr_iter_domain(as, as->as.search.name, fqdn, sizeof(fqdn));
if (r == -1) {
async_set_state(as, ASR_STATE_NOT_FOUND);
break;
}
if (r == 0) {
ar->ar_errno = EINVAL;
ar->ar_h_errno = NO_RECOVERY;
ar->ar_datalen = -1;
ar->ar_data = NULL;
async_set_state(as, ASR_STATE_HALT);
break;
}
as->as_subq = _res_query_async_ctx(fqdn,
as->as.search.class, as->as.search.type, as->as_ctx);
if (as->as_subq == NULL) {
ar->ar_errno = errno;
if (errno == EINVAL)
ar->ar_h_errno = NO_RECOVERY;
else
ar->ar_h_errno = NETDB_INTERNAL;
ar->ar_datalen = -1;
ar->ar_data = NULL;
async_set_state(as, ASR_STATE_HALT);
break;
}
async_set_state(as, ASR_STATE_SUBQUERY);
break;
case ASR_STATE_SUBQUERY:
if ((r = asr_run(as->as_subq, ar)) == ASYNC_COND)
return (ASYNC_COND);
as->as_subq = NULL;
if (ar->ar_h_errno == NETDB_SUCCESS) {
async_set_state(as, ASR_STATE_HALT);
break;
}
if (ar->ar_errno) {
async_set_state(as, ASR_STATE_HALT);
break;
}
free(ar->ar_data);
if (as->as_dom_flags & ASYNC_DOM_NDOTS)
as->as.search.saved_h_errno = ar->ar_h_errno;
if (as->as_dom_flags & ASYNC_DOM_DOMAIN) {
if (ar->ar_h_errno == NO_DATA)
as->as_flags |= ASYNC_NODATA;
else if (ar->ar_h_errno == TRY_AGAIN)
as->as_flags |= ASYNC_AGAIN;
}
async_set_state(as, ASR_STATE_NEXT_DOMAIN);
break;
case ASR_STATE_NOT_FOUND:
if (as->as.search.saved_h_errno != HERRNO_UNSET)
ar->ar_h_errno = as->as.search.saved_h_errno;
else if (as->as_flags & ASYNC_NODATA)
ar->ar_h_errno = NO_DATA;
else if (as->as_flags & ASYNC_AGAIN)
ar->ar_h_errno = TRY_AGAIN;
ar->ar_datalen = -1;
ar->ar_data = NULL;
async_set_state(as, ASR_STATE_HALT);
break;
case ASR_STATE_HALT:
return (ASYNC_DONE);
default:
ar->ar_errno = EOPNOTSUPP;
ar->ar_h_errno = NETDB_INTERNAL;
async_set_state(as, ASR_STATE_HALT);
break;
}
goto next;
}
static size_t
domcat(const char *name, const char *domain, char *buf, size_t buflen)
{
size_t r;
r = _asr_make_fqdn(name, domain, buf, buflen);
if (r == 0)
return (0);
buf[r - 1] = '\0';
return (r - 1);
}
enum {
DOM_INIT,
DOM_DOMAIN,
DOM_DONE
};
int
_asr_iter_domain(struct asr_query *as, const char *name, char * buf, size_t len)
{
const char *c;
int dots;
switch (as->as_dom_step) {
case DOM_INIT:
if (strlen(name) && name[strlen(name) - 1] == '.') {
DPRINT("asr: iter_domain(\"%s\") fqdn\n", name);
as->as_dom_flags |= ASYNC_DOM_FQDN;
as->as_dom_step = DOM_DONE;
return (domcat(name, NULL, buf, len));
}
as->as_dom_step = DOM_DOMAIN;
as->as_dom_idx = 0;
dots = 0;
for (c = name; *c; c++)
dots += (*c == '.');
if (dots >= as->as_ctx->ac_ndots) {
DPRINT("asr: iter_domain(\"%s\") ndots\n", name);
as->as_dom_flags |= ASYNC_DOM_NDOTS;
if (strlcpy(buf, name, len) >= len)
return (0);
return (strlen(buf));
}
case DOM_DOMAIN:
if (as->as_dom_idx < as->as_ctx->ac_domcount &&
(as->as_ctx->ac_options & RES_DNSRCH || (
as->as_ctx->ac_options & RES_DEFNAMES &&
as->as_dom_idx == 0 &&
strchr(name, '.') == NULL))) {
DPRINT("asr: iter_domain(\"%s\") domain \"%s\"\n",
name, as->as_ctx->ac_dom[as->as_dom_idx]);
as->as_dom_flags |= ASYNC_DOM_DOMAIN;
return (domcat(name,
as->as_ctx->ac_dom[as->as_dom_idx++], buf, len));
}
as->as_dom_step = DOM_DONE;
if (!(as->as_dom_flags & ASYNC_DOM_NDOTS)) {
DPRINT("asr: iter_domain(\"%s\") as is\n", name);
as->as_dom_flags |= ASYNC_DOM_ASIS;
if (strlcpy(buf, name, len) >= len)
return (0);
return (strlen(buf));
}
case DOM_DONE:
default:
DPRINT("asr: iter_domain(\"%s\") done\n", name);
return (-1);
}
}