/* * Copyright (c) 2004-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 RFC 2821 addresses (or certs) to SMAR for access checks. ** Used by t-access-[012].sh */ #include "sm/generic.h" SM_RCSID("@(#)$Id: t-access-0.c,v 1.37 2007/06/10 16:14:42 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/smar.h" #include "sm/reccom.h" #include "sm/unixsock.h" #include "sm/sysexits.h" #include "sm/test.h" /* context for requests */ struct t_req_S { char **rq_pa; char *rq_pa2; int rq_count; uint32_t rq_rt; uint32_t rq_type; 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 uint MaxCount = (DNS_TIMEOUT + 1) * 10; static int Requests = 0; static int aqt_rcpts_ar = 0; static sm_evthr_ctx_P evthr_ctx; static rcbcom_ctx_T rcb_com; 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 sleept, delay; ++count; if (Verbose > 3 && (count % 10 == 0)) sm_io_fprintf(smioerr, "fcts: count=%u/%u, ", count, MaxCount); /* Theoretically Requests needs to be protected by a mutex... */ if (Requests <= 0 || count > MaxCount) return EVTHR_DEL|EVTHR_TERM; sm_memzero(&sleept, sizeof(sleept)); gettimeofday(&sleept, NULL); delay.tv_sec = 1; delay.tv_usec = 0; timeradd(&sleept, &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); if (is_valid_fd(tsk->evthr_t_fd)) { ret = EVTHR_WAITQ; if (evthr_got_wr(tsk)) { if (Verbose > 3) sm_io_fprintf(smioerr, "write\n"); ret = qmgr2ar(tsk); /* XXX check ret here? */ if (sm_is_err(ret) && Verbose > 0) fprintf(stderr, "qmgr2ar=%x\n", ret); } if (evthr_got_rd(tsk)) { if (Verbose > 3) sm_io_fprintf(smioerr, "read\n"); ret = ar2qmgr(tsk); } if (sm_is_err(ret)) { if (Verbose > 1) sm_io_fprintf(smioerr, "qmgr_ar=%x\n", ret); return ret; } return ret; } return EVTHR_DEL; } /* ** QR2AR -- Send address data to AR, i.e., put it into RCB ** ** Parameters: ** pa -- printable address ** rt -- record type ** type -- lookup type ** flags -- lookup flags ** rcbe -- RCB entry ** ** Returns: ** usual sm_error code */ static sm_ret_T qr2ar(sm_str_P pa, sm_str_P pa2, uint32_t rt, uint32_t type, uint32_t flags, sm_rcbe_P rcbe) { sm_rcb_P rcb; sm_ret_T ret; sessta_id_T ta_id; ret = SM_SUCCESS; rcb = &rcbe->rcbe_rcb; sm_snprintf(ta_id, sizeof(ta_id), SMTPS_STID_FORMAT, (ulonglong_T)0, 0); /* ** Currently clt2ar.c doesn't care much about the RT, i.e., ** it can be CLT_A even though it is a mail address. */ ret = sm_rcb_putv(rcb, RCB_PUTV_FIRST, SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT, SM_RCBV_INT, RT_S2A_ID, 1, SM_RCBV_BUF, RT_S2A_TAID, ta_id, SMTP_STID_SIZE, SM_RCBV_STR, rt, pa, SM_RCBV_INT2, RT_S2A_LTYPE, type, flags, SM_RCBV_INT, SM_IS_FLAG(type, SMARA_LT_GREY) ? RT_S2A_TIME : RT_NOSEND, (uint32_t) time(NULL), SM_RCBV_END); if (sm_is_err(ret)) goto error; if (pa2 != NULL) { ret = sm_rcb_putv(rcb, RCB_PUTV_NONE, SM_RCBV_STR, SM_IS_FLAG(type, SMARA_LT_RCPT_PROT) ? RT_S2A_MAIL : RT_S2A_CERTSUB, pa2, SM_RCBV_END); if (sm_is_err(ret)) goto error; } if (Verbose > 4) sm_io_fprintf(smioerr, "func=qr2ar, ret=%d, rw=%d, size=%d, len=%d, max=%d\n" , ret , rcb->sm_rcb_rw , rcb->sm_rcb_size , rcb->sm_rcb_len , rcb->sm_rcb_max ); return ret; error: /* XXX leave rcb in a consistent state? */ return ret; } /* ** RCPT2AR -- send a recipient to SMAR ** ** Parameters: ** qar_tsk -- evthr task ** pa -- printable address of recipient ** rt -- record type ** type -- lookup type ** flags -- lookup flags ** qar_com -- RCBCOM context ** ** Returns: ** usual sm_error code */ static sm_ret_T rcpt2ar(sm_evthr_task_P qar_tsk, sm_str_P pa, sm_str_P pa2, uint32_t rt, uint32_t type, uint32_t flags, rcbcom_ctx_P qar_com) { sm_ret_T ret; sm_rcbe_P rcbe; ret = sm_rcbe_new_enc(&rcbe, -1, 0); if (sm_is_err(ret)) goto error; ret = qr2ar(pa, pa2, rt, type, flags, rcbe); if (sm_is_err(ret)) goto error; ret = sm_rcbcom_endrep(qar_com, qar_tsk, true, &rcbe); if (sm_is_err(ret)) goto error; ++aqt_rcpts_ar; return SM_SUCCESS; error: if (rcbe != NULL) sm_rcbe_free(rcbe); 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); if (Verbose > 4) sm_io_fprintf(smioerr, "qmgr2ar, ret=%d\n", ret); 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; sm_ret_T ret; sm_rcb_P rcb; sessta_id_T ta_id; char statt[256], rhs2[256]; t_req_P t_req; rcbcom_ctx_P qar_com; sm_cstr_P cstr; bool gotstatt; /* decode rcb */ t_req = (t_req_P) tsk->evthr_t_actx; qar_com = t_req->rq_qar_com; rcb = qar_com->rcbcom_rdrcb; cstr = NULL; ret = sm_rcb_open_dec(rcb); if (sm_is_err(ret)) goto error; /* total length of record */ ret = sm_rcb_getuint32(rcb, &tl); 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); 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! */ /* */ /* XXX decode data, act accordingly... */ ret = sm_rcb_get3uint32(rcb, &l, &rt, &v); if (sm_is_err(ret) || l != 4 || rt != RT_A2S_ID) goto err2; ret = sm_rcb_get2uint32(rcb, &l, &rt); if (sm_is_err(ret) || l != SMTP_STID_SIZE || rt != RT_A2S_TAID) goto err2; ret = sm_rcb_getn(rcb, (uchar *) ta_id, l); if (sm_is_err(ret)) goto err2; ret = sm_rcb_get3uint32(rcb, &l, &rt, &v); if (sm_is_err(ret) || l != 4) goto err3; SM_TEST(RT_A2S_MAP_RES == rt); if (RT_A2S_MAP_RES == rt && SM_ACC_FOUND == v) { ret = sm_rcb_get3uint32(rcb, &l, &rt, &v); if (sm_is_err(ret) || l != 4) goto err3; /* SM_TEST(RT_A2S_MAIL_ST == rt); */ if (RT_A2S_CLT_A_ST == rt) sm_io_fprintf(smioerr, "clt_a_status=%d\n", v); if (RT_A2S_CLT_N_ST == rt) sm_io_fprintf(smioerr, "clt_n_status=%d\n", v); if (RT_A2S_MAIL_ST == rt) sm_io_fprintf(smioerr, "status=%d\n", v); if (RT_A2S_RCPT_ST == rt) sm_io_fprintf(smioerr, "rcpt-status=%d\n", v); if (RT_A2S_CERT_ST == rt) sm_io_fprintf(smioerr, "cert-status=%d\n", v); } gotstatt = false; while (!SM_RCB_ISEOB(rcb)) { ret = sm_rcb_get2uint32(rcb, &l, &rt); switch (rt) { case RT_A2S_STATT: gotstatt = true; if (l < sizeof(statt)) { sm_memzero(statt, sizeof(statt)); ret = sm_rcb_getn(rcb, (uchar *) statt, l); sm_io_fprintf(smioerr, "react=statt, ret=%x, statt='%.256s'\n", ret, statt); if (sm_is_err(ret)) goto err2; } else { sm_io_fprintf(smioerr, "ERROR: react=statt, stat=too-long\n"); SM_TEST(false); goto err2; } break; case RT_A2S_RVRS_ST: ret = sm_rcb_getuint32(rcb, &v); if (sm_is_err(ret)) goto err2; sm_io_fprintf(smioerr, "react=rvrs_st, ret=%x, v=%#x\n", ret, v); break; case RT_A2S_MAP2_RES: ret = sm_rcb_getuint32(rcb, &v); if (sm_is_err(ret)) goto err2; sm_io_fprintf(smioerr, "react=rhs2, ret=%x, res2=%#x\n", ret, v); break; case RT_A2S_RHS2_TXT: if (l < sizeof(rhs2)) { sm_memzero(rhs2, sizeof(rhs2)); ret = sm_rcb_getn(rcb, (uchar *) rhs2, l); sm_io_fprintf(smioerr, "react=rhs2, ret=%x, rhs2='%.256s'\n", ret, rhs2); if (sm_is_err(ret)) goto err2; } else { sm_io_fprintf(smioerr, "ERROR: react=rhs2, stat=too-long\n"); SM_TEST(false); goto err2; } break; case RT_A2S_MAP_RES_CNF: ret = sm_rcb_getuint32(rcb, &v); if (sm_is_err(ret)) goto err2; sm_io_fprintf(smioerr, "react=rhsconf, ret=%x, resconf=%#x\n", ret, v); break; case RT_A2S_RHS_CNF: if (l < sizeof(rhs2)) { sm_memzero(rhs2, sizeof(rhs2)); ret = sm_rcb_getn(rcb, (uchar *) rhs2, l); sm_io_fprintf(smioerr, "react=rhsconf, ret=%x, rhsconf='%.256s'\n", ret, rhs2); if (sm_is_err(ret)) goto err2; } else { sm_io_fprintf(smioerr, "ERROR: react=rhsconf, stat=too-long\n"); SM_TEST(false); goto err2; } break; default: sm_io_fprintf(smioerr, "ERROR: record-type=unknown, rt=%x, l=%d\n", rt, l); SM_TEST(false); sm_rcb_skip(rcb, l); break; } } if (!gotstatt) sm_io_fprintf(smioerr, "react=no_match\n"); #if 0 ret = sm_rcb_get3uint32(rcb, &l, &rt, &v); if (sm_is_err(ret) || l != 4 || rt != RT_R2Q_RCPT_NAR) goto err3; #endif /* 0 */ 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); sm_io_fprintf(smioerr, "ERROR: react=%x\n", ret); #if 0 errret: #endif --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 > 4) sm_io_fprintf(smioerr, "ar2qmgr, ret=%d, rw=%d, size=%d, len=%d, max=%d\n" , ret , qar_com->rcbcom_rdrcb->sm_rcb_rw , qar_com->rcbcom_rdrcb->sm_rcb_size , qar_com->rcbcom_rdrcb->sm_rcb_len , qar_com->rcbcom_rdrcb->sm_rcb_max ); else if (Verbose > 1) sm_io_fprintf(smioerr, "ar2qmgr, ret=%d\n", ret); if (ret > 0) return EVTHR_WAITQ; else if (0 == ret) { 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 (QMGR_R_WAITQ == ret) return EVTHR_WAITQ; else if (QMGR_R_ASYNC == ret) return EVTHR_OK; else if (EVTHR_DEL == ret) goto termit; else return ret; } else if (SM_IO_EOF == ret) { 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 */ return EVTHR_DEL; } else /* if (ret < 0) */ { } #if 0 error: #endif 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 *addr; sm_str_P pa, pa2; SM_REQUIRE(tsk != NULL); t_req = (t_req_P) tsk->evthr_t_actx; if (NULL == t_req) return EVTHR_DEL|EVTHR_TERM; pa = NULL; pa2 = NULL; if (t_req->rq_pa2 != NULL) { pa2 = sm_str_scpy(NULL, t_req->rq_pa2, SMAXLEN); SM_TEST(pa2 != NULL); if (NULL == pa2) goto error; } #if 0 n = pthread_mutex_lock(&mutex); SM_TEST(0 == n); n = pthread_cond_wait(&cond, &mutex); SM_TEST(0 == n); #endif /* 0 */ if (Verbose > 1) sm_io_fprintf(smioerr, "lookup, count=%d\n", t_req->rq_count); for (n = 0; n < t_req->rq_count; n++) { addr = t_req->rq_pa[n]; if (NULL == addr || *addr == '\0') return EVTHR_DEL|EVTHR_TERM; if (NULL == pa) { pa = sm_str_scpy(NULL, addr, SMAXLEN); SM_TEST(pa != NULL); if (NULL == pa) goto error; } else { sm_str_clr(pa); ret = sm_str_scat(pa, addr); SM_TEST(SM_SUCCESS == ret); if (sm_is_err(ret)) goto error; } if (Verbose > 1) sm_io_fprintf(smioerr, "addr[%d]=%.256s\n", n, addr); ret = rcpt2ar(tsk, pa, pa2, t_req->rq_rt, t_req->rq_type, t_req->rq_flags, t_req->rq_qar_com); if (sm_is_err(ret)) 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) sm_io_fprintf(smioerr, "wakeup=%x\n", ret); SM_TEST(sm_is_success(ret)); } /* Paranoia... */ t_req->rq_count = 0; SM_STR_FREE(pa); /* We're done */ return EVTHR_DEL; error: SM_STR_FREE(pa); 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 sleept; int lfd, n; rcbcom_ctx_P qar_com; evthr_ctx = NULL; task = 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(0 == n); n = pthread_mutex_init(&mutex, NULL); SM_TEST(0 == n); qar_com = t_req->rq_qar_com; ret = sm_rcbcom_open(qar_com); 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(smarsock, &lfd); #endif if (lfd < 0) { ret = sm_error_perm(SM_EM_AR, errno); goto error; } ret = sm_rcb_open_rcv(qar_com->rcbcom_rdrcb); if (sm_is_err(ret)) goto error; ret = sm_fd_nonblock(lfd, true); 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); if (sm_is_err(ret)) goto error; t_req->rq_wr_tsk = task; n = gettimeofday(&sleept, NULL); SM_TEST(0 == n); 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); 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); ret = evthr_loop(evthr_ctx); SM_TEST(sm_is_success(ret)); goto done; error: sm_io_fprintf(smioerr, "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" "perform access checks, needs smar server\n" "default checks: lookup type=SMARA_LT_MAIL_ACC, flags=LFL_2821\n" "-A set lookup type SMARA_LT_MAIL_ACC\n" "-C set lookup type SMARA_LT_CLT_A_ACC\n" "-c certsubject set lookup type SMARA_LT_CERT_RELAY\n" "-D implicitly match +detail for protected addresses\n" "-e set lookup type SMARA_LT_EHLO_ACC\n" "-F flags set lookup flags\n" "-g perform greylisting too\n" "-L set lookup type SMARA_LT_MAIL_LOCAL\n" "-M set lookup type SMARA_LT_MAIL_ROUTE\n" "-m set lookup flag SMARA_LFL_PROTMAP\n" "-p address set lookup type SMARA_LT_RCPT_PROT\n" "-r set lookup type SMARA_LT_RCPT_ACC\n" "-S set lookup type SMARA_LT_SS_SE_CONF\n" "-T type set lookup type\n" "-V increase verbosity\n" "-X set lookup flag SMARA_LFL_MXACC\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 *pa2; char **h; void *ptr; ptr = NULL; h = NULL; pa2 = NULL; t_req.rq_rt = 0; t_req.rq_type = 0; t_req.rq_flags = 0; while ((r = getopt(argc, argv, "ACc:DeF:fgLMmp:rR:SsT:VX")) != -1) { switch (r) { case 'A': t_req.rq_type |= SMARA_LT_MAIL_ACC; break; case 'C': t_req.rq_type = SMARA_LT_CLT_A_ACC; t_req.rq_flags = SMARA_LFL_IPV4|SMARA_LFL_SUB |SMARA_LFL_RVRS4|SMARA_LFL_RVACC |SMARA_LFL_DNSBL; break; case 'c': t_req.rq_flags = SMARA_LFL_CERT; t_req.rq_type |= SMARA_LT_CERT_RELAY; pa2 = strdup(optarg); if (NULL == pa2) exit(EX_OSERR); break; case 'D': t_req.rq_flags |= SMARA_LFL_PROTIMPLDET; break; case 'e': t_req.rq_rt = RT_S2A_EHLO; t_req.rq_type |= SMARA_LT_EHLO_ACC; t_req.rq_flags = SMARA_LFL_STR; break; case 'F': t_req.rq_flags = strtoul(optarg, NULL, 0); break; case 'f': t_req.rq_type = SMARA_LT_CLT_A_ACC|SMARA_LT_SS_SE_CONF; t_req.rq_flags = SMARA_LFL_IPV4|SMARA_LFL_SUB |SMARA_LFL_RVRS4|SMARA_LFL_RVACC |SMARA_LFL_DNSBL; break; case 'g': t_req.rq_flags = SMARA_LFL_IPV4|SMARA_LFL_SUB; t_req.rq_type = SMARA_LT_GREY|SMARA_LT_CLT_A_ACC; break; case 'L': t_req.rq_type |= SMARA_LT_MAIL_LOCAL; break; case 'M': t_req.rq_type |= SMARA_LT_MAIL_ROUTE; break; case 'm': t_req.rq_flags |= SMARA_LFL_PROTMAP; break; case 'p': t_req.rq_type |= SMARA_LT_RCPT_PROT; t_req.rq_flags = SMARA_LFL_2821; pa2 = strdup(optarg); if (NULL == pa2) exit(EX_OSERR); break; case 'r': t_req.rq_type |= SMARA_LT_RCPT_ACC; break; case 'R': MaxCount = atoi(optarg); break; case 'S': t_req.rq_flags = SMARA_LFL_IPV4|SMARA_LFL_SUB; t_req.rq_type = SMARA_LT_CLT_A_ACC|SMARA_LT_SS_SE_CONF; break; case 'T': t_req.rq_type = strtoul(optarg, NULL, 0); break; case 'V': Verbose++; #if RCBCOMM_DEBUG rcbcomm_debug++; #endif break; case 'X': t_req.rq_flags |= SMARA_LFL_MXACC; break; default: usage(argv[0]); return 1; } } if (0 == t_req.rq_rt) t_req.rq_rt = RT_S2A_CLT_A; if (0 == t_req.rq_type) t_req.rq_type = SMARA_LT_MAIL_ACC; if (0 == t_req.rq_flags) t_req.rq_flags = SMARA_LFL_2821; sm_test_begin(argc, argv, "test access 0"); t_req.rq_count = argc - optind; h = (char **) sm_malloc(sizeof(char *) * t_req.rq_count); ptr = h; SM_TEST(h != NULL); if (NULL == h) goto error; t_req.rq_pa = h; t_req.rq_qar_com = &rcb_com; if (pa2 != NULL) t_req.rq_pa2 = pa2; else t_req.rq_pa2 = NULL; for (r = optind; r < argc; r++) { *h = argv[r]; ++h; ++Requests; } testsmar(&t_req); error: if (ptr != NULL) sm_free(ptr); return sm_test_end(); }