/*
* Copyright (c) 2003-2006 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.
*/
/*
** Send address (printable recipient address) to SMAR for "routing".
** Get back list of IP addresses to which the recipient should be sent.
** Used by various t-smar-?.sh scripts.
*/
#include "sm/generic.h"
SM_RCSID("@(#)$Id: t-smar-1.c,v 1.49 2007/11/05 05:50:13 ca Exp $")
#include "sm/error.h"
#include "sm/assert.h"
#include "sm/memops.h"
#include "sm/io.h"
#include "sm/rcb.h"
#include "sm/qmgr.h"
#include "sm/qmgr-int.h"
#include "sm/dns.h"
#include "sm/reccom.h"
#include "sm/unixsock.h"
#include "sm/smar.h"
#include "sm/sysexits.h"
#include "sm/misc.h"
#include "sm/test.h"
/* context for requests */
struct t_req_S
{
char **rq_rcpt_pa;
int rq_count;
uint rq_timeout;
uint rq_loops;
uint32_t rq_flags;
rcbcom_ctx_P rq_qar_com;
sm_evthr_task_P rq_wr_tsk;
};
#define SMAXLEN 256
typedef struct t_req_S t_req_T, *t_req_P;
static pthread_mutex_t mutex;
static pthread_cond_t cond;
static int Verbose = 0;
static bool Oneline = false;
static bool Perf = false;
static uint MaxCount = (DNS_TIMEOUT + 1) * 10;
/*
** note: these should be protected by mutexes; however, they aren't
** "important" enough (mostly some simple statistics).
*/
static int Requests = 0;
static uint Req_Sent = 0;
static uint Req_Rcvd = 0;
static uint Req_Max_Open = 100;
static int aqt_rcpts_ar = 0;
static sm_evthr_ctx_P evthr_ctx;
static rcbcom_ctx_T rcb_com;
static char *SMAR_socket = smarsock;
static sm_ret_T ar2qmgr(sm_evthr_task_P _tsk);
static sm_ret_T qmgr2ar(sm_evthr_task_P _tsk);
/*
** FCTS -- "sleep" function: this function terminates the evthr system
** either after a certain number of iterations or if there are no
** more outstanding requests.
**
** Parameters:
** tsk -- evthr task
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
fcts(sm_evthr_task_P tsk)
{
static uint count = 0;
struct timeval sleep_tv, delay;
++count;
sm_memzero(&sleep_tv, sizeof(sleep_tv));
gettimeofday(&sleep_tv, NULL);
if (Verbose > 3 && (count % 10 == 0))
fprintf(stderr, "%ld.%07ld: fcts: count=%u/%u\n"
, (long) sleep_tv.tv_sec
, (long) sleep_tv.tv_usec
, count, MaxCount);
/* Theoretically Requests needs to be protected by a mutex... */
if (Perf && Requests <= 0)
{
fprintf(stderr, "%ld.%07ld: fcts: requests=0\n"
, (long) sleep_tv.tv_sec, (long) sleep_tv.tv_usec);
return EVTHR_DEL|EVTHR_TERM;
}
if (Requests <= 0 || count > MaxCount)
return EVTHR_DEL|EVTHR_TERM;
delay.tv_sec = 1;
delay.tv_usec = 0;
timeradd(&sleep_tv, &delay, &tsk->evthr_t_sleep);
return EVTHR_SLPQ;
}
/*
** QMGR_AR -- test program to AR interface
**
** Parameters:
** tsk -- evthr task
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
qmgr_ar(sm_evthr_task_P tsk)
{
sm_ret_T ret;
SM_IS_EVTHR_TSK(tsk);
SM_TEST(is_valid_fd(tsk->evthr_t_fd));
if (is_valid_fd(tsk->evthr_t_fd))
{
ret = EVTHR_WAITQ;
if (evthr_got_wr(tsk))
{
if (Verbose > 3)
fprintf(stderr, "got write\n");
ret = qmgr2ar(tsk); /* XXX check ret here? */
if (sm_is_err(ret) && Verbose > 1)
fprintf(stderr, "ERROR: qmgr2ar=%x\n", ret);
}
if (evthr_got_rd(tsk))
{
if (Verbose > 3)
fprintf(stderr, "got read\n");
ret = ar2qmgr(tsk);
}
if (sm_is_err(ret))
{
if (Verbose > 1)
fprintf(stderr, "ERROR: qmgr_ar=%x\n", ret);
return ret;
}
return ret;
}
return EVTHR_DEL;
}
/*
** QR2AR -- Send recipient data to AR, i.e., put it into RCB
**
** Parameters:
** rcpt_pa -- printable address of recipient
** rcpt_idx -- recipient index
** timeout -- timeout
** flags -- request flags
** rcbe -- RCB entry
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
qr2ar(sm_str_P rcpt_pa, int rcpt_idx, uint timeout, uint32_t flags, sm_rcbe_P rcbe)
{
sm_rcb_P rcb;
sm_ret_T ret;
rcpt_id_T rcpt_id;
ret = SM_SUCCESS;
rcb = &rcbe->rcbe_rcb;
sm_snprintf(rcpt_id, sizeof(rcpt_id),
SMTP_RCPTID_FORMAT,
"0", /* bogus TA id */
rcpt_idx);
/* XXX send rcpt_idx and ta_id separately? */
ret = sm_rcb_putv(rcb, RCB_PUTV_FIRST,
SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
SM_RCBV_BUF, RT_Q2R_RCPT_ID, rcpt_id, SMTP_RCPTID_SIZE,
SM_RCBV_STR, RT_Q2R_RCPT_PA, rcpt_pa,
SM_RCBV_INT, RT_Q2R_FLAGS, flags,
SM_RCBV_INT, RT_Q2R_TIMEOUT, timeout,
SM_RCBV_END);
if (sm_is_err(ret))
goto error;
return ret;
error:
/* XXX leave rcb in a consistent state? */
if (Verbose > 1)
fprintf(stderr, "ERROR: ar2ar=%x\n", ret);
return ret;
}
/*
** RCPT2AR -- send a recipient to SMAR
**
** Parameters:
** qar_tsk -- evthr task
** rcpt_pa -- printable address of recipient
** rcpt_idx -- recipient index
** timeout -- timeout
** flags -- request flags
** qar_com -- RCBCOM context
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
rcpt2ar(sm_evthr_task_P qar_tsk, sm_str_P rcpt_pa, int rcpt_idx, uint timeout, uint32_t flags, rcbcom_ctx_P qar_com)
{
sm_ret_T ret;
sm_rcbe_P rcbe;
ret = sm_rcbe_new_enc(&rcbe, -1, 0);
SM_TEST(!sm_is_err(ret));
if (sm_is_err(ret))
goto error;
ret = qr2ar(rcpt_pa, rcpt_idx, timeout, flags, rcbe);
SM_TEST(!sm_is_err(ret));
if (sm_is_err(ret))
goto error;
ret = sm_rcbcom_endrep(qar_com, qar_tsk, true, &rcbe);
SM_TEST(!sm_is_err(ret));
if (sm_is_err(ret))
goto error;
++aqt_rcpts_ar;
return SM_SUCCESS;
error:
if (rcbe != NULL)
sm_rcbe_free(rcbe);
if (Verbose > 1)
fprintf(stderr, "ERROR: rcpt2ar=%x\n", ret);
return ret;
}
/*
** QMGR2AR -- Test program to AR interface
**
** Parameters:
** tsk -- evthr task
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
qmgr2ar(sm_evthr_task_P tsk)
{
sm_ret_T ret;
rcbcom_ctx_P qar_com;
t_req_P t_req;
SM_IS_EVTHR_TSK(tsk);
t_req = (t_req_P) tsk->evthr_t_actx;
qar_com = t_req->rq_qar_com;
ret = sm_rcbcom2mod(tsk, qar_com);
SM_TEST(!sm_is_err(ret));
if (sm_is_err(ret) && Verbose > 1)
fprintf(stderr, "ERROR: qmgr2ar=%x\n", ret);
return ret;
}
/*
** T1_QAR_RCPT_ST -- Decode error status received from AR
**
** Parameters:
** v -- status code
** eol -- end-of-line character
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
t1_qar_rcpt_st(uint32_t v, char eol)
{
switch (v)
{
case DNSR_TEMP:
fprintf(stderr, "WARN: lookup=temp_fail%c", eol);
break;
case DNSR_TIMEOUT:
fprintf(stderr, "WARN: lookup=timeout%c", eol);
break;
case DNSR_REFUSED:
fprintf(stderr, "WARN: lookup=refused%c", eol);
break;
case DNSR_NOTFOUND:
fprintf(stderr, "WARN: lookup=notfound%c", eol);
break;
case DNSR_PERM:
fprintf(stderr, "WARN: lookup=perm%c", eol);
break;
case DNSR_NO_DATA:
fprintf(stderr, "WARN: lookup=no_data%c", eol);
break;
case sm_error_perm(SM_EM_AR, SM_E_MXEMPTY):
fprintf(stderr,
"WARN: lookup=MX_list_empty_after_removing_local%c", eol);
break;
case sm_error_perm(SM_EM_AR, SM_E_ALIASEXP):
fprintf(stderr,
"WARN: lookup=alias_expansion_failed_permanently%c", eol);
break;
case sm_error_temp(SM_EM_AR, SM_E_ALIASEXP):
fprintf(stderr,
"WARN: lookup=alias_expansion_failed_temporarily%c", eol);
break;
case sm_error_perm(SM_EM_AR, SM_E_ALIAS_REC):
fprintf(stderr, "WARN: lookup=alias_nested_too_deep%c", eol);
break;
case sm_error_perm(SM_EM_AR, ELOOP):
fprintf(stderr, "WARN: lookup=loop(cname?)%c", eol);
break;
case SMTP_R_TEMP:
fprintf(stderr, "WARN: lookup=TEMP%c", eol);
return SM_SUCCESS;
break;
case SMTP_R_REJECT:
fprintf(stderr, "WARN: lookup=REJECT%c", eol);
return SM_SUCCESS;
break;
default:
if (Verbose == 0)
{
if (sm_is_temp_err(v))
fprintf(stderr, "WARN: lookup=temp%c", eol);
else if (sm_is_perm_err(v))
fprintf(stderr, "WARN: lookup=perm%c", eol);
else
fprintf(stderr,
"WARN: lookup=unknown=%#x%c", v, eol);
}
else
fprintf(stderr, "WARN: lookup=unknown=%#x%c", v, eol);
break;
}
if (!(sm_is_temp_err(v) || sm_is_perm_err(v)))
return SM_FAILURE;
return SM_SUCCESS;
}
/*
** T1_QAR_ALIAS -- Decode data received from AR for alias expansion
** (see qar_alias())
**
** Parameters:
** nae -- number of aliases
** rcb -- RCB from which to read data
** prv -- (pointer to) return value for caller to use (output)
** returns an error if "fatal" for communication, e.g.,
** protocol error.
**
** Returns:
** usual sm_error code
**
** Locking:
** aq_ctx and defedb should be locked by caller
** (XXX but aren't yet...)
**
** Called by: sm_qar_react()
*/
static sm_ret_T
t1_qar_alias(uint32_t nae, sm_rcb_P rcb, int *prv)
{
uint32_t v, l, rt, nr, i, alias_idx;
int owner_idx;
sm_ret_T ret, st;
sm_str_P pa;
struct in_addr addr;
ret = SM_SUCCESS;
fprintf(stderr, "aliases=%u\n", nae);
for (alias_idx = 0; alias_idx < nae; alias_idx++)
{
fprintf(stderr, "alias %u/%u%.256s", alias_idx, nae
, Oneline ? ":" : "\n");
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret))
{
*prv = ret;
goto erra_0;
}
st = SM_SUCCESS;
if (rt == RT_R2Q_RCPT_ST)
{
/* handle error code */
ret = t1_qar_rcpt_st(v, ' ');
SM_TEST(!sm_is_err(ret));
if (sm_is_err(ret))
goto erra_0;
st = v;
/* get pa, see below for further error check */
}
else if (rt != RT_R2Q_RCPT_DA)
{
*prv = sm_error_perm(SM_EM_Q_AR2Q, SM_E_PR_ERR);
goto erra_0;
}
/* aq_rcpt_a->aqr_da_idx = v; */
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4)
goto erra_0;
if (rt == RT_R2Q_RCPT_PORT)
{
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
}
if (sm_is_err(ret) || l != 4 || rt != RT_R2Q_OWN_REF)
{
fprintf(stderr, "expected RT_R2Q_OWN_REF\n");
goto erra_0;
}
if (st == SM_SUCCESS)
fprintf(stderr, "owner_idx=%d\n", v);
owner_idx = v;
ret = sm_rcb_get2uint32(rcb, &l, &rt);
if (sm_is_err(ret) || rt != RT_R2Q_RCPT_PA)
{
*prv = sm_is_err(ret) ? ret
: sm_error_perm(SM_EM_Q_AR2Q, SM_E_PR_ERR);
goto erra_0;
}
pa = NULL;
ret = sm_rcb_getnstr(rcb, &pa, l);
if (sm_is_err(ret))
goto err3;
/* did this address have an error? */
if (st != SM_SUCCESS)
{
fprintf(stderr, "aliased=%.256s%.256s"
, sm_str_getdata(pa)
, Oneline ? ":" : "\n");
continue;
}
fprintf(stderr, "aliased=%.256s%.256s", sm_str_getdata(pa)
, Oneline ? ":" : "\n");
ret = sm_rcb_get2uint32(rcb, &l, &rt);
if (sm_is_err(ret) || rt != RT_R2Q_RCPT_NAR || l != 4)
{
*prv = sm_is_err(ret) ? ret
: sm_error_perm(SM_EM_Q_AR2Q, SM_E_PR_ERR);
goto erra_0;
}
/* get value for RT_R2Q_RCPT_NAR */
ret = sm_rcb_getuint32(rcb, &v);
if (sm_is_err(ret))
{
*prv = ret;
goto erra_0;
}
nr = v;
if (nr < 1)
goto erra_0;
for (i = 0; i < nr; i++)
{
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
SM_TEST(!(sm_is_err(ret) || l != 4 || rt != RT_R2Q_RCPT_IPV4));
if (sm_is_err(ret) || l != 4 || rt != RT_R2Q_RCPT_IPV4)
goto err3;
/* print IP address? */
addr.s_addr = v;
if (!Perf || Verbose > 0)
fprintf(stderr,
"IP[%d]=%.256s [%x]%.256s",
i, inet_ntoa(addr)
, (uint) htonl(v)
, Oneline ? ":" : "\n");
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
SM_TEST(!(sm_is_err(ret) || l != 4 || rt != RT_R2Q_RCPT_PRE));
if (sm_is_err(ret) || l != 4 || rt != RT_R2Q_RCPT_PRE)
goto err3;
if (!Perf || Verbose > 0)
fprintf(stderr, "pref[%d]=%d%.256s", i, v
, Oneline ? ":" : "\n");
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
SM_TEST(!(sm_is_err(ret) || l != 4 || rt != RT_R2Q_RCPT_TTL));
if (sm_is_err(ret) || l != 4 || rt != RT_R2Q_RCPT_TTL)
goto err3;
if (!Perf || Verbose > 0)
fprintf(stderr, "ttl[%d]=%d%.256s", i, v
, Oneline ? ":" : "\n");
}
if (!Perf || Verbose > 0)
fprintf(stderr, "owner_idx=%d%.256s", owner_idx
, Oneline ? ":" : "\n");
if ((!Perf || Verbose > 0) && Oneline)
fputc('\n', stderr);
}
return SM_SUCCESS;
erra_0:
/* XXX cleanup alias list... */
err3:
fprintf(stderr, "ERROR: t1_qar_alias: rt=%x, v=%d, l=%d, ret=%x, rv=%x\n", rt, v, l, ret, *prv);
/* a protocol error but ret does not indicate an error -> set ret too */
if (sm_is_err(*prv) && !sm_is_err(ret))
ret = *prv;
return ret;
}
/*
** REACT -- Decode data received from AR and act accordingly
**
** Parameters:
** tsk -- evthr task
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
react(sm_evthr_task_P tsk)
{
uint32_t v, l, rt, tl, nr, i, nae;
sm_ret_T ret, st;
sm_rcb_P rcb;
rcpt_id_T rcpt_id;
t_req_P t_req;
rcbcom_ctx_P qar_com;
struct in_addr addr;
/* decode rcb */
t_req = (t_req_P) tsk->evthr_t_actx;
qar_com = t_req->rq_qar_com;
nae = 1;
rcb = qar_com->rcbcom_rdrcb;
ret = sm_rcb_open_dec(rcb);
SM_TEST(!sm_is_err(ret));
if (sm_is_err(ret))
goto error;
++Req_Rcvd;
/* total length of record */
ret = sm_rcb_getuint32(rcb, &tl);
SM_TEST(!sm_is_err(ret));
if (sm_is_err(ret) || tl > QM_AR_MAX_REC_LEN ||
tl > sm_rcb_getlen(rcb))
goto err2;
/* protocol header: version */
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
SM_TEST(!sm_is_err(ret));
if (sm_is_err(ret))
goto err2;
if (l != 4 || rt != RT_PROT_VER || v != PROT_VER_RT)
goto err2;
/* XXX define protocol first in smX docs! */
/*
RT_R2Q_RCPT_ID, smar_rcpt->arr_id,
RT_R2Q_RCPT_DA, smar_rcpt->arr_da,
RT_R2Q_RCPT_IPV4, smar_rcpt->arr_ipv4,
*/
/* XXX decode data, act accordingly... */
ret = sm_rcb_get2uint32(rcb, &l, &rt);
SM_TEST(!sm_is_err(ret));
if (sm_is_err(ret) || l != SMTP_RCPTID_SIZE || rt != RT_R2Q_RCPT_ID)
goto err2;
ret = sm_rcb_getn(rcb, (uchar *) rcpt_id, l);
SM_TEST(!sm_is_err(ret));
if (sm_is_err(ret))
goto err2;
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
SM_TEST(!sm_is_err(ret));
if (sm_is_err(ret) || l != 4)
goto err3;
if (rt == RT_R2Q_RCPT_ST)
{
if (Verbose > 3)
{
struct timeval now;
(void) gettimeofday(&now, NULL);
fprintf(stderr, "%ld.%07ld: result\n"
, (long) now.tv_sec, (long) now.tv_usec);
}
ret = t1_qar_rcpt_st(v, '\n');
SM_TEST(!sm_is_err(ret));
if (sm_is_err(ret))
goto err3;
goto done;
}
if (rt == RT_R2Q_OWN_N)
{
int n;
sm_str_P pa;
fprintf(stderr, "owners=%d\n", v);
for (n = v; n > 0; n--)
{
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4 || rt != RT_R2Q_OWN_IDX)
goto err3;
ret = sm_rcb_get2uint32(rcb, &l, &rt);
if (sm_is_err(ret) || rt != RT_R2Q_OWN_PA)
goto err3;
pa = NULL;
ret = sm_rcb_getnstr(rcb, &pa, l);
if (sm_is_err(ret))
goto err3;
fprintf(stderr, "owner=%.256s, idx=%d\n"
, sm_str_getdata(pa), v);
SM_STR_FREE(pa);
}
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
SM_TEST(!sm_is_err(ret));
if (sm_is_err(ret) || l != 4)
goto err3;
}
if (rt == RT_R2Q_RCPT_AE)
{
nae = v;
fprintf(stderr, "nae=%u\n", nae);
SM_TEST(nae > 1);
if (nae <= 1)
goto err3;
}
if (nae == 1)
{
st = SM_SUCCESS;
if (rt == RT_R2Q_RCPT_ST)
{
/* handle error code */
ret = t1_qar_rcpt_st(v, ' ');
SM_TEST(!sm_is_err(ret));
if (sm_is_err(ret))
goto err3;
st = v;
/* get pa, see below for further error check */
}
else if (rt != RT_R2Q_RCPT_DA)
{
if (Verbose > 0)
fprintf(stderr,
"rt=0x%x, expected=RT_R2Q_RCPT_DA\n",
rt);
goto err3;
}
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4)
goto err3;
if (rt == RT_R2Q_RCPT_PORT)
{
if (Verbose > 0)
fprintf(stderr, "port=%d\n", v);
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
}
if (sm_is_err(ret) || l != 4 || rt != RT_R2Q_OWN_REF)
goto err3;
if (Verbose > 0)
fprintf(stderr, "owner_idx=%d\n", v);
if (st != SM_SUCCESS && SM_RCB_ISEOB(rcb))
goto done;
ret = sm_rcb_get2uint32(rcb, &l, &rt);
if (sm_is_err(ret))
goto err3;
if (rt == RT_R2Q_RCPT_PA)
{
sm_str_P pa;
pa = NULL;
ret = sm_rcb_getnstr(rcb, &pa, l);
if (sm_is_err(ret))
goto err3;
fprintf(stderr, "aliased=%.256s\n", sm_str_getdata(pa));
if (st != SM_SUCCESS)
goto done;
/* get RT_R2Q_RCPT_NAR, value is read below */
ret = sm_rcb_get2uint32(rcb, &l, &rt);
}
if (sm_is_err(ret) || rt != RT_R2Q_RCPT_NAR || l != 4)
goto err3;
/* get value for RT_R2Q_RCPT_NAR */
ret = sm_rcb_getuint32(rcb, &v);
if (sm_is_err(ret))
goto err3;
/* number of address records to follow */
nr = v;
if (nr < 1)
goto err3;
for (i = 0; i < nr; i++)
{
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
SM_TEST(!(sm_is_err(ret) || l != 4
|| rt != RT_R2Q_RCPT_IPV4));
if (sm_is_err(ret) || l != 4
|| rt != RT_R2Q_RCPT_IPV4)
{
fprintf(stderr, "ret=%x, l=%d, rt=%x\n"
, ret, l, rt);
goto err3;
}
/* print IP address? */
addr.s_addr = v;
if (!Perf || Verbose > 0)
fprintf(stderr, "IP[%d]=%.256s [%x]\n"
, i, inet_ntoa(addr)
, (uint) htonl(v));
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
SM_TEST(!(sm_is_err(ret) || l != 4 || rt != RT_R2Q_RCPT_PRE));
if (sm_is_err(ret) || l != 4 || rt != RT_R2Q_RCPT_PRE)
goto err3;
if (!Perf || Verbose > 0)
fprintf(stderr, "pref[%d]=%d\n", i, v);
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
SM_TEST(!(sm_is_err(ret) || l != 4 || rt != RT_R2Q_RCPT_TTL));
if (sm_is_err(ret) || l != 4 || rt != RT_R2Q_RCPT_TTL)
goto err3;
if (!Perf || Verbose > 0)
fprintf(stderr, "ttl[%d]=%d\n", i, v);
}
}
else
{
sm_ret_T rv;
ret = t1_qar_alias(nae, rcb, &rv);
if (sm_is_err(ret))
goto err3;
}
done:
ret = sm_rcb_close_dec(qar_com->rcbcom_rdrcb);
(void) sm_rcb_open_rcv(qar_com->rcbcom_rdrcb);
--Requests;
return ret;
err3:
err2:
/* use rcb functions that don't do check the state */
(void) sm_rcb_close_decn(qar_com->rcbcom_rdrcb);
error:
/* open rcb for receiving next record */
(void) sm_rcb_open_rcvn(qar_com->rcbcom_rdrcb);
fprintf(stderr, "ERROR: react=%x\n", ret);
--Requests;
return ret;
}
/*
** AR2QMGR -- AR - test program interface
**
** Parameters:
** tsk -- evthr task
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
ar2qmgr(sm_evthr_task_P tsk)
{
int fd;
sm_ret_T ret;
t_req_P t_req;
rcbcom_ctx_P qar_com;
SM_IS_EVTHR_TSK(tsk);
t_req = (t_req_P) tsk->evthr_t_actx;
qar_com = t_req->rq_qar_com;
fd = tsk->evthr_t_fd; /* checked in caller */
ret = sm_rcb_rcv(fd, qar_com->rcbcom_rdrcb, QSS_RC_MINSZ);
if (Verbose > 1)
fprintf(stderr, "ar2qmgr, ret=%d\n", ret);
if (ret > 0)
{
return EVTHR_WAITQ;
}
else if (ret == 0)
{
ret = sm_rcb_close_rcv(qar_com->rcbcom_rdrcb);
/* start appropriate function ... */
ret = react(tsk);
if (sm_is_err(ret))
goto termit; /* too harsh? */
else if (ret == QMGR_R_WAITQ)
return EVTHR_WAITQ;
else if (ret == QMGR_R_ASYNC)
return EVTHR_OK;
else if (ret == EVTHR_DEL)
goto termit;
else
return ret;
}
else if (ret == SM_IO_EOF)
{
ret = sm_rcb_close_rcv(qar_com->rcbcom_rdrcb);
termit:
close(fd);
/* XXX see comment in qm_fr_ss() */
tsk->evthr_t_fd = INVALID_FD; /* make it invalid */
SM_TEST(false);
return EVTHR_DEL;
}
else /* if (ret < 0) */
{
SM_TEST(!sm_is_err(ret));
}
SM_TEST(false);
return EVTHR_DEL;
}
/*
** LOOKUP -- queue recipient address lookups to SMAR
**
** Parameters:
** tsk -- evthr task
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
lookup(sm_evthr_task_P tsk)
{
t_req_P t_req;
int n;
sm_ret_T ret;
char *rcpt;
sm_str_P rcpt_pa;
SM_REQUIRE(tsk != NULL);
t_req = (t_req_P) tsk->evthr_t_actx;
SM_TEST(t_req != NULL);
if (t_req == NULL)
return EVTHR_DEL|EVTHR_TERM;
rcpt_pa = NULL;
#if 0
n = pthread_mutex_lock(&mutex);
SM_TEST(n == 0);
n = pthread_cond_wait(&cond, &mutex);
SM_TEST(n == 0);
#endif /* 0 */
if (Verbose > 1)
fprintf(stderr, "lookup, count=%d\n", t_req->rq_count);
for (n = 0; n < t_req->rq_count; n++)
{
rcpt = t_req->rq_rcpt_pa[n];
SM_TEST(!(rcpt == NULL || *rcpt == '\0'));
if (rcpt == NULL || *rcpt == '\0')
return EVTHR_DEL|EVTHR_TERM;
if (rcpt_pa == NULL)
{
rcpt_pa = sm_str_scpy(NULL, rcpt, SMAXLEN);
SM_TEST(rcpt_pa != NULL);
if (rcpt_pa == NULL)
goto error;
}
else
{
sm_str_clr(rcpt_pa);
ret = sm_str_scat(rcpt_pa, rcpt);
SM_TEST(ret == SM_SUCCESS);
if (sm_is_err(ret))
goto error;
}
if (Verbose > 1)
fprintf(stderr, "rcpt_pa[%d]=%.256s\n", n, rcpt);
ret = rcpt2ar(tsk, rcpt_pa, n, t_req->rq_timeout,
t_req->rq_flags,
t_req->rq_qar_com);
SM_TEST(!sm_is_err(ret));
if (sm_is_err(ret))
goto error;
++Req_Sent;
}
/* Wakeup write task */
SM_TEST(t_req->rq_wr_tsk != NULL);
if (t_req->rq_wr_tsk != NULL)
{
ret = evthr_en_wr(t_req->rq_wr_tsk);
if (Verbose > 1)
fprintf(stderr, "wakeup=%x\n", ret);
SM_TEST(sm_is_success(ret));
}
/* Paranoia... */
t_req->rq_count = 0;
SM_STR_FREE(rcpt_pa);
/* We're done */
return EVTHR_DEL;
error:
SM_STR_FREE(rcpt_pa);
SM_TEST(false);
return EVTHR_DEL|EVTHR_TERM;
}
/*
** PERF_LOOKUP -- queue recipient address lookups to SMAR
**
** Parameters:
** tsk -- evthr task
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
perf_lookup(sm_evthr_task_P tsk)
{
t_req_P t_req;
int n;
uint loops, cnt;
sm_ret_T ret;
char *rcpt;
sm_str_P rcpt_pa;
SM_REQUIRE(tsk != NULL);
t_req = (t_req_P) tsk->evthr_t_actx;
SM_TEST(t_req != NULL);
if (t_req == NULL)
return EVTHR_DEL|EVTHR_TERM;
rcpt_pa = sm_str_new(NULL, SMAXLEN, SMAXLEN);
SM_TEST(rcpt_pa != NULL);
if (rcpt_pa == NULL)
goto error;
if (Verbose > 1)
fprintf(stderr, "lookup, count=%d\n", t_req->rq_count);
cnt = 0;
for (loops = 0; loops < t_req->rq_loops; loops++)
{
for (n = 0; n < t_req->rq_count; n++)
{
rcpt = t_req->rq_rcpt_pa[n];
SM_TEST(!(rcpt == NULL || *rcpt == '\0'));
if (rcpt == NULL || *rcpt == '\0')
return EVTHR_DEL|EVTHR_TERM;
sm_str_clr(rcpt_pa);
if (strchr(rcpt, '%') != NULL)
{
ret = sm_strprintf(rcpt_pa, rcpt, cnt);
SM_TEST(ret > 0);
if (ret <= 0)
goto error;
}
else
{
ret = sm_str_scat(rcpt_pa, rcpt);
SM_TEST(ret == SM_SUCCESS);
if (sm_is_err(ret))
goto error;
}
if (Verbose > 1)
fprintf(stderr, "rcpt_pa[%d]=%.256s\n", n, rcpt);
ret = rcpt2ar(tsk, rcpt_pa, cnt, t_req->rq_timeout,
t_req->rq_flags,
t_req->rq_qar_com);
SM_TEST(!sm_is_err(ret));
if (sm_is_err(ret))
goto error;
++Req_Sent;
++cnt;
}
/* Wakeup write task */
SM_TEST(t_req->rq_wr_tsk != NULL);
if (0 == loops && t_req->rq_wr_tsk != NULL)
{
ret = evthr_en_wr(t_req->rq_wr_tsk);
if (Verbose > 1)
fprintf(stderr, "wakeup=%x\n", ret);
SM_TEST(sm_is_success(ret));
}
if (Req_Sent > Req_Rcvd + Req_Max_Open)
{
struct timeval tv_begin, tv_now;
(void) gettimeofday(&tv_begin, NULL);
if (t_req->rq_wr_tsk != NULL)
{
ret = evthr_en_wr(t_req->rq_wr_tsk);
if (Verbose > 1)
fprintf(stderr, "wait: wakeup=%x\n"
, ret);
SM_TEST(sm_is_success(ret));
}
while (Req_Sent > Req_Rcvd + Req_Max_Open)
{
#if HAVE_PTHREAD_YIELD
pthread_yield();
#endif
sm_us_sleep(1000);
(void) gettimeofday(&tv_now, NULL);
if (tv_now.tv_sec - tv_begin.tv_sec > 2)
{
fprintf(stderr,
"func=perf_lookup, where=stuck, cnt=%u, Req_Sent=%u, Req_Rcvd=%u\n"
, cnt, Req_Sent, Req_Rcvd);
goto error;
}
}
}
}
/* Wakeup write task */
SM_TEST(t_req->rq_wr_tsk != NULL);
if (t_req->rq_wr_tsk != NULL)
{
ret = evthr_en_wr(t_req->rq_wr_tsk);
if (Verbose > 1)
fprintf(stderr, "wakeup=%x\n", ret);
SM_TEST(sm_is_success(ret));
}
/* Paranoia... */
t_req->rq_count = 0;
SM_STR_FREE(rcpt_pa);
/* We're done */
return EVTHR_DEL;
error:
SM_STR_FREE(rcpt_pa);
SM_TEST(false);
return EVTHR_DEL|EVTHR_TERM;
}
/*
** TESTSMAR -- Test SMAR
**
** Parameters:
** t_req -- test context
**
** Returns:
** none.
*/
static void
testsmar(t_req_P t_req)
{
sm_ret_T ret;
sm_evthr_task_P task, task2, task3;
struct timeval sleep_tv, delay_tv, res_tv;
int lfd, n;
rcbcom_ctx_P qar_com;
evthr_ctx = NULL;
task = task2 = task3 = 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);
n = pthread_cond_init(&cond, NULL);
SM_TEST(n == 0);
n = pthread_mutex_init(&mutex, NULL);
SM_TEST(n == 0);
qar_com = t_req->rq_qar_com;
ret = sm_rcbcom_open(qar_com);
SM_TEST(sm_is_success(ret));
if (sm_is_err(ret))
goto error;
SM_IS_RCB(qar_com->rcbcom_rdrcb);
#if SMAR_TCP_NET
(void) net_client_connect(smarip, smarport, &lfd);
#else
(void) unix_client_connect(SMAR_socket, &lfd);
#endif
if (lfd < 0)
{
ret = sm_error_perm(SM_EM_AR, errno);
SM_TEST(sm_is_success(ret));
goto error;
}
ret = sm_rcb_open_rcv(qar_com->rcbcom_rdrcb);
SM_TEST(sm_is_success(ret));
if (sm_is_err(ret))
goto error;
ret = sm_fd_nonblock(lfd, true);
SM_TEST(sm_is_success(ret));
if (sm_is_err(ret))
goto error; /* XXX COMPLAIN */
ret = evthr_task_new(evthr_ctx, &task, EVTHR_EV_RD,
lfd, NULL, qmgr_ar, (void *) t_req);
SM_TEST(sm_is_success(ret));
if (sm_is_err(ret))
goto error;
t_req->rq_wr_tsk = task;
SM_IS_EVTHR_TSK(task);
delay_tv.tv_usec = 1000;
delay_tv.tv_sec = 0;
n = gettimeofday(&sleep_tv, NULL);
SM_TEST(n == 0);
timeradd(&sleep_tv, &delay_tv, &res_tv);
sleep_tv = res_tv;
if (Verbose > 3)
{
fprintf(stderr, "%ld.%07ld: start\n"
, (long) sleep_tv.tv_sec, (long) sleep_tv.tv_usec);
}
ret = evthr_task_new(evthr_ctx, &task3, EVTHR_EV_SL, -1, &sleep_tv,
fcts, (void *) NULL);
SM_TEST(sm_is_success(ret));
SM_TEST(task3 != NULL);
SM_IS_EVTHR_TSK(task3);
delay_tv.tv_usec = 100;
delay_tv.tv_sec = 0;
timeradd(&sleep_tv, &delay_tv, &res_tv);
sleep_tv = res_tv;
ret = evthr_task_new(evthr_ctx, &task2, EVTHR_EV_SL, -1, &sleep_tv,
Perf ? perf_lookup : lookup, (void *) t_req);
SM_TEST(sm_is_success(ret));
SM_TEST(task2 != NULL);
SM_IS_EVTHR_TSK(task2);
ret = evthr_loop(evthr_ctx);
SM_TEST(sm_is_success(ret));
if (Verbose > 3)
{
(void) gettimeofday(&sleep_tv, NULL);
fprintf(stderr, "%ld.%07ld: done, ret=%X\n"
, (long) sleep_tv.tv_sec, (long) sleep_tv.tv_usec, ret);
}
goto done;
error:
fprintf(stderr, "ERROR\n");
done:
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
ret = evthr_stop(evthr_ctx);
SM_TEST(sm_is_success(ret));
errt1:
ret = thr_stop();
SM_TEST(sm_is_success(ret));
errq:
return;
}
/*
** USAGE -- usage message
**
** Parameters:
** prg -- program name
**
** Returns:
** none
*/
static void
usage(const char *prg)
{
fprintf(stderr, "usage: %s [options] addresses...\n"
"-A alias expansion for all addresses\n"
"-L alias expansion for local addresses including domain\n"
"-M do not remove local MX records\n"
"-O single line output\n"
"-o check owner\n"
"-R n wait n times 0.1s\n"
"-S sock use sock for communication with SMAR\n"
"-T n set request timeout\n"
"-V increase verbosity\n"
, prg
);
return;
}
/*
** MAIN -- guess...
**
** Parameters:
** argc -- arg counter
** argv -- arg vector
**
** Returns:
** usual exit code
*/
int
main(int argc, char *argv[])
{
int r;
t_req_T t_req;
char **h;
void *ptr;
ptr = NULL;
h = NULL;
t_req.rq_timeout = 0;
t_req.rq_loops = 1;
t_req.rq_flags = SMARRQ_FL_ALIAS;
while ((r = getopt(argc, argv, "ALlMOoR:r:S:T:V")) != -1)
{
switch (r)
{
case 'A':
/*
t_req.rq_flags |= SMARRQ_FL_RALIAS;
*/
fprintf(stderr, "-A doesn't work anymore\n");
break;
case 'L':
/*
t_req.rq_flags |= SMARRQ_FL_LALIAS;
*/
fprintf(stderr, "-L doesn't work anymore\n");
break;
case 'l':
t_req.rq_flags |= SMARRQ_FL_CHK_LU;
break;
case 'M':
t_req.rq_flags |= SMARRQ_FL_NOMXCUT;
break;
case 'O':
Oneline = true;
break;
case 'o':
t_req.rq_flags |= SMARRQ_FL_OWNER;
break;
case 'R':
MaxCount = atoi(optarg);
break;
case 'r':
t_req.rq_loops = atoi(optarg);
Perf = true;
break;
case 'S':
SMAR_socket = optarg;
break;
case 'T':
t_req.rq_timeout = atoi(optarg);
break;
case 'V':
Verbose++;
#if RCBCOMM_DEBUG
rcbcomm_debug++;
#endif
break;
default:
usage(argv[0]);
return EX_USAGE;
}
}
sm_test_begin(argc, argv, "test smar 0");
t_req.rq_count = argc - optind;
h = (char **) sm_malloc(sizeof(char *) * t_req.rq_count);
ptr = h;
SM_TEST(h != NULL);
if (h == NULL)
goto error;
t_req.rq_rcpt_pa = h;
t_req.rq_qar_com = &rcb_com;
for (r = optind; r < argc; r++)
{
*h = argv[r];
++h;
++Requests;
}
Requests *= t_req.rq_loops;
testsmar(&t_req);
error:
if (ptr != NULL)
sm_free(ptr);
return sm_test_end();
}
syntax highlighted by Code2HTML, v. 0.9.1