/*
* 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);
}
syntax highlighted by Code2HTML, v. 0.9.1