/* * Copyright (c) 2002-2005 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: qss_nseid.c,v 1.69 2007/10/16 04:19:36 ca Exp $") #include "sm/error.h" #include "sm/assert.h" #include "sm/memops.h" #include "sm/io.h" #include "sm/rcb.h" #include "sm/qmgr.h" #include "sm/qmgr-int.h" #include "sm/reccom.h" #include "sm/ssocc.h" #include "qmgr.h" #include "log.h" /* ** QM_SS_NSEID -- decide whether to accept NSEID, put qss_sess into IQDB ** ** Parameters: ** qss_ctx -- QMGR/SMTPS context ** qss_sess -- session context ** ** Returns: ** <0: usual sm_error code (won't happen in this impl.) ** >=0: acceptance/rejection code ** ** Side Effects: ** if IQDB is full qss_control() is called. ** ** Last code review: 2003-10-22 16:59:20, see below ** Last code change: 2005-03-16 00:53:46 */ sm_ret_T qm_ss_nseid(qss_ctx_P qss_ctx, qss_sess_P qss_sess) { sm_ret_T ret, rate; uint limit; int r; uint fct_state; qss_sess_P rqss_sess; ssocc_entry_P ssocc_entry; ssocc_ctx_P ssocc_ctx; sm_str_P str, tag, rhs; #define FST_LOCKED 0x01 /* ssocc_entry is locked */ #define FST_INCR 0x02 /* ssocce_open_se has been increased */ SM_IS_QSS_CTX(qss_ctx); SM_IS_QS_SE(qss_sess); rqss_sess = NULL; rhs = str = tag = NULL; fct_state = 0; /* do some checks here: is it ok to accept this session? */ /* XXX this should be in some other function? (ssocc_new_se()) */ ssocc_entry = NULL; ssocc_ctx = qss_ctx->qss_qmgr_ctx->qmgr_ssocc_ctx; ret = ssocc_entry_find(ssocc_ctx, qss_sess->qsess_client.s_addr, &ssocc_entry, THR_LOCK_IT); SM_SET_FLAG(fct_state, FST_LOCKED); rate = sm_connctl(ssocc_ctx->ssocc_cctx, qss_sess->qsess_client, time(NULLT)); if (sm_is_err(ret)) { /* add it... */ ret = ssocc_entry_new(ssocc_ctx, qss_sess->qsess_client.s_addr, &ssocc_entry, THR_UNL_IF_ERR); if (sm_is_err(ret)) { SM_CLR_FLAG(fct_state, FST_LOCKED); QM_LEV_DPRINTFC(QDC_S2Q, 1, (QM_DEBFP, "sev=ERROR, func=qm_ss_nseid, ss_sess=%s, ssocc_entry=failed, ret=%r\n", qss_sess->qsses_id, ret)); goto reject; } #if 0 /* TEST */ ret = sm_error_temp(SM_EM_DA, SM_E_FULL); goto reject; #endif /* 0 */ } QM_LEV_DPRINTFC(QDC_S2Q, 5, (QM_DEBFP, "func=qm_ss_nseid, ss_sess=%s, ipv4=%A, open_se=%d, rate=%d\n", qss_sess->qsses_id, (ipv4_T) qss_sess->qsess_client.s_addr, ssocc_entry->ssocce_open_se, rate)); str = sm_str_new(NULL, 10, 16); /* check size */ tag = sm_str_new(NULL, 8, 16); /* check size */ rhs = sm_str_new(NULL, 14, 16); /* check size */ limit = qss_ctx->qss_qmgr_ctx->qmgr_cnf.q_cnf_max_open_se; if (qss_ctx->qss_qmgr_ctx->qmgr_conf_map != NULL) { if (str != NULL && tag != NULL && rhs != NULL) { /* incoming connections max */ #define ICMTAG "icm:" sm_str_clr(tag); sm_str_clr(str); if (sm_str_scat(tag, ICMTAG) == SM_SUCCESS && sm_inet_inaddr2str(qss_sess->qsess_client, str) == SM_SUCCESS && sm_map_lookup_ip(qss_ctx->qss_qmgr_ctx->qmgr_conf_map, str, tag, SMMAP_LFL_SUBNETS|SMMAP_LFL_TAG, rhs) == SM_SUCCESS && sm_str_getlen(rhs) > 0) { ulong v; errno = 0; v = strtoul((char *) sm_str_getdata(rhs), NULL, 0); if (v != ULONG_MAX && errno != ERANGE) limit = (uint) v; } QM_LEV_DPRINTFC(QDC_S2Q, 4, (QM_DEBFP, "func=qm_ss_nseid, ss_sess=%s, ipv4=%A, %slookup=%r, rhs=%S, open_se_max=%d\n", qss_sess->qsses_id, (ipv4_T) qss_sess->qsess_client.s_addr, ICMTAG, ret, rhs, limit)); } } if (ssocc_entry->ssocce_open_se >= limit) { ++ssocc_entry->ssocce_open_se_exc; sm_log_write(qss_ctx->qss_qmgr_ctx->qmgr_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_INFO, 10, "sev=INFO, func=qm_ss_nseid, ss_sess=%s, ipv4=%A, open_se=%d, max=%u, status=exceeded, times=%u" , qss_sess->qsses_id , (ipv4_T) qss_sess->qsess_client.s_addr , ssocc_entry->ssocce_open_se, limit , ssocc_entry->ssocce_open_se_exc); ret = SMTP_R_SSD|SMTP_R_QUICK; /* Better error code?? */ goto errunl; } /* Look up individual connection rate. */ limit = qss_ctx->qss_qmgr_ctx->qmgr_cnf.q_cnf_max_conn_rate; if (qss_ctx->qss_qmgr_ctx->qmgr_conf_map != NULL) { if (str != NULL && tag != NULL && rhs != NULL) { #define ICRTAG "icr:" sm_str_clr(tag); sm_str_clr(str); if (sm_str_scat(tag, ICRTAG) == SM_SUCCESS && sm_inet_inaddr2str(qss_sess->qsess_client, str) == SM_SUCCESS && sm_map_lookup_ip(qss_ctx->qss_qmgr_ctx->qmgr_conf_map, str, tag, SMMAP_LFL_SUBNETS|SMMAP_LFL_TAG, rhs) == SM_SUCCESS && sm_str_getlen(rhs) > 0) { ulong v; errno = 0; v = strtoul((char *) sm_str_getdata(rhs), NULL, 0); if (v != ULONG_MAX && errno != ERANGE) limit = (uint) v; } QM_LEV_DPRINTFC(QDC_S2Q, 4, (QM_DEBFP, "func=qm_ss_nseid, ss_sess=%s, ipv4=%A, %slookup=%r, rhs=%S, icr_max=%d\n", qss_sess->qsses_id, (ipv4_T) qss_sess->qsess_client.s_addr, ICRTAG, ret, rhs, limit)); } } else QM_LEV_DPRINTFC(QDC_S2Q, 4, (QM_DEBFP, "conf_map=%p\n", qss_ctx->qss_qmgr_ctx->qmgr_conf_map)); SM_STR_FREE(str); SM_STR_FREE(tag); SM_STR_FREE(rhs); /* connection rate control */ if (rate > 0 && (uint)rate > limit) { sm_log_write(qss_ctx->qss_qmgr_ctx->qmgr_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_INFO, 10, "sev=INFO, func=qm_ss_nseid, ss_sess=%s, ipv4=%A, rate=%d, max=%u, status=exceeded" , qss_sess->qsses_id , (ipv4_T) qss_sess->qsess_client.s_addr , rate, limit); ret = SMTP_R_SSD|SMTP_R_QUICK; /* Better error code?? */ goto errunl; } ++ssocc_entry->ssocce_open_se; SM_SET_FLAG(fct_state, FST_INCR); r = pthread_mutex_unlock(&ssocc_ctx->ssocc_mutex); if (r != 0) { QM_LEV_DPRINTFC(QDC_S2Q, 0, (QM_DEBFP, "sev=ERROR, func=qm_ss_nseid, unlock(ssocc_ctx)=%d\n", r)); } else SM_CLR_FLAG(fct_state, FST_LOCKED); ret = iqdb_session_new(qss_ctx->qss_qmgr_ctx->qmgr_iqdb, qss_sess->qsses_id, SMTP_STID_SIZE, qss_sess, (void **) &rqss_sess, THR_LOCK_UNLOCK); if (sm_is_err(ret)) { QM_LEV_DPRINTFC(QDC_S2Q, 2, (QM_DEBFP, "sev=WARN, func=qm_ss_nseid, ss_sess=%s, iqdb_session_new=%r\n", qss_sess->qsses_id, ret)); if (sm_error_value(ret) == SM_E_FULL || sm_error_value(ret) == ENOMEM) (void) qss_control(qss_ctx, QMGR_THROTTLE, 100, QMGR_RFL_IQD_I, THR_LOCK_UNLOCK); goto reject; } ++qss_ctx->qss_cur_session; #if QMGR_STATS if (qss_ctx->qss_cur_session > qss_ctx->qss_max_session) { qss_ctx->qss_max_session = qss_ctx->qss_cur_session; QM_LEV_DPRINTFC(QDC_S2Q, 5, (QM_DEBFP, "func=qm_ss_nseid, id=%d, max_sess=%d\n", qss_ctx->qss_id, qss_ctx->qss_max_session)); } #endif /* QMGR_STATS */ return SMTP_R_OK; reject: /* more appropriate error code?? */ ret = SMTP_R_SSD|SMTP_R_QUICK; errunl: if ((ret & ~SMTP_R_QUICK) == SMTP_R_SSD) { if (ssocc_entry != NULL) { if (!SM_IS_FLAG(fct_state, FST_LOCKED)) { r = pthread_mutex_lock(&ssocc_ctx->ssocc_mutex); SM_LOCK_OK(r); if (r != 0) { /* OOOPS */ QM_LEV_DPRINTFC(QDC_S2Q, 0, (QM_DEBFP, "sev=ERROR, func=qm_ss_nseid, ss_sess=%s, lock=%d\n", qss_sess->qsses_id, r)); goto failed; } SM_SET_FLAG(fct_state, FST_LOCKED); } if (SM_IS_FLAG(fct_state, FST_INCR) && ssocc_entry->ssocce_open_se > 0) { --ssocc_entry->ssocce_open_se; SM_CLR_FLAG(fct_state, FST_INCR); } if (ssocc_entry->ssocce_open_se == 0) (void) ssocc_entry_free(ssocc_ctx, ssocc_entry, THR_NO_LOCK); } } if (SM_IS_FLAG(fct_state, FST_LOCKED)) { r = pthread_mutex_unlock(&ssocc_ctx->ssocc_mutex); if (r != 0) { QM_LEV_DPRINTFC(QDC_S2Q, 0, (QM_DEBFP, "sev=ERROR, func=qm_ss_nseid, unlock(ssocc_ctx)=%d\n", r)); } } failed: #if 0 /* ** No cleanup needed. iqdb_session_new() does not add the entry ** if there is a problem. if more functions are called above ** after iqdb_session_new() then iqdb_session_rm() would be ** required [if (rqss_sess != NULL)] ** ** What about ssocc? If the session isn't accepted, shouldn't ** it be removed from the open session cache? */ #else /* 0 */ SM_ASSERT(rqss_sess == NULL); #endif /* 0 */ return ret; } /* ** QM_2SS_NSEID -- Reply to NSEID, put data into RCB entry ** ** Parameters: ** qss_ctx -- QMGR/SMTPS context ** rcbe -- RCB entry (must be open for encoding) ** sessid -- session id ** status -- session status (accepted, rejected, ...) ** ** Returns: ** usual sm_error code ** ** Last code review: 2003-10-22 16:59:20 */ sm_ret_T qm_2ss_nseid(qss_ctx_P qss_ctx, sm_rcbe_P rcbe, uchar *sessid, int status) { sm_rcb_P rcb; rcb = &rcbe->rcbe_rcb; return sm_rcb_putv(rcb, RCB_PUTV_FIRST, SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT, SM_RCBV_INT, RT_Q2S_ID, qss_ctx->qss_id, SM_RCBV_BUF, RT_Q2S_SEID, sessid, SMTP_STID_SIZE, SM_RCBV_INT, RT_Q2S_STAT, (uint32_t) status, SM_RCBV_END); }