/* adns.c, liboop, copyright 1999 Dan Egnor

   This is free software; you can redistribute it and/or modify it under the
   terms of the GNU Lesser General Public License, version 2.1 or later.
   See the file COPYING for details. */

#ifdef HAVE_ADNS

#include "oop.h"
#include "adns.h"
#include "oop-adns.h"

#include <assert.h>

#ifdef HAVE_STRING_H
#include <string.h>   /* Needed on NetBSD1.1/SPARC due to bzero/FD_ZERO. */
#endif

#ifdef HAVE_STRINGS_H
#include <strings.h>  /* Needed on AIX 4.2 due to bzero/FD_ZERO. */
#endif

struct oop_adapter_adns {
	oop_source *source;
	oop_adapter_select *select;
	adns_state state;
	int count;
};

struct oop_adns_query {
	oop_adapter_adns *a;
	adns_query query;
	oop_adns_call *call;
	void *data;
};

static oop_call_select on_select;
static oop_call_time on_process;
static void set_select(oop_adapter_adns *);

oop_adapter_adns *oop_adns_new(
	oop_source *source,
	adns_initflags flags,FILE *diag) 
{
	oop_adapter_adns *a = oop_malloc(sizeof(*a));
	if (NULL == a) return NULL;
	a->select = NULL;
	a->state = NULL;

	if (adns_init(&a->state,flags | adns_if_noautosys,diag)
	|| (NULL == (a->select = oop_select_new(source,on_select,a)))) {
		if (NULL != a->state) adns_finish(a->state);
		if (NULL != a->select) oop_select_delete(a->select);
		oop_free(a);
		return NULL;
	}

	a->source = source;
	a->count = 0;
	return a;
}

void oop_adns_delete(oop_adapter_adns *a) {
	assert(0 == a->count && 
	       "deleting oop_adapter_adns with outstanding queries");
	a->source->cancel_time(a->source,OOP_TIME_NOW,on_process,a);
	oop_select_delete(a->select);
	adns_finish(a->state);
	oop_free(a);
}

oop_adns_query *oop_adns_submit(
	oop_adapter_adns *a,int *errcode,
	const char *owner,adns_rrtype type,adns_queryflags flags,
	oop_adns_call *call,void *data)
{
	oop_adns_query *q = oop_malloc(sizeof(*q));
	int err;
	if (NULL == q) return NULL;

	err = adns_submit(a->state,owner,type,flags,q,&q->query);
	if (errcode) *errcode = err;
	if (err) {
		oop_free(q);
		return NULL;
	}

	q->a = a;
	q->call = call;
	q->data = data;
	++q->a->count;
	set_select(a);
	return q;
}

oop_adns_query *oop_adns_submit_reverse(
	oop_adapter_adns *a,int *errcode,
	const struct sockaddr *addr,adns_rrtype type,adns_queryflags flags,
	oop_adns_call *call,void *data)
{
	oop_adns_query *q = oop_malloc(sizeof(*q));
	int err;
	if (NULL == q) return NULL;

	err = adns_submit_reverse(a->state,addr,type,flags,q,&q->query);
	if (errcode) *errcode = err;
	if (err) {
		oop_free(q);
		return NULL;
	}

	q->a = a;
	q->call = call;
	q->data = data;
	++q->a->count;
	set_select(a);
	return q;
}

void oop_adns_cancel(oop_adns_query *q) {
	adns_cancel(q->query);
	--q->a->count;
	set_select(q->a);
	oop_free(q);
}

static void set_select(oop_adapter_adns *a) {
	fd_set rfd,wfd,xfd;
	struct timeval buf,*out = NULL,now;
	int maxfd = 0;
	FD_ZERO(&rfd);
	FD_ZERO(&wfd);
	FD_ZERO(&xfd);
	gettimeofday(&now,NULL);
	adns_beforeselect(a->state,&maxfd,&rfd,&wfd,&xfd,&out,&buf,&now);
	oop_select_set(a->select,maxfd,&rfd,&wfd,&xfd,out);
}

static void *on_process(oop_source *source,struct timeval when,void *data) {
	oop_adapter_adns *a = (oop_adapter_adns *) data;
	adns_answer *r;
	adns_query query;
	oop_adns_query *q = NULL;
	void *adns_data;

	query = NULL;
	if (0 == adns_check(a->state,&query,&r,&adns_data)) {
		q = (oop_adns_query *) adns_data;
		assert(query == q->query);
	}

	set_select(a);

	if (NULL != q) {
		oop_adns_call *call = q->call;
		void *data = q->data;
		assert(a == q->a);
		--q->a->count;
		oop_free(q);
		source->on_time(source,when,on_process,a);
		return call(a,r,data);
	}

	return OOP_CONTINUE;
}

static void *on_select(
	oop_adapter_select *select,
	int num,fd_set *rfd,fd_set *wfd,fd_set *xfd,
	struct timeval now,void *data)
{
	oop_adapter_adns *a = (oop_adapter_adns *) data;

	adns_afterselect(a->state,num,rfd,wfd,xfd,&now);
	return on_process(a->source,OOP_TIME_NOW,a);
}

#endif


syntax highlighted by Code2HTML, v. 0.9.1