/*
* Copyright (c) 2002-2005 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-1.c,v 1.37 2007/11/05 05:47:53 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... */
static int Requests = 0;
static bool Terminate = false;
static bool Start = false;
static int Verbose = 0;
static int EvDbg = 0;
static uint MaxCount = 50;
static uint dnstimeout = DNS_TIMEOUT;
static ushort dnsretries = 0;
/* Test program options */
#define T_OPT_USE_TCP 0x0001
#define T_OPT_TWICE 0x0002
#define T_OPT_MX 0x0004
#define T_OPT_MX_A 0x0014 /* MX followed by A */
#define T_OPT_A 0x0008
#define T_OPT_PTR 0x0020
#define T_OPT_DEBUG 0x1000
struct t_req_S
{
char **hosts;
int count;
int iter;
uint flags;
dns_mgr_ctx_P dns_mgr_ctx;
sm_evthr_task_P wr_tsk;
};
typedef struct t_req_S t_req_T, *t_req_P;
static sm_ret_T
fcts(sm_evthr_task_P tsk)
{
static uint count = 0;
struct timeval sleept, delay;
++count;
if (Verbose > 3 && (count % 10 == 0))
fprintf(stderr, "fcts: count=%u, ", count);
if ((Requests <= 0 && Start) || count > MaxCount)
{
if (Verbose > 3)
fprintf(stderr,
"fcts: Requests=%d, count=%d, MaxCount=%d!\n",
Requests, count, MaxCount);
return EVTHR_DEL|EVTHR_TERM;
}
if (Terminate)
{
if (Verbose > 3)
fprintf(stderr, "fcts: terminate!\n");
return EVTHR_DEL|EVTHR_TERM;
}
sm_memzero(&sleept, sizeof(sleept));
gettimeofday(&sleept, NULL);
delay.tv_sec = 0;
delay.tv_usec = 10000;
timeradd(&sleept, &delay, &tsk->evthr_t_sleep);
return EVTHR_SLPQ;
}
static sm_ret_T
callback(dns_res_P dns_res, void *ctx)
{
sm_ret_T ret;
dns_type_T type;
dns_rese_P dns_rese;
dns_mgr_ctx_P dns_mgr_ctx;
sm_cstr_P q;
struct in_addr inaddr;
SM_TEST(dns_res != NULL);
if (Verbose > 1)
fprintf(stderr, "callback: got %p, requests=%u ", dns_res, Requests);
if (dns_res == NULL)
{
--Requests;
return SM_FAILURE;
}
if (Verbose > 1)
fprintf(stderr, "query='%s' ", sm_cstr_data(dns_res->dnsres_query));
if (Terminate)
{
if (Verbose > 3)
fprintf(stderr, "callback: terminate!\n");
return SM_FAILURE;
}
dns_mgr_ctx = (dns_mgr_ctx_P) ctx;
if (sm_is_err(dns_res->dnsres_ret))
{
if (Verbose > 0)
{
fprintf(stderr, "callback: error=%x\n",
dns_res->dnsres_ret);
switch (dns_res->dnsres_ret)
{
case DNSR_REFUSED:
fprintf(stderr, "DNSR_REFUSED\n");
break;
case DNSR_TEMP:
fprintf(stderr, "DNSR_TEMP\n");
break;
case DNSR_NOTFOUND:
fprintf(stderr, "DNSR_NOTFOUND\n");
break;
case DNSR_PERM:
fprintf(stderr, "DNSR_PERM\n");
break;
case DNSR_TIMEOUT:
fprintf(stderr, "DNSR_TIMEOUT\n");
break;
default:
fprintf(stderr, "unknown\n");
break;
}
}
--Requests;
return SM_FAILURE;
}
if (Verbose == 0)
{
--Requests;
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));
q = dns_rese->dnsrese_val.dnsresu_name;
if (SM_CSTR_LAST(q) == '.')
{
/* HACK */
/*
sm_cstr_getlen(q)--;
sm_cstr_data(q)[sm_cstr_getlen(q)] = '\0';
*/
}
ret = dns_req_add(dns_mgr_ctx, q, T_A, 0, callback,
(void *) dns_mgr_ctx);
if (Verbose > 1)
fprintf(stderr, "callback: add T_A(%s)=%x\n",
sm_cstr_data(q),
ret);
SM_TEST(sm_is_success(ret));
if (sm_is_err(ret))
break;
++Requests;
break;
case T_A:
inaddr.s_addr = dns_rese->dnsrese_val.dnsresu_a;
if (Verbose > 1)
fprintf(stderr, "IPv4=%X [%s]\n", (uint)
htonl(dns_rese->dnsrese_val.dnsresu_a),
inet_ntoa(inaddr));
else
fprintf(stderr, "IPv4=%X\n", (uint)
htonl(dns_rese->dnsrese_val.dnsresu_a));
break;
case T_PTR:
#if 0
fprintf(stderr, "pref=%u\n", dns_rese->dnsrese_pref);
fprintf(stderr, "weight=%u\n",
dns_rese->dnsrese_weight);
#endif /* 0 */
fprintf(stderr, "value=%s\n\n",
sm_cstr_data(dns_rese->dnsrese_val.dnsresu_name));
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;
}
}
--Requests;
return SM_SUCCESS;
}
static sm_ret_T
lookup(sm_evthr_task_P tsk)
{
t_req_P t_req;
int n;
sm_ret_T ret;
dns_type_T type;
sm_cstr_P query;
uint flags;
dns_mgr_ctx_P dns_mgr_ctx;
char *host;
SM_REQUIRE(tsk != NULL);
t_req = (t_req_P) tsk->evthr_t_actx;
if (t_req == NULL)
return EVTHR_DEL|EVTHR_TERM;
flags = t_req->flags;
type = T_A; /* Just to keep gcc happy */
dns_mgr_ctx = t_req->dns_mgr_ctx;
again:
if (Verbose > 1)
fprintf(stderr, "lookup, count=%d\n", t_req->count);
for (n = 0; n < t_req->count; n++)
{
if (Terminate)
{
if (Verbose > 3)
fprintf(stderr,
"lookup: terminate (n=%d/%d)!\n",
n, t_req->count);
return SM_FAILURE;
}
host = t_req->hosts[n];
if (host == NULL || *host == '\0')
return EVTHR_DEL|EVTHR_TERM;
query = sm_cstr_scpyn0((const uchar *) host, strlen(host));
SM_TEST(query != NULL);
if (query == NULL)
return EVTHR_DEL|EVTHR_TERM;
if (flags & T_OPT_MX)
{
type = T_MX;
if (Verbose > 1)
fprintf(stderr, "adding T_MX(%s)\n", host);
ret = dns_req_add(dns_mgr_ctx, query, type, 0, callback,
(void *) dns_mgr_ctx);
if (Verbose > 1)
fprintf(stderr, "add T_MX(%s)=%x, ", host, ret);
SM_TEST(sm_is_success(ret));
if (sm_is_err(ret))
goto error;
++Requests;
}
if (flags & T_OPT_A)
{
type = T_A;
ret = dns_req_add(dns_mgr_ctx, query, type, 0, callback,
(void *) dns_mgr_ctx);
if (Verbose > 1)
fprintf(stderr, "add T_A(%s)=%x, ", host, ret);
SM_TEST(sm_is_success(ret));
if (sm_is_err(ret))
goto error;
++Requests;
}
if (flags & T_OPT_PTR)
{
type = T_PTR;
ret = dns_req_add(dns_mgr_ctx, query, type, 0, callback,
(void *) dns_mgr_ctx);
if (Verbose > 1)
fprintf(stderr, "add T_PTR(%s)=%x, ", host, ret);
SM_TEST(sm_is_success(ret));
if (sm_is_err(ret))
goto error;
++Requests;
}
/* test: add the same query again... */
if (flags & T_OPT_TWICE)
{
ret = dns_req_add(dns_mgr_ctx, query, type, 0, callback,
(void *) dns_mgr_ctx);
if (Verbose > 1)
fprintf(stderr, "add again %s=%x, ", host, ret);
SM_TEST(sm_is_success(ret));
if (sm_is_err(ret))
goto error;
++Requests;
}
SM_CSTR_FREE(query);
}
Start = true;
if (Terminate)
{
if (Verbose > 3)
fprintf(stderr, "callback: terminate after loop!\n");
return SM_FAILURE;
}
/* Wakeup dns write task */
ret = evthr_en_wr(t_req->wr_tsk);
if (Verbose > 1)
fprintf(stderr, "wakeup=%x\n", ret);
SM_TEST(sm_is_success(ret));
if (t_req->iter-- > 0)
{
++Requests;
sleep(2);
goto again;
}
/* Paranoia... */
t_req->count = 0;
/* We're done */
return EVTHR_DEL;
error:
Start = true;
SM_CSTR_FREE(query);
return EVTHR_DEL|EVTHR_TERM;
}
static void
testdns(uint flags, uint32_t ipv4, t_req_P t_req)
{
sm_ret_T ret;
int n;
#if 0
dns_rslv_ctx_P dns_rslv_ctx;
dns_mxe_P dns_mxe;
dns_mx_P dns_mx;
#endif
dns_mgr_ctx_P dns_mgr_ctx;
sm_evthr_ctx_P evthr_ctx;
sm_evthr_task_P task, task2, task3;
dns_tsk_P dns_tsk;
struct timeval sleept, delay, slpt;
dns_tsk = NULL;
evthr_ctx = NULL;
task = task3 = NULL;
dns_mgr_ctx = NULL;
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);
if (EvDbg > 0)
evthr_set_dbglvl(evthr_ctx, EvDbg);
n = gettimeofday(&sleept, NULL);
SM_TEST(n == 0);
ret = dns_rslv_new((int) sleept.tv_usec
#if 0
, &dns_rslv_ctx
#endif
);
SM_TEST(ret == SM_SUCCESS);
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 (dns_rslv_ctx == NULL)
goto errt;
#endif
ret = dns_mgr_ctx_new(0, dnstimeout, 0, 0, &dns_mgr_ctx);
SM_TEST(ret == SM_SUCCESS);
if (sm_is_err(ret))
goto err0;
SM_TEST(dns_mgr_ctx != NULL);
if (dns_mgr_ctx == NULL)
goto err0;
dns_mgr_ctx->dnsmgr_retries = dnsretries;
if (Verbose > 4)
fprintf(stderr, "calling dns_tsk_new\n");
ret = dns_tsk_new(dns_mgr_ctx,
(flags & T_OPT_USE_TCP) ? DNS_TSK_FL_USETCP : 0,
ipv4, &dns_tsk);
SM_TEST(ret == SM_SUCCESS);
if (sm_is_err(ret))
{
fprintf(stderr, "dns_tsk_new: ret=%x\n", ret);
goto err1;
}
SM_TEST(dns_tsk != NULL);
if (dns_tsk == NULL)
goto err1;
if (Verbose > 4)
fprintf(stderr, "calling dns_tsk_start\n");
ret = dns_tsk_start(dns_mgr_ctx, evthr_ctx);
task = dns_mgr_ctx->dnsmgr_tsk[0];
SM_TEST(sm_is_success(ret));
if (sm_is_err(ret))
goto err1;
t_req->dns_mgr_ctx = dns_mgr_ctx;
t_req->wr_tsk = task;
sleept.tv_usec += 1;
ret = evthr_task_new(evthr_ctx, &task2, EVTHR_EV_SL, -1, &sleept,
lookup, (void *) t_req);
SM_TEST(sm_is_success(ret));
SM_TEST(task2 != NULL);
delay.tv_sec = 0;
delay.tv_usec = 10000;
timeradd(&sleept, &delay, &slpt);
ret = evthr_task_new(evthr_ctx, &task3, EVTHR_EV_SL, -1, &slpt,
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
if (Verbose > 4)
fprintf(stderr, "calling evthr_loop\n");
ret = evthr_loop(evthr_ctx);
SM_TEST(sm_is_success(ret));
/* need to get result... */
err1:
Terminate = true;
if (Verbose > 3)
fprintf(stderr, "calling dns_mgr_ctx_del\n");
ret = dns_mgr_ctx_del(dns_mgr_ctx);
SM_TEST(ret == SM_SUCCESS);
err0:
#if 0
ret = dns_rslv_free(dns_rslv_ctx);
SM_TEST(ret == SM_SUCCESS);
#endif
errt:
ret = evthr_stop(evthr_ctx);
SM_TEST(sm_is_success(ret));
errt1:
ret = thr_stop();
SM_TEST(sm_is_success(ret));
errq:
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"
"-O n set DNS timeout\n"
"-p query for PTR record (input: D.C.B.A.in-addr.arpa)\n"
"-t n limit request time (unit: 0.1s)\n"
"-T use TCP instead of UDP\n"
"-V increase verbosity\n"
);
}
int
main(int argc, char *argv[])
{
int r, i, iter;
uint flags;
uint32_t ipv4;
t_req_T t_req;
char **h, *prg;
prg = argv[0];
flags = T_OPT_MX;
ipv4 = htonl(LOCALHOST_IP);
iter = 0;
while ((r = getopt(argc, argv, "adDe:i:I:mO:pr:t: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 'e':
EvDbg = atoi(optarg);
break;
case 'i':
ipv4 = inet_addr(optarg);
break;
case 'I':
iter = atoi(optarg);
break;
case 'm':
flags &= ~T_OPT_MX;
break;
case 'O':
dnstimeout = atoi(optarg);
break;
case 'p':
flags |= T_OPT_PTR;
flags &= ~T_OPT_MX;
break;
case 'r':
dnsretries = atoi(optarg);
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");
t_req.count = argc - optind;
h = (char **) sm_malloc(sizeof(char *) * t_req.count);
SM_TEST(h != NULL);
if (h == NULL)
goto error;
t_req.hosts = h;
t_req.flags = flags;
t_req.iter = iter;
for (r = optind; r < argc; r++)
{
*h = argv[r];
++h;
}
testdns(flags, ipv4, &t_req);
sm_free(t_req.hosts);
error:
return sm_test_end();
}
syntax highlighted by Code2HTML, v. 0.9.1