/*
 * Copyright (c) 2002-2004 Sendmail, 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.
 */

#include "sm/generic.h"
SM_RCSID("@(#)$Id: t-dnstsk-0.c,v 1.31 2007/06/23 04:48:44 ca Exp $")

#include "sm/assert.h"
#include "sm/magic.h"
#include "sm/error.h"
#include "sm/memops.h"
#include "sm/test.h"
#include "sm/evthr.h"
#include "sm/dns.h"
#include "sm/dns-int.h"
#include "sm/dnstsk.h"
#include <stdio.h>

/*
**  Should be protected by a mutex...
**  Moreover, this triggers too early if multiple callbacks are used.
*/
static bool terminate = false;

static int Verbose = 0;
static uint MaxCount = 50;

/* Test program options */
#define T_OPT_USE_TCP	0x0001
#define T_OPT_TWICE	0x0002
#define T_OPT_MX	0x0004
#define T_OPT_A		0x0008
#define T_OPT_DEBUG	0x1000

static sm_ret_T
fcts(sm_evthr_task_P tsk)
{
	static uint count = 0;
	struct timeval sleept, delay;

	++count;
	if (Verbose > 1 && (count % 10 == 0))
		fprintf(stderr, "fcts: count=%u, ", count);
	if (terminate || count > MaxCount)
		return EVTHR_DEL|EVTHR_TERM;
	sm_memzero(&sleept, sizeof(sleept));
	gettimeofday(&sleept, NULL);
	delay.tv_sec = 0;
	delay.tv_usec = 1000;
	timeradd(&sleept, &delay, &tsk->evthr_t_sleep);
	return EVTHR_SLPQ;
}

static sm_ret_T
callback(dns_res_P dns_res, void *ctx)
{
	dns_type_T type;
	dns_rese_P dns_rese;

	SM_TEST(dns_res != NULL);
	if (Verbose > 1)
		fprintf(stderr, "callback: got %p, ", dns_res);
	if (NULL == dns_res) {
		terminate = true;
		return SM_FAILURE;
	}
	if (sm_is_err(dns_res->dnsres_ret)) {
		fprintf(stderr, "error: ret=%x\n", dns_res->dnsres_ret);
		terminate = true;
		return SM_FAILURE;
	}

	if (Verbose == 0) {
		terminate = true;
		return SM_SUCCESS;
	}

	fprintf(stderr, "dnsres: entries=%u\n", dns_res->dnsres_entries);
	fprintf(stderr, "dnsres: max=%u\n", dns_res->dnsres_maxentries);

	for (dns_rese = DRESL_FIRST(dns_res);
	     dns_rese != DRESL_END(dns_res);
	     dns_rese = DRESL_NEXT(dns_rese))
	{
		type = dns_rese->dnsrese_type;
		fprintf(stderr, "ttl=%u\n", dns_rese->dnsrese_ttl);
		switch (type)
		{
		  case T_MX:
			fprintf(stderr, "pref=%u\n", dns_rese->dnsrese_pref);
			fprintf(stderr, "weight=%u\n",
				dns_rese->dnsrese_weight);
			fprintf(stderr, "value=%s\n\n",
				sm_cstr_data(dns_rese->dnsrese_val.dnsresu_name));
			break;
		  case T_A:
			fprintf(stderr, "IPv4=%x\n",
				dns_rese->dnsrese_val.dnsresu_a);
			break;
		  case T_CNAME:
			fprintf(stderr, "CNAME=%s\n\n",
				sm_cstr_data(dns_rese->dnsrese_val.dnsresu_name));
			break;
		  default:
			fprintf(stderr, "unknown type=%d\n", type);
			break;
		}
	}
	terminate = true;
	return SM_SUCCESS;
}

/*
**  XXX Change this to take a list of hosts? (argv[])
*/

static void
testdns(char *host, uint flags, uint32_t ipv4)
{
	sm_ret_T ret;
	int n;
#if 0
	dns_rslv_ctx_P dns_rslv_ctx;
#endif /* 0 */
	dns_mgr_ctx_P dns_mgr_ctx;
#if 0
	dns_mxe_P dns_mxe;
	dns_mx_P dns_mx;
#endif /* 0 */
	sm_evthr_ctx_P evthr_ctx;
	sm_evthr_task_P	task, task3;
	dns_tsk_P dns_tsk;
	sm_cstr_P query;
	dns_type_T type;
	struct timeval sleept;

	if (host == NULL || *host == '\0')
		return;

	dns_tsk = NULL;
	evthr_ctx = NULL;
	task = task3 = NULL;
	dns_mgr_ctx = NULL;

	query = sm_cstr_scpyn0((const uchar *) host, strlen(host));
	SM_TEST(query != NULL);
	if (NULL == query)
		return;
	sm_memzero(&sleept, sizeof(sleept));

	ret = thr_init();
	SM_TEST(sm_is_success(ret));
	if (sm_is_err(ret))
		goto errq;
	ret = evthr_init(&evthr_ctx, 1, 6, 10);
	SM_TEST(sm_is_success(ret));
	if (sm_is_err(ret))
		goto errt1;
	SM_TEST(evthr_ctx != NULL);

	ret = dns_rslv_new(strlen(host)
#if 0
			, &dns_rslv_ctx
#endif /* 0 */
			);
	SM_TEST(SM_SUCCESS == ret);
	if (sm_is_err(ret))
		goto errt;
	if (flags & T_OPT_DEBUG)
		_res.options |= RES_DEBUG;
#if 0
	SM_TEST(dns_rslv_ctx != NULL);
	if (NULL == dns_rslv_ctx)
		goto errt;
#endif /* 0 */

	ret = dns_mgr_ctx_new(0, DNS_TIMEOUT, 0, 0, &dns_mgr_ctx);
	SM_TEST(SM_SUCCESS == ret);
	if (sm_is_err(ret))
		goto err0;
	SM_TEST(dns_mgr_ctx != NULL);
	if (NULL == dns_mgr_ctx)
		goto err0;

	ret = dns_tsk_new(dns_mgr_ctx,
			(flags & T_OPT_USE_TCP) ? DNS_TSK_FL_USETCP : 0,
			ipv4, &dns_tsk);
	SM_TEST(SM_SUCCESS == ret);
	if (sm_is_err(ret)) {
		fprintf(stderr, "dns_tsk_new: ret=%x\n", ret);
		goto err1;
	}
	SM_TEST(dns_tsk != NULL);
	if (NULL == dns_tsk)
		goto err1;

	ret = dns_tsk_start(dns_mgr_ctx, evthr_ctx);
	SM_TEST(sm_is_success(ret));
	if (sm_is_err(ret))
		goto err1;

	type = T_A;
	if (flags & T_OPT_MX) {
		type = T_MX;
		ret = dns_req_add(dns_mgr_ctx, query, type, 0, callback, (void *) NULL);
		SM_TEST(sm_is_success(ret));
		if (sm_is_err(ret))
			goto err1;
	}

	if (flags & T_OPT_A) {
		type = T_A;
		ret = dns_req_add(dns_mgr_ctx, query, type, 0, callback, (void *) NULL);
		SM_TEST(sm_is_success(ret));
		if (sm_is_err(ret))
			goto err1;
	}

	/* test: add the same query again... */
	if (flags & T_OPT_TWICE) {
		ret = dns_req_add(dns_mgr_ctx, query, type, 0, callback, (void *) NULL);
		SM_TEST(sm_is_success(ret));
		if (sm_is_err(ret))
			goto err1;
	}

	n = gettimeofday(&sleept, NULL);
	SM_TEST(n == 0);
	sleept.tv_usec += 1000;
	ret = evthr_task_new(evthr_ctx, &task3, EVTHR_EV_SL, -1, &sleept,
				fcts, (void *) NULL);
	SM_TEST(sm_is_success(ret));
	SM_TEST(task3 != NULL);

#if 0
	ret = dnstskwr(dns_tsk);
	ret = dns_receive(dns_tsk);
#endif /* 0 */

	ret = evthr_loop(evthr_ctx);
	SM_TEST(sm_is_success(ret));

	/* need to get result... */

  err1:
	ret = dns_mgr_ctx_del(dns_mgr_ctx);
	SM_TEST(SM_SUCCESS == ret);
  err0:
#if 0
	ret = dns_rslv_free(dns_rslv_ctx);
	SM_TEST(SM_SUCCESS == ret);
#endif /* 0 */
  errt:
	ret = evthr_stop(evthr_ctx);
	SM_TEST(sm_is_success(ret));
  errt1:
	ret = thr_stop();
	SM_TEST(sm_is_success(ret));
  errq:
	SM_CSTR_FREE(query);
	return;
}

static void
usage(const char *prg)
{
	fprintf(stderr, "Usage: %s [options] names\n", prg);
	fprintf(stderr, "Options:\n"
		"-a	query for A record\n"
		"-d	query twice\n"
		"-D	turn on debugging\n"
		"-i ip	use ip address for nameserver [127.0.0.1]\n"
		"-m	do not query for MX record\n"
		"-T	use TCP instead of UDP\n"
		"-V	increase verbosity\n"
		);
}

int
main(int argc, char *argv[])
{
	int r, i;
	uint flags;
	uint32_t ipv4;

	flags = T_OPT_MX;
	ipv4 = htonl(LOCALHOST_IP);
	while ((r = getopt(argc, argv, "adDi:mt:TV")) != -1)
	{
		switch (r)
		{
		  case 'a':
			flags |= T_OPT_A;
			break;
		  case 'd':
			flags |= T_OPT_TWICE;
			break;
		  case 'D':
			flags |= T_OPT_DEBUG;
			break;
		  case 'i':
			ipv4 = inet_addr(optarg);
			break;
		  case 'm':
			flags &= ~T_OPT_MX;
			break;
		  case 't':
			i = atoi(optarg);
			if (i <= 0)
				return 1;
			MaxCount = (uint) i;
			break;
		  case 'T':
			flags |= T_OPT_USE_TCP;
			break;
		  case 'V':
			Verbose++;
			break;
		  default:
			usage(argv[0]);
			return 1;
		}
	}

	sm_test_begin(argc, argv, "test getmxrr");
	for (r = optind; r < argc; r++)
		testdns(argv[r], flags, ipv4);
	return sm_test_end();
}


syntax highlighted by Code2HTML, v. 0.9.1