/*
* 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: sc.c,v 1.78 2007/10/25 05:24:06 ca Exp $")
#include "sm/assert.h"
#include "sm/error.h"
#include "sm/str.h"
#include "sm/limits.h"
#include "sm/qmgrcomm.h"
#include "sm/memops.h"
#include "sm/net.h"
#include "smtpc.h"
#include "c2q.h"
#include "log.h"
/*
** SC_T_CTX_CLR -- clear a SMTPC thread context such that it can be reused.
**
** Parameters:
** sc_t_ctx -- SMTPC thread context
**
** Returns:
** SM_SUCCESS
*/
sm_ret_T
sc_t_ctx_clr(sc_t_ctx_P sc_t_ctx)
{
if (NULL == sc_t_ctx)
return SM_SUCCESS;
SM_IS_SC_T_CTX(sc_t_ctx);
(void) sm_rcb_close_decn(sc_t_ctx->sct_rcb);
return SM_SUCCESS;
}
/*
** SC_T_CTX_NEW -- create a new SMTPC thread context.
** allocate new sc_t_ctx structure, put a pointer to it into
** the array of thread contexts in SMTPC context.
**
** Parameters:
** sc_ctx -- SMTPC context
** psc_t_ctx -- pointer to SMTPC thread context (output)
** thr_id -- thread id
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sc_t_ctx_new(sc_ctx_P sc_ctx, sc_t_ctx_P *psc_t_ctx, uint thr_id)
{
sm_ret_T ret;
sc_t_ctx_P sc_t_ctx;
SM_REQUIRE(psc_t_ctx != NULL);
sc_t_ctx = sm_zalloc(sizeof(*sc_t_ctx));
if (NULL == sc_t_ctx)
return sm_error_temp(SM_EM_SMTPC, ENOMEM);
sc_t_ctx->sct_rcb = sm_rcb_new(NULL, QSS_RC_SZ, QSS_RC_MAXSZ);
if (NULL == sc_t_ctx->sct_rcb)
{
ret = sm_error_temp(SM_EM_SMTPC, ENOMEM);
goto error;
}
sc_t_ctx->sct_sc_ctx = sc_ctx;
sc_t_ctx->sct_thr_id = thr_id;
sc_t_ctx->sct_cond_rd = st_cond_new();
if (NULL == sc_t_ctx->sct_cond_rd)
{
ret = sm_error_temp(SM_EM_SMTPC, errno);
goto error;
}
SM_ASSERT(thr_id < SC_MAX_THREADS(sc_ctx));
(sc_ctx->scc_scts)[thr_id] = sc_t_ctx;
sc_t_ctx->sm_magic = SM_SC_T_CTX_MAGIC;
*psc_t_ctx = sc_t_ctx;
return SM_SUCCESS;
error:
if (sc_t_ctx->sct_cond_rd != NULL)
(void) st_cond_destroy(sc_t_ctx->sct_cond_rd);
SM_RCB_FREE(sc_t_ctx->sct_rcb);
sm_free_size(sc_t_ctx, sizeof(*sc_t_ctx));
return ret;
}
/*
** SC_T_CTX_FREE -- free a SMTPC thread context,
** clear entry in the array of thread contexts in SMTPC context.
**
** Parameters:
** sc_ctx -- SMTPC context
** sc_t_ctx -- SMTPC thread context
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sc_t_ctx_free(sc_ctx_P sc_ctx, sc_t_ctx_P sc_t_ctx)
{
int r;
sm_ret_T ret;
uint thr_id;
SM_REQUIRE(sc_ctx != NULL);
if (NULL == sc_t_ctx)
return SM_SUCCESS;
SM_IS_SC_T_CTX(sc_t_ctx);
ret = SM_SUCCESS;
sc_t_ctx->sct_status = SC_T_NOTRDY;
thr_id = sc_t_ctx->sct_thr_id;
r = st_cond_destroy(sc_t_ctx->sct_cond_rd);
if (r == -1)
{
sm_log_write(sc_ctx->scc_lctx,
SC_LCAT_INTERN, SC_LMOD_INTERN,
SM_LOG_ERR, 7,
"sev=ERROR, func=sc_t_ctx_free, thread=%u, st_cond_destroy=%x",
thr_id, r);
ret = sm_error_perm(SM_EM_SMTPC, errno);
}
SM_ASSERT(thr_id < SC_MAX_THREADS(sc_ctx));
(sc_ctx->scc_scts)[thr_id] = NULL;
SM_RCB_FREE(sc_t_ctx->sct_rcb);
sc_t_ctx->sm_magic = SM_MAGIC_NULL;
sm_free_size(sc_t_ctx, sizeof(*sc_t_ctx));
return ret;
}
/*
** SC_SESS_NEW -- create a new SMTPC session context,
** set it as current context in SMTPC thread context.
**
** Parameters:
** psc_sess -- pointer to SMTPC session context (output)
** sc_t_ctx -- SMTPC thread context
**
** Returns:
** usual sm_error code
*/
#define SC_IOBUFSIZE (16*1024)
sm_ret_T
sc_sess_new(sc_sess_P *psc_sess, sc_t_ctx_P sc_t_ctx)
{
sm_ret_T ret;
sc_sess_P sc_sess;
sm_str_P str;
SM_REQUIRE(psc_sess != NULL);
sc_sess = sm_zalloc(sizeof(*sc_sess));
if (NULL == sc_sess)
goto errnomem;
sc_sess->scse_sct_ctx = sc_t_ctx;
sc_t_ctx->sct_sess = sc_sess;
str = sm_str_new(NULL, SC_IOBUFSIZE, SC_IOBUFSIZE);
if (NULL == str)
goto errnomem;
sc_sess->scse_wr = str;
str = sm_str_new(NULL, SC_IOBUFSIZE, SC_IOBUFSIZE);
if (NULL == str)
goto errnomem;
sc_sess->scse_rd = str;
str = sm_str_new(NULL, SC_IOBUFSIZE, SC_IOBUFSIZE);
if (NULL == str)
goto errnomem;
sc_sess->scse_str = str;
#if MTA_USE_TLS
ret = tlsi_new(&sc_sess->scse_tlsi);
if (sm_is_err(ret))
goto error;
#endif
sm_memzero(&sc_sess->scse_rmt_addr, sizeof(sc_sess->scse_rmt_addr));
sc_sess->scse_rmt_addr.sin.sin_family = AF_INET;
sc_sess->scse_rmt_addr.sin.sin_port = htons(SMTPC_PORT);
sc_sess->scse_c2q_idx = SCSE_C2Q_IDX_NONE;
sc_sess->scse_state = SCSE_ST_NONE;
/* initialize flags */
SCSE_INHERIT_FLAG(sc_sess);
sc_sess->sm_magic = SM_SC_SESS_MAGIC;
*psc_sess = sc_sess;
return SM_SUCCESS;
errnomem:
ret = sm_error_temp(SM_EM_SMTPC, ENOMEM);
#if MTA_USE_TLS
error:
#endif
(void) sc_sess_free(sc_sess, sc_t_ctx);
return ret;
}
/*
** SC_SESS_CLR -- clear a SMTPC session context (reset values to default)
**
** Parameters:
** sc_sess -- SMTPC session context
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sc_sess_clr(sc_sess_P sc_sess)
{
extern c2q_ctx_T c2q_ctx;
if (NULL == sc_sess)
return SM_SUCCESS;
SM_IS_SC_SE(sc_sess);
(void) sc_rm_sess_rq(sc_sess, &c2q_ctx);
if (sc_sess->scse_fp != NULL)
{
sm_io_close(sc_sess->scse_fp, SM_IO_CF_NONE);
sc_sess->scse_fp = NULL;
}
/* other fields? */
sc_sess->scse_cap = SCSE_CAP_NONE;
sc_sess->scse_flags = SCSE_FL_NONE;
sc_sess->scse_da_idx = 0;
sc_sess->scse_max_sz_b = 0;
SCSE_INHERIT_FLAG(sc_sess);
#if 0
sc_sess->scse_client = NULL;
#endif
/* sc_sess->scse_ta = NULL; ??? */
#if 0
(void) sm_rcb_close_decn(sc_sess->scse_rcb);
#endif
SESSTA_CLR(sc_sess->scse_id);
sc_sess->scse_c2q_idx = SCSE_C2Q_IDX_NONE;
sc_sess->scse_state = SCSE_ST_NONE;
sc_sess->scse_err_st = 0;
SM_STR_CLR(sc_sess->scse_reply);
SM_STR_CLR(sc_sess->scse_wr);
SM_STR_CLR(sc_sess->scse_rd);
SM_STR_CLR(sc_sess->scse_str);
sm_memzero(&sc_sess->scse_rmt_addr, sizeof(sc_sess->scse_rmt_addr));
sc_sess->scse_rmt_addr.sin.sin_family = AF_INET;
sc_sess->scse_rmt_addr.sin.sin_port = htons(SMTPC_PORT);
#if MTA_USE_TLS
(void) tlsi_clr(sc_sess->scse_tlsi);
#endif
return SM_SUCCESS;
}
/*
** SC_SESS_FREE -- free a SMTPC session context,
** including current transaction and clearing pointer in sc_t_ctx
**
** Parameters:
** sc_sess -- SMTPC session context
** sc_t_ctx -- SMTPC thread context
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sc_sess_free(sc_sess_P sc_sess, sc_t_ctx_P sc_t_ctx)
{
sm_ret_T ret;
extern c2q_ctx_T c2q_ctx;
if (NULL == sc_sess)
return SM_SUCCESS;
SM_IS_SC_SE(sc_sess);
ret = sc_rm_sess_rq(sc_sess, &c2q_ctx);
sc_t_ctx->sct_sess = NULL;
if (sc_sess->scse_fp != NULL)
sm_io_close(sc_sess->scse_fp, SM_IO_CF_NONE);
ret = sc_ta_free(sc_sess->scse_ta);
SM_STR_FREE(sc_sess->scse_wr);
SM_STR_FREE(sc_sess->scse_rd);
SM_STR_FREE(sc_sess->scse_str);
SM_STR_FREE(sc_sess->scse_reply);
#if MTA_USE_TLS
ret = tlsi_free(sc_sess->scse_tlsi);
if (sc_sess->scse_cnf != NULL) {
sm_conf_destroy(sc_sess->scse_cnf);
sc_sess->scse_cnf = NULL;
}
#endif
sc_sess->sm_magic = SM_MAGIC_NULL;
SM_FREE_SIZE(sc_sess, sizeof(*sc_sess));
return ret;
}
/*
** SC_MAIL_NEW -- create new mail entry
**
** Parameters:
** sc_ta -- transaction
**
** Returns:
** usual sm_error code
**
** Note: this does not create scm_pa.
*/
sm_ret_T
sc_mail_new(sc_ta_P sc_ta)
{
sc_mail_P sc_mail;
SM_IS_SC_TA(sc_ta);
sc_mail = (sc_mail_P) sm_rpool_zalloc(sc_ta->scta_rpool,
sizeof(*sc_mail));
if (NULL == sc_mail)
return sm_error_temp(SM_EM_SMTPC, ENOMEM);
sc_ta->scta_mail = sc_mail;
return SM_SUCCESS;
}
/*
** SC_MAIL_FREE -- free mail entry
**
** Parameters:
** sc_ta -- transaction
**
** Returns:
** usual sm_error code
**
** Note: this does free scm_pa.
*/
sm_ret_T
sc_mail_free(sc_ta_P sc_ta)
{
SM_IS_SC_TA(sc_ta);
if (NULL == sc_ta->scta_mail)
return SM_SUCCESS;
SM_STR_FREE(sc_ta->scta_mail->scm_pa);
SM_STR_FREE(sc_ta->scta_mail->scm_reply);
SM_RPOOL_FREE_SIZE(sc_ta->scta_rpool, sc_ta->scta_mail,
sizeof(*sc_ta->scta_mail));
return SM_SUCCESS;
}
/*
** SC_RCPTS_NEW -- add a new recipient to the recipient list
**
** Parameters:
** sc_ta -- transaction
** sc_rcpt_pa -- recipient (printable address)
** sc_rcpt_idx -- recipient index
** psc_rcpt -- recipient (return parameter)
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sc_rcpts_new(sc_ta_P sc_ta, sm_str_P sc_rcpt_pa, rcpt_idx_T sc_rcpt_idx, sc_rcpt_P *psc_rcpt)
{
sc_rcpt_P sc_rcpt;
SM_IS_SC_TA(sc_ta);
SM_REQUIRE(sc_rcpt_pa != NULL);
SM_REQUIRE(psc_rcpt != NULL);
sc_rcpt = (sc_rcpt_P) sm_rpool_zalloc(sc_ta->scta_rpool, sizeof(*sc_rcpt));
if (NULL == sc_rcpt)
goto error;
sc_rcpt->scr_pa = sc_rcpt_pa;
sc_rcpt->scr_idx = sc_rcpt_idx;
sc_rcpt->scr_st = SMTP_NO_REPLY;
SC_RCPTS_INSERT_TAIL(&sc_ta->scta_rcpts, sc_rcpt);
sc_ta->scta_rcpts_tot++;
*psc_rcpt = sc_rcpt;
return SM_SUCCESS;
error:
SM_RPOOL_FREE(sc_ta->scta_rpool, sc_rcpt);
return sm_error_temp(SM_EM_SMTPC, ENOMEM);
}
/*
** SC_RCPT_FREE -- free a single recipient address
**
** Parameters:
** sc_ta -- transaction
** sc_rcpt -- recipient
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sc_rcpt_free(sc_ta_P sc_ta, sc_rcpt_P sc_rcpt)
{
if (NULL == sc_rcpt)
return SM_SUCCESS;
SM_IS_SC_TA(sc_ta);
SM_REQUIRE(sc_ta->scta_rcpts_tot > 0);
sm_str_free(sc_rcpt->scr_pa);
SM_STR_FREE(sc_rcpt->scr_reply);
SC_RCPTS_REMOVE(&sc_ta->scta_rcpts, sc_rcpt);
#if MTA_USE_TLS
if (sc_rcpt->scr_cnf != NULL) {
sm_conf_destroy(sc_rcpt->scr_cnf);
sc_rcpt->scr_cnf = NULL;
}
#endif
sm_rpool_free(sc_ta->scta_rpool, sc_rcpt);
sc_ta->scta_rcpts_tot--;
return SM_SUCCESS;
}
/*
** SC_RCPTS_FREE -- free an entire recipient list
**
** Parameters:
** sc_ta -- transaction
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sc_rcpts_free(sc_ta_P sc_ta)
{
sc_rcpts_P sc_rcpts;
sc_rcpt_P sc_rcpt, sc_rcpt_nxt;
SM_IS_SC_TA(sc_ta);
sc_rcpts = &sc_ta->scta_rcpts;
if (SC_RCPTS_EMPTY(sc_rcpts))
return SM_SUCCESS;
for (sc_rcpt = SC_RCPTS_FIRST(sc_rcpts); sc_rcpt != SC_RCPTS_END(sc_rcpts);
sc_rcpt = sc_rcpt_nxt)
{
sc_rcpt_nxt = SC_RCPTS_NEXT(sc_rcpt);
sc_rcpt_free(sc_ta, sc_rcpt);
}
SC_RCPTS_INIT(&sc_ta->scta_rcpts);
return SM_SUCCESS;
}
/*
** SC_TA_NEW -- create a new SMTPC transaction context,
** assign it to SMTPC session as current transaction.
**
** Parameters:
** psc_ta -- pointer to SMTPC transaction context (output)
** sc_sess -- SMTPC session context
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sc_ta_new(sc_ta_P *psc_ta, sc_sess_P sc_sess)
{
sm_ret_T ret;
sc_ta_P sc_ta;
SM_REQUIRE(psc_ta != NULL);
sc_ta = sm_zalloc(sizeof(*sc_ta));
if (NULL == sc_ta)
goto errnomem;
sc_ta->scta_sess = sc_sess;
sc_sess->scse_ta = sc_ta;
SC_RCPTS_INIT(&sc_ta->scta_rcpts);
/* more data? */
sc_ta->sm_magic = SM_SC_TA_MAGIC;
*psc_ta = sc_ta;
return SM_SUCCESS;
errnomem:
ret = sm_error_temp(SM_EM_SMTPC, ENOMEM);
sc_ta_free(sc_ta); /* overkill right now... */
return ret;
}
/*
** SC_TA_FREE -- free a SMTPC transaction context
**
** Parameters:
** sc_ta -- SMTPC transaction context
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sc_ta_free(sc_ta_P sc_ta)
{
sm_ret_T ret;
if (NULL == sc_ta)
return SM_SUCCESS;
SM_IS_SC_TA(sc_ta);
ret = sc_ta_clr(sc_ta);
sc_ta->sm_magic = SM_MAGIC_NULL;
SM_FREE(sc_ta);
return ret;
}
/*
** SC_TA_CLR -- clear a SMTPC transaction context (for reuse etc)
**
** Parameters:
** sc_ta -- SMTPC transaction context
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sc_ta_clr(sc_ta_P sc_ta)
{
if (NULL == sc_ta)
return SM_SUCCESS;
SM_IS_SC_TA(sc_ta);
/* free data inside transaction... */
(void) sc_rcpts_free(sc_ta);
(void) sc_mail_free(sc_ta);
SM_STR_FREE(sc_ta->scta_cdb_id);
SM_STR_FREE(sc_ta->scta_reply);
SM_STR_FREE(sc_ta->scta_b_msg);
/* closed in smtpc_ta() or sc_data() */
SM_ASSERT(NULL == sc_ta->scta_cdb_fp);
/* clean out all data */
sm_memzero(sc_ta, sizeof(*sc_ta));
/* initialize necessary data */
sc_ta->sm_magic = SM_SC_TA_MAGIC;
SC_RCPTS_INIT(&sc_ta->scta_rcpts);
sm_hdrmodl_free(&sc_ta->scta_hdrmodhd);
return SM_SUCCESS;
}
syntax highlighted by Code2HTML, v. 0.9.1