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