1 /*
2 * $Id$
3 *
4 * Copyright (C) 2012 Smile Communications, jason.penton@smilecoms.com
5 * Copyright (C) 2012 Smile Communications, richard.good@smilecoms.com
6 *
7 * The initial version of this code was written by Dragos Vingarzan
8 * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
9 * Fruanhofer Institute. It was and still is maintained in a separate
10 * branch of the original SER. We are therefore migrating it to
11 * Kamailio/SR and look forward to maintaining it from here on out.
12 * 2011/2012 Smile Communications, Pty. Ltd.
13 * ported/maintained/improved by
14 * Jason Penton (jason(dot)penton(at)smilecoms.com and
15 * Richard Good (richard(dot)good(at)smilecoms.com) as part of an
16 * effort to add full IMS support to Kamailio/SR using a new and
17 * improved architecture
18 *
19 * NB: Alot of this code was originally part of OpenIMSCore,
20 * FhG Fokus.
21 * Copyright (C) 2004-2006 FhG Fokus
22 * Thanks for great work! This is an effort to
23 * break apart the various CSCF functions into logically separate
24 * components. We hope this will drive wider use. We also feel
25 * that in this way the architecture is more complete and thereby easier
26 * to manage in the Kamailio/SR environment
27 *
28 * This file is part of Kamailio, a free SIP server.
29 *
30 * Kamailio is free software; you can redistribute it and/or modify
31 * it under the terms of the GNU General Public License as published by
32 * the Free Software Foundation; either version 2 of the License, or
33 * (at your option) any later version
34 *
35 * Kamailio is distributed in the hope that it will be useful,
36 * but WITHOUT ANY WARRANTY; without even the implied warranty of
37 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
38 * GNU General Public License for more details.
39 *
40 * You should have received a copy of the GNU General Public License
41 * along with this program; if not, write to the Free Software
42 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
43 *
44 *
45
46 *! \file
47 * \brief USRLOC - Userloc domain handling functions
48 * \ingroup usrloc
49 *
50 * - Module: \ref usrloc
51 */
52
53 #include "udomain.h"
54 #include <string.h>
55 #include "../../core/hashes.h"
56 #include "../../core/parser/parse_methods.h"
57 #include "../../core/mem/shm_mem.h"
58 #include "../../core/dprint.h"
59 #include "../../lib/srdb1/db.h"
60 #include "../../core/socket_info.h"
61 #include "../../core/ut.h"
62 #include "../../core/counters.h"
63 #include "ims_usrloc_scscf_mod.h" /* usrloc module parameters */
64 #include "usrloc.h"
65 #include "utime.h"
66 #include "usrloc.h"
67 #include "bin_utils.h"
68 #include "usrloc_db.h"
69 #include "contact_hslot.h"
70 #include "ul_scscf_stats.h"
71 #include "hslot_sp.h"
72 #include "dlist.h"
73
74 extern int unreg_validity;
75 extern int db_mode;
76 extern struct contact_list* contact_list;
77 extern struct ims_subscription_list* ims_subscription_list;
78 extern int subs_hash_size;
79
80 extern int contact_delete_delay;
81
82 /*!
83 * \brief Create a new domain structure
84 * \param _n is pointer to str representing name of the domain, the string is
85 * not copied, it should point to str structure stored in domain list
86 * \param _s is hash table size
87 * \param _d new created domain
88 * \return 0 on success, -1 on failure
89 */
new_udomain(str * _n,int _s,udomain_t ** _d)90 int new_udomain(str* _n, int _s, udomain_t** _d) {
91 int i;
92
93 /* Must be always in shared memory, since
94 * the cache is accessed from timer which
95 * lives in a separate process
96 */
97 *_d = (udomain_t*) shm_malloc(sizeof (udomain_t));
98 if (!(*_d)) {
99 LM_ERR("new_udomain(): No memory left\n");
100 goto error0;
101 }
102 memset(*_d, 0, sizeof (udomain_t));
103
104 (*_d)->table = (hslot_t*) shm_malloc(sizeof (hslot_t) * _s);
105 if (!(*_d)->table) {
106 LM_ERR("no memory left 2\n");
107 goto error1;
108 }
109
110 (*_d)->name = _n;
111
112 for (i = 0; i < _s; i++) {
113 init_slot(*_d, &((*_d)->table[i]), i);
114 }
115
116 (*_d)->size = _s;
117
118 return 0;
119
120 error1:
121 shm_free(*_d);
122 error0:
123 return -1;
124 }
125
126 /*!
127 * \brief Free all memory allocated for the domain
128 * \param _d freed domain
129 */
free_udomain(udomain_t * _d)130 void free_udomain(udomain_t* _d) {
131 int i;
132
133 if (_d->table) {
134 for (i = 0; i < _d->size; i++) {
135 lock_ulslot(_d, i);
136 deinit_slot(_d->table + i);
137 unlock_ulslot(_d, i);
138 }
139 shm_free(_d->table);
140 }
141 shm_free(_d);
142 }
143
144 /*!
145 * \brief Returns a static dummy impurecord for temporary usage
146 * \param _d domain (needed for the name)
147 * \param _aor address of record
148 * \param _r new created urecord
149 */
get_static_impurecord(udomain_t * _d,str * _aor,struct impurecord ** _r)150 static inline void get_static_impurecord(udomain_t* _d, str* _aor, struct impurecord** _r) {
151 static struct impurecord r;
152
153 memset(&r, 0, sizeof (struct impurecord));
154 r.public_identity = *_aor;
155 r.domain = _d->name;
156 *_r = &r;
157 }
158
159 /*!
160 * \brief Debugging helper function
161 */
print_udomain(FILE * _f,udomain_t * _d)162 void print_udomain(FILE* _f, udomain_t* _d) {
163 int i;
164 int max = 0, slot = 0, n = 0;
165 struct impurecord* r;
166 fprintf(_f, "---Domain---\n");
167 fprintf(_f, "name : '%.*s'\n", _d->name->len, ZSW(_d->name->s));
168 fprintf(_f, "size : %d\n", _d->size);
169 fprintf(_f, "table: %p\n", _d->table);
170 fprintf(_f, "\n");
171 for (i = 0; i < _d->size; i++) {
172 r = _d->table[i].first;
173 n += _d->table[i].n;
174 if (max < _d->table[i].n) {
175 max = _d->table[i].n;
176 slot = i;
177 }
178 while (r) {
179 print_impurecord(_f, r);
180 r = r->next;
181 }
182 }
183 fprintf(_f, "\nMax slot: %d (%d/%d)\n", max, slot, n);
184 fprintf(_f, "\n---/Domain---\n");
185 }
186
time2str(time_t _v,char * _s,int * _l)187 inline int time2str(time_t _v, char* _s, int* _l) {
188 struct tm* t;
189 int l;
190
191 if ((!_s) || (!_l) || (*_l < 2)) {
192 LM_ERR("Invalid parameter value\n");
193 return -1;
194 }
195
196 *_s++ = '\'';
197
198 /* Convert time_t structure to format accepted by the database */
199 t = localtime(&_v);
200 l = strftime(_s, *_l - 1, "%Y-%m-%d %H:%M:%S", t);
201
202 if (l == 0) {
203 LM_ERR("Error during time conversion\n");
204 /* the value of _s is now unspecified */
205 _s = NULL;
206 _l = 0;
207 return -1;
208 }
209 *_l = l;
210
211 *(_s + l) = '\'';
212 *_l = l + 2;
213 return 0;
214 }
215
216 /*!
217 * \brief Insert a new record into domain in memory
218 * \param _d domain the record belongs to
219 * \param _aor address of record
220 * \param _r new created record
221 * \return 0 on success, -1 on failure
222 */
mem_insert_impurecord(struct udomain * _d,str * public_identity,str * private_identity,int reg_state,int barring,ims_subscription ** s,str * ccf1,str * ccf2,str * ecf1,str * ecf2,struct impurecord ** _r)223 int mem_insert_impurecord(struct udomain* _d, str* public_identity, str* private_identity, int reg_state, int barring,
224 ims_subscription** s, str* ccf1, str* ccf2, str* ecf1, str* ecf2,
225 struct impurecord** _r) {
226 int sl;
227
228 if (new_impurecord(_d->name, public_identity, private_identity, reg_state, barring, s, ccf1, ccf2, ecf1,
229 ecf2, _r) < 0) {
230 LM_ERR("creating impurecord failed\n");
231 return -1;
232 }
233
234 sl = ((*_r)->aorhash) & (_d->size - 1);
235 slot_add(&_d->table[sl], *_r);
236 counter_inc(ul_scscf_cnts_h.active_impus);
237
238 LM_DBG("inserted new impurecord into memory [%.*s]\n", (*_r)->public_identity.len, (*_r)->public_identity.s);
239 return 0;
240 }
241
242 /*!
243 * \brief Remove a record from domain in memory
244 * \param _d domain the record belongs to
245 * \param _r deleted record
246 */
mem_delete_impurecord(udomain_t * _d,struct impurecord * _r)247 void mem_delete_impurecord(udomain_t* _d, struct impurecord* _r) {
248 LM_DBG("deleting impurecord from memory [%.*s]\n", _r->public_identity.len, _r->public_identity.s);
249 slot_rem(_r->slot, _r);
250 free_impurecord(_r);
251 counter_add(ul_scscf_cnts_h.active_impus, -1);
252 }
253
254 static unsigned int expired_contacts_size = 0;
255 ucontact_t** expired_contacts = 0;
256
257
258 /*!
259 * \brief Run timer handler for given domain
260 * \param _d domain
261 */
mem_timer_udomain(udomain_t * _d,int istart,int istep)262 void mem_timer_udomain(udomain_t* _d, int istart, int istep) {
263 struct impurecord* ptr, *t;
264 struct ucontact* contact_ptr;
265 unsigned int num_expired_contacts = 0;
266 int i, n, temp;
267 time_t now;
268 int abort = 0;
269 int slot;
270 int ref_count_db;
271
272 now = time(0);
273
274 if (istart == 0) {
275 int numcontacts = contact_list->size*2; //assume we should be ok for each slot to have 2 collisions
276 if (expired_contacts_size < numcontacts) {
277 LM_DBG("Changing expired_contacts list size from %d to %d\n", expired_contacts_size, numcontacts);
278 if (expired_contacts){
279 pkg_free(expired_contacts);
280 }
281 expired_contacts = (ucontact_t**)pkg_malloc(numcontacts*sizeof(ucontact_t**));
282 if (!expired_contacts) {
283 LM_ERR("no more pkg mem trying to allocate [%lu] bytes\n", numcontacts*sizeof(ucontact_t**));
284 return;
285 }
286 expired_contacts_size = numcontacts;
287 }
288
289 //go through contacts first
290 n = contact_list->max_collisions;
291 LM_DBG("*** mem_timer_udomain - checking contacts - START ***\n");
292 for (i=0; i < contact_list->size; i++) {
293 lock_contact_slot_i(i);
294 contact_ptr = contact_list->slot[i].first;
295 while (contact_ptr) {
296 if (num_expired_contacts >= numcontacts) {
297 LM_WARN("we don't have enough space to expire all contacts in this pass - will continue in next pass\n");
298 abort = 1;
299 break;
300 }
301 LM_DBG("We have a [3gpp=%d] contact in the new contact list in slot %d = [%.*s] (%.*s) which expires in %lf seconds and has a ref count of %d (state: %s)\n",
302 contact_ptr->is_3gpp, i, contact_ptr->aor.len, contact_ptr->aor.s, contact_ptr->c.len, contact_ptr->c.s,
303 (double) contact_ptr->expires - now, contact_ptr->ref_count,
304 get_contact_state_as_string(contact_ptr->state));
305 //contacts are now deleted during impurecord processing
306 if ((contact_ptr->expires-now) <= 0) {
307 if (contact_ptr->state == CONTACT_DELAYED_DELETE) {
308 if (contact_ptr->ref_count <= 0) {
309 LM_DBG("contact in state CONTACT_DELATED_DELETE is about to be deleted\n");
310 expired_contacts[num_expired_contacts] = contact_ptr;
311 num_expired_contacts++;
312 } else {
313 /* we could fall here not because contact is still
314 referenced but also because we failed before to
315 get a lock to unref the contact, so we check if
316 contact is really referenced*/
317 if (db_mode != NO_DB) {
318 LM_DBG("contact in state CONTACT_DELAYED_DELETE still has a ref count of [%d] in memory. Check on DB \n", contact_ptr->ref_count);
319 ref_count_db = db_check_if_contact_is_linked(contact_ptr);
320 if (ref_count_db < 0) {
321 LM_ERR("Unable to check if contact is unlinked\n");
322 } else if (ref_count_db == 0) {
323 LM_DBG("Contact has ref count [%d] but there's no link on the DB. Deleting contact\n", contact_ptr->ref_count);
324 contact_ptr->ref_count = 0;
325 expired_contacts[num_expired_contacts] = contact_ptr;
326 num_expired_contacts++;
327 } else {
328 LM_DBG("Contact in state CONTACT_DELAYED_DELETE has ref count [%d] on DB\n", ref_count_db);
329 }
330 } else {
331 LM_DBG("contact in state CONTACT_DELAYED_DELETE still has a ref count of [%d] in memory. Not doing anything for now \n", contact_ptr->ref_count);
332 }
333 }
334 } else if (contact_ptr->state != CONTACT_DELETED) {
335 LM_DBG("expiring contact [%.*s](%.*s).... setting to CONTACT_EXPIRE_PENDING_NOTIFY\n",
336 contact_ptr->aor.len, contact_ptr->aor.s, contact_ptr->c.len, contact_ptr->c.s);
337 contact_ptr->state = CONTACT_EXPIRE_PENDING_NOTIFY;
338 ref_contact_unsafe(contact_ptr);
339 expired_contacts[num_expired_contacts] = contact_ptr;
340 num_expired_contacts++;
341 }
342 }
343 contact_ptr = contact_ptr->next;
344 }
345 if (contact_list->slot[i].n > n) {
346 n = contact_list->slot[i].n;
347 }
348 unlock_contact_slot_i(i);
349 contact_list->max_collisions = n;
350 if (abort == 1) {
351 break;
352 }
353 }
354 LM_DBG("*** mem_timer_udomain - checking contacts - FINISHED ***\n");
355 }
356
357 temp = 0;
358 n = _d->max_collisions;
359
360 LM_DBG("*** mem_timer_udomain - checking IMPUs - START ***\n");
361 for (i = istart; i < _d->size; i+=istep) {
362 lock_ulslot(_d, i);
363 ptr = _d->table[i].first;
364 temp = 0;
365 while (ptr) {
366 temp = 1;
367 #ifdef EXTRA_DEBUG
368 LM_DBG("ULSLOT %d LOCKED\n", i);
369 #endif
370 t = ptr;
371 ptr = ptr->next;
372 timer_impurecord(t);
373 }
374 if (temp) {
375 #ifdef EXTRA_DEBUG
376 LM_DBG("ULSLOT %d UN-LOCKED\n", i);
377 #endif
378 }
379 if (_d->table[i].n > n)
380 n = _d->table[i].n;
381
382 unlock_ulslot(_d, i);
383 _d->max_collisions = n;
384 }
385 LM_DBG("*** mem_timer_udomain - checking IMPUs - FINISHED ***\n");
386
387 if (istart == 0) {
388 n = ims_subscription_list->max_collisions;
389 for (i = 0; i < ims_subscription_list->size; i++) {
390 lock_subscription_slot(i);
391 if (ims_subscription_list->slot[i].n > n) {
392 n = ims_subscription_list->slot[i].n;
393 }
394 unlock_subscription_slot(i);
395 }
396 ims_subscription_list->max_collisions = n;
397
398 /* now we delete the expired contacts. (mark them for deletion */
399 for (i=0; i<num_expired_contacts; i++) {
400 slot = expired_contacts[i]->sl;
401 lock_contact_slot_i(slot);
402 if (expired_contacts[i]->state != CONTACT_DELAYED_DELETE) {
403 LM_DBG("Setting contact state to CONTACT_DELETED for contact [%.*s](%.*s)\n",
404 expired_contacts[i]->aor.len, expired_contacts[i]->aor.s, expired_contacts[i]->c.len, expired_contacts[i]->c.s);
405 expired_contacts[i]->state = CONTACT_DELETED;
406 unref_contact_unsafe(expired_contacts[i]);
407 } else {
408 LM_DBG("deleting contact [%.*s](%.*s)\n",
409 expired_contacts[i]->aor.len, expired_contacts[i]->aor.s, expired_contacts[i]->c.len, expired_contacts[i]->c.s);
410 delete_scontact(expired_contacts[i]);
411 }
412 unlock_contact_slot_i(slot);
413 }
414 }
415
416 }
417
418
419 /*!
420 * \brief Get lock for a domain
421 * \param _d domain
422 * \param _aor adress of record, used as hash source for the lock slot
423 */
lock_udomain(udomain_t * _d,str * _aor)424 void lock_udomain(udomain_t* _d, str* _aor) {
425 unsigned int sl;
426 sl = core_hash(_aor, 0, _d->size);
427 lock_ulslot(_d, sl);
428 }
429
430 /*!
431 * \brief Release lock for a domain
432 * \param _d domain
433 * \param _aor address of record, uses as hash source for the lock slot
434 */
unlock_udomain(udomain_t * _d,str * _aor)435 void unlock_udomain(udomain_t* _d, str* _aor) {
436 unsigned int sl;
437 sl = core_hash(_aor, 0, _d->size);
438 unlock_ulslot(_d, sl);
439 }
440
441 /*!
442 * \brief Get lock for a slot
443 * \param _d domain
444 * \param i slot number
445 */
lock_ulslot(udomain_t * _d,int i)446 void lock_ulslot(udomain_t* _d, int i) {
447 #ifdef EXTRA_DEBUG
448 LM_DBG("LOCKING UDOMAIN SLOT [%d]\n", i);
449 #endif
450 int mypid;
451 mypid = my_pid();
452 if (likely(atomic_get(&_d->table[i].locker_pid) != mypid)) {
453 lock_get(_d->table[i].lock);
454 atomic_set(&_d->table[i].locker_pid, mypid);
455 } else {
456 /* locked within the same process that executed us */
457 _d->table[i].recursive_lock_level++;
458 }
459 }
460
461 /*!
462 * \brief Release lock for a slot
463 * \param _d domain
464 * \param i slot number
465 */
unlock_ulslot(udomain_t * _d,int i)466 void unlock_ulslot(udomain_t* _d, int i) {
467 #ifdef EXTRA_DEBUG
468 LM_DBG("UN-LOCKING UDOMAIN SLOT [%d]\n", i);
469 #endif
470 if (likely(_d->table[i].recursive_lock_level == 0)) {
471 atomic_set(&_d->table[i].locker_pid, 0);
472 lock_release(_d->table[i].lock);
473 } else {
474 /* recursive locked => decrease lock count */
475 _d->table[i].recursive_lock_level--;
476 }
477 }
478
lock_contact_slot(str * contact_uri)479 void lock_contact_slot(str* contact_uri) {
480 unsigned int sl;
481 sl = core_hash(contact_uri, 0, contact_list->size);
482 lock_contact_slot_i(sl);
483 }
484
unlock_contact_slot(str * contact_uri)485 void unlock_contact_slot(str* contact_uri) {
486 unsigned int sl;
487 sl = core_hash(contact_uri, 0, contact_list->size);
488
489 unlock_contact_slot_i(sl);
490 }
491
lock_contact_slot_i(int i)492 void lock_contact_slot_i(int i) {
493 #ifdef EXTRA_DEBUG
494 LM_DBG("LOCKING CONTACT SLOT [%d]\n", i);
495 #endif
496 #ifdef GEN_LOCK_T_PREFERED
497 lock_get(contact_list->slot[i].lock);
498 #else
499 lock_contacts_idx(contact_list->slot[i].lockidx);
500 #endif
501 }
502
unlock_contact_slot_i(int i)503 void unlock_contact_slot_i(int i) {
504 #ifdef EXTRA_DEBUG
505 LM_DBG("UN-LOCKING CONTACT SLOT [%d]\n", i);
506 #endif
507 #ifdef GEN_LOCK_T_PREFERED
508 lock_release(contact_list->slot[i].lock);
509 #else
510 release_contact_idx(contact_list->slot[i].lockidx);
511 #endif
512 }
513
lock_subscription(ims_subscription * s)514 void lock_subscription(ims_subscription* s) {
515 #ifdef EXTRA_DEBUG
516 LM_DBG("LOCKING SUBSCRIPTION %p (Refcount: %d)\n", s->lock, s->ref_count);
517 LM_DBG("(SUBSCRIPTION PRIVATE IDENTITY [%.*s])\n", s->private_identity.len, s->private_identity.s);
518 #endif
519 lock_get(s->lock);
520 }
521
unlock_subscription(ims_subscription * s)522 void unlock_subscription(ims_subscription* s) {
523 if (s == 0)
524 return;
525 #ifdef EXTRA_DEBUG
526 LM_DBG("UN-LOCKING SUBSCRIPTION %p (Refcount: %d)\n", s->lock, s->ref_count);
527 LM_DBG("(SUBSCRIPTION PRIVATE IDENTITY [%.*s])\n", s->private_identity.len, s->private_identity.s);
528 #endif
529 lock_release(s->lock);
530 }
531
lock_subscription_slot(int i)532 void lock_subscription_slot(int i) {
533 #ifdef EXTRA_DEBUG
534 LM_DBG("LOCKING SUBSCRIPTION slot %d)\n", i);
535 #endif
536 lock_get(ims_subscription_list->slot[i].lock);
537 }
538
unlock_subscription_slot(int i)539 void unlock_subscription_slot(int i) {
540 #ifdef EXTRA_DEBUG
541 LM_DBG("UN-LOCKING SUBSCRIPTION slot %d\n", i);
542 #endif
543 lock_release(ims_subscription_list->slot[i].lock);
544 }
545
546 /*!
547 * \brief Create and insert a new impurecord assumes domain is locked
548 * @param _d domain to insert the new record
549 * @param public_identity IMPU of new record
550 * @param private_identity IMPI of new record
551 * @param reg_state state to insert in
552 * @param barring is impu barred or not
553 * @param s associated subscription data
554 * @param ccf1
555 * @param ccf2
556 * @param ecf1
557 * @param ecf2
558 * @param _r pointer to returned IMPU record
559 * @return 0 on success with _r populated
560 */
insert_impurecord(struct udomain * _d,str * public_identity,str * private_identity,int reg_state,int barring,ims_subscription ** s,str * ccf1,str * ccf2,str * ecf1,str * ecf2,struct impurecord ** _r)561 int insert_impurecord(struct udomain* _d, str* public_identity, str* private_identity, int reg_state, int barring,
562 ims_subscription** s, str* ccf1, str* ccf2, str* ecf1, str* ecf2,
563 struct impurecord** _r) {
564
565 if (s == 0 || (*s) == 0) {
566 LM_WARN("Can't insert an impurecord without it being associated to a subscription\n");
567 goto error;
568 }
569
570 if (!private_identity || !private_identity->len || !private_identity->s) {
571 LM_WARN("Can't insert an impurecord without it being associated to a subscription (private_identity\n");
572 goto error;
573 }
574
575 /* check to see if we already have this subscription information in memory*/
576 if (mem_insert_impurecord(_d, public_identity, private_identity, reg_state, barring, s, ccf1, ccf2, ecf1, ecf2, _r) < 0) {
577 LM_ERR("inserting record failed\n");
578 goto error;
579 }
580
581 /*DB?*/
582 if (db_mode == WRITE_THROUGH && db_insert_impurecord(_d, public_identity, reg_state, barring, s, ccf1, ccf2, ecf1, ecf2, _r) != 0) {
583 LM_ERR("error inserting contact into db\n");
584 goto error;
585 }
586
587 return 0;
588
589 error:
590 return -1;
591 }
592
593 /*!
594 * \brief Obtain a impurecord pointer if the impurecord exists in domain. You should call this function with a lock on the domain
595 * \param _d domain to search the record
596 * \param _aor address of record
597 * \param _r new created record
598 * \return 0 if a record was found, 1 if nothing could be found (assumes caller has lock on domain)
599 */
get_impurecord_unsafe(udomain_t * _d,str * public_identity,struct impurecord ** _r)600 int get_impurecord_unsafe(udomain_t* _d, str* public_identity, struct impurecord** _r) {
601 unsigned int sl, i, aorhash;
602 impurecord_t* r;
603
604 /* search in cache */
605 aorhash = core_hash(public_identity, 0, 0);
606 sl = aorhash & (_d->size - 1);
607 r = _d->table[sl].first;
608
609 for (i = 0; i < _d->table[sl].n; i++) {
610 if ((r->aorhash == aorhash) && (r->public_identity.len == public_identity->len)
611 && !memcmp(r->public_identity.s, public_identity->s, public_identity->len)) {
612 *_r = r;
613 return 0;
614 }
615
616 r = r->next;
617 }
618 return 1; /* Nothing found */
619 }
620
621 /*!
622 * \brief Obtain a impurecord pointer if the impurecord exists in domain. domain must be locked before calling
623 * \param _d domain to search the record
624 * \param public_identity address of record
625 * \param _r returned record - null if not found
626 * \return 0 if a record was found, 1 if nothing could be found
627 */
get_impurecord(udomain_t * _d,str * public_identity,struct impurecord ** _r)628 int get_impurecord(udomain_t* _d, str* public_identity, struct impurecord** _r) {
629 unsigned int ret;
630
631 ret = get_impurecord_unsafe(_d, public_identity, _r);
632
633 return ret;
634 }
635
636 /*!
637 * \brief release the lock on the impurecord - effectively the domain slot
638 * \param _d domain
639 * \param _r impurecord to release (unlock)
640 */
release_impurecord(udomain_t * _d,struct impurecord * _r)641 void release_impurecord(udomain_t* _d, struct impurecord* _r) {
642 unlock_udomain(_d, &_r->public_identity);
643 }
644
645 /*!
646 * \brief Delete a impurecord from domain
647 * \param _d domain where the record should be deleted
648 * \param _aor address of record - used only if _r in next param is null
649 * \param _r deleted record to delete - if null will use the aor to search (assumed that domain is locked).
650 * \return 0 on success, -1 if the record could not be deleted
651 */
delete_impurecord(udomain_t * _d,str * _aor,struct impurecord * _r)652 int delete_impurecord(udomain_t* _d, str* _aor, struct impurecord* _r) {
653 LM_DBG("Deleting IMPURECORD [%.*s]\n", _r->public_identity.len, _r->public_identity.s);
654
655 if (_r == 0) {
656 LM_DBG("no impurecord passed in - let's search\n");
657 if (get_impurecord(_d, _aor, &_r) != 0) {
658 return 0;
659 }
660 }
661
662 if (exists_ulcb_type(_r->cbs, UL_IMPU_DELETE)) {
663 run_ul_callbacks(_r->cbs, UL_IMPU_DELETE, _r, 0);
664 }
665
666 /*DB?*/
667 if (db_mode == WRITE_THROUGH
668 && db_delete_impurecord(_d, _r) != 0) {
669 LM_ERR("error deleting IMPU record from db...continuing to remove from memory\n");
670 }
671
672 mem_delete_impurecord(_d, _r);
673
674 return 0;
675 }
676
677 /*
678 * get all IMPUs as string from a subscription related to an impurecord. apply filter for barring (assumed to be called with lock on impurec)
679 * you should have some for of lock on the subscription (ie a reference)
680 * barring-1 get all barred
681 * barring-0 get all unbarred
682 * barring-(-1) get all records
683 * NB. Remember to free the block of memory pointed to by impus (pkg_malloc)
684 */
get_impus_from_subscription_as_string(udomain_t * _d,impurecord_t * impu_rec,int barring,str ** impus,int * num_impus,int is_shm)685 int get_impus_from_subscription_as_string(udomain_t* _d, impurecord_t* impu_rec, int barring, str** impus, int* num_impus, int is_shm) {
686 int i, j, count;
687 *num_impus = 0;
688 *impus = 0;
689 ims_public_identity* impi;
690 int bytes_needed = 0;
691 int len = 0;
692
693 LM_DBG("getting IMPU subscription set\n");
694
695 if (!impu_rec) {
696 LM_ERR("no impu record provided\n");
697 return 1;
698 }
699
700 if (!impu_rec->s) {
701 LM_DBG("no subscription associated with impu\n");
702 return 0;
703 }
704
705 lock_subscription(impu_rec->s);
706 for (i = 0; i < impu_rec->s->service_profiles_cnt; i++) {
707 for (j = 0; j < impu_rec->s->service_profiles[i].public_identities_cnt; j++) {
708 impi = &(impu_rec->s->service_profiles[i].public_identities[j]);
709 LM_DBG("Got Record %.*s (%i)\n", impi->public_identity.len,
710 impi->public_identity.s, impi->public_identity.len);
711
712 if (barring < 0) {
713 //get all records
714 bytes_needed += impi->public_identity.len;
715 (*num_impus)++;
716 } else {
717 if (impi->barring == barring) {
718 //add the record to the list
719 bytes_needed += impi->public_identity.len;
720 (*num_impus)++;
721 }
722 }
723 }
724 }
725 LM_DBG("num of records returned is %d and we need %d bytes\n", *num_impus, bytes_needed);
726
727 len = (sizeof (str)*(*num_impus)) + bytes_needed;
728 if (is_shm)
729 *impus = (str*) shm_malloc(len);
730 else
731 *impus = (str*) pkg_malloc(len); //TODO: rather put this on the stack... dont' fragment pkg....
732
733 if (*impus == 0) {
734 if (is_shm)
735 LM_ERR("no more shm_mem\n");
736 else
737 LM_ERR("no more pkg_mem\n");
738 return 1;
739 }
740 char* ptr = (char*) (*impus + *num_impus);
741
742 //now populate the data
743 count = 0;
744 for (i = 0; i < impu_rec->s->service_profiles_cnt; i++) {
745 for (j = 0; j < impu_rec->s->service_profiles[i].public_identities_cnt; j++) {
746 impi = &(impu_rec->s->service_profiles[i].public_identities[j]);
747 if (barring < 0) {
748 //get all records
749 (*impus)[count].s = ptr;
750 memcpy(ptr, impi->public_identity.s, impi->public_identity.len);
751 (*impus)[count].len = impi->public_identity.len;
752 ptr += impi->public_identity.len;
753 count++;
754 } else {
755 if (impi->barring == barring) {
756 //add the record to the list
757 (*impus)[count].s = ptr;
758 memcpy(ptr, impi->public_identity.s, impi->public_identity.len);
759 (*impus)[count].len = impi->public_identity.len;
760 ptr += impi->public_identity.len;
761 count++;
762 }
763 }
764 }
765 }
766
767 if (ptr != ((char*) *impus + len)) {
768 LM_CRIT("buffer overflow\n");
769 return 1;
770 }
771
772 unlock_subscription(impu_rec->s);
773
774 return 0;
775 }
776
777 /**
778 * @brief Get a subscription from the subscription list based on the IMPI
779 * NB - does not return with a lock on the subscription but does increment ref count
780 * @param impu string of impu to search for
781 * @param s ims_subscription to be returned if found
782 * @param leave_slot_locked if no subscription is found return with the slot locked (in case we want to add)
783 * @return 0 on success
784 */
get_subscription(str * impi_s,ims_subscription ** s,int leave_slot_locked)785 int get_subscription(str* impi_s, ims_subscription** s, int leave_slot_locked) {
786 int subscription_hash, sl;
787 ims_subscription* ptr;
788
789 subscription_hash = core_hash(impi_s, 0, 0);
790 sl = subscription_hash & (subs_hash_size - 1);
791 lock_subscription_slot(sl);
792 ptr = ims_subscription_list->slot[sl].first;
793 while (ptr) {
794 if ((impi_s->len == ptr->private_identity.len) && (memcmp(impi_s->s, ptr->private_identity.s, impi_s->len) == 0)) {
795 LM_DBG("found an existing subscription for IMPI [%.*s]\n", impi_s->len, impi_s->s);
796 (*s) = ptr;
797 lock_subscription(ptr);
798 ref_subscription_unsafe(ptr);
799 unlock_subscription(ptr);
800 unlock_subscription_slot(sl);
801 return 0;
802 }
803 ptr = ptr->next;
804 }
805 if (!leave_slot_locked)
806 unlock_subscription_slot(sl);
807 return 1;
808 }
809
add_subscription_unsafe(ims_subscription * s)810 void add_subscription_unsafe(ims_subscription* s) {
811 int sl;
812 sl = core_hash(&s->private_identity, 0, subs_hash_size);
813 subs_slot_add(&ims_subscription_list->slot[sl], s);
814 s->sl = sl;
815
816 }
817
add_subscription(ims_subscription * s)818 void add_subscription(ims_subscription* s) {
819 int sl;
820 sl = core_hash(&s->private_identity, 0, subs_hash_size);
821 lock_subscription_slot(sl);
822 add_subscription_unsafe(s);
823 unlock_subscription_slot(sl);
824 }
825
delete_subscription(ims_subscription * s)826 void delete_subscription(ims_subscription* s) {
827 LM_DBG("Deleting subscription %p [%.*s]\n", s, s->private_identity.len, s->private_identity.s);
828 free_ims_subscription_data(s);
829 }
830
release_subscription(ims_subscription * s)831 void release_subscription(ims_subscription* s) {
832 LM_DBG("Releasing subscription %p [%.*s]\n", s, s->private_identity.len, s->private_identity.s);
833 unref_subscription(s);
834 }
835
836 /**
837 * @brief update/add subscription
838 * @param s
839 * @return
840 */
update_subscription(ims_subscription * s)841 int update_subscription(ims_subscription* s) {
842 return 0;
843 }
844
ref_contact_unsafe(ucontact_t * c)845 void ref_contact_unsafe(ucontact_t* c) {
846 LM_DBG("incrementing ref count on contact [%.*s], was %d\n", c->c.len, c->c.s, c->ref_count);
847 c->ref_count++;
848 }
849
850 /**
851 * @brief unref contact - assume a lock on the slot is held prior to calling this
852 * @param c
853 */
unref_contact_unsafe(ucontact_t * c)854 void unref_contact_unsafe(ucontact_t* c) {
855 LM_DBG("decrementing ref count on contact [%.*s], was %d\n", c->c.len, c->c.s, c->ref_count);
856 c->ref_count--;
857 if (c->ref_count <= 0) {
858 LM_DBG("contact [%.*s] no longer referenced.... deleting\n", c->c.len, c->c.s);
859 if (c->ref_count < 0) {
860 LM_WARN("reference dropped below zero... this should not happen\n");
861 }
862 c->state = CONTACT_DELAYED_DELETE;
863 c->expires = time(NULL) + contact_delete_delay;
864 // delete_scontact(c);
865 }
866 }
867