/* * 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(); }