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