/*
 * unbound.c - unbound validating resolver public API implementation
 *
 * Copyright (c) 2007, NLnet Labs. All rights reserved.
 *
 * This software is open source.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * 
 * 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.
 * 
 * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
 */

/**
 * \file
 *
 * This file contains functions to resolve DNS queries and 
 * validate the answers. Synchonously and asynchronously.
 *
 */

/* include the public api first, it should be able to stand alone */
#include "libunbound/unbound.h"
#include "config.h"
#include "util/locks.h"
#include "util/config_file.h"
#include "util/alloc.h"

/**
 * The context structure
 *
 * Contains two pipes for async service
 *	qq : write queries to the async service pid/tid.
 *	rr : read results from the async service pid/tid.
 */
struct ub_val_ctx {
	/** mutex on query write pipe */
	lock_basic_t qqpipe_lock;
	/** the query write pipe, [0] read from, [1] write on */
	int qqpipe[2];
	/** mutex on result read pipe */
	lock_basic_t rrpipe_lock;
	/** the result read pipe, [0] read from, [1] write on */
	int rrpipe[2];

	/** configuration options */
	struct config_file* cfg;
	/** do threading (instead of forking) */
	int dothread;

	/** shared caches, and so on */
	struct alloc_cache superalloc;
	/** module env master value */
	struct module_env* env;
	/** number of modules active, ids from 0 to num-1. */
	int num_modules;
	/** the module callbacks, array of num_modules length */
	struct module_func_block** modfunc;
	/** local authority zones */
	struct local_zones* local_zones;

	/** TODO list of outstanding queries */
};

/**
 * The error constants
 */
enum ub_ctx_err {
	/** no error */
	UB_NOERROR = 0,
	/** alloc failure */
	UB_NOMEM,
	/** socket operation */
	UB_SOCKET,
	/** syntax error */
	UB_SYNTAX,
	/** DNS service failed */
	UB_SERVFAIL
};


struct ub_val_ctx* 
ub_val_ctx_create()
{
	struct ub_val_ctx* ctx = (struct ub_val_ctx*)calloc(1, sizeof(*ctx));
	if(!ctx) {
		errno = ENOMEM;
		return NULL;
	}
	if(socketpair(AF_UNIX, SOCK_STREAM, 0, ctx->qqpipe) == -1) {
		free(ctx);
		return NULL;
	}
	if(socketpair(AF_UNIX, SOCK_STREAM, 0, ctx->rrpipe) == -1) {
		int e = errno;
		close(ctx->qqpipe[0]);
		close(ctx->qqpipe[1]);
		free(ctx);
		errno = e;
		return NULL;
	}
	lock_basic_init(&ctx->qqpipe_lock);
	lock_basic_init(&ctx->rrpipe_lock);
	ctx->cfg = config_create();
	if(!ctx->cfg) {
		ub_val_ctx_delete(ctx);
		errno = ENOMEM;
		return NULL;
	}
	return ctx;
}

void 
ub_val_ctx_delete(struct ub_val_ctx* ctx)
{
	if(!ctx) return;
	lock_basic_destroy(&ctx->qqpipe_lock);
	lock_basic_destroy(&ctx->rrpipe_lock);
	close(ctx->qqpipe[0]);
	close(ctx->qqpipe[1]);
	close(ctx->rrpipe[0]);
	close(ctx->rrpipe[1]);
	config_delete(ctx->cfg);
	free(ctx);
}

int 
ub_val_ctx_config(struct ub_val_ctx* ctx, char* fname)
{
	if(!config_read(ctx->cfg, fname)) {
		return UB_SYNTAX;
	}
	return UB_NOERROR;
}

int 
ub_val_ctx_add_ta(struct ub_val_ctx* ctx, char* ta)
{
	char* dup = strdup(ta);
	if(!dup) return UB_NOMEM;
	if(!cfg_strlist_insert(&ctx->cfg->trust_anchor_list, dup)) {
		free(dup);
		return UB_NOMEM;
	}
	return UB_NOERROR;
}

int 
ub_val_ctx_trustedkeys(struct ub_val_ctx* ctx, char* fname)
{
	char* dup = strdup(fname);
	if(!dup) return UB_NOMEM;
	if(!cfg_strlist_insert(&ctx->cfg->trusted_keys_file_list, dup)) {
		free(dup);
		return UB_NOMEM;
	}
	return UB_NOERROR;
}

int 
ub_val_ctx_async(struct ub_val_ctx* ctx, int dothread)
{
	ctx->dothread = dothread;
	return UB_NOERROR;
}

/** perform a select() on the result read pipe */
static int 
pollit(struct ub_val_ctx* ctx, struct timeval* t)
{
	fd_set r;
#ifndef S_SPLINT_S
	FD_ZERO(&r);
	FD_SET(ctx->rrpipe[0], &r);
#endif
	if(select(ctx->rrpipe[0]+1, &r, NULL, NULL, t) == -1) {
		return 0;
	}
	errno = 0;
	return FD_ISSET(ctx->rrpipe[0], &r);
}

int 
ub_val_ctx_poll(struct ub_val_ctx* ctx)
{
	struct timeval t;
	int r;
	memset(&t, 0, sizeof(t));
	lock_basic_lock(&ctx->rrpipe_lock);
	r = pollit(ctx, &t);
	lock_basic_unlock(&ctx->rrpipe_lock);
	return r;
}

int 
ub_val_ctx_wait(struct ub_val_ctx* ctx)
{
	/* TODO until no more queries outstanding */
	while(1) {
		lock_basic_lock(&ctx->rrpipe_lock);
		(void)pollit(ctx, NULL);
		lock_basic_unlock(&ctx->rrpipe_lock);
		ub_val_ctx_process(ctx);
	}
	return UB_NOERROR;
}

int 
ub_val_ctx_fd(struct ub_val_ctx* ctx)
{
	return ctx->rrpipe[0];
}

int 
ub_val_ctx_process(struct ub_val_ctx* ctx)
{
	return UB_NOERROR;
}

int 
ub_val_resolve(struct ub_val_ctx* ctx, char* name, int rrtype, 
	int rrclass, int* secure, int* data, struct ub_val_result** result)
{
	/* become a resolver thread for a bit */

	return UB_NOERROR;
}

int 
ub_val_resolve_async(struct ub_val_ctx* ctx, char* name, int rrtype, 
	int rrclass, void* mydata, ub_val_callback_t callback, int* async_id)
{
	return UB_NOERROR;
}

int 
ub_val_cancel(struct ub_val_ctx* ctx, int async_id)
{
	return UB_NOERROR;
}

void 
ub_val_result_free(struct ub_val_result* result)
{
	char** p;
	if(!result) return;
	free(result->qname);
	free(result->canonname);
	for(p = result->data; *p; p++)
		free(*p);
	free(result->data);
	free(result->len);
	free(result);
}

const char* 
ub_val_strerror(int err)
{
	switch(err) {
		case UB_NOERROR: return "no error";
		case UB_NOMEM: return "out of memory";
		case UB_SOCKET: return "socket io error";
		case UB_SYNTAX: return "syntax error";
		case UB_SERVFAIL: return "server failure";
		default: return "unknown error";
	}
}
