/*
* 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.
*/
#include "sm/generic.h"
SM_RCSID("@(#)$Id: smtpch.c,v 1.23 2007/08/19 17:39:33 ca Exp $")
#include "sm/common.h"
#include "sm/assert.h"
#include "sm/error.h"
#include "sm/io.h"
#include "sm/sysexits.h"
#include "sm/str.h"
#include "sm/string.h"
#include "sm/limits.h"
#include "sm/net.h"
#include "statethreads/st.h"
#include "sm/ctype.h"
#include "sm/memops.h"
#include "sm/signal.h"
#include "sm/wait.h"
#include "sm/qmgrcomm.h"
#include "sm/hostname.h"
#include "sm/misc.h"
#include "smtpc.h"
#include "sm/da.h"
#include "sm/das.h"
#include "c2q.h"
#include "smtpch.h"
#include "sm/log.h"
#include "sm/version.h"
#include "log.h"
#include "sm/resource.h"
#include "sm/smtpdef.h"
#include "sm/sm-conf.h"
#include "sm/sm-conf-prt.h"
#include "sm/sccnfdef.h"
#include "sm/confsetpath.h"
static sm_file_T *Sc_Errfp = smiolog;
/*
** SC_INIT0 -- initialize SMTPC context
** before options are read
**
** Parameters:
** sc_ctx -- SMTPC context
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sc_init0(sc_ctx_P sc_ctx)
{
sm_ret_T ret;
SM_IS_SC_CTX(sc_ctx);
ret = sm_log_create(NULL, &sc_ctx->scc_lctx, &sc_ctx->scc_lcfg);
if (sm_is_err(ret))
return ret;
ret = sm_log_setfp_fd(sc_ctx->scc_lctx, Sc_Errfp, SMIOLOG_FILENO);
if (sm_is_err(ret))
return ret;
sc_ctx->scc_cnf.sc_cnf_cdb_base = "";
sc_ctx->scc_cnf.sc_cnf_smtpcsock = smsmtpcsock;
sc_ctx->scc_cnf.sc_cnf_timeout = SC_IO_TIMEOUT;
sc_ctx->scc_cnf.sc_cnf_qmgr_tmo = SC_RCB_SND_TMO;
sc_ctx->scc_cnf.sc_cnf_lmtpsock = LMTPSOCK;
sc_ctx->scc_cnf.sc_cnf_port = SMTPC_PORT;
sc_ctx->scc_cnf.sc_cnf_loglevel = UINT_MAX;
sc_ctx->scc_cnf.sc_cnf_wait4srv = 1;
sc_ctx->scc_cnf.sc_cnf_id = SMTPC_ID;
SC_MAX_WAIT_THREADS(sc_ctx) = 0;
SC_MIN_WAIT_THREADS(sc_ctx) = 2;
#if MTA_USE_TLS
ret = sm_tlsversionok();
if (sm_is_err(ret))
return ret;
#endif /* MTA_USE_TLS */
return ret;
}
/*
** SC_INIT_CHK -- check configuration etc (after options are read)
** after options are read
**
** Parameters:
** sc_ctx -- SMTPC context
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sc_init_chk(sc_ctx_P sc_ctx)
{
sm_ret_T ret;
SM_IS_SC_CTX(sc_ctx);
ret = SM_SUCCESS;
#if MTA_USE_TLS
ret = sm_tlsversionok();
if (sm_is_err(ret))
return ret;
#endif
return ret;
}
/*
** SC_INIT1 -- initialize SMTPC context
** after options are read
**
** Parameters:
** sc_ctx -- SMTPC context
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sc_init1(sc_ctx_P sc_ctx)
{
sm_ret_T ret;
size_t l;
SM_IS_SC_CTX(sc_ctx);
ret = sm_log_setdebuglevel(sc_ctx->scc_lctx, sc_ctx->scc_cnf.sc_cnf_loglevel);
if (sm_is_err(ret))
return ret;
l = SC_MAX_THREADS(sc_ctx) * sizeof(*(sc_ctx->scc_scts));
sc_ctx->scc_scts = sm_zalloc(l);
if (NULL == sc_ctx->scc_scts)
return sm_error_temp(SM_EM_SMTPC, ENOMEM);
/* convert timeout from s to micro seconds for statethreads */
sc_ctx->scc_qmgr_tmo = SEC2USEC(sc_ctx->scc_cnf.sc_cnf_qmgr_tmo);
/* might have been set in the configuration file */
if (NULL == sc_ctx->scc_hostname) {
ret = sm_myhostname(&sc_ctx->scc_hostname);
if (sm_is_err(ret)) {
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_INIT, SC_LMOD_CONFIG,
SM_LOG_ERR, 1,
"sev=ERROR, func=sc_init1, status=cannot_determine_my_hostname, ret=%m"
, ret);
return ret;
}
}
ret = sm_gen_conf_path(sc_ctx->scc_cnf.sc_cnf_cdb_base,
sc_ctx->scc_cnf.sc_cnf_smtpcsock,
smsmtpcsock,
&sc_ctx->scc_cnf.sc_cnf_smtpcsock_abs,
&sc_ctx->scc_cnf.sc_cnf_smtpcsock_alloc);
if (sm_is_err(ret))
return ret;
ret = sm_gen_conf_path(sc_ctx->scc_cnf.sc_cnf_cdb_base,
sc_ctx->scc_cnf.sc_cnf_lmtpsock,
LMTPSOCK,
&sc_ctx->scc_cnf.sc_cnf_lmtpsock_abs,
&sc_ctx->scc_cnf.sc_cnf_lmtpsock_alloc);
if (sm_is_err(ret))
return ret;
ret = cdb_start(sc_ctx->scc_cnf.sc_cnf_cdb_base, &sc_ctx->scc_cdb_ctx);
if (sm_is_err(ret)) {
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_INIT, SC_LMOD_CONFIG,
SM_LOG_ERR, 1,
"sev=ERROR, func=sc_init1, cdb_start=%m", ret);
return ret;
}
ret = c2q_init(sc_ctx, &c2q_ctx, SC_MAX_THREADS(sc_ctx));
if (sm_is_err(ret)) {
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_INIT, SC_LMOD_CONFIG,
SM_LOG_ERR, 1,
"sev=ERROR, func=sc_init1, c2q_init=%m", ret);
return ret;
}
#if MTA_USE_TLS
ret = sm_tls_init_library(&sc_ctx->scc_tlsl_ctx);
if (sm_is_success(ret)) {
char confdir[PATH_MAX];
ret = sm_dirname(sc_ctx->scc_cnf.sc_cnf_conffile, confdir, sizeof(confdir));
if (sm_is_err(ret)) {
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_INIT, SC_LMOD_CONFIG,
SM_LOG_ERR, 1,
"sev=ERROR, func=sc_init1, sm_dirname=%m", ret);
return ret;
}
TLS_GEN_PATHS(sc_ctx->scc_cnf.sc_cnf_tls, sc_ctx->scc_cnf, sc);
ret = sm_tls_init(sc_ctx->scc_tlsl_ctx, &sc_ctx->scc_ssl_ctx,
TLS_I_CLT, false, &sc_ctx->scc_cnf.sc_cnf_tls);
if (sm_is_success(ret))
SC_SET_FLAG(sc_ctx, SCC_FL_TLS_OK);
else {
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_INIT, SC_LMOD_CONFIG,
SM_LOG_INFO, 9,
"sev=INFO, func=sc_init1, sm_tls_init=%m", ret);
}
}
ret = SM_SUCCESS; /* fake it */
#endif /* MTA_USE_TLS */
SC_SET_FLAG(sc_ctx, SCC_FL_INIT);
return ret;
#if MTA_USE_TLS
enomem:
return sm_error_temp(SM_EM_SMTPC, ENOMEM);
#endif
}
/*
** SC_SET_THREAD_THROTTLING -- set appropriate values for number of threads
**
** Parameters:
** sc_ctx -- SMTPC context
**
** Returns:
** none.
*/
void
sc_set_thread_throttling(sc_ctx_P sc_ctx)
{
uint max_wait_threads;
/*
** For simplicity, the minimal size of thread pool is considered
** as a maximum number of spare threads (max_wait_threads) that
** will be created upon server startup. The pool size can grow up
** to the max_threads value. Note that this is a per listening
** socket limit. It is also possible to limit the total number of
** threads for all sockets rather than impose a per socket limit.
**
** Calculate total values across all processes.
** All numbers are per listening socket.
*/
/* just a shorter name; will be reassigned before return */
max_wait_threads = SC_MAX_WAIT_THREADS(sc_ctx);
if (0 == max_wait_threads)
max_wait_threads = MAX_WAIT_THREADS_DEFAULT;
/* Assuming that each client session needs SC_FD_PER_THREAD fds */
if (SC_MAX_THREADS(sc_ctx) == 0)
SC_MAX_THREADS(sc_ctx) = st_getfdlimit() / SC_FD_PER_THREAD;
if (max_wait_threads > SC_MAX_THREADS(sc_ctx))
max_wait_threads = SC_MAX_THREADS(sc_ctx);
if (SC_MIN_WAIT_THREADS(sc_ctx) > max_wait_threads)
SC_MIN_WAIT_THREADS(sc_ctx) = max_wait_threads;
SC_MAX_WAIT_THREADS(sc_ctx) = max_wait_threads;
}
/*
** SC_START_THREADS -- start threads
**
** Parameters:
** sc_ctx -- SMTPC context
**
** Returns:
** none; may exit if no threads have been started.
*/
void
sc_start_threads(sc_ctx_P sc_ctx)
{
uint n, threads;
/* Create connections handling threads */
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_INIT, SC_LMOD_INTERN,
SM_LOG_INFO, 10,
"sev=INFO, func=sc_start_threads, version=%s, index=%d, pid=%d, threads=%d"
, MTA_VERSION_STR, sc_index, sc_pid
, SC_MAX_WAIT_THREADS(sc_ctx));
SC_WAIT_THREADS(sc_ctx) = 0;
SC_IDLE_THREADS(sc_ctx) = 0;
SC_CLOSING_THREADS(sc_ctx) = 0;
SC_BUSY_THREADS(sc_ctx) = 0;
SC_MAX_USED_THREADS(sc_ctx) = 0;
SC_RQST_COUNT(sc_ctx) = 0;
#if SC_STATS
SC_MAIL_COUNT(sc_ctx) = 0;
SC_RCPT_COUNT(sc_ctx) = 0;
SC_TA_CNT_OK(sc_ctx) = 0;
SC_RCPT_CNT_OK(sc_ctx) = 0;
SC_SE_REUSE(sc_ctx) = 0;
SC_OPEN_SE(sc_ctx) = 0;
SC_MAX_OPEN_SE(sc_ctx) = 0;
#endif /* SC_STATS */
threads = 0;
for (n = 0; n < SC_MAX_WAIT_THREADS(sc_ctx); n++) {
if (st_thread_create(sc_hdl_requests, (void *) sc_ctx, 0, 0) == NULL) {
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_INIT, SC_LMOD_INTERN,
SM_LOG_ERR, 2,
"sev=ERROR, func=sc_start_threads, index=%d, pid=%d, wait=%u, idle=%u, busy=%u, st_thread_create()=failed"
, sc_index, sc_pid
, SC_WAIT_THREADS(sc_ctx)
, SC_IDLE_THREADS(sc_ctx)
, SC_BUSY_THREADS(sc_ctx)
);
}
else {
SC_WAIT_THREADS(sc_ctx)++;
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_INIT, SC_LMOD_INTERN,
SM_LOG_INFO, 14,
"sev=INFO, func=sc_start_threads, index=%d, pid=%d, wait=%u, idle=%u, busy=%u, sc_hdl_requests=created"
, sc_index, sc_pid
, SC_WAIT_THREADS(sc_ctx)
, SC_IDLE_THREADS(sc_ctx)
, SC_BUSY_THREADS(sc_ctx)
);
threads++;
}
}
if (0 == threads) {
c2q_stop(&c2q_ctx);
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_INIT, SC_LMOD_INTERN,
SM_LOG_ERR, 0,
"sev=ERROR, func=sc_start_threads, status=no_thread_started__terminating");
exit(EX_OSERR);
}
}
/*
** GET_NTHR_ID -- get a free thread id
**
** Parameters:
** sc_ctx -- SMTPC context
**
** Returns:
** <SC_MAX_THREADS(sc_ctx): free thread id
** >=SC_MAX_THREADS(sc_ctx): no unused thread available
** Notice: maybe use >=0: ok, <0: error?
*/
static uint
get_nthr_id(sc_ctx_P sc_ctx)
{
uint i;
SM_IS_SC_CTX(sc_ctx);
for (i = 0; i < SC_MAX_THREADS(sc_ctx); i++)
{
if (NULL == (sc_ctx->scc_scts)[i])
break;
}
return i;
}
/*
** SC_HDL_REQUESTS -- handle requests from QMGR (thread)
**
** Parameters:
** arg -- SMTPC context
**
** Returns:
** none (NULL)
*/
void *
sc_hdl_requests(void *arg)
{
uint thr_id;
int r;
sc_ctx_P sc_ctx;
sc_t_ctx_P sc_t_ctx;
sm_ret_T ret;
sc_sess_P sess;
sess = NULL;
sc_ctx = (sc_ctx_P) arg;
SM_IS_SC_CTX(sc_ctx);
/* get a free thread id */
thr_id = get_nthr_id(sc_ctx);
if (thr_id >= SC_MAX_THREADS(sc_ctx)) {
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_CLIENT, SC_LMOD_CLIENT,
SM_LOG_ERR, 1,
"sev=ERROR, func=sc_hdl_requests, process=%d, pid=%d, thread=%u, max_threads=%u, status=cannot_find_thread_slot"
, sc_index, sc_pid, thr_id, SC_MAX_THREADS(sc_ctx));
SM_ASSERT(0);
SC_WAIT_THREADS(sc_ctx)--;
return NULL; /* question: some error?? */
}
/* allocate thread context */
ret = sc_t_ctx_new(sc_ctx, &sc_t_ctx, thr_id);
if (sm_is_err(ret)) {
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_CLIENT, SC_LMOD_CLIENT,
SM_LOG_ERR, 1,
"sev=ERROR, func=sc_hdl_requests, process=%d, pid=%d, sc_t_ctx_new=%m"
, sc_index, sc_pid, ret);
SC_WAIT_THREADS(sc_ctx)--;
return NULL; /* question: some error?? */
}
/* allocate session context */
ret = sc_sess_new(&sess, sc_t_ctx);
if (sm_is_err(ret)) {
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_CLIENT, SC_LMOD_CLIENT,
SM_LOG_ERR, 1,
"sev=ERROR, func=sc_hdl_requests, process=%d, pid=%d, sc_sess_new=%m"
, sc_index, sc_pid, ret);
(void) sc_t_ctx_free(sc_ctx, sc_t_ctx);
SC_WAIT_THREADS(sc_ctx)--;
return NULL; /* question: some error?? */
}
sc_t_ctx->sct_status = SC_T_FREE;
if (sc_ctx->scc_cnf.sc_cnf_debug > 3) {
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_CLIENT, SC_LMOD_CLIENT,
SM_LOG_INFO, 14,
"sev=INFO, func=sc_hdl_requests, thread=%u, status=free, sc_t_ctx->sct_sess=%p, waitthr=%u, busythr=%u"
, thr_id, sc_t_ctx->sct_sess, SC_WAIT_THREADS(sc_ctx)
, SC_BUSY_THREADS(sc_ctx));
}
/* SC_WAIT_THREADS(sc_ctx)++; */
/* notify c2q thread that we are ready to go */
if (SC_IS_FLAG(sc_ctx, SCC_FL_INIT) && !SC_IS_FLAG(sc_ctx, SCC_FL_COMMOK)) {
SC_SET_FLAG(sc_ctx, SCC_FL_COMMOK);
r = st_cond_signal(c2q_ctx.c2q_cond_rd);
if (0 == r)
SC_SET_FLAG(sc_ctx, SCC_FL_NOTIFIED);
else
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_CLIENT, SC_LMOD_CLIENT,
SM_LOG_ERR, 4,
"sev=ERROR, func=sc_hdl_requests, thread=%u, st_cond_signal=%x"
, thr_id, r);
}
while (SC_AVAIL_THREADS(sc_ctx) <= SC_MAX_WAIT_THREADS(sc_ctx)
|| sc_t_ctx->sct_status != SC_T_FREE)
{
/* wait for a request from QMGR (indirectly) */
r = st_cond_timedwait(sc_t_ctx->sct_cond_rd, SEC2USEC(REQUEST_TIMEOUT));
if (SC_IS_FLAG(sc_ctx, SCC_FL_SHUTDOWN)) {
/* shutdown session and terminate */
ret = sc_shutdown_session(sc_t_ctx, false);
SC_LEV_DPRINTF(sc_t_ctx->sct_sc_ctx, 1, (smioerr,
"func=sc_hdl_requests, sc_t_ctx=%p, shutdown=%m\n"
, sc_t_ctx, ret));
break;
}
if (r == -1) {
if (EINTR == errno)
continue;
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_CLIENT, SC_LMOD_CLIENT,
SM_LOG_INFO, SC_T_BUSY == sc_t_ctx->sct_status ? 9 : 19,
"sev=INFO, func=sc_hdl_requests, thread=%u, status=timeout_waiting_for_QMGR, thread_stat=%d"
, thr_id, sc_t_ctx->sct_status);
/*
** "race condition": the wait could have timed out,
** but the thread just got work, i.e., it is marked
** as busy by sc_rcb_from_qmgr().
** this seems like a side effect from the statethreads
** implementation: the thread is not activated as
** long as another thread is running.
*/
if (sc_t_ctx->sct_status != SC_T_BUSY) {
ret = sc_timeout_session(sc_t_ctx);
continue;
}
}
/* alread set in sc_rcb_from_qmgr() */
/* sc_t_ctx->sct_status = SC_T_BUSY; */
SC_WAIT_THREADS(sc_ctx)--;
SC_BUSY_THREADS(sc_ctx)++;
if (SC_MAX_USED_THREADS(sc_ctx) < SC_BUSY_THREADS(sc_ctx))
SC_MAX_USED_THREADS(sc_ctx) = SC_BUSY_THREADS(sc_ctx);
SM_ASSERT(SC_WAIT_THREADS(sc_ctx) >= SC_IDLE_THREADS(sc_ctx) +
SC_CLOSING_THREADS(sc_ctx));
if (SC_AVAIL_THREADS(sc_ctx) < SC_MIN_WAIT_THREADS(sc_ctx)
&& SC_TOTAL_THREADS(sc_ctx) < SC_MAX_THREADS(sc_ctx)
&& !SC_IS_FLAG(sc_ctx, SCC_FL_SHUTDOWN))
{
/* Create another spare thread */
if (NULL == st_thread_create(sc_hdl_requests, (void *)sc_ctx, 0, 0))
{
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_CLIENT, SC_LMOD_CLIENT,
SM_LOG_ERR, 1,
"sev=ERROR, func=sc_hdl_requests, process=%d, pid=%d, status=cannot_create_thread"
, sc_index, sc_pid);
}
else {
SC_WAIT_THREADS(sc_ctx)++;
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_INIT, SC_LMOD_INTERN,
SM_LOG_INFO, 14,
"sev=INFO, func=sc_hdl_requests, index=%d, pid=%d, wait=%u, idle=%u, busy=%u, sc_hdl_requests=created"
, sc_index, sc_pid
, SC_WAIT_THREADS(sc_ctx)
, SC_IDLE_THREADS(sc_ctx)
, SC_BUSY_THREADS(sc_ctx)
);
}
}
ret = sc_hdl_session(sc_t_ctx);
/* any error will be handled below after some cleanup */
/* sess_error: */
SC_WAIT_THREADS(sc_ctx)++;
SM_ASSERT(SC_BUSY_THREADS(sc_ctx) > 0);
SC_BUSY_THREADS(sc_ctx)--;
/* this thread is only free if there is no active session */
if (NULL == sc_t_ctx->sct_sess || SCSE_ST_NONE == sc_t_ctx->sct_sess->scse_state)
sc_t_ctx->sct_status = SC_T_FREE;
else {
SC_IDLE_THREADS(sc_ctx)++;
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_CLIENT, SC_LMOD_CLIENT,
SM_LOG_INFO, 4,
"sev=INFO, func=sc_hdl_requests, sess=%p, se_id=%s, status=idle, busy=%u, wait=%u, idle=%u"
, sc_t_ctx->sct_sess
, sc_t_ctx->sct_sess->scse_id
, SC_BUSY_THREADS(sc_ctx), SC_WAIT_THREADS(sc_ctx)
, SC_IDLE_THREADS(sc_ctx));
sc_t_ctx->sct_status = SC_T_IDLE;
}
/* possible error from sc_hdl_session() */
if (sm_is_err(ret)) {
/* complain? or did sc_hdl_session() do that? */
break; /* question: some error?? */
}
}
/* XXX preserve ret from possible error above? */
ret = sc_sess_free(sc_t_ctx->sct_sess, sc_t_ctx);
if (sm_is_err(ret))
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_CLIENT, SC_LMOD_CLIENT,
SM_LOG_ERR, 4,
"sev=ERROR, func=sc_hdl_requests, thread=%u, sc_sess_free=%m"
, thr_id, ret);
ret = sc_t_ctx_free(sc_ctx, sc_t_ctx);
if (sm_is_err(ret))
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_CLIENT, SC_LMOD_CLIENT,
SM_LOG_ERR, 4,
"sev=ERROR, func=sc_hdl_requests, thread=%u, sc_t_ctx_free=%m"
, thr_id, ret);
if (sc_ctx->scc_cnf.sc_cnf_debug > 3)
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_CLIENT, SC_LMOD_CLIENT,
SM_LOG_INFO, 18,
"sev=INFO, func=sc_hdl_requests, thread=%u, status=terminates, ret=%m, busy=%u, wait=%u"
, thr_id, ret, SC_BUSY_THREADS(sc_ctx)
, SC_WAIT_THREADS(sc_ctx));
SC_WAIT_THREADS(sc_ctx)--;
if (0 == SC_WAIT_THREADS(sc_ctx) && 0 == SC_BUSY_THREADS(sc_ctx) &&
0 == SC_IDLE_THREADS(sc_ctx) && 0 == SC_CLOSING_THREADS(sc_ctx))
{
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_CLIENT, SC_LMOD_CLIENT,
SM_LOG_INFO, 4,
"sev=INFO, func=sc_hdl_requests, status=terminating, busy=%u, wait=%u, idle=%u, closing=%u"
, SC_BUSY_THREADS(sc_ctx), SC_WAIT_THREADS(sc_ctx)
, SC_IDLE_THREADS(sc_ctx), SC_CLOSING_THREADS(sc_ctx));
exit(0);
}
return NULL;
}
/*
** SC_RD_HDRMODS -- read header modification list
**
** Parameters:
** sc_ta -- transaction
** rcb -- RCB
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
sc_rd_hdrmods(sc_ta_P sc_ta, sm_rcb_P rcb)
{
sm_hdrmod_P sm_hdrmod;
uint32_t l, rt, n1, n2;
sm_ret_T ret;
while (!SM_RCB_ISEOB(rcb)) {
ret = sm_rcb_peek2uint32(rcb, &l, &rt);
if (sm_is_err(ret))
return ret;
if (rt != RT_Q2C_HM_T_P || l != 8)
return SM_SUCCESS;
ret = sm_hdrmod_new(&sc_ta->scta_hdrmodhd, true, &sm_hdrmod);
if (sm_is_err(ret))
return ret;
ret = sm_rcb_get4uint32(rcb, &l, &rt, &n1, &n2);
if (sm_is_err(ret) || rt != RT_Q2C_HM_T_P || l != 8) {
return sm_is_err(ret) ? ret
: sm_error_perm(SM_EM_SMTPC, SM_E_PR_ERR);
}
sm_hdrmod->sm_hm_type = n1;
if (SM_HM_TYPE_APPEND == n1 ||
SM_HM_TYPE_INSERT == n1 ||
SM_HM_TYPE_REMOVE == n1 ||
SM_HM_TYPE_REPLACE == n1)
SCTA_SET_FLAG(sc_ta, SCTA_FL_HDR_SCAN);
sm_hdrmod->sm_hm_pos = n2;
if (SM_RCB_ISEOB(rcb))
break;
ret = sm_rcb_peek2uint32(rcb, &l, &rt);
if (sm_is_err(ret)) {
sm_hdrmod_rm_last(&sc_ta->scta_hdrmodhd);
return ret;
}
if (RT_Q2C_HM_T_P == rt && 8 == l)
continue;
if (rt != RT_Q2C_HM_HDR)
break;
ret = sm_rcb_get2uint32(rcb, &l, &rt);
if (sm_is_err(ret) || rt != RT_Q2C_HM_HDR || l > SM_MAXHDRLEN) {
return sm_is_err(ret) ? ret
: sm_error_perm(SM_EM_SMTPC, SM_E_PR_ERR);
}
ret = sm_rcb_getncstr(rcb, &sm_hdrmod->sm_hm_hdr, l);
if (sm_is_err(ret))
return ret;
}
return SM_SUCCESS;
}
/*
** SC_HANDLE_SESSION -- handle SMTPC session/transaction
**
** Parameters:
** sc_t_ctx -- SMTPC thread context
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sc_hdl_session(sc_t_ctx_P sc_t_ctx)
{
uint32_t v, l, rt;
sm_ret_T ret, res;
bool gotport;
off_t off;
sc_sess_P sc_sess;
sc_ta_P sc_ta;
sm_str_P rcpt_pa;
rcpt_idx_T rcpt_idx;
sc_rcpt_P rcpt;
sc_ctx_P sc_ctx;
SM_IS_SC_T_CTX(sc_t_ctx);
sc_sess = sc_t_ctx->sct_sess;
SM_IS_SC_SE(sc_sess);
sc_ctx = sc_t_ctx->sct_sc_ctx;
SM_IS_SC_CTX(sc_ctx);
SC_LEV_DPRINTF(sc_ctx, 3, (smioerr, "func=sc_hdl_session, se_id=%s, state=%x\n", sc_sess->scse_id, sc_sess->scse_state));
ret = SM_SUCCESS;
gotport = false;
SCSE_CLR_FLAG(sc_sess, SCSE_FL_SND_ST);
SCSE_SET_FLAG(sc_sess, SCSE_FL_SE_CLS2QMGR);
/* always get server IP address */
/* fixme: server IP address; must match structure later on! */
ret = sm_rcb_get3uint32(sc_t_ctx->sct_rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4)
goto error;
if (RT_Q2C_SRVPORT == rt) {
sc_sess->scse_rmt_addr.sin.sin_port = htons((short) v);
gotport = true;
ret = sm_rcb_get3uint32(sc_t_ctx->sct_rcb, &l, &rt, &v);
}
if (RT_Q2C_DA_IDX == rt) {
sc_sess->scse_da_idx = v;
ret = sm_rcb_get3uint32(sc_t_ctx->sct_rcb, &l, &rt, &v);
}
SC_LEV_DPRINTF(sc_ctx, 5, (smioerr, "func=sc_hdl_session, l=%d, rt=%x, v=%x, ret=%m\n", l, rt, v, ret));
if (sm_is_err(ret) || l != 4 || (rt != RT_Q2C_SRVIPV4 && rt != RT_Q2C_LMTP))
goto error;
/*
** NOTE: v is used below, do NOT transfer data after RT_Q2C_SRVIPV4
** that is read before this section!
*/
if (SCSE_ST_NEW == sc_sess->scse_state) {
/* get connection data */
/*
** HACK ahead: address LMTP_IPV4_ADDR is "special",
** RT_Q2C_LMTP is not yet used!
** da_idx can be used to select the "protocol".
*/
if (RT_Q2C_LMTP == rt || v == LMTP_IPV4_ADDR
|| DA_IDX_LMTP_UNIX == sc_sess->scse_da_idx)
{
sc_sess->scse_rmt_addr.sunix.sun_family = AF_UNIX;
if (strlcpy(sc_sess->scse_rmt_addr.sunix.sun_path,
sc_ctx->scc_cnf.sc_cnf_lmtpsock_abs,
sizeof(sc_sess->scse_rmt_addr.sunix.sun_path))
>= sizeof(sc_sess->scse_rmt_addr.sunix.sun_path))
goto error;
#if HAVE_SOCK_UN_SUN_LEN
sc_sess->scse_rmt_addr.sunix.sun_len =
strlen(sc_ctx->scc_cnf.sc_cnf_lmtpsock_abs);
#endif
/* the following hacks should depend on da_idx! */
/* HACK turn on LMTP */
SCSE_SET_FLAG(sc_sess, SCSE_FL_LMTP);
SCSE_SET_FLAG(sc_sess, SCSE_FL_LWR);
/* HACK Add Return-Path: */
SCSE_SET_FLAG(sc_sess, SCSE_FL_RETPATH);
}
else if (sc_ctx->scc_cnf.sc_cnf_sink != 0) {
sc_sess->scse_rmt_addr.sin.sin_addr.s_addr = sc_ctx->scc_cnf.sc_cnf_sink;
sc_sess->scse_rmt_addr.sin.sin_family = AF_INET;
#if 0
/* should smar port override connect_to default port? */
if (!gotport)
#endif
sc_sess->scse_rmt_addr.sin.sin_port =
htons(sc_ctx->scc_cnf.sc_cnf_port);
}
else {
sc_sess->scse_rmt_addr.sin.sin_addr.s_addr = v;
sc_sess->scse_rmt_addr.sin.sin_family = AF_INET;
if (!gotport)
sc_sess->scse_rmt_addr.sin.sin_port =
htons(sc_ctx->scc_cnf.sc_cnf_port);
}
/* use LMTP? */
if (DA_IDX_LMTP_UNIX == sc_sess->scse_da_idx ||
DA_IDX_LMTP_INET == sc_sess->scse_da_idx)
SCSE_SET_FLAG(sc_sess, SCSE_FL_LMTP);
/* port? more? */
ret = sc_sess_open(sc_t_ctx);
if (ret != SM_SUCCESS) {
if (!SCSE_IS_FLAG(sc_sess, SCSE_FL_LOGGED)) {
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_CLIENT, SC_LMOD_CLIENT,
SM_LOG_WARN, 8,
"sev=WARN, func=sc_hdl_session, se_id=%s, state=%d, flags=%x, sc_sess_open=%m"
, sc_sess->scse_id, sc_sess->scse_state
, sc_sess->scse_flags, ret);
SCSE_SET_FLAG(sc_sess, SCSE_FL_LOGGED);
}
/* send reply codes back, HACK */
ret = sc_c2q(sc_t_ctx, RT_C2Q_SESTAT, ret, &c2q_ctx);
SCSE_CLR_FLAG(sc_sess, SCSE_FL_SE_CLS2QMGR);
/* ret is used as return value for this function */
goto error;
}
#if SC_STATS
++SC_OPEN_SE(sc_ctx);
if (SC_MAX_OPEN_SE(sc_ctx) < SC_OPEN_SE(sc_ctx))
SC_MAX_OPEN_SE(sc_ctx) = SC_OPEN_SE(sc_ctx);
#endif /* SC_STATS */
}
#if 0
else {
/* compare v with sc_sess->scse_rmt_addr.sin.sin_addr.s_addr */
}
#endif /* 0 */
SC_LEV_DPRINTF(sc_ctx, 3, (smioerr, "func=sc_hdl_session, after open: state=%d, flags=%x, ret=%m\n", sc_sess->scse_state, sc_sess->scse_flags, ret));
if (sc_sess->scse_state != SCSE_ST_OPEN) {
/* session isn't open for some reason: close it and stop */
goto closesess;
}
if (SCSE_IS_FLAG(sc_sess, SCSE_FL_CLOSE))
goto closesess;
/* read transaction data */
sc_ta = sc_sess->scse_ta;
SC_LEV_DPRINTF(sc_ctx, 3, (smioerr, "func=sc_hdl_session, se_id=%s, ta=%p\n", sc_sess->scse_id, sc_ta));
if (NULL == sc_ta)
ret = sc_ta_new(&sc_ta, sc_sess);
else
ret = sc_ta_clr(sc_ta);
if (sm_is_err(ret))
goto closesess;
SCTA_SET_STATE(sc_ta, SCTA_INIT);
/* get transaction data */
ret = sm_rcb_get2uint32(sc_t_ctx->sct_rcb, &l, &rt);
SC_LEV_DPRINTF(sc_ctx, 4, (smioerr, "func=sc_hdl_session, ta: l=%d, rt=%x, ret=%m\n", l, rt, ret));
if (sm_is_err(ret) || l != SMTP_STID_SIZE ||
(rt != RT_Q2C_NTAID && rt != RT_Q2C_NTAIDB && rt != RT_Q2C_NTAIDDB
&& rt != RT_Q2C_NTAIDD))
goto abortsess;
if (RT_Q2C_NTAIDD == rt)
SCTA_SET_FLAG(sc_ta, SCTA_FL_DELAY);
else if (RT_Q2C_NTAIDB == rt)
SCTA_SET_FLAG(sc_ta, SCTA_FL_BOUNCE);
else if (RT_Q2C_NTAIDDB == rt)
SCTA_SET_FLAG(sc_ta, SCTA_FL_DBOUNCE);
ret = sm_rcb_getn(sc_t_ctx->sct_rcb, (uchar *) sc_ta->scta_id, SMTP_STID_SIZE);
if (sm_is_err(ret))
goto abortsess;
#if SC_TIMING
SC_HRBT_DPRINTF((smioerr, "receive da_ta_id=%s\n", sc_ta->scta_id));
#endif
ret = sm_rcb_get2uint32(sc_t_ctx->sct_rcb, &l, &rt);
if (sm_is_err(ret) || l != SMTP_STID_SIZE || rt != RT_Q2C_SSTAID)
goto abortsess;
ret = sm_rcb_getn(sc_t_ctx->sct_rcb, (uchar *) sc_ta->scta_ssta_id, SMTP_STID_SIZE);
if (sm_is_err(ret))
goto abortsess;
ret = sm_rcb_get3off_t(sc_t_ctx->sct_rcb, &l, &rt, &off);
SC_LEV_DPRINTF(sc_ctx, 4, (smioerr, "func=sc_hdl_session, where=msg_size, l=%d, rt=%x, off=%lu, ret=%m\n", l, rt, (ulong) off, ret));
if (sm_is_err(ret) || l != SIZEOF_OFF_T || rt != RT_Q2C_SIZE_B)
goto abortsess;
sc_ta->scta_msg_sz_b = off;
ret = sm_rcb_get2uint32(sc_t_ctx->sct_rcb, &l, &rt);
SC_LEV_DPRINTF(sc_ctx, 4, (smioerr, "func=sc_hdl_session, where=mail, l=%d, rt=%x, ret=%m\n", l, rt, ret));
if (sm_is_err(ret) || rt != RT_Q2C_MAIL)
goto abortsess;
ret = sc_mail_new(sc_ta);
if (sm_is_err(ret))
goto abortsess;
ret = sm_rcb_getnstr(sc_t_ctx->sct_rcb, &sc_ta->scta_mail->scm_pa, l);
if (sm_is_err(ret))
goto abortsess;
ret = sm_rcb_get2uint32(sc_t_ctx->sct_rcb, &l, &rt);
SC_LEV_DPRINTF(sc_ctx, 4, (smioerr, "func=sc_hdl_session, where=cdb, l=%d, rt=%x, ret=%m\n", l, rt, ret));
if (sm_is_err(ret) ||
((rt != RT_Q2C_CDBID) && (rt != RT_Q2C_CDBID_NO)
&& (rt != RT_Q2C_CDBID_HDR))
)
goto abortsess;
ret = sm_rcb_getn0str(sc_t_ctx->sct_rcb, &sc_ta->scta_cdb_id, l);
if (sm_is_err(ret)) {
SC_LEV_DPRINTF(sc_ctx, 4, (smioerr, "sev=ERROR, func=sc_hdl_session, where=cdb_id, l=%d, rt=%x, ret=%m\n", l, rt, ret));
goto abortsess;
}
if (RT_Q2C_CDBID_NO == rt)
SCTA_SET_FLAG(sc_ta, SCTA_FL_NO_BODY);
else if (RT_Q2C_CDBID_HDR == rt)
SCTA_SET_FLAG(sc_ta, SCTA_FL_HDR_ONLY);
ret = sm_rcb_get3uint32(sc_t_ctx->sct_rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4 || rt != RT_Q2C_TA_FLAGS)
goto abortsess;
sc_ta->scta_flags |= v;
SC_LEV_DPRINTF(sc_ctx, 1, (smioerr, "sev=INFO, func=sc_hdl_session, flags=%#x\n", v));
/* should be changed later when there is more conf data */
#if MTA_USE_TLS
ret = sm_rcb_peek2uint32(sc_t_ctx->sct_rcb, &l, &rt);
if (sm_is_err(ret)) {
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_CLIENT, SC_LMOD_CLIENT,
SM_LOG_ERR, 12,
"sev=ERROR, func=sc_hdl_requests, sc_sess=%s, ta_peek=%r"
, sc_sess->scse_id, ret);
goto abortsess;
}
if (RT_Q2C_MAP_RES_CNF_SRV == rt) {
ret = sm_rcb_get3uint32(sc_t_ctx->sct_rcb, &l, &rt, &v);
if (sm_is_err(ret)) goto abortsess;
/* sc_sess->scse_maprescnf = v; */
ret = sm_rcb_get2uint32(sc_t_ctx->sct_rcb, &l, &rt);
if (RT_Q2C_RHS_CNF_SRV != rt) goto abortsess;
sm_str_clr(sc_sess->scse_str);
ret = sm_rcb_getstr(sc_t_ctx->sct_rcb, sc_sess->scse_str, l);
if (sm_is_err(ret)) goto abortsess;
if (SM_SUCCESS == v && sm_str_getlen(sc_sess->scse_str) > 0) {
sm_ret_T r;
r = sc_extra_conf(sc_ctx, sc_sess->scse_str,
&sc_sess->scse_tlsreq_cnf, &sc_sess->scse_cnf);
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_CLIENT, SC_LMOD_CLIENT,
SM_LOG_DEBUG, 12,
"sev=DBG, func=sc_hdl_requests, sc_sess=%s, scse_conf=%#T, ss_sess_conf=%r"
, sc_sess->scse_id, sc_sess->scse_str, r);
}
}
#endif /* MTA_USE_TLS */
if (!SM_RCB_ISEOB(sc_t_ctx->sct_rcb) &&
!sm_is_err(ret = sm_rcb_peek2uint32(sc_t_ctx->sct_rcb, &l, &rt)) &&
RT_Q2C_HM_T_P == rt && 8 == l)
{
ret = sc_rd_hdrmods(sc_ta, sc_t_ctx->sct_rcb);
if (sm_is_err(ret))
goto abortsess;
}
do {
ret = sm_rcb_get3uint32(sc_t_ctx->sct_rcb, &l, &rt, &v);
SC_LEV_DPRINTF(sc_ctx, 4, (smioerr, "func=sc_hdl_session, where=rcptidx, l=%d, rt=%x, v=%d, ret=%m\n", l, rt, v, ret));
if (sm_is_err(ret) || l != 4 || rt != RT_Q2C_RCPT_IDX)
goto abortsess;
rcpt_idx = v;
ret = sm_rcb_get2uint32(sc_t_ctx->sct_rcb, &l, &rt);
SC_LEV_DPRINTF(sc_ctx, 4, (smioerr, "func=sc_hdl_session, where=rcpt, l=%d, rt=%x, ret=%m\n", l, rt, ret));
if (sm_is_err(ret) || rt != RT_Q2C_RCPT)
goto abortsess;
ret = sm_rcb_getnstr(sc_t_ctx->sct_rcb, &rcpt_pa, l);
if (sm_is_err(ret))
goto abortsess;
if (SCSE_IS_FLAG(sc_sess, SCSE_FL_LWR))
sm_str2lower(rcpt_pa);
/* add recipient to list */
ret = sc_rcpts_new(sc_ta, rcpt_pa, rcpt_idx, &rcpt);
if (sm_is_err(ret))
goto abortsess;
/* should be changed later when there is more conf data */
#if MTA_USE_TLS
if (SM_RCB_ISEOB(sc_t_ctx->sct_rcb))
break;
ret = sm_rcb_peek2uint32(sc_t_ctx->sct_rcb, &l, &rt);
if (sm_is_err(ret)) {
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_CLIENT, SC_LMOD_CLIENT,
SM_LOG_ERR, 12,
"sev=ERROR, func=sc_hdl_requests, sc_sess=%s, rcpt_peek=%r"
, sc_sess->scse_id, ret);
goto abortsess;
}
if (RT_Q2C_MAP_RES_CNF_RCPT == rt) {
ret = sm_rcb_get3uint32(sc_t_ctx->sct_rcb, &l, &rt, &v);
if (sm_is_err(ret)) goto abortsess;
/* sc_sess->scse_maprescnf = v; */
ret = sm_rcb_get2uint32(sc_t_ctx->sct_rcb, &l, &rt);
if (RT_Q2C_RHS_CNF_RCPT != rt) goto abortsess;
sm_str_clr(sc_sess->scse_str);
ret = sm_rcb_getstr(sc_t_ctx->sct_rcb, sc_sess->scse_str, l);
if (sm_is_err(ret)) goto abortsess;
if (SM_SUCCESS == v && sm_str_getlen(sc_sess->scse_str) > 0) {
sm_ret_T r;
r = sc_extra_conf(sc_ctx, sc_sess->scse_str,
&rcpt->scr_tlsreq_cnf, &rcpt->scr_cnf);
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_CLIENT, SC_LMOD_CLIENT,
SM_LOG_DEBUG, 12,
"sev=DBG, func=sc_hdl_requests, sc_sess=%s, rcpt_conf=%#T, ss_sess_conf=%r"
, sc_sess->scse_id, sc_sess->scse_str, r);
}
}
#endif /* MTA_USE_TLS */
SC_LEV_DPRINTF(sc_ctx, 4, (smioerr,
"func=sc_hdl_sess, ta-id=%s, rcptaddr=%N, l=%d, nrctps=%d\n",
(char *) (sc_ta->scta_id), rcpt_pa, l,
sc_ta->scta_rcpts_tot));
/* Only one recipient allowed for bounces! */
} while (!SM_RCB_ISEOB(sc_t_ctx->sct_rcb)
&& !SCTA_IS_FLAG(sc_ta, SCTA_FL_DSN));
if (SCTA_IS_FLAG(sc_ta, SCTA_FL_DSN)) {
ret = sm_rcb_get2uint32(sc_t_ctx->sct_rcb, &l, &rt);
if (sm_is_err(ret) || rt != RT_Q2C_B_MSG)
goto abortsess;
ret = sm_rcb_getnstr(sc_t_ctx->sct_rcb, &sc_ta->scta_b_msg, l);
if (sm_is_err(ret))
goto abortsess;
}
else if (SCTA_IS_FLAG(sc_ta, SCTA_FL_NO_BODY|SCTA_FL_HDR_ONLY)) {
/*
** It's not a bounce but no body to send?
** Treat this as inconsistent for now.
** Later on this might become a "probe", but that should
** use an explicit flag so DATA isn't even tried.
*/
ret = sm_error_perm(SM_EM_SMTPC, SM_E_UNEXPECTED);
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_CLIENT, SC_LMOD_CLIENT,
SM_LOG_ERR, 5,
"sev=ERROR, func=sc_hdl_requests, se_id=%s, da_ta=%s, status=no_bounce_and_no_body"
, sc_sess->scse_id, sc_ta->scta_id);
goto abortsess;
}
/* fake message size if it's a bounce without body */
if (SCTA_IS_FLAG(sc_ta, SCTA_FL_DSN) &&
SCTA_IS_FLAG(sc_ta, SCTA_FL_NO_BODY|SCTA_FL_HDR_ONLY))
{
sc_ta->scta_msg_sz_b = 0;
}
if (SCSE_IS_CAP(sc_sess, SCSE_CAP_SIZE) && sc_sess->scse_max_sz_b > 0
&& sc_ta->scta_msg_sz_b > 0
&& sc_ta->scta_msg_sz_b > sc_sess->scse_max_sz_b)
{
/* message too big */
ret = SMTP_R_PERM;
sc_ta->scta_err_state = DA_TA_ERR_SIZE_I;
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_CLIENT, SC_LMOD_CLIENT,
SM_LOG_INFO, 9,
"sev=INFO, func=sc_hdl_requests, se_id=%s, da_ta=%s, msg_size=%ld, accepted_size=%ld, status=message_size_bigger_than_accepted_by_server"
, sc_sess->scse_id, sc_ta->scta_id
, sc_ta->scta_msg_sz_b, sc_sess->scse_max_sz_b);
}
else {
/* repeat transactions */
ret = sc_one_ta(sc_t_ctx);
}
/* need to deal with 421 */
if (SMTP_R_SSD == ret || SCSE_IS_FLAG(sc_sess, SCSE_FL_LAST_TA))
SCSE_SET_FLAG(sc_sess, SCSE_FL_CLOSE);
ret = sc_c2q(sc_t_ctx, RT_C2Q_TASTAT, ret, &c2q_ctx);
if (!sm_is_err(ret))
SCSE_SET_FLAG(sc_sess, SCSE_FL_SND_ST);
else
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_CLIENT, SC_LMOD_CLIENT,
SM_LOG_ERR, 5,
"sev=ERROR, func=sc_hdl_session, se_id=%s, da_ta=%s, ss_ta=%s, sc_c2q=%m"
, sc_sess->scse_id, sc_ta->scta_id, sc_ta->scta_ssta_id
, ret);
if (SCSE_IS_FLAG(sc_sess, SCSE_FL_CLOSE)) {
closesess:
{
bool sendstatus;
#if SC_TIMING
sessta_id_T da_ta_id;
if (sc_sess->scse_ta != NULL)
SESSTA_COPY(da_ta_id, sc_sess->scse_ta->scta_id);
else
da_ta_id[0] = '\0';
#endif
/* free transaction */
sc_ta_free(sc_t_ctx->sct_sess->scse_ta);
sc_ta = sc_t_ctx->sct_sess->scse_ta = NULL;
/* close session */
sendstatus = SCSE_IS_FLAG(sc_sess, SCSE_FL_SND_ST);
SCSE_CLR_FLAG(sc_sess, SCSE_FL_SE_CLS2QMGR);
ret = sc_sess_close(sc_t_ctx);
sc_sess_clr(sc_sess);
if (sendstatus) {
#if SC_TIMING
SC_HRBT_DPRINTF((smioerr, "send da_ta_id=%s\n", da_ta_id));
#endif
res = sc_rcb_send(sc_t_ctx->sct_rcb, &c2q_ctx);
if (sm_is_err(res)) {
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_CLIENT, SC_LMOD_CLIENT,
SM_LOG_ERR, 6,
"sev=ERROR, func=sc_hdl_session, thread=%u, sc_rcb_send=%m"
, sc_t_ctx->sct_thr_id, res);
goto error;
}
}
if (sm_is_err(ret))
goto error2;
/* sc_t_ctx->sct_status = SC_T_FREE; */
}
}
else if (SCSE_IS_FLAG(sc_sess, SCSE_FL_SND_ST)) {
res = sc_rcb_send(sc_t_ctx->sct_rcb, &c2q_ctx);
if (sm_is_err(res)) {
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_CLIENT, SC_LMOD_CLIENT,
SM_LOG_ERR, 6,
"sev=ERROR, func=sc_hdl_session, sc_rcb_send=%m"
, res);
goto error;
}
SCSE_CLR_FLAG(sc_sess, SCSE_FL_SND_ST);
}
/* XXX send appropriate status code to QMGR! */
(void) sm_rcb_close_decn(sc_t_ctx->sct_rcb);
return ret;
abortsess:
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_CLIENT, SC_LMOD_CLIENT,
SM_LOG_ERR, 9,
"sev=ERROR, func=sc_hdl_session, status=abort, l=%d, rt=%x, sc_ta=%p, ret=%m"
, l, rt, sc_ta, ret);
sc_ta_free(sc_ta);
sc_ta = sc_t_ctx->sct_sess->scse_ta = NULL;
(void) sc_sess_close(sc_t_ctx);
sc_t_ctx->sct_status = SC_T_FREE;
error:
/* XXX send appropriate status code to QMGR! */
sc_sess_clr(sc_sess);
sc_t_ctx_clr(sc_t_ctx);
error2:
return ret;
}
/*
** SC_TIMEOUT_SESSION -- timeout occurred while waiting for QMGR: close session
**
** Parameters:
** sc_t_ctx -- SMTPC thread context
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sc_timeout_session(sc_t_ctx_P sc_t_ctx)
{
sm_ret_T ret;
sc_sess_P sc_sess;
sc_ctx_P sc_ctx;
SM_IS_SC_T_CTX(sc_t_ctx);
sc_sess = sc_t_ctx->sct_sess;
sc_ctx = sc_t_ctx->sct_sc_ctx;
if (sc_sess != NULL && sc_sess->scse_state != SCSE_ST_NONE) {
/*
** add a "last used" timestamp?? close session only if a
** "session timeout" value is exceeded?
*/
if (sc_ctx->scc_cnf.sc_cnf_debug > 3) {
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_CLIENT, SC_LMOD_CLIENT,
SM_LOG_INFO, 17,
"sev=INFO, func=sc_timeout_session, sess=%p, se_id=%s"
, sc_sess, sc_sess->scse_id);
}
/* SESSION REUSE DEBUG */
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_CLIENT, SC_LMOD_CLIENT,
SM_LOG_INFO, 9,
"sev=INFO, func=sc_timeout_session, sess=%p, se_id=%s, cls2qmgr=%d"
, sc_sess, sc_sess->scse_id
, SCSE_IS_FLAG(sc_sess, SCSE_FL_SE_CLS2QMGR));
SM_REQUIRE(sc_t_ctx->sct_status != SC_T_BUSY);
SM_REQUIRE(SC_T_IDLE == sc_t_ctx->sct_status); /* ??? */
SM_ASSERT(SC_IDLE_THREADS(sc_ctx) > 0);
SC_IDLE_THREADS(sc_ctx)--;
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_CLIENT, SC_LMOD_CLIENT,
SM_LOG_INFO, 9,
"sev=INFO, func=sc_timeout_session, se_id=%s, status=idle, busy=%u, wait=%u, idle=%u"
, sc_sess->scse_id
, SC_BUSY_THREADS(sc_ctx), SC_WAIT_THREADS(sc_ctx)
, SC_IDLE_THREADS(sc_ctx));
sc_t_ctx->sct_status = SC_T_CLOSING;
SC_CLOSING_THREADS(sc_ctx)++;
/* close SMTP session */
ret = sc_sess_close(sc_t_ctx);
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_CLIENT, SC_LMOD_CLIENT,
SM_LOG_INFO, 11,
"sev=INFO, func=sc_timeout_session, se_id=%s, where=after_sc_sess_close(), status=%d, busy=%u, wait=%u, idle=%u"
, sc_sess->scse_id, sc_t_ctx->sct_status
, SC_BUSY_THREADS(sc_ctx), SC_WAIT_THREADS(sc_ctx)
, SC_IDLE_THREADS(sc_ctx));
/* tell qmgr about status change? */
if (SCSE_IS_FLAG(sc_sess, SCSE_FL_SE_CLS2QMGR))
{
sc_sess->scse_err_st = 0;
(void) sc_c2q(sc_t_ctx, RT_C2Q_SECLSD,
0 /* SESS_TIMEOUT? */, &c2q_ctx);
SCSE_CLR_FLAG(sc_sess, SCSE_FL_SE_CLS2QMGR);
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_CLIENT, SC_LMOD_CLIENT,
SM_LOG_INFO, 11,
"sev=INFO, func=sc_timeout_session, se_id=%s, where=after_sc_c2q(), status=%d, busy=%u, wait=%u, idle=%u"
, sc_sess->scse_id, sc_t_ctx->sct_status
, SC_BUSY_THREADS(sc_ctx), SC_WAIT_THREADS(sc_ctx)
, SC_IDLE_THREADS(sc_ctx));
}
SM_ASSERT(SC_CLOSING_THREADS(sc_ctx) > 0);
SC_CLOSING_THREADS(sc_ctx)--;
(void) sc_sess_clr(sc_sess);
if (sm_is_err(ret)) goto error;
}
else {
if (SC_T_IDLE == sc_t_ctx->sct_status) {
SM_ASSERT(SC_IDLE_THREADS(sc_ctx) > 0);
SC_IDLE_THREADS(sc_ctx)--;
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_CLIENT, SC_LMOD_CLIENT,
SM_LOG_INFO, 9,
"sev=INFO, func=sc_timeout_session, sess=%p, se_id=%s, status=idle, busy=%u, wait=%u, idle=%u"
, sc_sess
, sc_sess != NULL ? sc_sess->scse_id : "-"
, SC_BUSY_THREADS(sc_ctx), SC_WAIT_THREADS(sc_ctx)
, SC_IDLE_THREADS(sc_ctx));
}
else if (SC_T_BUSY == sc_t_ctx->sct_status) {
SC_WAIT_THREADS(sc_ctx)++;
SM_ASSERT(SC_BUSY_THREADS(sc_ctx) > 0);
SC_BUSY_THREADS(sc_ctx)--;
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_CLIENT, SC_LMOD_CLIENT,
SM_LOG_WARN, 9,
"sev=WARN, func=sc_timeout_session, sess=%p, se_id=%s, waitthr=%u, busythr=%u, total=%u"
, sc_sess
, sc_sess != NULL ? sc_sess->scse_id : "-"
, SC_WAIT_THREADS(sc_ctx)
, SC_BUSY_THREADS(sc_ctx)
, SC_TOTAL_THREADS(sc_ctx));
}
}
sc_t_ctx_clr(sc_t_ctx);
if (SC_T_REUSE == sc_t_ctx->sct_status)
(void) st_cond_signal(c2q_ctx.c2q_cond_rd);
else
sc_t_ctx->sct_status = SC_T_FREE;
return SM_SUCCESS;
error:
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_CLIENT, SC_LMOD_CLIENT,
SM_LOG_ERR, 8,
"sev=ERROR, func=sc_timeout_session, ret=%m", ret);
sc_t_ctx_clr(sc_t_ctx);
if (SC_T_REUSE == sc_t_ctx->sct_status)
(void) st_cond_signal(c2q_ctx.c2q_cond_rd);
else
sc_t_ctx->sct_status = SC_T_FREE;
return ret;
}
/*
** SC_SHUTDOWN_SESSION -- shutdown session because the system is shutting down
**
** Parameters:
** sc_t_ctx -- SMTPC thread context
** fast -- shutdown fast
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sc_shutdown_session(sc_t_ctx_P sc_t_ctx, bool fast)
{
sm_ret_T ret;
sc_sess_P sc_sess;
SM_IS_SC_T_CTX(sc_t_ctx);
sc_sess = sc_t_ctx->sct_sess;
ret = SM_SUCCESS;
if (!fast && sc_sess != NULL && sc_sess->scse_state != SCSE_ST_NONE) {
/* close session */
ret = sc_sess_close(sc_t_ctx);
if (sm_is_err(ret)) {
sm_log_write(sc_t_ctx->sct_sc_ctx->scc_lctx,
SC_LCAT_CLIENT, SC_LMOD_CLIENT,
SM_LOG_ERR, 7,
"sev=ERROR, func=sc_shutdown_session, sc_sess_close=%x"
, ret);
}
}
sc_sess_clr(sc_sess);
return ret;
}
#if 0
/*
** SC_LOAD_CONFIGS -- load configs; fixme: this does nothing right now.
**
** Parameters:
** sc_ctx -- SMTPC context
**
** Returns:
** none
*/
void
sc_load_configs(sc_ctx_P sc_ctx)
{
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_INIT, SC_LMOD_CONFIG,
SM_LOG_INFO, 12,
"sev=INFO, func=sc_load_configs, index=%d, pid=%d, status=configuration_reload_not_yet_implemented"
, sc_index, sc_pid);
}
#endif /* 0 */
/*
** SC_DUMP_INFO -- print some status information
**
** Parameters:
** sc_ctx -- SMTPC context
**
** Returns:
** none
*/
void
sc_dump_info(sc_ctx_P sc_ctx)
{
char *buf;
int len, s, l;
ssize_t b;
struct rusage rusage;
s = 512;
if ((buf = sm_malloc(s)) == NULL) {
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_INIT, SC_LMOD_CONFIG, SM_LOG_ERR, 1,
"sev=ERROR, func=sc_dump_info, bytes=%d, malloc=failed", s);
return;
}
len = sm_snprintf(buf, s, "\n\nProcess #%d (pid %d):\n", sc_index,
(int)sc_pid);
l = sm_snprintf(buf + len, s - len,
"Max threads %u\n"
"Min waiting threads %u\n"
"Max waiting threads %u\n"
"Wait threads %u\n"
"Idle threads %u\n"
"Busy threads %u\n"
"Max used threads %u\n"
"Requests %u\n"
#if SC_STATS
"Transactions %u\n"
"Recipients %u\n"
"Transactions ok %u\n"
"Recipients ok %u\n"
"Sessions reused %u\n"
"Max open sessions %u\n"
#endif /* SC_STATS */
, SC_MAX_THREADS(sc_ctx)
, SC_MIN_WAIT_THREADS(sc_ctx)
, SC_MAX_WAIT_THREADS(sc_ctx)
, SC_WAIT_THREADS(sc_ctx)
, SC_IDLE_THREADS(sc_ctx)
, SC_BUSY_THREADS(sc_ctx)
, SC_MAX_USED_THREADS(sc_ctx)
, SC_RQST_COUNT(sc_ctx)
#if SC_STATS
, SC_MAIL_COUNT(sc_ctx)
, SC_RCPT_COUNT(sc_ctx)
, SC_TA_CNT_OK(sc_ctx)
, SC_RCPT_CNT_OK(sc_ctx)
, SC_SE_REUSE(sc_ctx)
, SC_MAX_OPEN_SE(sc_ctx)
#endif /* SC_STATS */
);
if (l < s - len)
len += l;
l = getrusage(RUSAGE_SELF, &rusage);
if (l == 0 && len < s) {
l = sm_snprintf(buf + len, s - len,
"ru_utime= %7ld.%07ld\n"
"ru_stime= %7ld.%07ld\n"
"ru_maxrss= %7ld\n"
"ru_ixrss= %7ld\n"
"ru_idrss= %7ld\n"
"ru_isrss= %7ld\n"
"ru_minflt= %7ld\n"
"ru_majflt= %7ld\n"
"ru_nswap= %7ld\n"
"ru_inblock= %7ld\n"
"ru_oublock= %7ld\n"
"ru_msgsnd= %7ld\n"
"ru_msgrcv= %7ld\n"
"ru_nsignals=%7ld\n"
"ru_nvcsw= %7ld\n"
"ru_nivcsw= %7ld\n"
, rusage.ru_utime.tv_sec
, rusage.ru_utime.tv_usec
, rusage.ru_stime.tv_sec
, rusage.ru_stime.tv_usec
, rusage.ru_maxrss
, rusage.ru_ixrss
, rusage.ru_idrss
, rusage.ru_isrss
, rusage.ru_minflt
, rusage.ru_majflt
, rusage.ru_nswap
, rusage.ru_inblock
, rusage.ru_oublock
, rusage.ru_msgsnd
, rusage.ru_msgrcv
, rusage.ru_nsignals
, rusage.ru_nvcsw
, rusage.ru_nivcsw
);
if (l < s - len)
len += l;
}
sm_io_write(smioerr, (uchar *) buf, len, &b);
sm_free_size(buf, s);
}
/*
** SC_SHOWVERSION -- show version information
**
** Parameters:
** sc_ctx -- SMTP Client context
** progname -- program name
** cnf -- configuration file name
** level -- how much detail?
**
** Returns:
** usual error code
*/
sm_ret_T
sc_showversion(sc_ctx_P sc_ctx, const char *progname, const char *cnf, uint level)
{
char *prefix;
prefix = "";
if (level >= SM_SV_RD_CONF)
prefix = "# ";
sm_io_fprintf(smioout, "%s%s: %s\n", prefix, progname, MTA_VERSION_STR);
if (SM_SHOW_VERSION(level))
sm_io_fprintf(smioout, "%sversion=0x%08x\n", prefix, MTA_VERSION);
#if MTA_USE_TLS
if (SM_SHOW_LIBS(level))
(void) sm_tlsversionprt(smioout);
#endif
if (SM_SHOW_OPTIONS(level)) {
sm_io_fprintf(smioout, "compile time options:\n"
#if MTA_USE_TLS
"MTA_USE_TLS\n"
#endif
#if SC_DEBUG
"SC_DEBUG\n"
#endif
#if MTA_USE_RSAD
"MTA_USE_RSAD\n"
#endif
#if SC_TEST
"SC_TEST\n"
#endif
);
}
if (SM_SHOW_RD_CONF(level) && cnf != NULL && *cnf != '\0') {
sm_io_fprintf(smioout, "# configuration-file=%s\n\n", cnf);
(void) sm_conf_prt_conf(sc_global_defs, &sc_ctx->scc_cnf, smioout);
}
else if (SM_SHOW_DFLT_CONF(level)) {
sm_io_fprintf(smioout, "# default configuration:\n\n");
(void) sm_conf_prt_dflt(sc_global_defs, 0, smioout);
}
else if (SM_SHOW_ALL_CONF(level)) {
sm_io_fprintf(smioout,
"# configuration including flags.\n"
"# note: this data cannot be used as configuration file\n"
"# but it shows the available flags.\n"
);
(void) sm_conf_prt_dflt(sc_global_defs, SMC_FLD_FLAGS, smioout);
}
sm_io_flush(smioout);
return sm_error_perm(SM_EM_SMTPC, SM_E_DONTSTART);
}
/* should be changed later when there is more conf data */
#if MTA_USE_TLS
/*
** SC_EXTRA_CONF -- set "extra" configuration
**
** Parameters:
** sc_ctx -- SMTPC context
** conf_str -- configuration data
**
** Returns:
** usual return code
*/
static sm_conf_definition_T
scse_tlsreq_defs[] = {
TLSREQ_DEFS(tlsreq_cnf_T, tlsreqcnf),
/* Sentinel */
{ SM_CONF_DEF_MAGIC, NULL, 0, 0, 0, NULL, 0, NULL, NULL, NULL, NULL SM_LC_NO_ISSET SM_LC_SET_MAGIC(0)}
};
static sm_conf_definition_T
scse_mapcnf_defs[] = {
{ SM_CONF_DEF_MAGIC, "tls_requirements", sm_conf_type_section,
0, sizeof(tlsreq_cnf_T), NULL,
SM_CONF_FLAG_KEEP_DEFAULT,
scse_tlsreq_defs,
NULL, NULL, NULL
SM_LC_NO_ISSET SM_LC_SET_MAGIC(0) },
/* Sentinel */
{ SM_CONF_DEF_MAGIC, NULL, 0, 0, 0, NULL, 0, NULL, NULL, NULL, NULL SM_LC_NO_ISSET SM_LC_SET_MAGIC(0)}
};
sm_ret_T
sc_extra_conf(sc_ctx_P sc_ctx, sm_str_P conf_str, tlsreq_cnf_P tlsreq_cnf, sm_conf_T **pcnf)
{
int err;
sm_conf_T *cnf;
#define SC_EXTRA_CONF "sc_extra_conf"
SM_REQUIRE(conf_str != NULL);
SM_REQUIRE(pcnf != NULL);
SM_REQUIRE(tlsreq_cnf != NULL);
sm_memzero(tlsreq_cnf, sizeof(*tlsreq_cnf));
cnf = sm_conf_new(SC_EXTRA_CONF);
if (NULL == cnf) {
err = errno;
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_INIT, SC_LMOD_CONFIG,
SM_LOG_ERR, 8,
"sev=ERROR, func=sc_extra_conf, sm_conf_new=NULL, errno=%d\n",
err);
return sm_error_temp(SM_EM_SMTPC, ENOMEM);
}
err = sm_conf_read_data(cnf, (char *)sm_str_getdata(conf_str),
sm_str_getlen(conf_str), true);
if (err != 0) {
sm_prt_conferr(SC_EXTRA_CONF, cnf, err, smioerr);
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_INIT, SC_LMOD_CONFIG,
SM_LOG_ERR, 8,
"sev=ERROR, func=sc_extra_conf, sm_conf_read_data=%m\n",
err);
sm_conf_destroy(cnf);
cnf = NULL;
return err;
}
err = sm_conf_scan(cnf, scse_mapcnf_defs, 0, tlsreq_cnf);
if (err != 0) {
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_INIT, SC_LMOD_CONFIG,
SM_LOG_ERR, 8,
"sev=ERROR, func=sc_extra_conf, sm_conf_scan=%m\n",
err);
}
return err;
}
#endif /* MTA_USE_TLS */
syntax highlighted by Code2HTML, v. 0.9.1