123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600 |
- /*
- * $Id$
- *
- * Copyright (C) 2001-2003 FhG Fokus
- *
- * This file is part of ser, a free SIP server.
- *
- * ser 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
- *
- * For a license to use the ser software under conditions
- * other than those described here, or to purchase support for this
- * software, please contact iptel.org by e-mail at the following addresses:
- * [email protected]
- *
- * ser 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * History:
- * ---------
- * 2003-03-11 changed to the new locking scheme: locking.h (andrei)
- * 2003-03-12 added replication mark and zombie state (nils)
- * 2004-06-07 updated to the new DB api (andrei)
- * 2004-08-23 hash function changed to process characters as unsigned
- * -> no negative results occur (jku)
- * 2005-02-25 incoming socket is saved in ucontact record (bogdan)
- * 2006-11-23 switched to better hash functions and fixed hash size (andrei)
- *
- */
- #include "udomain.h"
- #include <string.h>
- #include "../../mem/shm_mem.h"
- #include "../../dprint.h"
- #include "../../lib/srdb2/db.h"
- #include "../../ut.h"
- #include "../../parser/parse_param.h"
- #include "../../parser/parse_uri.h"
- #include "../../resolve.h"
- #include "../../socket_info.h"
- #include "ul_mod.h" /* usrloc module parameters */
- #include "notify.h"
- #include "reg_avps.h"
- #include "reg_avps_db.h"
- #include "utime.h"
- #include "../../hashes.h"
- /* #define HASH_STRING_OPTIMIZE */
- /*
- * Hash function
- */
- static inline int hash_func(udomain_t* _d, unsigned char* _s, int _l)
- {
- #ifdef HASH_STRING_OPTIMIZE
- return get_hash1_raw((char*)_s, _l) % UDOMAIN_HASH_SIZE;
- #else
- return get_hash1_raw2((char*)_s, _l) % UDOMAIN_HASH_SIZE;
- #endif
- }
- /*
- * Add a record to list of all records in a domain
- */
- static inline void udomain_add(udomain_t* _d, urecord_t* _r)
- {
- if (_d->d_ll.n == 0) {
- _d->d_ll.first = _r;
- _d->d_ll.last = _r;
- } else {
- _r->d_ll.prev = _d->d_ll.last;
- _d->d_ll.last->d_ll.next = _r;
- _d->d_ll.last = _r;
- }
- _d->d_ll.n++;
- }
- /*
- * Remove a record from list of all records in a domain
- */
- static inline void udomain_remove(udomain_t* _d, urecord_t* _r)
- {
- if (_d->d_ll.n == 0) return;
- if (_r->d_ll.prev) {
- _r->d_ll.prev->d_ll.next = _r->d_ll.next;
- } else {
- _d->d_ll.first = _r->d_ll.next;
- }
- if (_r->d_ll.next) {
- _r->d_ll.next->d_ll.prev = _r->d_ll.prev;
- } else {
- _d->d_ll.last = _r->d_ll.prev;
- }
- _r->d_ll.prev = _r->d_ll.next = 0;
- _d->d_ll.n--;
- }
- /*
- * Create a new domain structure
- * _n is pointer to str representing
- * name of the domain, the string is
- * not copied, it should point to str
- * structure stored in domain list
- */
- int new_udomain(str* _n, udomain_t** _d)
- {
- int i;
-
- /* 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)) {
- LOG(L_ERR, "new_udomain(): No memory left\n");
- return -1;
- }
- memset(*_d, 0, sizeof(udomain_t));
-
- (*_d)->table = (hslot_t*)shm_malloc(sizeof(hslot_t) * UDOMAIN_HASH_SIZE);
- if (!(*_d)->table) {
- LOG(L_ERR, "new_udomain(): No memory left 2\n");
- shm_free(*_d);
- return -2;
- }
- (*_d)->name = _n;
-
- for(i = 0; i < UDOMAIN_HASH_SIZE; i++) {
- if (init_slot(*_d, &((*_d)->table[i])) < 0) {
- LOG(L_ERR, "new_udomain(): Error while initializing hash table\n");
- shm_free((*_d)->table);
- shm_free(*_d);
- return -3;
- }
- }
- lock_init(&(*_d)->lock);
- (*_d)->users = 0;
- (*_d)->expired = 0;
-
- return 0;
- }
- /*
- * Free all memory allocated for
- * the domain
- */
- void free_udomain(udomain_t* _d)
- {
- int i;
-
- lock_udomain(_d);
- if (_d->table) {
- for(i = 0; i < UDOMAIN_HASH_SIZE; i++) {
- deinit_slot(_d->table + i);
- }
- shm_free(_d->table);
- }
- unlock_udomain(_d);
- lock_destroy(&_d->lock);/* destroy the lock (required for SYSV sems!)*/
- shm_free(_d);
- }
- /*
- * Just for debugging
- */
- void print_udomain(FILE* _f, udomain_t* _d)
- {
- struct urecord* r;
- fprintf(_f, "---Domain---\n");
- fprintf(_f, "name : '%.*s'\n", _d->name->len, ZSW(_d->name->s));
- fprintf(_f, "size : %d\n", UDOMAIN_HASH_SIZE);
- fprintf(_f, "table: %p\n", _d->table);
- fprintf(_f, "d_ll {\n");
- fprintf(_f, " n : %d\n", _d->d_ll.n);
- fprintf(_f, " first: %p\n", _d->d_ll.first);
- fprintf(_f, " last : %p\n", _d->d_ll.last);
- fprintf(_f, "}\n");
- /*fprintf(_f, "lock : %d\n", _d->lock); -- can be a structure --andrei*/
- if (_d->d_ll.n > 0) {
- fprintf(_f, "\n");
- r = _d->d_ll.first;
- while(r) {
- print_urecord(_f, r);
- r = r->d_ll.next;
- }
- fprintf(_f, "\n");
- }
- fprintf(_f, "---/Domain---\n");
- }
- static struct socket_info* find_socket(str* received)
- {
- struct sip_uri puri;
- param_hooks_t hooks;
- struct hostent* he;
- struct ip_addr ip;
- struct socket_info* si;
- param_t* params;
- unsigned short port;
- char* buf;
- int error;
- if (!received) return 0;
- si = 0;
- if (parse_uri(received->s, received->len, &puri) < 0) {
- LOG(L_ERR, "find_socket: Error while parsing received URI\n");
- return 0;
- }
-
- if (parse_params(&puri.params, CLASS_URI, &hooks, ¶ms) < 0) {
- LOG(L_ERR, "find_socket: Error while parsing received URI parameters\n");
- return 0;
- }
- if (!hooks.uri.dstip || !hooks.uri.dstip->body.s || !hooks.uri.dstip->body.len) goto end;
- buf = (char*)pkg_malloc(hooks.uri.dstip->body.len + 1);
- if (!buf) {
- LOG(L_ERR, "find_socket: No memory left\n");
- goto end;
- }
- memcpy(buf, hooks.uri.dstip->body.s, hooks.uri.dstip->body.len);
- buf[hooks.uri.dstip->body.len] = '\0';
- he = resolvehost(buf);
- if (he == 0) {
- LOG(L_ERR, "find_socket: Unable to resolve '%s'\n", buf);
- pkg_free(buf);
- goto end;
- }
- pkg_free(buf);
- if (hooks.uri.dstport && hooks.uri.dstport->body.s && hooks.uri.dstport->body.len) {
- port = str2s(hooks.uri.dstport->body.s, hooks.uri.dstport->body.len, &error);
- if (error != 0) {
- LOG(L_ERR, "find_socket: Unable to convert port number\n");
- goto end;
- }
- } else {
- port = 0;
- }
- hostent2ip_addr(&ip, he, 0);
- si = find_si(&ip, port, puri.proto);
- if (si == 0) {
- LOG(L_ERR, "find_socket: Unable to find socket, using the default one\n");
- goto end;
- }
-
- end:
- if (params) free_params(params);
- return si;
- }
- int preload_udomain(udomain_t* _d)
- {
- db_fld_t columns[] = {
- {.name = uid_col.s, .type = DB_STR},
- {.name = contact_col.s, .type = DB_STR},
- {.name = expires_col.s, .type = DB_DATETIME},
- {.name = q_col.s, .type = DB_DOUBLE},
- {.name = callid_col.s, .type = DB_STR},
- {.name = cseq_col.s, .type = DB_INT},
- {.name = flags_col.s, .type = DB_BITMAP},
- {.name = user_agent_col.s, .type = DB_STR},
- {.name = received_col.s, .type = DB_STR},
- {.name = instance_col.s, .type = DB_STR},
- {.name = aor_col.s, .type = DB_STR},
- {.name = server_id_col.s, .type = DB_INT},
- {.name = avp_column, .type = DB_STR}, /* Must be the last element in the array */
- {.name = NULL}
- };
- db_res_t* res = NULL;
- db_rec_t* rec;
- db_cmd_t* get_all = NULL;
- struct socket_info* sock;
- str callid, ua, instance, aor;
- str* receivedp;
- qvalue_t q;
- unsigned int flags;
- urecord_t* r;
- ucontact_t* c;
- get_all = db_cmd(DB_GET, db, _d->name->s, columns, NULL, NULL);
- if (get_all == NULL) {
- ERR("usrloc: Error while compiling DB_GET command\n");
- return -1;
- }
- if (db_setopt(get_all, "fetch_all", 0) < 0) {
- ERR("usrloc: Error while disabling 'fetch_all' database option\n");
- }
- if (db_exec(&res, get_all) < 0) goto error;
- rec = db_first(res);
- if (rec == NULL) {
- DBG("preload_udomain(): Table is empty\n");
- db_res_free(res);
- db_cmd_free(get_all);
- return 0;
- }
- lock_udomain(_d);
- get_act_time();
- for(; rec != NULL; rec = db_next(res)) {
- /* UID column must never be NULL */
- if (rec->fld[0].flags & DB_NULL) {
- LOG(L_CRIT, "preload_udomain: ERROR: bad uid "
- "record in table %.*s, skipping...\n",
- _d->name->len, _d->name->s);
- continue;
- }
- /* Contact column must never be NULL */
- if (rec->fld[1].flags & DB_NULL) {
- LOG(L_CRIT, "ERROR: Bad contact for uid %.*s in table %.*s, skipping\n",
- rec->fld[0].v.lstr.len, rec->fld[0].v.lstr.s,
- _d->name->len, _d->name->s);
- continue;
- }
- /* We only skip expired contacts if db_skip_delete is enabled. If
- * db_skip_delete is disabled then we must load expired contacts
- * in memory so that the timer can delete them later.
- */
- if (db_skip_delete && (rec->fld[2].v.time < act_time)) {
- DBG("preload_udomain: Skipping expired contact\n");
- continue;
- }
- q = double2q(rec->fld[3].v.dbl);
- if (rec->fld[4].flags & DB_NULL) {
- callid.s = NULL;
- callid.len = 0;
- } else {
- callid = rec->fld[4].v.lstr;
- }
- if (rec->fld[7].flags & DB_NULL) {
- ua.s = NULL;
- ua.len = 0;
- } else {
- ua = rec->fld[7].v.lstr;
- }
- if (rec->fld[8].flags & DB_NULL) {
- receivedp = 0;
- sock = 0;
- } else {
- receivedp = &rec->fld[8].v.lstr;
- sock = find_socket(receivedp);
- }
- if (rec->fld[9].flags & DB_NULL) {
- instance.s = NULL;
- instance.len = 0;
- } else {
- instance = rec->fld[9].v.lstr;
- }
- if (rec->fld[10].flags & DB_NULL) {
- aor.s = NULL;
- aor.len = 0;
- } else {
- aor = rec->fld[10].v.lstr;
- }
- if (get_urecord(_d, &rec->fld[0].v.lstr, &r) > 0) {
- if (mem_insert_urecord(_d, &rec->fld[0].v.lstr, &r) < 0) {
- LOG(L_ERR, "preload_udomain(): Can't create a record\n");
- unlock_udomain(_d);
- goto error;
- }
- }
- flags = rec->fld[6].v.bitmap;
- if (rec->fld[11].v.int4 != server_id) {
- /* FIXME: this should not be hardcoded here this way */
- /* This is a records from another SIP server instance, mark
- * it as in memory only because the other SIP server is responsible
- * for updating the record in database
- */
- flags |= FL_MEM;
- }
- if (mem_insert_ucontact(r, &aor, &rec->fld[1].v.lstr, rec->fld[2].v.int4,
- q, &callid, rec->fld[5].v.int4, flags, &c, &ua, receivedp,
- sock, &instance, rec->fld[11].v.int4) < 0) {
- LOG(L_ERR, "preload_udomain(): Error while inserting contact\n");
- unlock_udomain(_d);
- goto error;
- }
- if (use_reg_avps() && ((rec->fld[12].flags & DB_NULL) != DB_NULL)) {
- c->avps = deserialize_avps(&rec->fld[12].v.lstr);
-
- }
- /* We have to do this, because insert_ucontact sets state to CS_NEW
- * and we have the contact in the database already
- * we also store zombies in database so we have to restore
- * the correct state
- */
- c->state = CS_SYNC;
- }
- unlock_udomain(_d);
- db_res_free(res);
- db_cmd_free(get_all);
- return 0;
- error:
- if (res) db_res_free(res);
- if (get_all) db_cmd_free(get_all);
- return -1;
- }
- /*
- * Insert a new record into domain
- */
- int mem_insert_urecord(udomain_t* _d, str* _uid, struct urecord** _r)
- {
- int sl;
-
- if (new_urecord(_d->name, _uid, _r) < 0) {
- LOG(L_ERR, "insert_urecord(): Error while creating urecord\n");
- return -1;
- }
- sl = hash_func(_d, (unsigned char*)_uid->s, _uid->len);
- slot_add(&_d->table[sl], *_r);
- udomain_add(_d, *_r);
- _d->users++;
- return 0;
- }
- /*
- * Remove a record from domain
- */
- void mem_delete_urecord(udomain_t* _d, struct urecord* _r)
- {
- if (_r->watchers == 0) {
- udomain_remove(_d, _r);
- slot_rem(_r->slot, _r);
- free_urecord(_r);
- _d->users--; /* FIXME */
- }
-
- }
- int timer_udomain(udomain_t* _d)
- {
- struct urecord* ptr, *t;
- lock_udomain(_d);
- ptr = _d->d_ll.first;
- while(ptr) {
- if (timer_urecord(ptr) < 0) {
- LOG(L_ERR, "timer_udomain(): Error in timer_urecord\n");
- unlock_udomain(_d);
- return -1;
- }
-
- /* Remove the entire record
- * if it is empty
- */
- if (ptr->contacts == 0) {
- t = ptr;
- ptr = ptr->d_ll.next;
- mem_delete_urecord(_d, t);
- } else {
- ptr = ptr->d_ll.next;
- }
- }
-
- unlock_udomain(_d);
- /* process_del_list(_d->name); */
- /* process_ins_list(_d->name); */
- return 0;
- }
- /*
- * Get lock
- */
- void lock_udomain(udomain_t* _d)
- {
- lock_get(&_d->lock);
- cur_cmd = _d->db_cmd_idx;
- }
- /*
- * Release lock
- */
- void unlock_udomain(udomain_t* _d)
- {
- lock_release(&_d->lock);
- }
- /*
- * Create and insert a new record
- */
- int insert_urecord(udomain_t* _d, str* _uid, struct urecord** _r)
- {
- if (mem_insert_urecord(_d, _uid, _r) < 0) {
- LOG(L_ERR, "insert_urecord(): Error while inserting record\n");
- return -1;
- }
- return 0;
- }
- /*
- * Obtain a urecord pointer if the urecord exists in domain
- */
- int get_urecord(udomain_t* _d, str* _uid, struct urecord** _r)
- {
- int sl, i;
- urecord_t* r;
- sl = hash_func(_d, (unsigned char*)_uid->s, _uid->len);
- r = _d->table[sl].first;
- for(i = 0; i < _d->table[sl].n; i++) {
- if ((r->uid.len == _uid->len) && !memcmp(r->uid.s, _uid->s, _uid->len)) {
- *_r = r;
- return 0;
- }
- r = r->s_ll.next;
- }
- return 1; /* Nothing found */
- }
- /*
- * Delete a urecord from domain
- */
- int delete_urecord(udomain_t* _d, str* _uid)
- {
- struct ucontact* c, *t;
- struct urecord* r;
- if (get_urecord(_d, _uid, &r) > 0) {
- return 0;
- }
- c = r->contacts;
- while(c) {
- t = c;
- c = c->next;
- if (delete_ucontact(r, t) < 0) {
- LOG(L_ERR, "delete_urecord(): Error while deleting contact\n");
- return -1;
- }
- }
- release_urecord(r);
- return 0;
- }
|