/* * 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: dadbh.c,v 1.39 2007/06/14 05:12:27 ca Exp $") #include "sm/types.h" #include "sm/assert.h" #include "sm/magic.h" #include "sm/str.h" #include "sm/time.h" #include "sm/mta.h" #include "sm/memops.h" #include "sm/qmgr.h" #include "sm/qmgr-int.h" #include "sm/dadb.h" #include "sm/io.h" #include "dadb.h" /* ** DADB_SET_LIMIT -- Set current maximum number of entries in DA DB ** The limit may change due to resource restrictions. ** ** Parameters: ** dadb_ctx -- DADB context ** entries_lim -- new limit ** locktype -- kind of locking ** ** Returns: ** usual sm_error code; SM_E_RANGE, (un)lock ** ** Side Effects: none on error (except if unlock fails) ** ** Locking: locks dadb_ctx if requested ** ** Last code review: 2005-03-17 00:35:27 ** Last code change: */ sm_ret_T dadb_set_limit(dadb_ctx_P dadb_ctx, size_t entries_lim, thr_lock_T locktype) { #undef SMFCT #define SMFCT "dadb_set_limit" sm_ret_T ret; int r; SM_IS_DADB(dadb_ctx); if (thr_lock_it(locktype)) { r = pthread_mutex_lock(&dadb_ctx->dadb_mutex); SM_LOCK_OK(r); if (r != 0) return sm_error_perm(SM_EM_DA, r); } /* ** Don't check against current number of entries as lower limit, ** the system will simple make sure that the new limit isn't ** exceeded, but it must not check against the new limit to detect ** "inconsistencies", entries_max is used for that purpose. */ if (entries_lim >= dadb_ctx->dadb_entries_max) ret = sm_error_perm(SM_EM_DA, SM_E_RANGE); else { dadb_ctx->dadb_entries_lim = entries_lim; ret = SM_SUCCESS; } if ((!sm_is_err(ret) && thr_unl_no_err(locktype)) || (sm_is_err(ret) && thr_unl_if_err(locktype))) { r = pthread_mutex_unlock(&dadb_ctx->dadb_mutex); SM_ASSERT(r == 0); if (r != 0 && sm_is_success(ret)) ret = sm_error_perm(SM_EM_DA, r); } return ret; } /* ** DADB_ENTRY_AVAIL -- Is there a free entry in DA DB? ** ** Parameters: ** dadb_ctx -- DADB context ** pda_avail -- (pointer to) how many entries are free? (output) ** pda_idle -- (pointer to) how many entries are idle? (output) ** locktype -- kind of locking ** ** Returns: ** SM_SUCCESS except for (un)lock errors ** ** Note: it might be useful to return counters instead of bool ** ** Side Effects: none on error ** ** Locking: locks dadb_ctx if requested ** ** Last code review: 2005-03-17 00:48:15 ** Last code change: 2006-02-22 00:25:40 */ sm_ret_T dadb_entry_avail(dadb_ctx_P dadb_ctx, uint *pda_avail, uint *pda_idle, thr_lock_T locktype) { #undef SMFCT #define SMFCT "dadb_entry_avail" int r; SM_IS_DADB(dadb_ctx); SM_REQUIRE(pda_avail != NULL); *pda_avail = 0; SM_REQUIRE(pda_idle != NULL); *pda_idle = 0; if (thr_lock_it(locktype)) { r = pthread_mutex_lock(&dadb_ctx->dadb_mutex); SM_LOCK_OK(r); if (r != 0) return sm_error_perm(SM_EM_DA, r); } if (DADB_IS_OK(dadb_ctx)) { *pda_avail = dadb_ctx->dadb_entries_cur - dadb_ctx->dadb_entries_lim; *pda_idle = dadb_ctx->dadb_entries_idle; } if (thr_unl_no_err(locktype)) { r = pthread_mutex_unlock(&dadb_ctx->dadb_mutex); SM_ASSERT(r == 0); if (r != 0) return sm_error_perm(SM_EM_DA, r); } return SM_SUCCESS; } /* ** DADB_ENTRY_GET -- get a free entry in DA DB (or allocate a new one) ** ** Parameters: ** dadb_ctx -- DADB context ** pdadb_entry -- pointer to DADB entry (output) ** pidx -- pointer to idx (output) ** locktype -- kind of locking ** ** Returns: ** usual sm_error code; ENOMEM, SM_E_FULL, ** ** Side Effects: none on error (except if unlock fails) ** ** Locking: locks dadb_ctx if requested ** *pdadb_entry will be locked (if successful) ** ** Last code review: 2005-03-17 00:55:16 ** Last code change: 2006-02-18 20:21:00 */ sm_ret_T dadb_entry_get(dadb_ctx_P dadb_ctx, dadb_entry_P *pdadb_entry, uint *pidx, thr_lock_T locktype) { #undef SMFCT #define SMFCT "dadb_entry_get" uint i; sm_ret_T ret; dadb_entry_P dadb_entry; int r; SM_IS_DADB(dadb_ctx); SM_REQUIRE(pdadb_entry != NULL); SM_REQUIRE(pidx != NULL); ret = sm_error_temp(SM_EM_DA, SM_E_FULL); if (thr_lock_it(locktype)) { r = pthread_mutex_lock(&dadb_ctx->dadb_mutex); SM_LOCK_OK(r); if (r != 0) return sm_error_perm(SM_EM_DA, r); } /* too many entries already? (get rid of goto for "nicer" structure?) */ if (dadb_ctx->dadb_entries_cur >= dadb_ctx->dadb_entries_lim) goto unl; if (!DADB_IS_OK(dadb_ctx)) goto unl; SM_ASSERT(dadb_ctx->dadb_entries != NULL); /* search for a free DA DB entry */ for (i = 0; i < dadb_ctx->dadb_entries_max; i++) { dadb_entry = (dadb_ctx->dadb_entries)[i]; if (NULL == dadb_entry) { dadb_entry = (dadb_entry_P) sm_zalloc(sizeof(*dadb_entry)); if (NULL == dadb_entry) { ret = sm_error_temp(SM_EM_DA, ENOMEM); goto unl; } #if DADB_CHECK dadb_entry->sm_magic = SM_DADBE_MAGIC; #endif #if DADBE_MUTEX r = pthread_mutex_init(&dadb_entry->dadbe_mutex, SM_PTHREAD_MUTEXATTR); if (r != 0) { ret = sm_error_perm(SM_EM_DA, r); goto unl; } #endif /* fill in more data?? */ (dadb_ctx->dadb_entries)[i] = dadb_entry; #if DADBE_MUTEX r = pthread_mutex_lock(&dadb_entry->dadbe_mutex); SM_LOCK_OK(r); if (r != 0) { ret = sm_error_perm(SM_EM_DA, r); goto unl; } #endif break; } else { #if DADBE_MUTEX r = pthread_mutex_trylock(&dadb_entry->dadbe_mutex); #else r = 0; #endif if (r == 0 && DADBE_IS_FREE(dadb_entry)) break; } } if (i < dadb_ctx->dadb_entries_max) { dadb_ctx->dadb_entries_cur++; #if DADB_STATS if (dadb_ctx->dadb_entries_cur > dadb_ctx->dadb_entries_max_used) dadb_ctx->dadb_entries_max_used = dadb_ctx->dadb_entries_cur; #endif /* DA_DB_STATS */ *pdadb_entry = dadb_entry; *pidx = i; ret = SM_SUCCESS; } unl: if ((!sm_is_err(ret) && thr_unl_no_err(locktype)) || (sm_is_err(ret) && thr_unl_if_err(locktype))) { r = pthread_mutex_unlock(&dadb_ctx->dadb_mutex); SM_ASSERT(r == 0); if (r != 0 && sm_is_success(ret)) ret = sm_error_perm(SM_EM_DA, r); } return ret; } /* ** DADB_SE_FIND_BY_IPV4 -- find a free session in DADB by IPv4 address ** HACK, see smX doc for proper index to access connection cache ** Note: this is NOT a connection cache as described in the doc; ** it is missing several data entries. ** Use a hash table, see connctl.c? ** ** Parameters: ** dadb_ctx -- DADB context ** ipv4 -- IPv4 address ** time_now -- current time ** pdadb_entry -- pointer to DA DB entry (output) ** ** Returns: ** >=0: number of entries found (that is not the actual number ** of entries for that IPv4 address as the algorithm ** stops as soon as it finds a free entry). ** usual sm_error code; only (un)lock errors ** ** Locking: locks entire dadb_ctx during operation, returns unlocked ** ** Last code review: 2005-03-17 00:59:40 ** Last code change: 2006-02-20 18:02:17 */ sm_ret_T dadb_se_find_by_ipv4(dadb_ctx_P dadb_ctx, ipv4_T ipv4, time_T time_now, dadb_entry_P *pdadb_entry) { #undef SMFCT #define SMFCT "dadb_se_find_by_ipv4" uint i; sm_ret_T ret; int r; dadb_entry_P dadb_entry; SM_IS_DADB(dadb_ctx); SM_REQUIRE(pdadb_entry != NULL); *pdadb_entry = NULL; /* do we really need to lock this?? */ r = pthread_mutex_lock(&dadb_ctx->dadb_mutex); SM_LOCK_OK(r); if (r != 0) return sm_error_perm(SM_EM_DA, r); SM_ASSERT(dadb_ctx->dadb_entries != NULL); ret = 0; for (i = 0; i < dadb_ctx->dadb_entries_max; i++) { dadb_entry = (dadb_ctx->dadb_entries)[i]; if (dadb_entry != NULL && DADBE_IS_ACTIVE(dadb_entry) && ipv4_addr_eq(dadb_entry->dadbe_srv_ipv4, ipv4)) { ++ret; /* use first free entry that is not too old */ if (*pdadb_entry == NULL && DADBE_IS_CONN(dadb_entry) && (0 == dadb_entry->dadbe_lastuse || dadb_entry->dadbe_lastuse >= time_now || time_now - dadb_entry->dadbe_lastuse <= SM_SESSION_TMO)) { SM_IS_DADBE(dadb_entry); *pdadb_entry = dadb_entry; break; } } } r = pthread_mutex_unlock(&dadb_ctx->dadb_mutex); SM_ASSERT(r == 0); if (r != 0 && sm_is_success(ret)) ret = sm_error_perm(SM_EM_DA, r); return ret; } #if 0 ///* //** DADB_ENTRY_FREE -- free a DA DB entry //** //** Parameters: //** dadb_ctx -- DADB context //** dadb_entry -- DADB entry //** locktype -- kind of locking //** //** Returns: //** usual sm_error code //*/ // //sm_ret_T //dadb_entry_free(dadb_ctx_P dadb_ctx, dadb_entry_P dadb_entry, thr_lock_T locktype) //{ // int r; // // if (NULL == dadb_entry) // return SM_SUCCESS; // SM_IS_DADB(dadb_ctx); // if (thr_lock_it(locktype)) // { // r = pthread_mutex_lock(&dadb_ctx->dadb_mutex); // SM_LOCK_OK(r); // if (r != 0) // return sm_error_perm(SM_EM_DA, r); // } // // /* remove it from array?? otherwise there's no need for locking dadb */ // sm_free(dadb_entry); // if ((!sm_is_err(ret) && thr_unl_no_err(locktype)) // || (sm_is_err(ret) && thr_unl_if_err(locktype))) // { // r = pthread_mutex_unlock(&dadb_ctx->dadb_mutex); // SM_ASSERT(r == 0); // if (r != 0 && sm_is_success(ret)) // ret = sm_error_perm(SM_EM_DA, r); // } // return SM_SUCCESS; //} #endif /* 0 */ #if 0 ///* //** DADB_ENTRY_NEW -- create new DA DB entry //** //** Parameters: //** dadb_ctx -- DADB context //** pdadb_entry -- pointer to DADB entry (output) //** locktype -- kind of locking //** //** Returns: //** usual sm_error code //*/ // //sm_ret_T //dadb_entry_new(dadb_ctx_P dadb_ctx, dadb_entry_P *pdadb_entry, thr_lock_T locktype) //{ // sm_ret_T ret; // int r; // dadb_entry_P dadb_entry; // // SM_IS_DADB(dadb_ctx); // if (thr_lock_it(locktype)) // { // r = pthread_mutex_lock(&dadb_ctx->dadb_mutex); // SM_LOCK_OK(r); // if (r != 0) // return sm_error_perm(SM_EM_DA, r); // } // if (dadb_ctx->dadb_entries_cur >= dadb_ctx->dadb_entries_max) // { // DADB_DPRINTF((smioerr, "dadb_entry_new: cur=%d, max=%d\n", dadb_ctx->dadb_entries_cur, dadb_ctx->dadb_entries_max)); // ret = sm_error_temp(SM_EM_DA, SM_E_FULL); // goto errunl; // } // dadb_entry = (dadb_entry_P) sm_zalloc(sizeof(*dadb_entry)); // if (NULL == dadb_entry) // goto enomem; //#if DADB_CHECK // /* set this early, otherwise the rest of the routines will fail */ // dadb_entry->sm_magic = SM_DADBE_MAGIC; //#endif /* DADB_CHECK */ // // /* fill in more data?? */ // // dadb_ctx->dadb_entries_cur++; // *pdadb_entry = dadb_entry; // if (thr_unl_no_err(locktype)) // { // r = pthread_mutex_unlock(&dadb_ctx->dadb_mutex); // SM_ASSERT(r == 0); // if (r != 0 && sm_is_success(ret)) // ret = sm_error_perm(SM_EM_DA, r); // } // return SM_SUCCESS; // // enomem: // ret = sm_error_temp(SM_EM_DA, ENOMEM); // // /* clean up...? nothing to do right now */ // errunl: // if (thr_unl_if_err(locktype)) // (void) pthread_mutex_unlock(&dadb_ctx->dadb_mutex); // return ret; //} #endif /* 0 */ #if 0 ///* //** DADB_ENTRY_UPDATE -- update DA DB entry //** //** Parameters: //** dadb_ctx -- DADB context //** sess_id -- session id from SMTPS //** ta_id -- transaction id from SMTPS //** dadb_entry -- entry //** //** Returns: //** usual sm_error code //** //** Locking: locks entire dadb_ctx during operation, returns unlocked //*/ // //sm_ret_T //dadb_entry_update(dadb_ctx_P dadb_ctx, sessta_id_P sess_id, sessta_id_P ta_id, // dadb_entry_P dadb_entry) //{ //#if 0 // sm_ret_T ret; //#endif /* 0 */ // int r; // // SM_IS_DADB(dadb_ctx); // r = pthread_mutex_lock(&dadb_ctx->dadb_mutex); // SM_LOCK_OK(r); // if (r != 0) // return sm_error_perm(SM_EM_DA, r); // //#if 0 // ret = bht2_add(dadb_ctx->dadb_bht, // sess_id, SMTP_STID_SIZE, // ta_id, SMTP_STID_SIZE, // dadb_entry, // &bht2e); // if (sm_is_err(ret)) // goto errunl; //#endif /* 0 */ // // dadb_ctx->dadb_entries_cur++; // r = pthread_mutex_unlock(&dadb_ctx->dadb_mutex); // SM_ASSERT(r == 0); // if (r != 0 && sm_is_success(ret)) // ret = sm_error_perm(SM_EM_DA, r); // return SM_SUCCESS; // //#if 0 // errunl: // r = pthread_mutex_unlock(&dadb_ctx->dadb_mutex); // return ret; //#endif /* 0 */ //} #endif /* 0 */