/* * Copyright (c) 2004, 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: sockmap.c,v 1.18 2007/01/22 17:25:06 ca Exp $") #include "sm/assert.h" #include "sm/error.h" #include "sm/memops.h" #include "sm/heap.h" #include "map.h" #include "sm/map.h" #include "sm/maps.h" #include "sm/mapc.h" #include "sm/mapclasses.h" #include "sockmap.h" static sm_map_alloc_F sm_sockmap_alloc; static sm_map_free_F sm_sockmap_free; static sm_map_locate_F sm_sockmap_locate; static sm_map_first_F sm_sockmap_first; static sm_map_next_F sm_sockmap_next; /* --------- socket map abstraction layer ------ */ /* ** SM_SOCKMAP_DESTROY -- destroy map ** XXX more parameters... ** ** Parameters: ** map -- map ** flags -- flags ** ** Returns: ** usual sm_error code */ static sm_ret_T sm_sockmap_destroy(sm_map_P map, uint32_t flags) { sm_ret_T ret; sm_mapc_P mapc; sm_sockmap_P db; SM_IS_MAP(map); mapc = map->sm_map_class; SM_IS_MAPC(mapc); ret = SM_SUCCESS; db = (sm_sockmap_P) map->sm_map_db; if (NULL == db) return sm_error_perm(SM_EM_MAP, ENOENT); sockmap_destroy(&db); map->sm_map_db = NULL; return ret; } /* ** SM_SOCKMAP_SETOPT -- set options for map ** ** Parameters: ** map -- map ** ap -- options ** ** Returns: ** usual sm_error code */ static sm_ret_T sm_sockmap_setopt(sm_map_P map, va_list ap) { sm_ret_T ret; uint k, u; sm_sockmap_P db; SM_IS_MAP(map); db = (sm_sockmap_P) map->sm_map_db; if (NULL == db) return sm_error_perm(SM_EM_MAP, ENOENT); ret = SM_SUCCESS; for (;;) { k = va_arg(ap, uint); if (SMPO_END == k) break; switch (k) { case SMPO_PORT: u = va_arg(ap, int); db->sockmap_port = (short) u; break; case SMPO_IPV4: u = va_arg(ap, ipv4_T); db->sockmap_ipv4 = u; break; case SMPO_TMOUT: db->sockmap_tmout = va_arg(ap, sm_intvl_T); break; case SMPO_SOCKPATH: db->sockmap_path = va_arg(ap, char *); break; default: /* silently ignore bogus options? */ break; } } return ret; } /* ** SM_SOCKMAP_GETOPT -- get options for map ** ** Parameters: ** map -- map ** which -- which option? ** valp -- pointer to place where result should be stored ** ** Returns: ** usual sm_error code */ static sm_ret_T sm_sockmap_getopt(sm_map_P map, int which, void *valp) { sm_ret_T ret; sm_sockmap_P db; SM_IS_MAP(map); db = (sm_sockmap_P) map->sm_map_db; if (NULL == db) return sm_error_perm(SM_EM_MAP, SM_E_NOMAP); /* ... */ ret = SM_SUCCESS; return ret; } /* ** SM_SOCKMAP_CLOSE -- close map ** XXX more parameters... ** ** Parameters: ** map -- map ** flags -- flags ** ** Returns: ** usual sm_error code */ static sm_ret_T sm_sockmap_close(sm_map_P map, uint32_t flags) { sm_sockmap_P db; SM_IS_MAP(map); db = map->sm_map_db; if (NULL == db) return sm_error_perm(SM_EM_MAP, SM_E_NOMAP); SM_REQUIRE(db != NULL); return sockmap_close(db); } /* ** SM_SOCKMAP_CREATE -- create map ** ** Parameters: ** mapc -- map context ** type -- type of map ** flags -- flags for map ** map -- map ** ** Returns: ** usual sm_error code */ static sm_ret_T sm_sockmap_create(sm_mapc_P mapc, const sm_cstr_P type, uint32_t flags, sm_map_P map) { sm_ret_T ret; sm_sockmap_P db; SM_IS_MAPC(mapc); SM_REQUIRE(map != NULL); db = NULL; ret = sockmap_new(&db); if (sm_is_success(ret)) { map->sm_map_db = db; map->sm_map_caps = SMMAP_CAPS_LTALL; } return ret; } /* ** SM_SOCKMAP_OPEN -- open map ** ** Parameters: ** mapc -- map context ** type -- type of map (currently ignored) ** flags -- flags for map (currently ignored) ** name -- name of map ** mode -- open mode (currently ignored) ** map -- map ** ** Returns: ** usual sm_error code */ static sm_ret_T sm_sockmap_open(sm_mapc_P mapc, const sm_cstr_P type, uint32_t flags, const char *name, int mode, sm_map_P map, va_list ap) { sm_ret_T ret; uint k, u; sm_sockmap_P db; SM_IS_MAPC(mapc); SM_REQUIRE(map != NULL); db = map->sm_map_db; SM_REQUIRE(db != NULL); db->sockmap_name = name; for (;;) { k = va_arg(ap, uint); if (SMPO_END == k) break; switch (k) { case SMPO_SOCKPATH: db->sockmap_path = va_arg(ap, char *); break; case SMPO_PORT: u = va_arg(ap, int); db->sockmap_port = (short) u; break; case SMPO_IPV4: u = va_arg(ap, ipv4_T); db->sockmap_ipv4 = u; break; default: /* silently ignore bogus options? */ break; } } ret = sockmap_open(db); if (sm_is_err(ret)) goto error; return SM_SUCCESS; error: if (db != NULL) { (void) sockmap_destroy(&db); db = NULL; } return ret; } /* ** SM_SOCKMAP_REOPEN -- reopen map ** ** Parameters: ** map -- map ** flags -- flags for map ** ** Returns: ** usual sm_error code */ static sm_ret_T sm_sockmap_reopen(sm_map_P map, uint32_t flags) { sm_ret_T ret; sm_sockmap_P db; SM_REQUIRE(map != NULL); db = map->sm_map_db; SM_REQUIRE(db != NULL); SMMAP_CLR_FL(map, SMMAP_FL_OPEN); SMMAP_SET_FL(map, SMMAP_FL_CLOSING); ret = sockmap_close(db); SMMAP_CLR_FL(map, SMMAP_FL_CLOSING); SMMAP_SET_FL(map, SMMAP_FL_CLOSED); ret = sockmap_open(db); if (sm_is_err(ret)) goto error; SMMAP_SET_FL(map, SMMAP_FL_OPEN); SMMAP_CLR_FL(map, SMMAP_FL_CLOSED); return SM_SUCCESS; error: /* cleanup? */ return ret; } /* ** SM_SOCKMAP_LOOKUP -- lookup a key in SOCKMAP, return data if found ** ** Parameters: ** map -- map context ** flags -- flags ** key -- key ** data -- data (output) ** ** Returns: ** usual sm_error code */ static sm_ret_T sm_sockmap_lookup(sm_map_P map, uint32_t flags, sm_map_key_P key, sm_map_data_P data) { sm_ret_T ret; sm_mapc_P mapc; sm_sockmap_P db; SM_IS_MAP(map); SM_IS_KEY(key); SM_IS_DATA(data); mapc = map->sm_map_class; SM_IS_MAPC(mapc); ret = SM_SUCCESS; db = (sm_sockmap_P) map->sm_map_db; if (NULL == db) return sm_error_perm(SM_EM_MAP, ENOENT); /* XXX */ if (!SMMAP_IS_FL(map, SMMAP_FL_OPEN)) { /* map closed but can be reopened? */ if (mapc->sm_mapc_reopenf != NULL) ret = mapc->sm_mapc_reopenf(map, 0); else ret = sm_error_perm(SM_EM_MAP, SM_E_CLOSEDMAP); if (sm_is_err(ret)) return ret; } /* XXX WARNING: changes key inplace! */ if (SM_IS_FLAG(flags, SMMAP_FL_LWR_KEY)) sm_str2lower(key); ret = sockmap_lookup(db, key, data); /* this doesn't look right: violation of abstraction... */ if (sm_is_err(ret) && NULL == db->sockmap_fp) { SMMAP_CLR_FL(map, SMMAP_FL_OPEN); SMMAP_SET_FL(map, SMMAP_FL_CLOSED); } return ret; } #if 0 /* ** SM_SOCKMAP_ADD -- add key/data to SOCKMAP ** ** Parameters: ** map -- map context ** key -- key ** data -- data ** flags -- flags ** ** Returns: ** usual sm_error code */ static sm_ret_T sm_sockmap_add(sm_map_P map, sm_map_key_P key, sm_map_data_P data, uint flags) { return sm_error_perm(SM_EM_MAP, EINVAL); } /* ** SM_SOCKMAP_RM -- remove key/data from SOCKMAP ** ** Parameters: ** map -- map context ** key -- key ** ** Returns: ** usual sm_error code */ static sm_ret_T sm_sockmap_rm(sm_map_P map, sm_map_key_P key) { return sm_error_perm(SM_EM_MAP, EINVAL); } #endif /* 0 */ /* ** SM_SOCKMAP_CLASS_CREATE -- create SOCKMAP map class ** ** Parameters: ** maps -- map system context ** ** Returns: ** usual sm_error code */ sm_ret_T sm_sockmap_class_create(sm_maps_P maps) { sm_ret_T ret; sm_mapc_P mapc; sm_cstr_P htype; #define SOCKMAP_TYPE "socket" ret = SM_SUCCESS; mapc = NULL; htype = sm_cstr_scpyn0((const uchar *)SOCKMAP_TYPE, strlen(SOCKMAP_TYPE)); if (NULL == htype) goto error; ret = sm_mapc_create(maps, htype, SMMAPC_FL_LCK_FULL, sm_sockmap_create, sm_sockmap_open, sm_sockmap_close, sm_sockmap_reopen, sm_sockmap_destroy, NULL /*sm_sockmap_add*/, NULL /*sm_sockmap_rm*/, sm_sockmap_alloc, sm_sockmap_free, sm_sockmap_lookup, sm_sockmap_locate, sm_sockmap_first, sm_sockmap_next, sm_sockmap_setopt, sm_sockmap_getopt, &mapc); SM_CSTR_FREE(htype); return ret; error: if (SM_SUCCESS == ret) ret = sm_error_temp(SM_EM_MAP, ENOMEM); /* cleanup mapc? */ return ret; }