You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kamailio/modules/ims_usrloc_pcscf/udomain.c

477 lines
11 KiB

/*
* $Id$
*
* Copyright (C) 2012 Smile Communications, jason.penton@smilecoms.com
* Copyright (C) 2012 Smile Communications, richard.good@smilecoms.com
*
* The initial version of this code was written by Dragos Vingarzan
* (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
* Fruanhofer Institute. It was and still is maintained in a separate
* branch of the original SER. We are therefore migrating it to
* Kamailio/SR and look forward to maintaining it from here on out.
* 2011/2012 Smile Communications, Pty. Ltd.
* ported/maintained/improved by
* Jason Penton (jason(dot)penton(at)smilecoms.com and
* Richard Good (richard(dot)good(at)smilecoms.com) as part of an
* effort to add full IMS support to Kamailio/SR using a new and
* improved architecture
*
* NB: Alot of this code was originally part of OpenIMSCore,
* FhG Fokus.
* Copyright (C) 2004-2006 FhG Fokus
* Thanks for great work! This is an effort to
* break apart the various CSCF functions into logically separate
* components. We hope this will drive wider use. We also feel
* that in this way the architecture is more complete and thereby easier
* to manage in the Kamailio/SR environment
*
* This file is part of Kamailio, a free SIP server.
*
* Kamailio is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version
*
* Kamailio is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "udomain.h"
#include <string.h>
#include "../../hashes.h"
#include "../../parser/parse_methods.h"
#include "../../mem/shm_mem.h"
#include "../../dprint.h"
#include "../../lib/srdb1/db.h"
#include "../../socket_info.h"
#include "../../ut.h"
#include "ul_mod.h" /* usrloc module parameters */
#include "usrloc.h"
#include "utime.h"
#include "usrloc.h"
#include "../../lib/ims/useful_defs.h"
#ifdef STATISTICS
static char *build_stat_name( str* domain, char *var_name)
{
int n;
char *s;
char *p;
n = domain->len + 1 + strlen(var_name) + 1;
s = (char*)shm_malloc( n );
if (s==0) {
LM_ERR("no more shm mem\n");
return 0;
}
memcpy( s, domain->s, domain->len);
p = s + domain->len;
*(p++) = '-';
memcpy( p , var_name, strlen(var_name));
p += strlen(var_name);
*(p++) = 0;
return s;
}
#endif
int new_udomain(str* _n, int _s, udomain_t** _d)
{
int i;
#ifdef STATISTICS
char *name;
#endif
/* Must be always in shared memory, since
* the cache is accessed from timer which
* lives in a separate process
*/
*_d = (udomain_t*)shm_malloc(sizeof(udomain_t));
if (!(*_d)) {
LM_ERR("new_udomain(): No memory left\n");
goto error0;
}
memset(*_d, 0, sizeof(udomain_t));
(*_d)->table = (hslot_t*)shm_malloc(sizeof(hslot_t) * _s);
if (!(*_d)->table) {
LM_ERR("no memory left 2\n");
goto error1;
}
(*_d)->name = _n;
for(i = 0; i < _s; i++) {
init_slot(*_d, &((*_d)->table[i]), i);
}
(*_d)->size = _s;
#ifdef STATISTICS
/* register the statistics */
if ( (name=build_stat_name(_n,"contacts"))==0 || register_stat("usrloc",
name, &(*_d)->contacts, STAT_NO_RESET|STAT_SHM_NAME)!=0 ) {
LM_ERR("failed to add stat variable\n");
goto error2;
}
if ( (name=build_stat_name(_n,"expires"))==0 || register_stat("usrloc",
name, &(*_d)->expired, STAT_SHM_NAME)!=0 ) {
LM_ERR("failed to add stat variable\n");
goto error2;
}
#endif
return 0;
#ifdef STATISTICS
error2:
shm_free((*_d)->table);
#endif
error1:
shm_free(*_d);
error0:
return -1;
}
void free_udomain(udomain_t* _d)
{
int i;
if (_d->table) {
for(i = 0; i < _d->size; i++) {
lock_ulslot(_d, i);
deinit_slot(_d->table + i);
unlock_ulslot(_d, i);
}
shm_free(_d->table);
}
shm_free(_d);
}
void print_udomain(FILE* _f, udomain_t* _d)
{
int i;
int max=0, slot=0, n=0;
struct pcontact* r;
fprintf(_f, "---Domain---\n");
fprintf(_f, "name : '%.*s'\n", _d->name->len, ZSW(_d->name->s));
fprintf(_f, "size : %d\n", _d->size);
fprintf(_f, "table: %p\n", _d->table);
/*fprintf(_f, "lock : %d\n", _d->lock); -- can be a structure --andrei*/
fprintf(_f, "\n");
for(i=0; i<_d->size; i++)
{
r = _d->table[i].first;
n += _d->table[i].n;
if(max<_d->table[i].n){
max= _d->table[i].n;
slot = i;
}
while(r) {
print_pcontact(_f, r);
r = r->next;
}
}
fprintf(_f, "\nMax slot: %d (%d/%d)\n", max, slot, n);
fprintf(_f, "\n---/Domain---\n");
}
inline int time2str(time_t _v, char* _s, int* _l)
{
struct tm* t;
int l;
if ((!_s) || (!_l) || (*_l < 2)) {
LM_ERR("Invalid parameter value\n");
return -1;
}
*_s++ = '\'';
/* Convert time_t structure to format accepted by the database */
t = localtime(&_v);
l = strftime(_s, *_l -1, "%Y-%m-%d %H:%M:%S", t);
if (l == 0) {
LM_ERR("Error during time conversion\n");
/* the value of _s is now unspecified */
_s = NULL;
_l = 0;
return -1;
}
*_l = l;
*(_s + l) = '\'';
*_l = l + 2;
return 0;
}
int mem_insert_pcontact(struct udomain* _d, str* _contact, struct pcontact_info* _ci, struct pcontact** _c){
int sl;
if (new_pcontact(_d->name, _contact, _ci, _c) < 0) {
LM_ERR("creating pcontact failed\n");
return -1;
}
sl = ((*_c)->aorhash) & (_d->size - 1);
slot_add(&_d->table[sl], *_c);
update_stat(_d->contacts, 1);
return 0;
}
void mem_delete_pcontact(udomain_t* _d, struct pcontact* _c)
{
slot_rem(_c->slot, _c);
free_pcontact(_c);
update_stat( _d->contacts, -1);
}
void mem_timer_udomain(udomain_t* _d)
{
struct pcontact* ptr;
int i;
for(i=0; i<_d->size; i++)
{
lock_ulslot(_d, i);
ptr = _d->table[i].first;
while(ptr) {
timer_pcontact(ptr);
/* Remove the entire record if it is empty */
//if (ptr->contacts == 0) {
// t = ptr;
// ptr = ptr->next;
// mem_delete_pcontact(_d, t);
//}//// else {
// ptr = ptr->next;
//}
ptr = ptr->next;
}
unlock_ulslot(_d, i);
}
}
void lock_udomain(udomain_t* _d, str* _aor)
{
unsigned int sl;
sl = core_hash(_aor, 0, _d->size);
#ifdef GEN_LOCK_T_PREFERED
lock_get(_d->table[sl].lock);
#else
ul_lock_idx(_d->table[sl].lockidx);
#endif
}
void unlock_udomain(udomain_t* _d, str* _aor)
{
unsigned int sl;
sl = core_hash(_aor, 0, _d->size);
#ifdef GEN_LOCK_T_PREFERED
lock_release(_d->table[sl].lock);
#else
ul_release_idx(_d->table[sl].lockidx);
#endif
}
void lock_ulslot(udomain_t* _d, int i)
{
#ifdef GEN_LOCK_T_PREFERED
lock_get(_d->table[i].lock);
#else
ul_lock_idx(_d->table[i].lockidx);
#endif
}
void unlock_ulslot(udomain_t* _d, int i)
{
#ifdef GEN_LOCK_T_PREFERED
lock_release(_d->table[i].lock);
#else
ul_release_idx(_d->table[i].lockidx);
#endif
}
int update_rx_regsession(struct udomain* _d, str* session_id, struct pcontact* _c) {
if (session_id->len > 0 && session_id->s) {
if (_c->rx_session_id.len > 0 && _c->rx_session_id.s) {
_c->rx_session_id.len = 0;
shm_free(_c->rx_session_id.s);
}
_c->rx_session_id.s = shm_malloc(session_id->len);
if (!_c->rx_session_id.s) {
LM_ERR("no more shm_mem\n");
return -1;
}
memcpy(_c->rx_session_id.s, session_id->s, session_id->len);
_c->rx_session_id.len = session_id->len;
} else {
return -1;
}
return 0;
}
int update_pcontact(struct udomain* _d, struct pcontact_info* _ci, struct pcontact* _c) //TODO: should prob move this to pcontact
{
int is_default = 1;
ppublic_t* ppublic_ptr;
int i;
_c->reg_state = _ci->reg_state;
if (_ci->expires > 0) {
_c->expires = _ci->expires;
}
if (_ci->num_service_routes > 0 && _ci->service_routes) {
//replace all existing service routes
if (_c->service_routes) { //remove old service routes
for (i=0; i<_c->num_service_routes; i++) {
if (_c->service_routes[i].s)
shm_free(_c->service_routes[i].s);
shm_free(_c->service_routes);
_c->service_routes=0;
_c->num_service_routes=0;
}
}
//now add the new service routes
if (_ci->num_service_routes > 0) {
_c->service_routes = shm_malloc(_ci->num_service_routes*sizeof(str));
if (!_c->service_routes) {
LM_ERR("no more shm mem trying to allocate [%ld bytes]\n", _ci->num_service_routes*sizeof(str));
goto out_of_memory;
} else {
for (i=0; i<_ci->num_service_routes; i++) {
STR_SHM_DUP(_c->service_routes[i], _ci->service_routes[i], "update_pcontact");
}
_c->num_service_routes = _ci->num_service_routes;
}
}
}
if (_ci->num_public_ids > 0 && _ci->public_ids) {
if (_c->head) {
LM_DBG("ppublic's already exist.... .not updating\n");
} else {
for (i = 0; i < _ci->num_public_ids; i++) {
if (i > 0)
is_default = 0; //only the first one is default - P-Associated-uri (first one is default)
if (new_ppublic(&_ci->public_ids[i], is_default, &ppublic_ptr) != 0) {
LM_ERR("unable to create new ppublic\n");
} else {
insert_ppublic(_c, ppublic_ptr);
}
}
}
}
// update received info:
if (_ci->received_host.len > 0) {
if (_c->received_host.s)
shm_free(_c->received_host.s);
STR_SHM_DUP(_c->received_host, _ci->received_host, "update_pcontact");
}
if (_ci->received_port > 0) _c->received_port = _ci->received_port;
if (_ci->received_proto > 0) _c->received_proto = _ci->received_proto;
//TODO: update path, etc
run_ul_callbacks(PCSCF_CONTACT_UPDATE, _c);
return 0;
out_of_memory:
return -1;
}
int insert_pcontact(struct udomain* _d, str* _contact, struct pcontact_info* _ci, struct pcontact** _c) {
if (mem_insert_pcontact(_d, _contact, _ci, _c)){
LM_ERR("inserting pcontact failed\n");
goto error;
}
if (exists_ulcb_type(PCSCF_CONTACT_INSERT)) {
run_ul_create_callbacks(*_c);
}
return 0;
error:
return -1;
}
int get_pcontact(udomain_t* _d, str* _contact, struct pcontact** _c) {
unsigned int sl, i, aorhash;
struct pcontact* c;
/* search in cache */
aorhash = core_hash(_contact, 0, 0);
sl = aorhash & (_d->size - 1);
c = _d->table[sl].first;
for (i = 0; i < _d->table[sl].n; i++) {
if ((c->aorhash == aorhash) && (c->aor.len == _contact->len)
&& !memcmp(c->aor.s, _contact->s, _contact->len)) {
*_c = c;
return 0;
}
c = c->next;
}
return 1; /* Nothing found */
}
int get_pcontact_by_src(udomain_t* _d, str * _host, unsigned short _port, unsigned short _proto, struct pcontact** _c) {
int i;
struct pcontact* c;
if (_d->table) {
for(i = 0; i < _d->size; i++) {
}
}
for(i=0; i<_d->size; i++)
{
c = _d->table[i].first;
while(c) {
// First check, if Proto and Port matches:
if ((c->reg_state == PCONTACT_REGISTERED) && (c->received_port == _port) && (c->received_proto == _proto)) {
// Then check the length:
if (c->received_host.len == _host->len) {
// Finally really compare the "received_host"
if (!memcmp(c->received_host.s, _host->s, _host->len)) {
*_c = c;
return 0;
}
}
}
c = c->next;
}
}
return 1; /* Nothing found */
}
int delete_pcontact(udomain_t* _d, str* _aor, struct pcontact* _c)
{
if (_c==0) {
if (get_pcontact(_d, _aor, &_c) > 0) {
return 0;
}
}
if (exists_ulcb_type(PCSCF_CONTACT_DELETE)) {
run_ul_callbacks(PCSCF_CONTACT_DELETE, _c);
}
mem_delete_pcontact(_d, _c);
return 0;
}