/* * 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): 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 */