/*
* Copyright (c) 2002-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.
*/
/*
** SMTPS - QMGR/SMAR communication module
*/
#include "sm/generic.h"
SM_RCSID("@(#)$Id: s2q.c,v 1.171 2007/10/23 03:57:40 ca Exp $")
#include "sm/assert.h"
#include "sm/error.h"
#include "sm/memops.h"
#include "sm/reccom.h"
#include "statethreads/st.h"
#include "sm/rcbst.h"
#include "sm/fcntl.h"
#include "sm/unixsock.h"
#include "sm/stsock.h"
#include "sm/stthreads.h"
#include "smtps-str.h"
#include "s2q.h"
#include "smtps.h"
#if MTA_USE_PMILTER
#include "sm/pmilter.h"
#include "sm/pmfdef.h"
#include "smtpsh.h"
# if MTA_USE_RSAD
# include "pmilter.h"
# endif
#endif
#include "sm/hdrmod.h"
#include "log.h"
#include "sm/smar.h"
/* just a debugging help: number of open requests */
/* however, this should be per context, not global (fixme) */
static uint Open_rqs = 0;
#define S2Q_SOCKNAME \
(SOCK_TYPE_UNIX == sockspec->sckspc_type) \
? sockspec->sock_unix.unixsckspc_path : "inet"
/*
** SM_S2Q_ID_MATCH -- check whether the id of the communication context
** matches the id stored in the session context
**
** Parameters:
** ss_sess -- session context
** s2q_ctx -- S2Q context
**
** Returns:
** match?
*/
bool
sm_s2q_id_match(ss_sess_P ss_sess, s2q_ctx_P s2q_ctx)
{
int idx;
bool match;
SM_IS_SS_SESS(ss_sess);
SM_REQUIRE(s2q_ctx != NULL);
if (S2Q_ID_NONE == s2q_ctx->s2q_q_id)
return true;
idx = s2q_ctx->s2q_srv_type;
SM_ASSERT(idx >= 0 && idx < SS_MAX_COMM_SRVS);
/* id not yet set? */
if (S2Q_ID_NONE == ss_sess->ssse_s2q_id[idx]) {
ss_sess->ssse_s2q_id[idx] = s2q_ctx->s2q_q_id;
return true;
}
match = (ss_sess->ssse_s2q_id[idx] == s2q_ctx->s2q_q_id);
if (!match) {
sm_log_write(s2q_ctx->s2q_ss_ctx->ssc_lctx,
SS_LCAT_COMM, SS_LMOD_COMM,
SM_LOG_WARN, 9,
"sev=WARN, func=sm_s2q_id_match, ss_sess=%s, s2q_srv_type=%u, ssse_s2q_id=%u, status=mismatch"
, ss_sess->ssse_id, s2q_ctx->s2q_q_id, ss_sess->ssse_s2q_id[idx]);
}
return match;
}
/*
** SS_ADD_RQ -- add a session to the list of outstanding requests
**
** Parameters:
** ss_sess -- session context
** tid -- transaction (or session) id
** s2q_ctx -- S2Q context
** pi -- (pointer to) index in s2q_ctx->s2q_sess (output)
**
** Returns:
** usual sm_error code
*/
sm_ret_T
ss_add_rq(ss_sess_P ss_sess, sessta_id_P tid, s2q_ctx_P s2q_ctx, uint *pi)
{
uint i;
SM_REQUIRE(s2q_ctx != NULL);
SM_IS_SS_SESS(ss_sess);
S2Q_CHK_CONN_R(ss_sess, s2q_ctx);
for (i = 0; i < s2q_ctx->s2q_maxrcbs; i++) {
if (s2q_ctx->s2q_sess[i] == NULL) {
s2q_ctx->s2q_sess[i] = ss_sess;
s2q_ctx->s2q_sids[i] = tid;
ss_sess->ssse_s2q_idx = i;
++Open_rqs;
break;
}
}
if (i >= s2q_ctx->s2q_maxrcbs) {
SSQ_DPRINTF((smioerr,
"sev=ERROR, func=ss_add_rq, sess=%p, i=%d, open=%u\n",
ss_sess, i, Open_rqs));
#if SSQ_DEBUG
dump_thrd_info();
#endif
/* SM_ASSERT(false); */
return sm_error_perm(SM_EM_SMTPS, SM_E_FULL);
}
if (pi != NULL)
*pi = i;
return SM_SUCCESS;
}
/*
** SS_SEND_RQ -- add a session to the list of outstanding requests
** (unless reply is false) and send RCB to server
**
** Parameters:
** ss_sess -- session context
** tid -- transaction (or session) id
** s2q_ctx -- S2Q context
** rcb -- RCB
** reply -- expect reply?
**
** Returns:
** usual sm_error code
*/
sm_ret_T
ss_send_rq(ss_sess_P ss_sess, sessta_id_P tid, s2q_ctx_P s2q_ctx, sm_rcb_P rcb, bool reply)
{
sm_ret_T ret;
uint i;
int r;
SM_REQUIRE(s2q_ctx != NULL);
if (reply) {
ret = ss_add_rq(ss_sess, tid, s2q_ctx, &i);
if (sm_is_err(ret)) {
SSQ_DPRINTF((smioerr,
"sev=ERROR, func=ss_send_rq, sess=%p, i=%d, open=%u\n",
ss_sess, i, Open_rqs));
#if SSQ_DEBUG
dump_thrd_info();
#endif
/* SM_ASSERT(false); */
return ret;
}
}
else {
i = s2q_ctx->s2q_maxrcbs;
}
ret = sm_rcb_open_snd(rcb);
if (sm_is_err(ret)) {
SSQ_DPRINTF((smioerr,
"sev=ERROR, func=ss_send_rq, err1, sess=%p, i=%d, open=%u\n",
ss_sess, i, Open_rqs));
goto err1;
}
r = st_mutex_lock(s2q_ctx->s2q_wr_mutex);
if (r != 0) {
SSQ_DPRINTF((smioerr,
"sev=ERROR, func=ss_send_rq, st_mutex_lock=%d, errno=%d\n",
r, errno));
goto err2;
}
ret = sm_rcb_snd(s2q_ctx->s2q_fd, rcb, s2q_ctx->s2q_ss_ctx->ssc_mod_tmo);
r = st_mutex_unlock(s2q_ctx->s2q_wr_mutex);
if (r != 0) {
SSQ_DPRINTF((smioerr,
"sev=ERROR, func=ss_send_rq, st_mutex_unlock=%d, errno=%d\n",
r, errno));
}
if (sm_is_err(ret)) {
SSQ_DPRINTF((smioerr,
"sev=ERROR, func=ss_send_rq, err2, sess=%p, i=%d, open=%u, ret=%m\n",
ss_sess, i, Open_rqs, ret));
if (ret == sm_error_perm(SM_EM_RECCOM, EPIPE)) {
S2Q_SET_IOERR(s2q_ctx);
ret = sm_error_perm(SM_EM_SMTPS, EIO);
}
goto err2;
}
/*
** This call doesn't fail, but what should we do if it could??
** The request has been sent... so: keep it or remove it?
** Let's keep it.
*/
ret = sm_rcb_close_snd(rcb);
if (sm_is_err(ret))
goto error;
return ret;
err2:
(void) sm_rcb_close_snd(rcb);
err1:
if (i < s2q_ctx->s2q_maxrcbs) {
s2q_ctx->s2q_sess[i] = NULL;
s2q_ctx->s2q_sids[i] = NULL;
ss_sess->ssse_s2q_idx = SSSE_S2Q_IDX_NONE;
SM_ASSERT(Open_rqs > 0);
--Open_rqs;
}
error:
return ret;
}
/*
** SS_FIND_SESS_RQ -- find session context based on session id
**
** Parameters:
** s2q_ctx -- S2Q context
** sid -- session id
**
** Returns:
** session context (NULL on failure)
**
** Side Effects:
** frees entry in array if found
*/
static ss_sess_P
ss_find_sess_rq(s2q_ctx_P s2q_ctx, sessta_id_P sid)
{
uint i;
ss_sess_P ss_sess;
SM_REQUIRE(s2q_ctx != NULL);
ss_sess = NULL;
for (i = 0; i < s2q_ctx->s2q_maxrcbs; i++) {
if (s2q_ctx->s2q_sess[i] != NULL &&
s2q_ctx->s2q_sids[i] != NULL &&
SESSTA_EQ(s2q_ctx->s2q_sids[i], sid))
{
/* simple check whether session is ok */
ss_sess = s2q_ctx->s2q_sess[i];
if (ss_sess->ssse_rcb == NULL
#if SS_SESS_CHECK
|| ss_sess->sm_magic != SM_SS_SESS_MAGIC
#endif
)
{
SSQ_DPRINTF((smioerr, "sev=ERROR, func=ss_find_sess_rq, sess=%p, flags=%x\n", ss_sess, ss_sess->ssse_flags));
ss_sess = NULL;
}
else
ss_sess->ssse_s2q_idx = SSSE_S2Q_IDX_NONE;
s2q_ctx->s2q_sess[i] = NULL;
s2q_ctx->s2q_sids[i] = NULL;
SM_ASSERT(Open_rqs > 0);
--Open_rqs;
return ss_sess;
}
}
SSQ_DPRINTF((smioerr, "sev=ERROR, func=ss_find_sess_rq, id=%s, i=%d, open=%u\n", sid, i, Open_rqs));
return NULL;
}
/*
** SS_CLR_SESS_RQ -- clear session request
**
** Parameters:
** s2q_ctx -- S2Q context
** ss_sess -- session context
**
** Returns:
** usual sm_error code
**
** Side Effects:
** frees entry in array if found
*/
sm_ret_T
ss_clr_sess_rq(s2q_ctx_P s2q_ctx, ss_sess_P ss_sess)
{
sm_ret_T ret;
int i;
uint u;
SM_REQUIRE(s2q_ctx != NULL);
SM_IS_SS_SESS(ss_sess);
i = ss_sess->ssse_s2q_idx;
if (SSSE_S2Q_IDX_NONE == i)
return SM_SUCCESS;
if (i >= 0 && (u = (uint)i) < s2q_ctx->s2q_maxrcbs
&& s2q_ctx->s2q_sess[u] != NULL
&& s2q_ctx->s2q_sids[u] != NULL
&& SESSTA_EQ(s2q_ctx->s2q_sids[u], ss_sess->ssse_id))
{
s2q_ctx->s2q_sess[u] = NULL;
s2q_ctx->s2q_sids[u] = NULL;
ss_sess->ssse_s2q_idx = SSSE_S2Q_IDX_NONE;
SM_ASSERT(Open_rqs > 0);
--Open_rqs;
ret = SM_SUCCESS;
}
else
ret = sm_error_perm(SM_EM_SMTPS, EINVAL);
return ret;
}
/*
** SS_SET_CLTNAME -- set clientname based on ssse_cltrslv
**
** Parameters:
** ss_sess -- session context
**
** Returns:
** usual sm_error code
**
** Side Effects:
** sets ss_sess->ssse_cltname (unless an error occurs)
*/
static sm_ret_T
ss_set_cltname(ss_sess_P ss_sess)
{
extern sm_cstr_P HostnameNoMatch, HostnameTempPTR, HostnameTempA,
HostnameBogus;
if (SM_RVRS_MATCH == ss_sess->ssse_cltrslv)
return SM_SUCCESS;
if (ss_sess->ssse_cltname != NULL)
SM_CSTR_FREE(ss_sess->ssse_cltname);
switch (ss_sess->ssse_cltrslv) {
case SM_RVRS_MATCH:
SM_ASSERT(ss_sess->ssse_cltrslv != SM_RVRS_MATCH);
break;
case SM_RVRS_NOMATCH:
ss_sess->ssse_cltname = SM_CSTR_DUP(HostnameNoMatch);
break;
case SM_RVRS_TEMP_PTR:
ss_sess->ssse_cltname = SM_CSTR_DUP(HostnameTempPTR);
break;
case SM_RVRS_TEMP_A:
ss_sess->ssse_cltname = SM_CSTR_DUP(HostnameTempA);
break;
default:
ss_sess->ssse_cltname = SM_CSTR_DUP(HostnameBogus);
/* SM_ASSERT(false); ??? */
break;
}
return SM_SUCCESS;
}
/*
** SS_GET_CLTNAME -- get clientname from RCB (ss_sess->ssse_cltname)
**
** Parameters:
** ss_sess -- session context
** l -- length of client name to read
**
** Returns:
** usual sm_error code
**
** Side Effects:
** sets ss_sess->ssse_cltname (unless an error occurs)
*/
static sm_ret_T
ss_get_cltname(ss_sess_P ss_sess, uint32_t l)
{
sm_ret_T ret;
sm_cstr_P cltname;
cltname = NULL;
if (ss_sess->ssse_cltrslv != SM_RVRS_MATCH) {
ret = sm_rcb_skip(ss_sess->ssse_rcb, l);
return ret;
}
ret = sm_rcb_getncstr(ss_sess->ssse_rcb, &cltname, l);
if (sm_is_err(ret))
return ret;
SM_ASSERT(cltname != NULL);
if (ss_sess->ssse_cltname != NULL)
SM_CSTR_FREE(ss_sess->ssse_cltname);
ss_sess->ssse_cltname = cltname;
return SM_SUCCESS;
}
/*
** SM_W4Q2S_REPLY -- wait for reply from QMGR/SMAR/pmilter, decode it,
** and store the data in the session context.
**
** Parameters:
** ss_sess -- session context
** tmo -- timeout (seconds)
** s2q_ctx -- S2Q context
**
** Returns:
** <=0: usual sm_error code
** >0: (SMTP) reply code (including flags: SMAR_R_flag)
**
** Side Effects:
** may set ss_sess->ssse_wr (error text)
*/
sm_ret_T
sm_w4q2s_reply(ss_sess_P ss_sess, uint tmo, s2q_ctx_P s2q_ctx)
{
int r;
uint32_t v, l, rt;
sm_ret_T ret, rv;
ss_acc_P ss_acc;
ss_ctx_P ss_ctx;
SM_IS_SS_SESS(ss_sess);
ss_acc = NULL;
SSQ_DPRINTF((smioerr, "sev=DBG, func=sm_w4q2s_reply, sess=%p\n", ss_sess));
do {
r = st_cond_timedwait(ss_sess->ssse_cond_rd, SEC2USEC(tmo));
SSQ_DPRINTF((smioerr, "sev=DBG, func=sm_w4q2s_reply, sess=%p, ret=%d, errno=%d\n", ss_sess, r, (0 == r) ? 0 : errno));
if (r != 0 && errno != EINTR) {
S2Q_SET_IOERR(s2q_ctx);
sm_rcb_close_n(ss_sess->ssse_rcb);
return sm_error_perm(SM_EM_SMTPS, errno);
}
} while (r != 0);
if (SSSE_S2Q_IDX_CLSD == ss_sess->ssse_s2q_idx) {
ss_sess->ssse_s2q_idx = SSSE_S2Q_IDX_NONE;
sm_rcb_close_n(ss_sess->ssse_rcb);
return SM_IO_EOF;
}
ss_ctx = s2q_ctx->s2q_ss_ctx;
/* rcb is now open for decoding... */
ret = sm_rcb_get3uint32(ss_sess->ssse_rcb, &l, &rt, &v);
SSQ_DPRINTF((smioerr, "sev=DBG, func=sm_w4q2s_reply, where=first, l=%d, rt=%x, v=%d, ret=%m\n", l, rt, v, ret));
if (sm_is_err(ret) || l != 4) {
/* complain? */
goto errdec;
}
/*
** Question: What should be returned to the caller??
** The result of the map lookup (found/notfound/...)
** or the value (OK, REJECT)?
** How about the following:
** map lookup == found -> return RHS value
** otherwise return map lookup
*/
/*
define protocol smar -> smtps!
it's not clear how the values are supposed to be transferred, e.g.,
should RT_A2S_xyz_STM and RT_A2S_xyz_STR be separate?
yes...
protocol:
RT_A2S_MAP_RES
if v == SM_ACC_FOUND RT_A2S_xyz_ST
then optional:
RT_A2S_STATT: status text (only if MAP_FOUND)
RT_A2S_RVRS_ST: status of reverse lookup
RT_A2S_RVRS_NAME: client name
change smar/access.c first!
*/
/* preserve result value */
rv = (sm_ret_T) v;
if (rt == RT_A2S_MAP_RES && rv == SM_ACC_FOUND) {
ret = sm_rcb_get3uint32(ss_sess->ssse_rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4 ||
(rt != RT_A2S_RCPT_ST && rt != RT_A2S_MAIL_ST
&& rt != RT_A2S_CLT_A_ST && rt != RT_A2S_CLT_N_ST
#if MTA_USE_TLS
&& rt != RT_A2S_CERT_ST
#endif
#if SS_EHLO_ACCESS_CHK
&& rt != RT_A2S_EHLO_ST
#endif
))
{
/* complain? */
goto errdec;
}
switch (rt) {
case RT_A2S_CLT_A_ST:
case RT_A2S_CLT_N_ST:
ss_acc = &ss_sess->ssse_acc;
break;
#if SS_EHLO_ACCESS_CHK
case RT_A2S_EHLO_ST:
ss_acc = &ss_sess->ssse_ta->ssta_ehlo_acc;
break;
#endif
case RT_A2S_MAIL_ST:
ss_acc = &ss_sess->ssse_ta->ssta_mail_acc;
break;
case RT_A2S_RCPT_ST:
ss_acc = &ss_sess->ssse_ta->ssta_rcpt_acc;
break;
}
/* HACK: do not store "CONT" here */
if (ss_acc != NULL && v != SMTP_R_CONT) {
ss_acc->ssa_map_result = rv;
ss_acc->ssa_reply_code = v;
}
#if 0
SSQ_DPRINTF((smioerr,
"sev=DBG, ss_sess=%s, ss_ta=%s, func=sm_w4q2s_reply, rt=%x, reply=%m\n"
, ss_sess->ssse_id, ss_sess->ssse_ta->ssta_id, rt
, v
));
#endif /* 0 */
rv = v; /* return map lookup RHS to caller */
}
else if (rt != RT_A2S_MAP_RES
&& rt != RT_Q2S_STAT && rt != RT_Q2S_STATV
#if MTA_USE_PMILTER
&& rt != RT_M2S_RCODE
#endif
#if MTA_USE_TLS
&& rt != RT_A2S_CERT_ST
#endif
)
{
/* complain? */
goto errdec;
}
else if (rt == RT_A2S_MAP_RES && rv == SM_ACC_NOTFOUND) {
/* HACK: if no entry is found return "DUNNO" (CONT) */
rv = (sm_ret_T) SMTP_R_CONT;
}
else {
rv = (sm_ret_T) v;
if (rv < 0)
goto errdec;
/* Need to clear error text if there is a new error */
if (rv != SM_SUCCESS && rt == RT_Q2S_STAT)
sm_str_clr(ss_sess->ssse_wr);
}
while (!SM_RCB_ISEOB(ss_sess->ssse_rcb)) {
ret = sm_rcb_get2uint32(ss_sess->ssse_rcb, &l, &rt);
switch (rt) {
case RT_Q2S_STATT:
case RT_A2S_STATT:
case RT_M2S_STATT:
sm_str_clr(ss_sess->ssse_wr);
ret = sm_rcb_getstr(ss_sess->ssse_rcb, ss_sess->ssse_wr, l);
if (sm_is_err(ret))
goto errdec;
break;
case RT_A2S_RVRS_ST:
ret = sm_rcb_getuint32(ss_sess->ssse_rcb, &v);
if (sm_is_err(ret))
goto errdec;
ss_sess->ssse_cltrslv = v;
ret = ss_set_cltname(ss_sess);
break;
case RT_A2S_RVRS_NAME:
ret = ss_get_cltname(ss_sess, l);
/* ignore error for now; client name won't be set */
break;
#if MTA_USE_PMILTER
case RT_M2S_MAIL_NEW:
{
ss_ta_P ss_ta;
ss_ta = ss_sess->ssse_ta;
if (l >= MAXADDRLEN)
ret = sm_err_perm(SM_E_2BIG);
else
ret = sm_rcb_getnstr(ss_sess->ssse_rcb, &ss_ta->ssta_mail_new, l);
if (sm_is_success(ret))
SSTA_SET_FLAG(ss_sess->ssse_ta, SSTA_FL_MAIL_MOD);
}
break;
case RT_M2S_RCPT_ADD:
case RT_M2S_RCPT_DEL:
{
rcpt_idx_T rcpt_idx;
ushort type;
rcpt_idx = 0;
type = (RT_M2S_RCPT_ADD == rt)
? PM_RCPT_ADD : PM_RCPT_DEL;
sm_str_clr(ss_sess->ssse_rd);
ret = sm_rcb_getstr(ss_sess->ssse_rcb, ss_sess->ssse_rd, l);
if (sm_is_success(ret) && RT_M2S_RCPT_DEL == rt) {
ret = sm_rcb_get3uint32(ss_sess->ssse_rcb, &l, &rt, &v);
if (sm_is_success(ret) && RT_M2S_RCPT_IDX == rt && 4 == l)
rcpt_idx = v;
/* else complain */
}
if (sm_is_success(ret))
ret = ssr_rcpt_add_mod(ss_sess, type, rcpt_idx);
if (sm_is_success(ret))
SSTA_SET_FLAG(ss_sess->ssse_ta, SSTA_FL_RCPT_MOD);
}
break;
case RT_M2S_HM_T_P:
{
sm_hdrmod_P sm_hdrmod;
ss_ta_P ss_ta;
ss_ta = ss_sess->ssse_ta;
sm_hdrmod = NULL;
if (l != 8)
goto errdec;
ret = sm_rcb_get2uint32(ss_sess->ssse_rcb, &l, &v);
if (sm_is_err(ret))
goto errdec;
ret = sm_hdrmod_new(&ss_ta->ssta_hdrmodhd, true, &sm_hdrmod);
if (sm_is_err(ret))
goto errdec;
sm_hdrmod->sm_hm_type = l;
sm_hdrmod->sm_hm_pos = v;
if (SM_RCB_ISEOB(ss_sess->ssse_rcb))
break;
ret = sm_rcb_peek2uint32(ss_sess->ssse_rcb, &l, &rt);
if (sm_is_err(ret)) {
sm_hdrmod_rm_last(&ss_ta->ssta_hdrmodhd);
goto errdec;
}
if (rt != RT_M2S_HM_HDR)
break;
ret = sm_rcb_get2uint32(ss_sess->ssse_rcb, &l, &rt);
if (sm_is_err(ret) || rt != RT_M2S_HM_HDR || l > SM_MAXHDRLEN) {
sm_hdrmod_rm_last(&ss_ta->ssta_hdrmodhd);
goto errdec;
}
ret = sm_rcb_getncstr(ss_sess->ssse_rcb, &sm_hdrmod->sm_hm_hdr, l);
if (sm_is_err(ret)) {
sm_hdrmod_rm_last(&ss_ta->ssta_hdrmodhd);
goto errdec;
}
}
break;
case RT_M2S_MSG_PART:
ret = ss_replacemsg(ss_sess, l);
if (sm_is_err(ret))
goto errdec;
break;
# if MTA_USE_RSAD
case RT_M2S_RCODES:
{
uint nreplies;
size_t u;
ss_ta_P ss_ta;
ss_ta = ss_sess->ssse_ta;
sspm_clrreplies(ss_ta);
nreplies = l / 4;
if (nreplies < 1) {
ret = sm_err_perm(EINVAL);
goto errdec;
}
ss_ta->ssta_nreplies = nreplies;
u = nreplies * sizeof(*ss_ta->ssta_rcodes);
if (u <= nreplies || u < sizeof(*ss_ta->ssta_rcodes)) {
ret = sm_err_perm(SM_E_OVFLW_SC);
goto errdec;
}
ss_ta->ssta_rcodes = (uint32_t *) sm_zalloc(u);
if (NULL == ss_ta->ssta_rcodes)
return sm_err_temp(ENOMEM);
ret = sm_rcb_getauint32(ss_sess->ssse_rcb, nreplies, NULL, NULL,
ss_ta->ssta_rcodes);
}
break;
# endif /* MTA_USE_RSAD */
#endif /* MTA_USE_PMILTER */
case RT_A2S_MAP_RES_CNF:
ret = sm_rcb_getuint32(ss_sess->ssse_rcb, &v);
if (sm_is_err(ret))
goto errdec;
ss_sess->ssse_maprescnf = v;
break;
case RT_A2S_RHS_CNF:
sm_str_clr(ss_sess->ssse_str);
ret = sm_rcb_getstr(ss_sess->ssse_rcb, ss_sess->ssse_str, l);
if (sm_is_err(ret))
goto errdec;
break;
default:
SSQ_DPRINTF((smioerr, "sev=DBG, func=sm_w4q2s_reply, record-type=unkown, l=%d, rt=%x, ret=%m\n", l, rt, ret));
goto errdec;
}
}
ret = sm_rcb_close_dec(ss_sess->ssse_rcb);
SSQ_DPRINTF((smioerr, "sev=DBG, func=sm_w4q2s_reply, where=return, l=%d, rt=%x, ret=%m, rv=%d\n", l, rt, ret, rv));
return rv;
errdec:
(void) sm_rcb_close_dec(ss_sess->ssse_rcb);
SSQ_DPRINTF((smioerr, "sev=DBG, func=sm_w4q2s_reply, where=errdec, l=%d, rt=%x, ret=%m\n", l, rt, ret));
/* no error code? then some protocol error occurred... */
if (!sm_is_err(ret))
ret = SMTP_R_SSD;
return ret;
}
#if MTA_USE_PMILTER
/*
** SM_GET_MACLIST -- get list of macros from libpmilter
**
** Parameters:
** s2q -- s2q context
** rcb -- RCB
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
sm_get_maclist(s2q_ctx_P s2q_ctx, sm_rcb_P rcb)
{
uint32_t v, l, rt, macw;
uint i;
sm_ret_T ret;
ss_ctx_P ss_ctx;
if (SM_RCB_ISEOB(rcb))
return SM_SUCCESS;
/*
** Protocol:
** (RT_M2S_MACW RT_M2S_MACW*)*
*/
ss_ctx = s2q_ctx->s2q_ss_ctx;
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4 || rt != RT_M2S_MACW)
goto errdec;
macw = v;
while (!SM_RCB_ISEOB(rcb) && rt == RT_M2S_MACW) {
if (macw >= PM_SMST_MAX) {
sm_log_write(s2q_ctx->s2q_ss_ctx->ssc_lctx,
SS_LCAT_COMM, SS_LMOD_COMM,
SM_LOG_WARN, 6,
"sev=WARN, func=sm_get_maclist, mac_stage=%u, max=%u, status=too_large"
, macw, PM_SMST_MAX);
break;
}
i = 0;
while (!SM_RCB_ISEOB(rcb)) {
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4)
goto errdec;
if (rt != RT_M2S_MACM) {
if (RT_M2S_MACW == rt)
macw = v;
break;
}
if (PM_MAX_MACROS == i) {
/* log it once */
sm_log_write(s2q_ctx->s2q_ss_ctx->ssc_lctx,
SS_LCAT_COMM, SS_LMOD_COMM,
SM_LOG_WARN, 6,
"sev=WARN, func=sm_get_maclist, mac_stage=%u, macros=%u, max=%u, status=too_many"
, macw, i, PM_MAX_MACROS);
}
else if (i < PM_MAX_MACROS)
ss_ctx->ssc_mac_names[macw][i] = v;
++i;
}
}
return ret;
errdec:
if (!sm_is_err(ret))
ret = sm_error_perm(SM_EM_SMTPS, SM_E_PR_ERR);
return ret;
}
#endif /* MTA_USE_PMILTER */
/*
** SM_FIRST_RCB_FROM_SRV -- receive first RCB from QMGR/SMAR
** fixme: this is in large parts the same as ss_rcb_from_srv();
** maybe do some common code extraction?
**
** Parameters:
** s2q -- s2q context
** rcb -- RCB
** type -- type of reply
**
** Returns:
** usual sm_error code
**
** Side Effects:
** initialize SMTPS id counter
*/
static sm_ret_T
sm_first_rcb_from_srv(s2q_ctx_P s2q_ctx, sm_rcb_P rcb, uint type)
{
uint32_t v, l, rt, tl;
uint64_t ul;
sm_ret_T ret;
ret = sm_rcb_open_rcv(rcb);
SSQ_DPRINTF((smioerr, "sev=DBG, func=sm_first_rcb_from_srv, s2q_ctx=%p, rcb_open_rcv=%r\n", s2q_ctx, ret));
if (sm_is_err(ret))
goto error;
ret = sm_rcb_rcv(s2q_ctx->s2q_fd, rcb, 12, s2q_ctx->s2q_ss_ctx->ssc_mod_tmo);
if (sm_is_err(ret)) {
SSQ_DPRINTF((smioerr, "sev=DBG, func=sm_first_rcb_from_srv, s2q_ctx=%p, sm_rcb_rcv=%r\n", s2q_ctx, ret));
goto error;
}
ret = sm_rcb_close_rcv(rcb);
if (sm_is_err(ret))
goto error;
ret = sm_rcb_open_dec(rcb);
if (sm_is_err(ret)) {
/* COMPLAIN */
SSQ_DPRINTF((smioerr, "sev=ERROR, func=sm_first_rcb_from_srv, open_dec=%m\n", ret));
goto error;
}
/* total length of record */
ret = sm_rcb_getuint32(rcb, &tl);
SSQ_DPRINTF((smioerr, "sev=DBG, func=sm_first_rcb_from_srv, s2q_ctx=%p, tl=%d, ret=%m\n", s2q_ctx, tl, ret));
if (sm_is_err(ret) || tl > QSS_RC_MAXSZ)
goto errdec;
/* protocol header: version */
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
SSQ_DPRINTF((smioerr, "sev=DBG, func=sm_first_rcb_from_srv, l=%d, rt=%x, prot=%d, ret=%m\n", l, rt, v, ret));
if (sm_is_err(ret) || l != 4 ||
rt != RT_PROT_VER || v != PROT_VER_RT)
goto errdec;
/* RT_Q2S_ID: int smtps-id */
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
SSQ_DPRINTF((smioerr, "sev=DBG, func=sm_first_rcb_from_srv, l=%d, rt=%x, smtps-id=%d, ret=%m\n", l, rt, v, ret));
if (sm_is_err(ret) || l != 4)
goto errdec;
if (RT_Q2S_NID == rt && s2q_ctx->s2q_ss_ctx->ssc_id < 0)
s2q_ctx->s2q_ss_ctx->ssc_id = v;
else if ((rt != RT_Q2S_ID && rt != RT_A2S_ID
#if MTA_USE_PMILTER
&& rt != RT_M2S_ID
#endif
)
|| v != s2q_ctx->s2q_ss_ctx->ssc_id)
goto errdec;
#if MTA_USE_PMILTER
if (S2Q_T_PMILTER == type) {
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
SSQ_DPRINTF((smioerr, "sev=DBG, func=sm_first_rcb_from_srv, l=%d, rt=%x, m2sprot=%x, ret=%m\n", l, rt, v, ret));
if (sm_is_err(ret) || l != 4 || rt != RT_M2S_PROT
|| v != SM_PMILTER_PROT)
goto errdec;
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4 || rt != RT_M2S_CAP)
goto errdec;
if ((v & SM_SCAP_PM_ALL) != v)
sm_log_write(s2q_ctx->s2q_ss_ctx->ssc_lctx,
SS_LCAT_COMM, SS_LMOD_COMM,
SM_LOG_WARN, 6,
"sev=WARN, func=sm_first_rcb_from_srv, server_cap=%#x, libpmilter_cap=%#x"
, SM_SCAP_PM_ALL, v);
s2q_ctx->s2q_ss_ctx->ssc_pmcap = v & SM_SCAP_PM_ALL;
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4 || rt != RT_M2S_FCT)
goto errdec;
/* XXX use this value somehow .... */
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4 || rt != RT_M2S_FEAT)
goto errdec;
/* XXX use this value somehow .... */
ret = sm_get_maclist(s2q_ctx, rcb);
}
else {
#endif /* MTA_USE_PMILTER */
/* expect RT_Q2S_IIDC (64bit) */
ret = sm_rcb_get3uint64(rcb, &l, &rt, &ul);
if (sm_is_err(ret)) {
SSQ_DPRINTF((smioerr, "sev=DBG, func=sm_first_rcb_from_srv, l=%d, rt=%x, ret=%m\n", l, rt, ret));
goto errdec;
}
if (l == 8 && rt == RT_Q2S_IIDC)
ret = ss_id_init(ul);
else
ret = sm_error_perm(SM_EM_SMTPS, SM_E_PR_ERR);
#if MTA_USE_PMILTER
}
#endif
/* fall through for closing rcb */
errdec:
if (sm_is_err(ret))
(void) sm_rcb_close_dec(rcb);
else
ret = sm_rcb_close_dec(rcb);
error:
return ret;
}
/*
** S2Q_CLOSE -- close connection to server
**
** Parameters:
** s2q_ctx -- S2Q context
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
s2q_close(s2q_ctx_P s2q_ctx)
{
sm_ret_T ret;
int r;
uint u;
ss_sess_P ss_sess;
SM_REQUIRE(s2q_ctx != NULL);
/* don't bother cleaning up if the process terminates */
if (SSC_IS_FLAG(s2q_ctx->s2q_ss_ctx, SSC_FL_TERMINATING))
return SM_SUCCESS;
if (s2q_ctx->s2q_fd != INVALID_NETFD) {
ret = un_st_socket_close(s2q_ctx->s2q_fd);
if (sm_is_err(ret))
goto error;
s2q_ctx->s2q_fd = INVALID_NETFD;
}
/* close all open connections. how to "notify" sessions of this??? */
for (u = 0; u < s2q_ctx->s2q_maxrcbs; u++) {
if (s2q_ctx->s2q_sess[u] == NULL)
continue;
ss_sess = s2q_ctx->s2q_sess[u];
ss_sess->ssse_s2q_idx = SSSE_S2Q_IDX_CLSD;
r = st_cond_signal(ss_sess->ssse_cond_rd);
if (r != 0) {
SSQ_DPRINTF((smioerr, "sev=ERROR, func=s2q_close, st_cond_signal=%d\n", r));
}
/* sm_io_fprintf(smioerr, "sev=DBG, func=s2q_close, u=%u, r=%d\n", u, r); */
s2q_ctx->s2q_sess[u] = NULL;
s2q_ctx->s2q_sids[u] = NULL;
SM_ASSERT(Open_rqs > 0);
--Open_rqs;
}
s2q_ctx->s2q_status = S2Q_ST_CLOSED;
return SM_SUCCESS;
error:
return ret;
}
/*
** SM_RCB_FROM_SRV -- receive RCB from QMGR/SMAR/..., notify thread (session)
** This permanently runs as a thread.
**
** Parameters:
** arg -- s2q context
**
** Returns:
** NULL on termination
*/
static void *
ss_rcb_from_srv(void *arg)
{
uint32_t v, l, rt, tl;
int r;
sm_ret_T ret;
sm_rcb_P rcb, rcbt;
s2q_ctx_P s2q_ctx;
sessta_id_T ssse_id;
ss_sess_P ss_sess;
SM_REQUIRE(arg != NULL);
s2q_ctx = (s2q_ctx_P) arg;
rcb = sm_rcb_new(NULL, S2Q_RCB_SIZE, QSS_RC_MAXSZ);
if (NULL == rcb)
goto errnomem;
/* check some "terminate now" variable? */
for (;;) {
ret = sm_rcb_open_rcv(rcb);
SSQ_DPRINTF((smioerr, "sev=DBG, func=ss_rcb_from_srv, s2q_ctx=%p sm_rcb_open_rcv=%m\n", s2q_ctx, ret));
if (sm_is_err(ret))
goto error;
/* note: no timeout, this will just wait for an RCB */
ret = sm_rcb_rcv(s2q_ctx->s2q_fd, rcb, 12, (st_utime_t) -1);
if (sm_is_err(ret)) {
if (SM_IO_EOF == ret) {
(void) s2q_close(s2q_ctx);
goto error;
}
else if (!E_IS_TEMP(sm_error_value(ret))) {
sm_log_write(s2q_ctx->s2q_ss_ctx->ssc_lctx,
SS_LCAT_COMM, SS_LMOD_COMM,
SM_LOG_ERR, 6,
"sev=ERROR, func=ss_rcb_from_srv, sm_rcb_rcv=%m",
ret);
goto error;
}
ret = sm_rcb_close_rcv(rcb);
if (sm_is_err(ret))
goto error;
continue;
}
ret = sm_rcb_close_rcv(rcb);
if (sm_is_err(ret))
goto error;
/*
** And now? Check protocol version etc, extract session ID.
** We must have a common header which can be analyzed here
** at least such that we can find the session (transaction)
** ID (either we have to guarantee that transaction and
** session ID have the same format or at least the same
** comparison routine (streq) or we need to store both;
** the latter is ugly to say at least).
**
** Hence the format should be:
** RT_Q2S_ID: int smtps-id
** either RT_Q2S_SEID: str session-id
** or RT_Q2S_TAID: str transaction-id
*/
ret = sm_rcb_open_dec(rcb);
if (sm_is_err(ret)) {
/* COMPLAIN */
SSQ_DPRINTF((smioerr, "sev=ERROR, func=ss_rcb_from_srv, open_dec=%m\n", ret));
continue;
}
/* total length of record */
ret = sm_rcb_getuint32(rcb, &tl);
SSQ_DPRINTF((smioerr, "sev=DBG, func=ss_rcb_from_srv, s2q_ctx=%p, tl=%d, ret=%m\n", s2q_ctx, tl, ret));
if (sm_is_err(ret) || tl > QSS_RC_MAXSZ)
goto errdec;
/* protocol header: version */
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
SSQ_DPRINTF((smioerr, "sev=DBG, func=ss_rcb_from_srv, l=%d, rt=%x, prot=%d, ret=%m\n", l, rt, v, ret));
if (sm_is_err(ret) || l != 4 ||
rt != RT_PROT_VER || v != PROT_VER_RT)
goto errdec;
/* RT_Q2S_ID: int smtps-id */
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
SSQ_DPRINTF((smioerr, "sev=DBG, func=ss_rcb_from_srv, l=%d, rt=%x, smtps-id=%d, ret=%m\n", l, rt, v, ret));
if (sm_is_err(ret) || l != 4
|| (rt != RT_Q2S_ID && rt != RT_A2S_ID
#if MTA_USE_PMILTER
&& rt != RT_M2S_ID
#endif
)
|| v != s2q_ctx->s2q_ss_ctx->ssc_id)
goto errdec;
/*
** Here we can get generic status information,
** e.g., "slow down", "shut down", "return to normal".
*/
ret = sm_rcb_get2uint32(rcb, &l, &rt);
if (sm_is_err(ret)) {
SSQ_DPRINTF((smioerr, "sev=DBG, func=ss_rcb_from_srv, l=%d, rt=%x, ret=%m\n", l, rt, ret));
goto errdec;
}
if (l == 4 && rt == RT_Q2S_THRDS) {
/* ... */
ret = sm_rcb_getuint32(rcb, &v);
if (sm_is_err(ret))
goto errdec;
SSQ_DPRINTF((smioerr, "sev=DBG, func=ss_rcb_from_srv, where=change Max_cur_threads, cur=%u, new=%u, max=%u\n", Max_cur_threads, v, s2q_ctx->s2q_ss_ctx->ssc_cnf.ss_cnf_max_threads));
if (v <= s2q_ctx->s2q_ss_ctx->ssc_cnf.ss_cnf_max_threads)
Max_cur_threads = v;
else
Max_cur_threads = s2q_ctx->s2q_ss_ctx->ssc_cnf.ss_cnf_max_threads;
ret = sm_rcb_close_dec(rcb);
if (sm_is_err(ret)) {
/* COMPLAIN */
}
continue;
}
SSQ_DPRINTF((smioerr, "sev=DBG, func=ss_rcb_from_srv, where=sess-id, l=%d, rt=%x, ret=%m\n", l, rt, ret));
if (l != SMTP_STID_SIZE ||
(rt != RT_Q2S_SEID && rt != RT_Q2S_TAID
&& rt != RT_A2S_TAID
#if MTA_USE_PMILTER
&& rt != RT_M2S_SEID
/* XXX check protocol
&& rt != RT_M2S_TAID
*/
#endif /* MTA_USE_PMILTER */
))
goto errdec;
/* session or transaction id */
ret = sm_rcb_getn(rcb, (uchar *) ssse_id, l);
if (sm_is_err(ret))
goto errdec;
ss_sess = ss_find_sess_rq(s2q_ctx, ssse_id);
SSQ_DPRINTF((smioerr, "sev=DBG, func=ss_rcb_from_srv, find_sess=%p, id=%s\n", ss_sess, ssse_id));
if (NULL == ss_sess) {
/* COMPLAIN */
SSQ_DPRINTF((smioerr, "sev=ERROR, func=ss_rcb_from_srv, ss_find_sess_rq=failed\n"));
goto errdec;
}
/* ss_sess != NULL */
/*
** Just "exchange" our RCB and the one of the session
** Notice: this requires that there is only one outstanding
** request per session. Otherwise we may exchange the RCB
** while the session is using it (which can only happen if
** the RCB is used across a scheduling point).
*/
rcbt = ss_sess->ssse_rcb;
ss_sess->ssse_rcb = rcb;
rcb = rcbt;
/* notify session */
r = st_cond_signal(ss_sess->ssse_cond_rd);
if (r != 0) {
/* COMPLAIN */
SSQ_DPRINTF((smioerr, "sev=ERROR, func=ss_rcb_from_srv, st_cond_signal=%d\n", r));
goto errdec;
}
/* allow for switch to another thread */
st_usleep(0);
/*
** Do NOT close the RCB: we exchanged it and we should
** have now a "clean" (closed) one.
*/
continue;
errdec:
ret = sm_rcb_close_dec(rcb);
if (sm_is_err(ret)) {
/* COMPLAIN */
continue;
}
}
errnomem:
ret = sm_error_temp(SM_EM_SMTPS, ENOMEM);
error:
if (rcb != NULL)
sm_rcb_free(rcb);
return NULL;
}
/*
** S2Q_CONNECT -- connect to server
**
** Parameters:
** s2q_ctx -- S2Q context
** sockspec -- socket to use for connection
** wait4srv -- time to wait for server (s)
** max_threads -- maximum number of threads
** smtps_id -- id of SMTPS
** srv_type -- server type
** flags -- flags
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
s2q_connect(s2q_ctx_P s2q_ctx, sockspec_P sockspec, uint wait4srv, uint max_threads, int smtps_id, uint srv_type, uint32_t flags)
{
sm_ret_T ret;
sm_rcb_P rcb;
time_t time1;
SM_REQUIRE(s2q_ctx != NULL);
rcb = NULL;
time1 = st_time();
for (;;) {
ret = st_sock_connect(sockspec, SEC2USEC(wait4srv), &s2q_ctx->s2q_fd);
if (sm_is_err(ret) && time1 + wait4srv > st_time())
st_sleep(1);
else
break;
}
if (sm_is_err(ret)) {
SSQ_DPRINTF((smioerr, "sev=ERROR, func=s2q_connect, socket=%s, un_st_client_connect=%m\n",
S2Q_SOCKNAME, ret));
goto error;
}
/* send out RCB? */
if (srv_type == S2Q_T_QMGR || srv_type == S2Q_T_SMAR
#if MTA_USE_PMILTER
|| srv_type == S2Q_T_PMILTER
#endif
)
{
rcb = sm_rcb_new(NULL, S2Q_RCB_SIZE, QSS_RC_MAXSZ);
if (NULL == rcb)
goto errnomem;
if (S2Q_T_QMGR == srv_type) {
ret = sm_rcb_putrec(rcb, RCB_PUTR_DFLT, 0, -1,
SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
SM_RCBV_INT, (smtps_id < 0) ? RT_S2Q_QID : RT_S2Q_NID, smtps_id,
SM_RCBV_INT, RT_S2Q_MAXTHRDS, max_threads,
SM_RCBV_END);
}
else if (S2Q_T_SMAR == srv_type) {
ret = sm_rcb_putrec(rcb, RCB_PUTR_DFLT, 0, -1,
SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
SM_RCBV_INT, RT_S2A_NID, smtps_id,
SM_RCBV_INT, RT_S2A_MAXTHRDS, max_threads,
SM_RCBV_INT, RT_S2A_FLAGS, flags,
SM_RCBV_END);
}
#if MTA_USE_PMILTER
else if (S2Q_T_PMILTER == srv_type) {
uint32_t u32;
u32 = SM_SCAP_PM_ALL
#if !MTA_USE_TLS
& ~SM_SCAP_PM_STTLS
#endif
#if !MTA_USE_SASL
& ~SM_SCAP_PM_AUTH
#endif
;
if (!SSC_IS_CFLAG(s2q_ctx->s2q_ss_ctx, SSC_CFL_RSAD))
u32 &= ~SM_SCAP_PM_RSAD;
ret = sm_rcb_putrec(rcb, RCB_PUTR_DFLT, 0, -1,
SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
SM_RCBV_INT, RT_S2M_NID, smtps_id,
SM_RCBV_INT, RT_S2M_PROT, SM_PMILTER_PROT,
SM_RCBV_INT, RT_S2M_MAXTHRDS, max_threads,
SM_RCBV_INT, RT_S2M_CAP, u32,
#if SM_SFCT_PM_ALL != 0
SM_RCBV_INT, RT_S2M_FCT, SM_SFCT_PM_ALL,
#endif
#if SM_SFEAT_PM_ALL != 0
SM_RCBV_INT, RT_S2M_FEAT, SM_SFEAT_PM_ALL,
#endif
#if SM_SMISC_PM_ALL != 0
SM_RCBV_INT, RT_S2M_MISC, SM_SMISC_PM_ALL,
#endif
SM_RCBV_END);
}
#endif /* MTA_USE_PMILTER */
if (sm_is_err(ret))
goto error;
ret = ss_send_rq(NULL, NULL, s2q_ctx, rcb, false);
if (sm_is_err(ret)) {
SSQ_DPRINTF((smioerr, "sev=DBG, func=s2q_init, socket=%s, ss_send_rq=%m\n",
S2Q_SOCKNAME, ret));
goto error;
}
if (srv_type == S2Q_T_QMGR
#if MTA_USE_PMILTER
|| srv_type == S2Q_T_PMILTER
#endif
)
{
ret = sm_first_rcb_from_srv(s2q_ctx, rcb, srv_type);
if (sm_is_err(ret)) {
SSQ_DPRINTF((smioerr, "sev=DBG, func=s2q_init, socket=%s, sm_first_rcb_from_srv=%m\n",
S2Q_SOCKNAME, ret));
goto error;
}
}
SM_RCB_FREE(rcb);
}
return SM_SUCCESS;
errnomem:
ret = sm_error_temp(SM_EM_SMTPS, ENOMEM);
error:
SM_RCB_FREE(rcb);
return ret;
}
/*
** SM_S2Q_CREATE -- create S2Q context
**
** Parameters:
** ps2q_ctx -- (pointer to) S2Q context (output)
** ss_ctx -- SMTP Server context
** sockspec -- specification of socket to use for connection
** maxrcbs -- maximum number of outstanding requests (RCBs)
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sm_s2q_create(s2q_ctx_P *ps2q_ctx, ss_ctx_P ss_ctx, uint maxrcbs)
{
sm_ret_T ret;
size_t n;
s2q_ctx_P s2q_ctx;
SM_REQUIRE(ps2q_ctx != NULL);
s2q_ctx = (s2q_ctx_P) sm_zalloc(sizeof(*s2q_ctx));
if (NULL == s2q_ctx)
goto errnomem;
s2q_ctx->s2q_ss_ctx = ss_ctx;
s2q_ctx->s2q_fd = INVALID_NETFD;
s2q_ctx->s2q_wr_mutex = st_mutex_new();
if (NULL == s2q_ctx->s2q_wr_mutex) {
ret = sm_error_temp(SM_EM_SMTPS, errno);
goto error;
}
s2q_ctx->s2q_maxrcbs = maxrcbs;
s2q_ctx->s2q_currcbs = 0;
n = maxrcbs * sizeof(*s2q_ctx->s2q_sids);
s2q_ctx->s2q_sids = (sessta_id_P *) sm_zalloc(n);
if (NULL == s2q_ctx->s2q_sids)
goto errnomem;
n = maxrcbs * sizeof(*s2q_ctx->s2q_sess);
s2q_ctx->s2q_sess = (ss_sess_P *) sm_zalloc(n);
if (NULL == s2q_ctx->s2q_sess)
goto errnomem;
*ps2q_ctx = s2q_ctx;
return SM_SUCCESS;
errnomem:
ret = sm_error_temp(SM_EM_SMTPS, ENOMEM);
error:
if (s2q_ctx != NULL) {
SM_FREE(s2q_ctx->s2q_sids);
SM_FREE(s2q_ctx->s2q_sess);
if (s2q_ctx->s2q_wr_mutex != NULL) {
st_mutex_destroy(s2q_ctx->s2q_wr_mutex);
s2q_ctx->s2q_wr_mutex = NULL;
}
SM_FREE_SIZE(s2q_ctx, sizeof(*s2q_ctx));
}
return ret;
}
/*
** SM_S2Q_OPEN -- open S2Q connection
**
** Parameters:
** s2q_ctx -- S2Q context
** sockspec -- specification of socket to use for connection
** wait4srv -- time to wait for server (s)
** max_threads -- maximum number of threads
** srv_type -- server type
** flags -- flags
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sm_s2q_open(s2q_ctx_P s2q_ctx, sockspec_P sockspec, uint wait4srv, uint max_threads, uint srv_type, uint32_t flags)
{
sm_ret_T ret;
st_thread_t rdtsk;
SM_REQUIRE(s2q_ctx != NULL);
ret = s2q_connect(s2q_ctx, sockspec, wait4srv, max_threads,
s2q_ctx->s2q_ss_ctx->ssc_id, srv_type, flags);
if (sm_is_err(ret))
goto error;
rdtsk = st_thread_create(ss_rcb_from_srv, (void *) s2q_ctx, 0, 0);
if (NULL == rdtsk) {
ret = sm_error_temp(SM_EM_SMTPS, errno);
goto error;
}
if (!S2Q_IS_STATELESS(srv_type))
++s2q_ctx->s2q_q_id;
s2q_ctx->s2q_srv_type = srv_type;
s2q_ctx->s2q_status = S2Q_ST_OK;
SSQ_DPRINTF((smioerr, "sev=DBG, func=s2q_open, sockname=%s, ctx=%p\n",
S2Q_SOCKNAME, s2q_ctx));
return SM_SUCCESS;
error:
return ret;
}
/*
** SM_S2Q_INIT -- initialize S2Q
**
** Parameters:
** ps2q_ctx -- (pointer to) S2Q context (output)
** ss_ctx -- SMTP Server context
** sockspec -- specification of socket to use for connection
** wait4srv -- time to wait for server (s)
** max_threads -- maximum number of threads
** smtps_id -- id of SMTPS
** maxrcbs -- maximum number of outstanding requests (RCBs)
** srv_type -- server type
** flags -- flags
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sm_s2q_init(s2q_ctx_P *ps2q_ctx, ss_ctx_P ss_ctx, sockspec_P sockspec, uint wait4srv, uint max_threads, int smtps_id, uint maxrcbs, uint srv_type, uint32_t flags)
{
sm_ret_T ret;
st_thread_t rdtsk;
size_t n;
s2q_ctx_P s2q_ctx;
SM_REQUIRE(ps2q_ctx != NULL);
s2q_ctx = (s2q_ctx_P) sm_zalloc(sizeof(*s2q_ctx));
if (NULL == s2q_ctx)
goto errnomem;
s2q_ctx->s2q_ss_ctx = ss_ctx;
s2q_ctx->s2q_fd = INVALID_NETFD;
s2q_ctx->s2q_wr_mutex = st_mutex_new();
if (NULL == s2q_ctx->s2q_wr_mutex) {
ret = sm_error_temp(SM_EM_SMTPS, errno);
goto error;
}
s2q_ctx->s2q_maxrcbs = maxrcbs;
s2q_ctx->s2q_currcbs = 0;
n = maxrcbs * sizeof(*s2q_ctx->s2q_sids);
s2q_ctx->s2q_sids = (sessta_id_P *) sm_zalloc(n);
if (NULL == s2q_ctx->s2q_sids)
goto errnomem;
n = maxrcbs * sizeof(*s2q_ctx->s2q_sess);
s2q_ctx->s2q_sess = (ss_sess_P *) sm_zalloc(n);
if (NULL == s2q_ctx->s2q_sess)
goto errnomem;
ret = s2q_connect(s2q_ctx, sockspec, wait4srv, max_threads, smtps_id,
srv_type, flags);
if (sm_is_err(ret))
goto error;
rdtsk = st_thread_create(ss_rcb_from_srv, (void *) s2q_ctx, 0, 0);
if (NULL == rdtsk) {
ret = sm_error_temp(SM_EM_SMTPS, errno);
goto error;
}
s2q_ctx->s2q_q_id = S2Q_IS_STATELESS(srv_type) ? S2Q_ID_NONE : 1;
s2q_ctx->s2q_srv_type = srv_type;
s2q_ctx->s2q_status = S2Q_ST_OK;
SSQ_DPRINTF((smioerr, "sev=DBG, func=s2q_init, sockname=%s, ctx=%p\n",
S2Q_SOCKNAME, s2q_ctx));
*ps2q_ctx = s2q_ctx;
return SM_SUCCESS;
errnomem:
ret = sm_error_temp(SM_EM_SMTPS, ENOMEM);
error:
if (s2q_ctx != NULL) {
SM_FREE(s2q_ctx->s2q_sids);
SM_FREE(s2q_ctx->s2q_sess);
if (s2q_ctx->s2q_wr_mutex != NULL) {
st_mutex_destroy(s2q_ctx->s2q_wr_mutex);
s2q_ctx->s2q_wr_mutex = NULL;
}
SM_FREE(s2q_ctx);
}
return ret;
}
/*
** SM_S2Q_INIT_U -- initialize S2Q
**
** Parameters:
** ps2q_ctx -- (pointer to) S2Q context (output)
** ss_ctx -- SMTP Server context
** sockname -- name of socket to use for connection
** wait4srv -- time to wait for server (s)
** max_threads -- maximum number of threads
** smtps_id -- id of SMTPS
** maxrcbs -- maximum number of outstanding requests (RCBs)
** srv_type -- server type
** flags -- flags
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sm_s2q_init_u(s2q_ctx_P *ps2q_ctx, ss_ctx_P ss_ctx, const char *sockname, uint wait4srv, uint max_threads, int smtps_id, uint maxrcbs, uint srv_type, uint32_t flags)
{
sockspec_T sockspec;
sockspec.sckspc_type = SOCK_TYPE_UNIX;
sockspec.sock_unix.unixsckspc_path = (char *)sockname;
return sm_s2q_init(ps2q_ctx, ss_ctx, &sockspec, wait4srv, max_threads,
smtps_id, maxrcbs, srv_type, flags);
}
/*
** SM_S2Q_STOP -- stop S2Q
**
** Parameters:
** s2q_ctx -- S2Q context
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sm_s2q_stop(s2q_ctx_P s2q_ctx)
{
sm_ret_T ret;
SM_REQUIRE(s2q_ctx != NULL);
ret = s2q_close(s2q_ctx);
if (s2q_ctx->s2q_wr_mutex != NULL) {
st_mutex_destroy(s2q_ctx->s2q_wr_mutex);
s2q_ctx->s2q_wr_mutex = NULL;
}
SM_FREE(s2q_ctx->s2q_sids);
SM_FREE(s2q_ctx->s2q_sess);
/* free s2q_ctx? only if allocated above! */
SM_FREE_SIZE(s2q_ctx, sizeof(*s2q_ctx));
return ret;
}
/*
need functions that are the counterpart for:
qmgr_smtps_open(IN smtps-info, OUT status):
RT_S2Q_NID: int smtps-id
XXX should this also transfer an initial status?
qmgr_session_open(IN connection-info, IN session-id, OUT status):
RT_S2Q_ID: int smtps-id
RT_S2Q_NSEID: str session-id
RT_S2Q_CLTIPV4/RT_S2Q_CLTIPV6: str client IP address
qmgr_session_status(IN connection-info, IN session-id, IN session-status, OUT status):
RT_S2Q_ID: int smtps-id
RT_S2Q_SEID: str session-id
XXX: ?
new: (2007-03-11)
qmgr_one_trans(IN trans, OUT status):
RT_S2Q_ID: int smtps-id
RT_S2Q_1TAID: str transaction-id
RT_S2Q_MAIL: str mail from
RT_S2Q_NRCPTS: int # of rcpts (not yet)
RT_S2Q_RCPT_IDX: int rcpt idx
RT_S2Q_RCPT: str rcpt to
RT_S2Q_CDBID: str cdb-id
see docs about this
optional: (and possibly multiple times)
RT_S2Q_RCPT_ADD/RT_S2Q_RCPT_DEL str rcpt_pa
RT_S2Q_RCPT_IDX: int rcpt_idx,
optional: (and possibly multiple times)
RT_S2Q_HM_T_P: int2 sm_hdrmod->sm_hm_type, sm_hdrmod->sm_hm_pos,
RT_S2Q_HM_HDR: cstr sm_hdrmod->sm_hm_hdr [optional]
qmgr_session_close(IN session-id, OUT status):
RT_S2Q_ID: int smtps-id
RT_S2Q_CSEID: str session-id
qmgr_smtps_close(IN smtps-info, OUT status):
RT_S2Q_CLID: int smtps-id
*/
/*
** SM_S2Q_NSEID -- new session
**
** Parameters:
** ss_sess -- session context
** s2q_ctx -- S2Q context
** sid -- session id
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sm_s2q_nseid(ss_sess_P ss_sess, s2q_ctx_P s2q_ctx, sessta_id_P sid)
{
sm_ret_T ret;
sm_rcb_P rcb;
SM_REQUIRE(s2q_ctx != NULL);
SM_IS_SS_SESS(ss_sess);
SM_REQUIRE(S2Q_ID_NONE == ss_sess->ssse_s2q_id[SS_COMM_QMGR]);
ss_sess->ssse_s2q_id[SS_COMM_QMGR] = s2q_ctx->s2q_q_id;
rcb = ss_sess->ssse_rcb;
ret = sm_rcb_putrec(rcb, RCB_PUTR_DFLT, 0, -1,
SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
SM_RCBV_INT, RT_S2Q_ID, s2q_ctx->s2q_ss_ctx->ssc_id,
SM_RCBV_BUF, RT_S2Q_NSEID, sid, SMTP_STID_SIZE,
SM_RCBV_INT, RT_S2Q_CLTIPV4, ss_sess->ssse_client.s_addr,
SM_RCBV_END);
if (sm_is_err(ret))
goto error;
ret = ss_send_rq(ss_sess, ss_sess->ssse_id, s2q_ctx, rcb, true);
if (sm_is_err(ret))
goto error;
return SM_SUCCESS;
error:
return ret;
}
/*
** SM_S2Q_CSEID -- close session
**
** Parameters:
** ss_sess -- session context
** s2q_ctx -- S2Q context
** sid -- session id
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sm_s2q_cseid(ss_sess_P ss_sess, s2q_ctx_P s2q_ctx, sessta_id_P sid)
{
sm_ret_T ret;
sm_rcb_P rcb;
SM_REQUIRE(s2q_ctx != NULL);
SM_IS_SS_SESS(ss_sess);
if (!SSSE_IS_FLAG(ss_sess, SSSE_FL_CSEID))
return SM_SUCCESS;
SSSE_CLR_FLAG(ss_sess, SSSE_FL_CSEID);
S2Q_CHK_CONN_R(ss_sess, s2q_ctx);
rcb = ss_sess->ssse_rcb;
ret = sm_rcb_putrec(rcb, RCB_PUTR_DFLT, 0, -1,
SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
SM_RCBV_INT, RT_S2Q_ID, s2q_ctx->s2q_ss_ctx->ssc_id,
SM_RCBV_BUF, RT_S2Q_CSEID, sid, SMTP_STID_SIZE,
SM_RCBV_END);
if (sm_is_err(ret))
goto error;
ret = ss_send_rq(ss_sess, ss_sess->ssse_id, s2q_ctx, rcb, false);
if (sm_is_err(ret))
goto error;
return SM_SUCCESS;
error:
return ret;
}
/*
** SM_S2Q_HDRMODS -- send header modifications to QMGR and free the list
**
** Parameters:
** ss_sess -- session context
** s2q_ctx -- S2Q context
** rcb -- RCB to fill in
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
sm_s2q_hdrmods(ss_sess_P ss_sess, s2q_ctx_P s2q_ctx, sm_rcb_P rcb)
{
ss_ta_P ss_ta;
ss_ta = ss_sess->ssse_ta;
SM_IS_SS_TA(ss_ta);
if (HDRMODL_EMPTY(ss_ta->ssta_hdrmodhd))
return SM_SUCCESS;
sm_hdrmodl_wr(ss_ta->ssta_hdrmodhd, rcb, RT_S2Q_HM_T_P, RT_S2Q_HM_HDR);
sm_hdrmodl_free(&ss_ta->ssta_hdrmodhd);
return SM_SUCCESS;
}
/*
** SM_S2Q_1TAID -- one complete transaction
**
** Parameters:
** ss_sess -- session context
** s2q_ctx -- S2Q context
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sm_s2q_1taid(ss_sess_P ss_sess, s2q_ctx_P s2q_ctx)
{
sm_ret_T ret;
uint ui;
sm_rcb_P rcb;
ss_ta_P ss_ta;
ss_rcpts_P ss_rcpt_hd;
ss_rcpt_P ss_rcpt;
SM_REQUIRE(s2q_ctx != NULL);
SM_IS_SS_SESS(ss_sess);
S2Q_CHK_CONN_R(ss_sess, s2q_ctx);
ss_ta = ss_sess->ssse_ta;
SM_IS_SS_TA(ss_ta);
rcb = ss_sess->ssse_rcb;
ret = sm_rcb_putrec(rcb, RCB_PUTR_FIRST|RCB_PUTR_OPEN, 0, -1,
SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
SM_RCBV_INT, RT_S2Q_ID, s2q_ctx->s2q_ss_ctx->ssc_id,
SM_RCBV_BUF, RT_S2Q_1TAID, ss_ta->ssta_id, SMTP_STID_SIZE,
SM_RCBV_STR, RT_S2Q_MAIL,
#if MTA_USE_PMILTER
SSTA_IS_FLAG(ss_ta, SSTA_FL_MAIL_MOD)
? ss_ta->ssta_mail_new :
#endif
ss_ta->ssta_mail->ssm_pa,
SM_RCBV_INT, SSTA_IS_FLAG(ss_ta, SSTA_FL_XVERP)
? RT_S2Q_MAIL_FL : RT_NOSEND, SM_TA_FL_VERP,
SM_RCBV_END);
/*
* TODO: What's the number of rcpts? ssta_rcpts_ok?
*/
ss_rcpt_hd = &ss_ta->ssta_rcpts;
for (ss_rcpt = SS_RCPTS_FIRST(ss_rcpt_hd);
ss_rcpt != SS_RCPTS_END(ss_rcpt_hd) && sm_is_success(ret);
ss_rcpt = SS_RCPTS_NEXT(ss_rcpt), ui++)
{
#if MTA_USE_PMILTER && MTA_USE_RSAD
/* send only accepted RCPTs */
if (ss_rcpt->ssr_rcode != SMTP_OK)
continue;
#endif
sm_str_clr(ss_sess->ssse_wr);
ret = t2821_str(&ss_rcpt->ssr_a2821, ss_sess->ssse_wr, 0);
if (sm_is_err(ret)) {
/* todo: log an error */
continue;
}
ret = sm_rcb_putv(rcb, 0,
SM_RCBV_INT, RT_S2Q_RCPT_IDX, ss_rcpt->ssr_idx,
SM_RCBV_STR, RT_S2Q_RCPT, ss_sess->ssse_wr,
SM_RCBV_END);
sm_str_clr(ss_sess->ssse_wr);
}
if (sm_is_err(ret))
goto error;
#if MTA_USE_PMILTER
if (ss_ta->ssta_cdb_id != NULL) {
ret = sm_rcb_putv(rcb, 0,
SM_RCBV_CSTR, RT_S2Q_CDBID, ss_ta->ssta_cdb_id,
SM_RCBV_OFF, RT_S2Q_SIZE_B, ss_ta->ssta_msg_sz_b,
SM_RCBV_END);
}
else
#endif /* MTA_USE_PMILTER */
{
ret = sm_rcb_putv(rcb, 0,
SM_RCBV_BUF, RT_S2Q_CDBID, ss_ta->ssta_id, SMTP_STID_SIZE,
SM_RCBV_OFF, RT_S2Q_SIZE_B, ss_ta->ssta_msg_sz_b,
SM_RCBV_END);
}
if (sm_is_err(ret))
goto error;
ret = sm_s2q_hdrmods(ss_sess, s2q_ctx, rcb);
if (sm_is_err(ret))
goto error;
ret = sm_rcb_close_enc(rcb);
if (sm_is_err(ret))
goto error;
ret = ss_send_rq(ss_sess, ss_ta->ssta_id, s2q_ctx, rcb, true);
if (sm_is_err(ret))
goto error;
return SM_SUCCESS;
error:
return ret;
}
syntax highlighted by Code2HTML, v. 0.9.1