Jelajahi Sumber

usrloc(k): new columns to store +sip.instance in record id

- each usrloc contact has now an unique internal id, generated via SRUID
- the unique id is stored in 'ruid' field
- if the Contact header has +sip.instance parameter (GRUU RFC5627),
  store it in field 'instance'
- database schema was updated to reflect the two new columns
- more functions were exported via API to deal with GRUU cases
Daniel-Constantin Mierla 13 tahun lalu
induk
melakukan
05823430e2

+ 98 - 68
modules_k/usrloc/README

@@ -51,17 +51,19 @@ Bogdan-Andrei Iancu
               3.13. received_column (string)
               3.14. socket_column (string)
               3.15. path_column (string)
-              3.16. use_domain (integer)
-              3.17. desc_time_order (integer)
-              3.18. timer_interval (integer)
-              3.19. db_url (string)
-              3.20. db_mode (integer)
-              3.21. matching_mode (integer)
-              3.22. cseq_delay (integer)
-              3.23. fetch_rows (integer)
-              3.24. hash_size (integer)
-              3.25. preload (string)
-              3.26. db_update_as_insert (string)
+              3.16. ruid_column (string)
+              3.17. instance_column (string)
+              3.18. use_domain (integer)
+              3.19. desc_time_order (integer)
+              3.20. timer_interval (integer)
+              3.21. db_url (string)
+              3.22. db_mode (integer)
+              3.23. matching_mode (integer)
+              3.24. cseq_delay (integer)
+              3.25. fetch_rows (integer)
+              3.26. hash_size (integer)
+              3.27. preload (string)
+              3.28. db_update_as_insert (string)
 
         4. Functions
         5. MI Commands
@@ -121,17 +123,19 @@ Bogdan-Andrei Iancu
    1.13. Set received_column parameter
    1.14. Set socket_column parameter
    1.15. Set path_column parameter
-   1.16. Set use_domain parameter
-   1.17. Set desc_time_order parameter
-   1.18. Set timer_interval parameter
-   1.19. Set db_url parameter
-   1.20. Set db_mode parameter
-   1.21. Set matching_mode parameter
-   1.22. Set cseq_delay parameter
-   1.23. Set fetch_rows parameter
-   1.24. Set hash_size parameter
-   1.25. Set preload parameter
-   1.26. Set db_update_as_insert parameter
+   1.16. Set ruid_column parameter
+   1.17. Set instance_column parameter
+   1.18. Set use_domain parameter
+   1.19. Set desc_time_order parameter
+   1.20. Set timer_interval parameter
+   1.21. Set db_url parameter
+   1.22. Set db_mode parameter
+   1.23. Set matching_mode parameter
+   1.24. Set cseq_delay parameter
+   1.25. Set fetch_rows parameter
+   1.26. Set hash_size parameter
+   1.27. Set preload parameter
+   1.28. Set db_update_as_insert parameter
 
 Chapter 1. Admin Guide
 
@@ -163,17 +167,19 @@ Chapter 1. Admin Guide
         3.13. received_column (string)
         3.14. socket_column (string)
         3.15. path_column (string)
-        3.16. use_domain (integer)
-        3.17. desc_time_order (integer)
-        3.18. timer_interval (integer)
-        3.19. db_url (string)
-        3.20. db_mode (integer)
-        3.21. matching_mode (integer)
-        3.22. cseq_delay (integer)
-        3.23. fetch_rows (integer)
-        3.24. hash_size (integer)
-        3.25. preload (string)
-        3.26. db_update_as_insert (string)
+        3.16. ruid_column (string)
+        3.17. instance_column (string)
+        3.18. use_domain (integer)
+        3.19. desc_time_order (integer)
+        3.20. timer_interval (integer)
+        3.21. db_url (string)
+        3.22. db_mode (integer)
+        3.23. matching_mode (integer)
+        3.24. cseq_delay (integer)
+        3.25. fetch_rows (integer)
+        3.26. hash_size (integer)
+        3.27. preload (string)
+        3.28. db_update_as_insert (string)
 
    4. Functions
    5. MI Commands
@@ -229,7 +235,7 @@ Chapter 1. Admin Guide
        retransmissions in this case.
 
    How to control/select the contact maching algorithm, please see the
-   module parameter matching_mode at Section 3.21, "matching_mode
+   module parameter matching_mode at Section 3.23, "matching_mode
    (integer)".
 
 2. Dependencies
@@ -265,17 +271,19 @@ Chapter 1. Admin Guide
    3.13. received_column (string)
    3.14. socket_column (string)
    3.15. path_column (string)
-   3.16. use_domain (integer)
-   3.17. desc_time_order (integer)
-   3.18. timer_interval (integer)
-   3.19. db_url (string)
-   3.20. db_mode (integer)
-   3.21. matching_mode (integer)
-   3.22. cseq_delay (integer)
-   3.23. fetch_rows (integer)
-   3.24. hash_size (integer)
-   3.25. preload (string)
-   3.26. db_update_as_insert (string)
+   3.16. ruid_column (string)
+   3.17. instance_column (string)
+   3.18. use_domain (integer)
+   3.19. desc_time_order (integer)
+   3.20. timer_interval (integer)
+   3.21. db_url (string)
+   3.22. db_mode (integer)
+   3.23. matching_mode (integer)
+   3.24. cseq_delay (integer)
+   3.25. fetch_rows (integer)
+   3.26. hash_size (integer)
+   3.27. preload (string)
+   3.28. db_update_as_insert (string)
 
 3.1. nat_bflag (integer)
 
@@ -446,7 +454,29 @@ modparam("usrloc", "socket_column", "socket")
 modparam("usrloc", "path_column", "path")
 ...
 
-3.16. use_domain (integer)
+3.16. ruid_column (string)
+
+   Name of column containing the record unique id.
+
+   Default value is "ruid".
+
+   Example 1.16. Set ruid_column parameter
+...
+modparam("usrloc", "ruid_column", "myruid")
+...
+
+3.17. instance_column (string)
+
+   Name of column containing the SIP instace (GRUU - RFC5627).
+
+   Default value is "instance".
+
+   Example 1.17. Set instance_column parameter
+...
+modparam("usrloc", "instance_column", "myinstance")
+...
+
+3.18. use_domain (integer)
 
    If the domain part of the user should be also saved and used for
    identifing the user (along with the username part). Useful in multi
@@ -454,24 +484,24 @@ modparam("usrloc", "path_column", "path")
 
    Default value is "0 (false)".
 
-   Example 1.16. Set use_domain parameter
+   Example 1.18. Set use_domain parameter
 ...
 modparam("usrloc", "use_domain", 1)
 ...
 
-3.17. desc_time_order (integer)
+3.19. desc_time_order (integer)
 
    If the user's contacts should be kept timestamp ordered; otherwise the
    contact will be ordered based on q value. Non 0 value means true.
 
    Default value is "0 (false)".
 
-   Example 1.17. Set desc_time_order parameter
+   Example 1.19. Set desc_time_order parameter
 ...
 modparam("usrloc", "desc_time_order", 1)
 ...
 
-3.18. timer_interval (integer)
+3.20. timer_interval (integer)
 
    Number of seconds between two timer runs. The module uses timer to
    delete expired contacts, synchronize with database and other tasks,
@@ -479,23 +509,23 @@ modparam("usrloc", "desc_time_order", 1)
 
    Default value is 60.
 
-   Example 1.18. Set timer_interval parameter
+   Example 1.20. Set timer_interval parameter
 ...
 modparam("usrloc", "timer_interval", 120)
 ...
 
-3.19. db_url (string)
+3.21. db_url (string)
 
    URL of the database that should be used.
 
    Default value is "mysql://openser:openserrw@localhost/openser".
 
-   Example 1.19. Set db_url parameter
+   Example 1.21. Set db_url parameter
 ...
 modparam("usrloc", "db_url", "dbdriver://username:password@dbhost/dbname")
 ...
 
-3.20. db_mode (integer)
+3.22. db_mode (integer)
 
    The usrloc module can utilize database for persistent contact storage.
    If you use database, your contacts will survive machine restarts or SW
@@ -536,12 +566,12 @@ Warning
 
    Default value is 0.
 
-   Example 1.20. Set db_mode parameter
+   Example 1.22. Set db_mode parameter
 ...
 modparam("usrloc", "db_mode", 2)
 ...
 
-3.21. matching_mode (integer)
+3.23. matching_mode (integer)
 
    What contact matching algorithm to be used. Refer to section
    Section 1.1, "Contact matching" for the description of the algorithms.
@@ -555,12 +585,12 @@ modparam("usrloc", "db_mode", 2)
 
    Default value is 0 (CONTACT_ONLY).
 
-   Example 1.21. Set matching_mode parameter
+   Example 1.23. Set matching_mode parameter
 ...
 modparam("usrloc", "matching_mode", 1)
 ...
 
-3.22. cseq_delay (integer)
+3.24. cseq_delay (integer)
 
    Delay (in seconds) for accepting as retransmissions register requests
    with same Call-ID and Cseq. The delay is calculated starting from the
@@ -574,12 +604,12 @@ modparam("usrloc", "matching_mode", 1)
 
    Default value is "20 seconds".
 
-   Example 1.22. Set cseq_delay parameter
+   Example 1.24. Set cseq_delay parameter
 ...
 modparam("usrloc", "cseq_delay", 5)
 ...
 
-3.23. fetch_rows (integer)
+3.25. fetch_rows (integer)
 
    The number of the rows to be fetched at once from database when loading
    the location records. This value can be used to tune the load time at
@@ -588,12 +618,12 @@ modparam("usrloc", "cseq_delay", 5)
 
    Default value is "2000".
 
-   Example 1.23. Set fetch_rows parameter
+   Example 1.25. Set fetch_rows parameter
 ...
 modparam("usrloc", "fetch_rows", 3000)
 ...
 
-3.24. hash_size (integer)
+3.26. hash_size (integer)
 
    The number of entries of the hash table used by usrloc to store the
    location records is 2^hash_size. For hash_size=4, the number of entries
@@ -601,12 +631,12 @@ modparam("usrloc", "fetch_rows", 3000)
 
    Default value is "9".
 
-   Example 1.24. Set hash_size parameter
+   Example 1.26. Set hash_size parameter
 ...
 modparam("usrloc", "hash_size", 10)
 ...
 
-3.25. preload (string)
+3.27. preload (string)
 
    Preload location table given as value. A location table is loaded based
    on fixup of registrar functions, therefore you need to use this
@@ -615,20 +645,20 @@ modparam("usrloc", "hash_size", 10)
 
    Default value is "NULL".
 
-   Example 1.25. Set preload parameter
+   Example 1.27. Set preload parameter
 ...
 modparam("usrloc", "preload", "location")
 ...
 
-3.26. db_update_as_insert (string)
+3.28. db_update_as_insert (string)
 
    Set this parameter if you want to do INSERT DB operations instead of
    UPDATE DB operations. It is recommended to set this parameter if you
    use Cassandra as a DB backend.
 
-   Default value is “0”.
+   Default value is "0".
 
-   Example 1.26. Set db_update_as_insert parameter
+   Example 1.28. Set db_update_as_insert parameter
 ...
 modparam("usrloc", "db_update_as_insert", 1)
 ...

+ 40 - 0
modules_k/usrloc/doc/usrloc_admin.xml

@@ -408,6 +408,46 @@ modparam("usrloc", "path_column", "path")
 		</example>
 	</section>
 
+	<section>
+		<title><varname>ruid_column</varname> (string)</title>
+		<para>
+		Name of column containing the record unique id.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>ruid</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>ruid_column</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("usrloc", "ruid_column", "myruid")
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section>
+		<title><varname>instance_column</varname> (string)</title>
+		<para>
+		Name of column containing the SIP instace (GRUU - RFC5627).
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>instance</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>instance_column</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("usrloc", "instance_column", "myinstance")
+...
+</programlisting>
+		</example>
+	</section>
+
 	<section>
 		<title><varname>use_domain</varname> (integer)</title>
 		<para>

+ 70 - 12
modules_k/usrloc/ucontact.c

@@ -77,6 +77,12 @@ ucontact_t* new_ucontact(str* _dom, str* _aor, str* _contact, ucontact_info_t* _
 	if (_ci->path && _ci->path->len) {
 		if (shm_str_dup( &c->path, _ci->path) < 0) goto error;
 	}
+	if (_ci->ruid.s && _ci->ruid.len) {
+		if (shm_str_dup( &c->ruid, &_ci->ruid) < 0) goto error;
+	}
+	if (_ci->instance.s && _ci->instance.len) {
+		if (shm_str_dup( &c->instance, &_ci->instance) < 0) goto error;
+	}
 
 	c->domain = _dom;
 	c->aor = _aor;
@@ -98,6 +104,8 @@ error:
 	if (c->user_agent.s) shm_free(c->user_agent.s);
 	if (c->callid.s) shm_free(c->callid.s);
 	if (c->c.s) shm_free(c->c.s);
+	if (c->ruid.s) shm_free(c->ruid.s);
+	if (c->instance.s) shm_free(c->instance.s);
 	shm_free(c);
 	return 0;
 }
@@ -116,6 +124,8 @@ void free_ucontact(ucontact_t* _c)
 	if (_c->user_agent.s) shm_free(_c->user_agent.s);
 	if (_c->callid.s) shm_free(_c->callid.s);
 	if (_c->c.s) shm_free(_c->c.s);
+	if (_c->ruid.s) shm_free(_c->ruid.s);
+	if (_c->instance.s) shm_free(_c->instance.s);
 	shm_free( _c );
 }
 
@@ -169,6 +179,10 @@ void print_ucontact(FILE* _f, ucontact_t* _c)
 		fprintf(_f, "Sock      : none (null)\n");
 	}
 	fprintf(_f, "Methods   : %u\n", _c->methods);
+	fprintf(_f, "ruid      : '%.*s'\n",
+		_c->ruid.len, ZSW(_c->ruid.s));
+	fprintf(_f, "instance  : '%.*s'\n",
+		_c->instance.len, ZSW(_c->instance.s));
 	fprintf(_f, "next      : %p\n", _c->next);
 	fprintf(_f, "prev      : %p\n", _c->prev);
 	fprintf(_f, "~~~/Contact~~~~\n");
@@ -388,8 +402,9 @@ int st_flush_ucontact(ucontact_t* _c)
 int db_insert_ucontact(ucontact_t* _c)
 {
 	char* dom;
-	db_key_t keys[15];
-	db_val_t vals[15];
+	db_key_t keys[17];
+	db_val_t vals[17];
+	int nr_cols;
 	
 	if (_c->flags & FL_MEM) {
 		return 0;
@@ -409,7 +424,9 @@ int db_insert_ucontact(ucontact_t* _c)
 	keys[11] = &sock_col;
 	keys[12] = &methods_col;
 	keys[13] = &last_mod_col;
-	keys[14] = &domain_col;
+	keys[14] = &ruid_col;
+	keys[15] = &instance_col;
+	keys[16] = &domain_col;
 
 	vals[0].type = DB1_STR;
 	vals[0].nul = 0;
@@ -489,19 +506,40 @@ int db_insert_ucontact(ucontact_t* _c)
 	vals[13].nul = 0;
 	vals[13].val.time_val = _c->last_modified;
 
+	nr_cols = 14;
+
+	if(_c->ruid.len>0)
+	{
+		keys[nr_cols] = &ruid_col;
+		vals[nr_cols].type = DB1_STR;
+		vals[nr_cols].nul = 0;
+		vals[nr_cols].val.str_val = _c->ruid;
+		nr_cols++;
+	}
+
+	if(_c->instance.len>0)
+	{
+		keys[nr_cols] = &instance_col;
+		vals[nr_cols].type = DB1_STR;
+		vals[nr_cols].nul = 0;
+		vals[nr_cols].val.str_val = _c->instance;
+		nr_cols++;
+	}
+
 	if (use_domain) {
-		vals[14].type = DB1_STR;
-		vals[14].nul = 0;
+		vals[nr_cols].type = DB1_STR;
+		vals[nr_cols].nul = 0;
 
 		dom = memchr(_c->aor->s, '@', _c->aor->len);
 		if (dom==0) {
 			vals[0].val.str_val.len = 0;
-			vals[14].val.str_val = *_c->aor;
+			vals[nr_cols].val.str_val = *_c->aor;
 		} else {
 			vals[0].val.str_val.len = dom - _c->aor->s;
-			vals[14].val.str_val.s = dom + 1;
-			vals[14].val.str_val.len = _c->aor->s + _c->aor->len - dom - 1;
+			vals[nr_cols].val.str_val.s = dom + 1;
+			vals[nr_cols].val.str_val.len = _c->aor->s + _c->aor->len - dom - 1;
 		}
+		nr_cols++;
 	}
 	
 	if (ul_dbf.use_table(ul_dbh, _c->domain) < 0) {
@@ -509,7 +547,7 @@ int db_insert_ucontact(ucontact_t* _c)
 		return -1;
 	}
 
-	if (ul_dbf.insert(ul_dbh, keys, vals, (use_domain) ? (15) : (14)) < 0) {
+	if (ul_dbf.insert(ul_dbh, keys, vals, nr_cols) < 0) {
 		LM_ERR("inserting contact in db failed\n");
 		return -1;
 	}
@@ -529,8 +567,10 @@ int db_update_ucontact(ucontact_t* _c)
 	db_key_t keys1[4];
 	db_val_t vals1[4];
 
-	db_key_t keys2[11];
-	db_val_t vals2[11];
+	db_key_t keys2[13];
+	db_val_t vals2[13];
+	int nr_cols2;
+
 
 	if (_c->flags & FL_MEM) {
 		return 0;
@@ -551,6 +591,8 @@ int db_update_ucontact(ucontact_t* _c)
 	keys2[8] = &sock_col;
 	keys2[9] = &methods_col;
 	keys2[10] = &last_mod_col;
+	keys2[11] = &ruid_col;
+	keys2[12] = &instance_col;
 
 	vals1[0].type = DB1_STR;
 	vals1[0].nul = 0;
@@ -624,6 +666,22 @@ int db_update_ucontact(ucontact_t* _c)
 	vals2[10].nul = 0;
 	vals2[10].val.time_val = _c->last_modified;
 
+	nr_cols2 = 11;
+	if(_c->ruid.len>0)
+	{
+		vals2[nr_cols2].type = DB1_STR;
+		vals2[nr_cols2].nul = 0;
+		vals2[nr_cols2].val.str_val = _c->ruid;
+		nr_cols2++;
+	}
+	if(_c->instance.len>0)
+	{
+		vals2[nr_cols2].type = DB1_STR;
+		vals2[nr_cols2].nul = 0;
+		vals2[nr_cols2].val.str_val = _c->instance;
+		nr_cols2++;
+	}
+
 	if (use_domain) {
 		vals1[3].type = DB1_STR;
 		vals1[3].nul = 0;
@@ -644,7 +702,7 @@ int db_update_ucontact(ucontact_t* _c)
 	}
 
 	if (ul_dbf.update(ul_dbh, keys1, 0, vals1, keys2, vals2, 
-	(use_domain) ? (4) : (3), 11) < 0) {
+	(use_domain) ? (4) : (3), nr_cols2) < 0) {
 		LM_ERR("updating database failed\n");
 		return -1;
 	}

+ 208 - 10
modules_k/usrloc/udomain.c

@@ -177,6 +177,7 @@ static inline void get_static_urecord(udomain_t* _d, str* _aor,
 
 	memset( &r, 0, sizeof(struct urecord) );
 	r.aor = *_aor;
+	r.aorhash = ul_get_aorhash(_aor);
 	r.domain = _d->name;
 	*_r = &r;
 }
@@ -333,6 +334,18 @@ static inline ucontact_info_t* dbrow2info( db_val_t *vals, str *contact)
 		ci.last_modified = VAL_TIME(vals+12);
 	}
 
+	/* record internal uid */
+	if (!VAL_NULL(vals+13)) {
+		ci.ruid.s = (char*)VAL_STRING(vals+13);
+		ci.ruid.len = strlen(ci.ruid.s);
+	}
+
+	/* sip instance */
+	if (!VAL_NULL(vals+14)) {
+		ci.instance.s = (char*)VAL_STRING(vals+14);
+		ci.instance.len = strlen(ci.instance.s);
+	}
+
 	return &ci;
 }
 
@@ -351,7 +364,7 @@ int preload_udomain(db1_con_t* _c, udomain_t* _d)
 	char uri[MAX_URI_SIZE];
 	ucontact_info_t *ci;
 	db_row_t *row;
-	db_key_t columns[15];
+	db_key_t columns[17];
 	db1_res_t* res = NULL;
 	str user, contact;
 	char* domain;
@@ -375,7 +388,9 @@ int preload_udomain(db1_con_t* _c, udomain_t* _d)
 	columns[11] = &sock_col;
 	columns[12] = &methods_col;
 	columns[13] = &last_mod_col;
-	columns[14] = &domain_col;
+	columns[14] = &ruid_col;
+	columns[15] = &instance_col;
+	columns[16] = &domain_col;
 
 	if (ul_dbf.use_table(_c, _d->name) < 0) {
 		LM_ERR("sql use_table failed\n");
@@ -387,7 +402,7 @@ int preload_udomain(db1_con_t* _c, udomain_t* _d)
 #endif
 
 	if (DB_CAPABILITY(ul_dbf, DB_CAP_FETCH)) {
-		if (ul_dbf.query(_c, 0, 0, 0, columns, 0, (use_domain)?(15):(14), 0,
+		if (ul_dbf.query(_c, 0, 0, 0, columns, 0, (use_domain)?(17):(16), 0,
 		0) < 0) {
 			LM_ERR("db_query (1) failed\n");
 			return -1;
@@ -433,8 +448,8 @@ int preload_udomain(db1_con_t* _c, udomain_t* _d)
 			}
 
 			if (use_domain) {
-				domain = (char*)VAL_STRING(ROW_VALUES(row) + 14);
-				if (VAL_NULL(ROW_VALUES(row)+14) || domain==0 || domain[0]==0){
+				domain = (char*)VAL_STRING(ROW_VALUES(row) + 16);
+				if (VAL_NULL(ROW_VALUES(row)+16) || domain==0 || domain[0]==0){
 					LM_CRIT("empty domain record for user %.*s...skipping\n",
 							user.len, user.s);
 					continue;
@@ -508,7 +523,7 @@ error:
 urecord_t* db_load_urecord(db1_con_t* _c, udomain_t* _d, str *_aor)
 {
 	ucontact_info_t *ci;
-	db_key_t columns[13];
+	db_key_t columns[15];
 	db_key_t keys[2];
 	db_key_t order;
 	db_val_t vals[2];
@@ -554,6 +569,8 @@ urecord_t* db_load_urecord(db1_con_t* _c, udomain_t* _d, str *_aor)
 	columns[10] = &sock_col;
 	columns[11] = &methods_col;
 	columns[12] = &last_mod_col;
+	columns[13] = &ruid_col;
+	columns[14] = &instance_col;
 
 	if (desc_time_order)
 		order = &last_mod_col;
@@ -565,7 +582,7 @@ urecord_t* db_load_urecord(db1_con_t* _c, udomain_t* _d, str *_aor)
 		return 0;
 	}
 
-	if (ul_dbf.query(_c, keys, 0, vals, columns, (use_domain)?2:1, 13, order,
+	if (ul_dbf.query(_c, keys, 0, vals, columns, (use_domain)?2:1, 15, order,
 				&res) < 0) {
 		LM_ERR("db_query failed\n");
 		return 0;
@@ -606,6 +623,129 @@ urecord_t* db_load_urecord(db1_con_t* _c, udomain_t* _d, str *_aor)
 	return r;
 }
 
+/*!
+ * \brief Loads from DB all contacts for a RUID
+ * \param _c database connection
+ * \param _d domain
+ * \param _aor address of record
+ * \return pointer to the record on success, 0 on errors or if nothing is found
+ */
+urecord_t* db_load_urecord_by_ruid(db1_con_t* _c, udomain_t* _d, str *_ruid)
+{
+	ucontact_info_t *ci;
+	db_key_t columns[17];
+	db_key_t keys[1];
+	db_key_t order;
+	db_val_t vals[1];
+	db1_res_t* res = NULL;
+	db_row_t *row;
+	str contact;
+	str aor;
+	char aorbuf[512];
+	str domain;
+	int i;
+
+	urecord_t* r;
+	ucontact_t* c;
+
+	keys[0] = &ruid_col;
+	vals[0].type = DB1_STR;
+	vals[0].nul = 0;
+	vals[0].val.str_val = *_ruid;
+
+	columns[0] = &contact_col;
+	columns[1] = &expires_col;
+	columns[2] = &q_col;
+	columns[3] = &callid_col;
+	columns[4] = &cseq_col;
+	columns[5] = &flags_col;
+	columns[6] = &cflags_col;
+	columns[7] = &user_agent_col;
+	columns[8] = &received_col;
+	columns[9] = &path_col;
+	columns[10] = &sock_col;
+	columns[11] = &methods_col;
+	columns[12] = &last_mod_col;
+	columns[13] = &ruid_col;
+	columns[14] = &instance_col;
+	columns[15] = &user_col;
+	columns[16] = &domain_col;
+
+	if (desc_time_order)
+		order = &last_mod_col;
+	else
+		order = &q_col;
+
+	if (ul_dbf.use_table(_c, _d->name) < 0) {
+		LM_ERR("failed to use table %.*s\n", _d->name->len, _d->name->s);
+		return 0;
+	}
+
+	if (ul_dbf.query(_c, keys, 0, vals, columns, 1, 15, order,
+				&res) < 0) {
+		LM_ERR("db_query failed\n");
+		return 0;
+	}
+
+	if (RES_ROW_N(res) == 0) {
+		LM_DBG("aor %.*s not found in table %.*s\n",_ruid->len, _ruid->s,
+				_d->name->len, _d->name->s);
+		ul_dbf.free_result(_c, res);
+		return 0;
+	}
+
+	r = 0;
+
+	/* use first row - shouldn't be more */
+	row = RES_ROWS(res);
+
+	ci = dbrow2info(  ROW_VALUES(RES_ROWS(res) + i), &contact);
+	if (ci==0) {
+		LM_ERR("skipping record for %.*s in table %s\n",
+				_ruid->len, _ruid->s, _d->name->s);
+		goto done;
+	}
+
+	aor.s = (char*)VAL_STRING(ROW_VALUES(row) + 15);
+	aor.len = strlen(aor.s);
+
+	if (use_domain) {
+		domain.s = (char*)VAL_STRING(ROW_VALUES(row) + 16);
+		if (VAL_NULL(ROW_VALUES(row)+16) || domain.s==0 || domain.s[0]==0){
+			LM_CRIT("empty domain record for user %.*s...skipping\n",
+					aor.len, aor.s);
+			goto done;
+		}
+		domain.len = strlen(domain.s);
+		if(aor.len + domain.len + 2 >= 512) {
+			LM_ERR("AoR is too big\n");
+			goto done;
+		}
+		memcpy(aorbuf, aor.s, aor.len);
+		aorbuf[aor.len] = '@';
+		memcpy(aorbuf + aor.len + 1, domain.s, domain.len);
+		aor.len += 1 + domain.len;
+		aor.s = aorbuf;
+		aor.s[aor.len] = '\0';
+	}
+	get_static_urecord( _d, &aor, &r);
+
+	if ( (c=mem_insert_ucontact(r, &contact, ci)) == 0) {
+		LM_ERR("mem_insert failed\n");
+		free_urecord(r);
+		ul_dbf.free_result(_c, res);
+		return 0;
+	}
+
+	/* We have to do this, because insert_ucontact sets state to CS_NEW
+	 * and we have the contact in the database already */
+	c->state = CS_SYNC;
+
+done:
+	ul_dbf.free_result(_c, res);
+	return r;
+}
+
 
 /*!
  * \brief Timer function to cleanup expired contacts, DB_ONLY db_mode
@@ -754,7 +894,7 @@ void lock_udomain(udomain_t* _d, str* _aor)
 	unsigned int sl;
 	if (db_mode!=DB_ONLY)
 	{
-		sl = core_hash(_aor, 0, _d->size);
+		sl = ul_get_aorhash(_aor) & (_d->size - 1);
 
 #ifdef GEN_LOCK_T_PREFERED
 		lock_get(_d->table[sl].lock);
@@ -775,7 +915,7 @@ void unlock_udomain(udomain_t* _d, str* _aor)
 	unsigned int sl;
 	if (db_mode!=DB_ONLY)
 	{
-		sl = core_hash(_aor, 0, _d->size);
+		sl = ul_get_aorhash(_aor) & (_d->size - 1);
 #ifdef GEN_LOCK_T_PREFERED
 		lock_release(_d->table[sl].lock);
 #else
@@ -852,7 +992,7 @@ int get_urecord(udomain_t* _d, str* _aor, struct urecord** _r)
 
 	if (db_mode!=DB_ONLY) {
 		/* search in cache */
-		aorhash = core_hash(_aor, 0, 0);
+		aorhash = ul_get_aorhash(_aor);
 		sl = aorhash&(_d->size-1);
 		r = _d->table[sl].first;
 
@@ -877,6 +1017,64 @@ int get_urecord(udomain_t* _d, str* _aor, struct urecord** _r)
 	return 1;   /* Nothing found */
 }
 
+/*!
+ * \brief Obtain a urecord pointer if the urecord exists in domain (lock slot)
+ * \param _d domain to search the record
+ * \param _aorhash hash id for address of record
+ * \param _ruid record internal unique id
+ * \param _r store pointer to location record
+ * \param _c store pointer to contact structure
+ * \return 0 if a record was found, 1 if nothing could be found
+ */
+int get_urecord_by_ruid(udomain_t* _d, unsigned int _aorhash,
+		str *_ruid, struct urecord** _r, struct ucontact** _c)
+{
+	unsigned int sl, i;
+	urecord_t* r;
+	ucontact_t* c;
+
+	sl = _aorhash&(_d->size-1);
+	lock_ulslot(_d, sl);
+
+	if (db_mode!=DB_ONLY) {
+		/* search in cache */
+		r = _d->table[sl].first;
+
+		for(i = 0; i < _d->table[sl].n; i++) {
+			if(r->aorhash==_aorhash) {
+				c = r->contacts;
+				while(c) {
+					if(c->ruid.len==_ruid->len
+							&& !memcmp(c->ruid.s, _ruid->s, _ruid->len)) {
+						*_r = r;
+						*_c = c;
+						return 0;
+					}
+				}
+			}
+			r = r->next;
+		}
+	} else {
+		/* search in DB */
+		r = db_load_urecord_by_ruid(ul_dbh, _d, _ruid);
+		if (r) {
+			if(r->aorhash==_aorhash) {
+				c = r->contacts;
+				while(c) {
+					if(c->ruid.len==_ruid->len
+							&& !memcmp(c->ruid.s, _ruid->s, _ruid->len)) {
+						*_r = r;
+						*_c = c;
+						return 0;
+					}
+				}
+			}
+		}
+	}
+
+	unlock_ulslot(_d, (_aorhash & (_d->size - 1)));
+	return -1;   /* Nothing found */
+}
 
 /*!
  * \brief Delete a urecord from domain

+ 11 - 0
modules_k/usrloc/udomain.h

@@ -189,6 +189,17 @@ int insert_urecord(udomain_t* _d, str* _aor, struct urecord** _r);
  */
 int get_urecord(udomain_t* _d, str* _aor, struct urecord** _r);
 
+/*!
+ * \brief Obtain a urecord pointer if the urecord exists in domain (lock slot)
+ * \param _d domain to search the record
+ * \param _aorhash hash id for address of record
+ * \param _ruid record internal unique id
+ * \param _r store pointer to location record
+ * \param _c store pointer to contact structure
+ * \return 0 if a record was found, 1 if nothing could be found
+ */
+int get_urecord_by_ruid(udomain_t* _d, unsigned int _aorhash,
+		str *_ruid, struct urecord** _r, struct ucontact** _c);
 
 /*!
  * \brief Delete a urecord from domain

+ 23 - 0
modules_k/usrloc/ul_mi.c

@@ -125,6 +125,14 @@ static inline int mi_add_aor_node(struct mi_node *parent, urecord_t* r, time_t t
 	if (short_dump)
 		return 0;
 
+#if 0
+	/* aor hash */
+	p = int2str((unsigned long)r->aorhash, &len);
+	node = add_mi_node_child( anode, MI_DUP_VALUE, "HashID", 6, p, len);
+	if (node==0)
+		return -1;
+#endif
+
 	for( c=r->contacts ; c ; c=c->next) {
 		/* contact */
 		cnode = add_mi_node_child( anode, MI_DUP_VALUE, "Contact", 7,
@@ -227,6 +235,21 @@ static inline int mi_add_aor_node(struct mi_node *parent, urecord_t* r, time_t t
 		if (node==0)
 			return -1;
 
+		/* ruid */
+		if (c->ruid.len) {
+			node = add_mi_node_child( cnode, MI_DUP_VALUE, "Ruid", 4,
+				c->ruid.s, c->ruid.len);
+			if (node==0)
+				return -1;
+		}
+
+		/* instance */
+		if (c->instance.len) {
+			node = add_mi_node_child( cnode, MI_DUP_VALUE, "Instance", 8,
+				c->instance.s, c->instance.len);
+			if (node==0)
+				return -1;
+		}
 	} /* for */
 
 	return 0;

+ 8 - 0
modules_k/usrloc/ul_mod.c

@@ -70,6 +70,7 @@
 
 MODULE_VERSION
 
+#define RUID_COL       "ruid"
 #define USER_COL       "username"
 #define DOMAIN_COL     "domain"
 #define CONTACT_COL    "contact"
@@ -84,6 +85,7 @@ MODULE_VERSION
 #define PATH_COL       "path"
 #define SOCK_COL       "socket"
 #define METHODS_COL    "methods"
+#define INSTANCE_COL   "instance"
 #define LAST_MOD_COL   "last_modified"
 
 static int mod_init(void);                          /*!< Module initialization function */
@@ -105,6 +107,7 @@ int ul_db_update_as_insert = 0;
  * Module parameters and their default values
  */
 
+str ruid_col        = str_init(RUID_COL); 		/*!< Name of column containing record unique id */
 str user_col        = str_init(USER_COL); 		/*!< Name of column containing usernames */
 str domain_col      = str_init(DOMAIN_COL); 		/*!< Name of column containing domains */
 str contact_col     = str_init(CONTACT_COL);		/*!< Name of column containing contact addresses */
@@ -119,6 +122,7 @@ str received_col    = str_init(RECEIVED_COL);		/*!< Name of column containing tr
 str path_col        = str_init(PATH_COL);		/*!< Name of column containing the Path header */
 str sock_col        = str_init(SOCK_COL);		/*!< Name of column containing the received socket */
 str methods_col     = str_init(METHODS_COL);		/*!< Name of column containing the supported methods */
+str instance_col    = str_init(INSTANCE_COL);		/*!< Name of column containing the SIP instance value */
 str last_mod_col     = str_init(LAST_MOD_COL);		/*!< Name of column containing the last modified date */
 str db_url          = str_init(DEFAULT_DB_URL);		/*!< Database URL */
 int timer_interval  = 60;				/*!< Timer interval in seconds */
@@ -151,6 +155,7 @@ static cmd_export_t cmds[] = {
  * Exported parameters 
  */
 static param_export_t params[] = {
+	{"ruid_column",         STR_PARAM, &ruid_col.s      },
 	{"user_column",         STR_PARAM, &user_col.s      },
 	{"domain_column",       STR_PARAM, &domain_col.s    },
 	{"contact_column",      STR_PARAM, &contact_col.s   },
@@ -170,6 +175,7 @@ static param_export_t params[] = {
 	{"path_column",         STR_PARAM, &path_col.s      },
 	{"socket_column",       STR_PARAM, &sock_col.s      },
 	{"methods_column",      STR_PARAM, &methods_col.s   },
+	{"instance_column",     STR_PARAM, &instance_col.s  },
 	{"matching_mode",       INT_PARAM, &matching_mode   },
 	{"cseq_delay",          INT_PARAM, &cseq_delay      },
 	{"fetch_rows",          INT_PARAM, &ul_fetch_rows   },
@@ -249,6 +255,7 @@ static int mod_init(void)
 	}
 
 	/* Compute the lengths of string parameters */
+	ruid_col.len = strlen(ruid_col.s);
 	user_col.len = strlen(user_col.s);
 	domain_col.len = strlen(domain_col.s);
 	contact_col.len = strlen(contact_col.s);
@@ -263,6 +270,7 @@ static int mod_init(void)
 	path_col.len = strlen(path_col.s);
 	sock_col.len = strlen(sock_col.s);
 	methods_col.len = strlen(methods_col.s);
+	instance_col.len = strlen(instance_col.s);
 	last_mod_col.len = strlen(last_mod_col.s);
 	db_url.len = strlen(db_url.s);
 

+ 3 - 1
modules_k/usrloc/ul_mod.h

@@ -43,8 +43,9 @@
  */
 
 
-#define UL_TABLE_VERSION 1004
+#define UL_TABLE_VERSION 5
 
+extern str ruid_col;
 extern str user_col;
 extern str domain_col;
 extern str contact_col;
@@ -59,6 +60,7 @@ extern str received_col;
 extern str path_col;
 extern str sock_col;
 extern str methods_col;
+extern str instance_col;
 extern str last_mod_col;
 
 extern str db_url;

+ 19 - 1
modules_k/usrloc/ul_rpc.c

@@ -87,8 +87,9 @@ static void ul_rpc_dump(rpc_t* rpc, void* ctx)
 						return;
 					}
 				} else {
-					if(rpc->struct_add(ah, "S{",
+					if(rpc->struct_add(ah, "Sd{",
 							"AoR", &r->aor,
+							"HashID", r->aorhash,
 							"Contacts", &ih)<0)
 					{
 						unlock_ulslot( dom, i);
@@ -261,6 +262,23 @@ static void ul_rpc_dump(rpc_t* rpc, void* ctx)
 									"Internal error adding methods");
 							return;
 						}
+						if(rpc->struct_add(vh, "S",
+								"Ruid", (c->ruid.len)?&c->ruid: &empty_str)<0)
+						{
+							unlock_ulslot( dom, i);
+							rpc->fault(ctx, 500,
+									"Internal error adding ruid");
+							return;
+						}
+						if(rpc->struct_add(vh, "S",
+								"Instance",
+								(c->instance.len)?&c->instance: &empty_str)<0)
+						{
+							unlock_ulslot( dom, i);
+							rpc->fault(ctx, 500,
+									"Internal error adding instance");
+							return;
+						}
 					}
 				}
 			}

+ 46 - 1
modules_k/usrloc/urecord.c

@@ -77,7 +77,7 @@ int new_urecord(str* _dom, str* _aor, urecord_t** _r)
 	memcpy((*_r)->aor.s, _aor->s, _aor->len);
 	(*_r)->aor.len = _aor->len;
 	(*_r)->domain = _dom;
-	(*_r)->aorhash = core_hash(_aor, 0, 0);
+	(*_r)->aorhash = ul_get_aorhash(_aor);
 	return 0;
 }
 
@@ -638,3 +638,48 @@ int get_ucontact(urecord_t* _r, str* _c, str* _callid, str* _path, int _cseq,
 
 	return 1;
 }
+
+/*
+ * Get pointer to ucontact with given contact and/or given sip.instance
+ */
+int get_ucontact_by_instance(urecord_t* _r, str* _c, str* _callid, str* _path, int _cseq,
+							str* _inst, struct ucontact** _co)
+{
+	ucontact_t* ptr;
+	str i1;
+	str i2;
+	
+	if (_inst == NULL || _inst->len <= 0) {
+		return get_ucontact(_r, _c, _callid, _path, _cseq, _co);
+	}
+
+	/* find by instance */
+	ptr = _r->contacts;
+	while(ptr) {
+		if (ptr->instance.len>0)
+		{
+			i1 = *_inst;
+			i2 = ptr->instance;
+			if(i1.s[0]=='<' && i1.s[i1.len-1]=='>') {
+				i1.s++;
+				i1.len-=2;
+			}
+			if(i2.s[0]=='<' && i2.s[i2.len-1]=='>') {
+				i2.s++;
+				i2.len-=2;
+			}
+			if(i1.len==i2.len && memcmp(i1.s, i2.s, i2.len)==0) {
+				*_co = ptr;
+				return 0;
+			}
+		}
+		
+		ptr = ptr->next;
+	}
+	return 1;
+}
+
+unsigned int ul_get_aorhash(str *_aor)
+{
+	return core_hash(_aor, 0, 0);
+}

+ 15 - 0
modules_k/usrloc/urecord.h

@@ -169,4 +169,19 @@ int get_ucontact(urecord_t* _r, str* _c, str* _callid, str* _path,
 		int _cseq,
 		struct ucontact** _co);
 
+/*!
+ * \brief Get pointer to ucontact with given contact
+ * \param _r record where to search the contacts
+ * \param _c contact string
+ * \param _callid callid
+ * \param _path path 
+ * \param _cseq CSEQ number
+ * \param _inst SIP instance
+ * \param _co found contact
+ * \return 0 - found, 1 - not found, -1 - invalid found, 
+ * -2 - found, but to be skipped (same cseq)
+ */
+int get_ucontact_by_instance(urecord_t* _r, str* _c, str* _callid, str* _path, int _cseq,
+							str* _inst, struct ucontact** _co);
+
 #endif

+ 4 - 0
modules_k/usrloc/usrloc.c

@@ -78,6 +78,10 @@ int bind_usrloc(usrloc_api_t* api)
 	api->get_ucontact       = get_ucontact;
 	api->update_ucontact    = update_ucontact;
 	api->register_ulcb      = register_ulcb;
+	api->get_aorhash        = ul_get_aorhash;
+
+	api->get_urecord_by_ruid      = get_urecord_by_ruid;
+	api->get_ucontact_by_instance = get_ucontact_by_instance;
 
 	api->use_domain = use_domain;
 	api->db_mode    = db_mode;

+ 19 - 1
modules_k/usrloc/usrloc.h

@@ -68,6 +68,7 @@ struct socket_info;
 /*! \brief Main structure for handling of registered Contact data */
 typedef struct ucontact {
 	str* domain;            /*!< Pointer to domain name (NULL terminated) */
+	str ruid;               /*!< Pointer to record internal unique id */
 	str* aor;               /*!< Pointer to the AOR string in record structure*/
 	str c;                  /*!< Contact address */
 	str received;           /*!< IP+port+protocol we received the REGISTER from */
@@ -83,6 +84,7 @@ typedef struct ucontact {
 	struct socket_info *sock; /*!< received socket */
 	time_t last_modified;   /*!< When the record was last modified */
 	unsigned int methods;   /*!< Supported methods */
+	str instance;           /*!< SIP instance value - gruu */
 	struct ucontact* next;  /*!< Next contact in the linked list */
 	struct ucontact* prev;  /*!< Previous contact in the linked list */
 } ucontact_t;
@@ -90,6 +92,7 @@ typedef struct ucontact {
 
 /*! \brief Informations related to a contact */
 typedef struct ucontact_info {
+	str ruid;                 /*!< Pointer to record internal unique id */
 	str received;             /*!< Received interface */
 	str* path;                /*!< Path informations */
 	time_t expires;           /*!< Contact expires */
@@ -101,6 +104,7 @@ typedef struct ucontact_info {
 	str *user_agent;          /*!< user agent header */
 	struct socket_info *sock; /*!< socket informations */
 	unsigned int methods;     /*!< supported methods */
+	str instance;             /*!< SIP instance value - gruu */
 	time_t last_modified;     /*!< last modified */
 } ucontact_info_t;
 
@@ -126,7 +130,10 @@ typedef struct urecord {
 
 typedef int (*insert_urecord_t)(struct udomain* _d, str* _aor, struct urecord** _r);
 
-typedef int  (*get_urecord_t)(struct udomain* _d, str* _aor, struct urecord** _r);
+typedef int (*get_urecord_t)(struct udomain* _d, str* _aor, struct urecord** _r);
+
+typedef int (*get_urecord_by_ruid_t)(udomain_t* _d, unsigned int _aorhash,
+		str *_ruid, struct urecord** _r, struct ucontact** _c);
 
 typedef int  (*delete_urecord_t)(struct udomain* _d, str* _aor, struct urecord* _r);
 
@@ -143,6 +150,10 @@ typedef int (*get_ucontact_t)(struct urecord* _r, str* _c, str* _callid,
 		str* _path, int _cseq,
 		struct ucontact** _co);
 
+typedef int (*get_ucontact_by_instance_t)(struct urecord* _r, str* _c, str* _callid,
+		str* _path, int _cseq, str* _inst,
+		struct ucontact** _co);
+
 typedef void (*lock_udomain_t)(struct udomain* _d, str *_aor);
 
 typedef void (*unlock_udomain_t)(struct udomain* _d, str *_aor);
@@ -154,6 +165,9 @@ typedef int  (*get_all_ucontacts_t) (void* buf, int len, unsigned int flags,
 
 typedef int (*get_udomain_t)(const char* _n, udomain_t** _d);
 
+typedef unsigned int (*ul_get_aorhash_t)(str *_aor);
+unsigned int ul_get_aorhash(str *_aor);
+
 /*! usrloc API export structure */
 typedef struct usrloc_api {
 	int           use_domain; /*! use_domain module parameter */
@@ -175,9 +189,13 @@ typedef struct usrloc_api {
 	delete_ucontact_t    delete_ucontact;
 	get_ucontact_t       get_ucontact;
 
+	get_urecord_by_ruid_t       get_urecord_by_ruid;
+	get_ucontact_by_instance_t  get_ucontact_by_instance;
+
 	update_ucontact_t    update_ucontact;
 
 	register_ulcb_t      register_ulcb;
+	ul_get_aorhash_t     get_aorhash;
 } usrloc_api_t;