diff --git a/modules/uac/doc/uac_admin.xml b/modules/uac/doc/uac_admin.xml index b1dc06f056b..a20515f05f9 100644 --- a/modules/uac/doc/uac_admin.xml +++ b/modules/uac/doc/uac_admin.xml @@ -429,6 +429,24 @@ modparam("uac", "reg_retry_interval", 300) +
+ <varname>reg_random_delay</varname> (int) + + Set a random reg_delay for each registration that has + reg_delay set to 0 in the database. + If set to 0, randomization will be disabled. + + The default value is 0 sec (disabled) + + Set <varname>reg_random_delay</varname> parameter + +... +modparam("uac", "reg_random_delay", 300) +... + + +
+
<varname>reg_db_table</varname> (string) @@ -462,6 +480,26 @@ modparam("uac", "reg_db_table", "uacreg") ... modparam("uac", "reg_contact_addr", "192.168.1.2:5080") +... + + +
+ +
+ <varname>reg_keep_callid</varname> (string) + + If set to 0 (default), a new Call-Id will be generated for each + registration attempt. + If set to non-zero, the same Call-Id will be used for + re-registrations, as recommended by RFC3261 section 10.2.4. + A new Call-Id will be generated when a previous registration + had failed. + + + Set <varname>reg_keep_callid</varname> parameter + +... +modparam("uac", "reg_keep_callid", 1) ... @@ -765,6 +803,57 @@ if(uac_reg_lookup("$rU", "$ru")) { lookup("location"); } +... + + +
+
+ + <function moreinfo="none">uac_reg_status(uuid)</function> + + + This function returns the current registration status for the uuid. + + + Return values: + + + + 1 - a valid registration exists. + + + + + -1 - uuid does not exist or an error occurred while executing + the function. + + + + + -2 - a registration attempt is ongoing (and currently there is + no valid registration). + + + + + -3 - registration is disabled. + + + + + -99 - no valid registration, waiting for new registration attempt. + + + + + + This function can be used from ANY_ROUTE. + + + <function>uac_reg_status</function> usage + +... +$var(status) = uac_reg_status("$rU"); ... diff --git a/modules/uac/uac.c b/modules/uac/uac.c index 7860ebfacf6..afd0cadc317 100644 --- a/modules/uac/uac.c +++ b/modules/uac/uac.c @@ -95,6 +95,7 @@ static int w_replace_to(struct sip_msg* msg, char* p1, char* p2); static int w_restore_to(struct sip_msg* msg); static int w_uac_auth(struct sip_msg* msg, char* str, char* str2); static int w_uac_reg_lookup(struct sip_msg* msg, char* src, char* dst); +static int w_uac_reg_status(struct sip_msg* msg, char* src, char* dst); static int w_uac_reg_request_to(struct sip_msg* msg, char* src, char* mode_s); static int fixup_replace_uri(void** param, int param_no); static int mod_init(void); @@ -127,6 +128,8 @@ static cmd_export_t cmds[]={ {"uac_req_send", (cmd_function)w_uac_req_send, 0, 0, 0, ANY_ROUTE}, {"uac_reg_lookup", (cmd_function)w_uac_reg_lookup, 2, fixup_pvar_pvar, fixup_free_pvar_pvar, ANY_ROUTE }, + {"uac_reg_status", (cmd_function)w_uac_reg_status, 1, fixup_pvar_pvar, 0, + ANY_ROUTE }, {"uac_reg_request_to", (cmd_function)w_uac_reg_request_to, 2, fixup_pvar_uint, fixup_free_pvar_uint, REQUEST_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE }, {"bind_uac", (cmd_function)bind_uac, 1, 0, 0, 0}, @@ -153,6 +156,8 @@ static param_export_t params[] = { {"reg_contact_addr", PARAM_STR, ®_contact_addr }, {"reg_timer_interval", INT_PARAM, ®_timer_interval }, {"reg_retry_interval", INT_PARAM, ®_retry_interval }, + {"reg_keep_callid", INT_PARAM, ®_keep_callid }, + {"reg_random_delay", INT_PARAM, ®_random_delay }, {0, 0, 0} }; @@ -569,6 +574,27 @@ static int w_uac_reg_lookup(struct sip_msg* msg, char* src, char* dst) } +static int w_uac_reg_status(struct sip_msg* msg, char* src, char* dst) +{ + pv_spec_t *spv; + pv_value_t val; + + spv = (pv_spec_t*)src; + if(pv_get_spec_value(msg, spv, &val) != 0) + { + LM_ERR("cannot get src uri value\n"); + return -1; + } + + if (!(val.flags & PV_VAL_STR)) + { + LM_ERR("src pv value is not string\n"); + return -1; + } + return uac_reg_status(msg, &val.rs, 0); +} + + static int w_uac_reg_request_to(struct sip_msg* msg, char* src, char* mode_s) { pv_spec_t *spv; diff --git a/modules/uac/uac_reg.c b/modules/uac/uac_reg.c index fcfb6d262e6..8a1fa384f03 100644 --- a/modules/uac/uac_reg.c +++ b/modules/uac/uac_reg.c @@ -39,6 +39,7 @@ #include "../../parser/parse_uri.h" #include "../../parser/parse_from.h" #include "../../parser/parse_to.h" +#include "../../parser/parse_expires.h" #include "../../parser/contact/parse_contact.h" #include "../../rpc.h" #include "../../rpc_lookup.h" @@ -57,9 +58,7 @@ #define MAX_UACH_SIZE 2048 #define UAC_REG_GC_INTERVAL 150 -#define UAC_REG_MAX_PASSWD_SIZE 63 - -#define UAC_REG_MAX_URI_SIZE 127 +#define UAC_REG_TM_CALLID_SIZE 90 typedef struct _reg_uac { @@ -74,11 +73,14 @@ typedef struct _reg_uac str auth_proxy; str auth_username; str auth_password; + str callid; + unsigned int cseq; unsigned int flags; unsigned int expires; time_t timer_expires; unsigned int reg_delay; time_t reg_init; + gen_lock_t *lock; } reg_uac_t; typedef struct _reg_item @@ -113,6 +115,8 @@ int reg_timer_interval = 90; int reg_retry_interval = 0; int reg_htable_size = 4; int reg_fetch_rows = 1000; +int reg_keep_callid = 0; +int reg_random_delay = 0; str reg_contact_addr = STR_NULL; str reg_db_url = STR_NULL; str reg_db_table = str_init("uacreg"); @@ -130,6 +134,7 @@ str expires_column = str_init("expires"); str flags_column = str_init("flags"); str reg_delay_column = str_init("reg_delay"); +str str_empty = str_init(""); #if 0 INSERT INTO version (table_name, table_version) values ('uacreg','1'); @@ -511,28 +516,16 @@ int reg_ht_add(reg_uac_t *reg) LM_ERR("bad parameters: %p/%p\n", reg, _reg_htable); return -1; } - if(reg->auth_password.len>UAC_REG_MAX_PASSWD_SIZE) - { - LM_ERR("bad parameters: %p/%p -- password too long %d\n", - reg, _reg_htable, reg->auth_password.len); - return -1; - } - if(reg->auth_proxy.len>UAC_REG_MAX_URI_SIZE) - { - LM_ERR("bad parameters: %p/%p -- proxy uri too long %d\n", - reg, _reg_htable, reg->auth_proxy.len); - return -1; - } - len = reg->l_uuid.len + 1 + reg->l_username.len + 1 + reg->l_domain.len + 1 + reg->r_username.len + 1 + reg->r_domain.len + 1 + reg->realm.len + 1 - + UAC_REG_MAX_URI_SIZE /*reg->auth_proxy.len*/ + 1 + + reg->auth_proxy.len + 1 + reg->auth_username.len + 1 - + UAC_REG_MAX_PASSWD_SIZE /*reg->auth_password.len*/ + 1; + + reg->auth_password.len + 1 + + (reg_keep_callid ? UAC_REG_TM_CALLID_SIZE : 0) + 1; nr = (reg_uac_t*)shm_malloc(sizeof(reg_uac_t) + len); if(nr==NULL) { @@ -542,7 +535,10 @@ int reg_ht_add(reg_uac_t *reg) memset(nr, 0, sizeof(reg_uac_t) + len); nr->expires = reg->expires; nr->flags = reg->flags; - nr->reg_delay = reg->reg_delay; + if (reg->reg_delay) + nr->reg_delay = reg->reg_delay; + else if (reg_random_delay>0) + nr->reg_delay = rand() % reg_random_delay; nr->reg_init = time(NULL); nr->h_uuid = reg_compute_hash(®->l_uuid); nr->h_user = reg_compute_hash(®->l_username); @@ -555,10 +551,10 @@ int reg_ht_add(reg_uac_t *reg) reg_copy_shm(&nr->r_username, ®->r_username, 0); reg_copy_shm(&nr->r_domain, ®->r_domain, 0); reg_copy_shm(&nr->realm, ®->realm, 0); - reg_copy_shm(&nr->auth_proxy, ®->auth_proxy, UAC_REG_MAX_URI_SIZE); + reg_copy_shm(&nr->auth_proxy, ®->auth_proxy, 0); reg_copy_shm(&nr->auth_username, ®->auth_username, 0); - /* password at the end, to be able to update it easily */ reg_copy_shm(&nr->auth_password, ®->auth_password, 0); + reg_copy_shm(&nr->callid, &str_empty, reg_keep_callid ? UAC_REG_TM_CALLID_SIZE : 0); reg_ht_add_byuser(nr); reg_ht_add_byuuid(nr); @@ -568,61 +564,82 @@ int reg_ht_add(reg_uac_t *reg) } -/** - * - */ -int reg_ht_update_attrs(reg_uac_t *reg) + /** + * + */ +int reg_ht_rm(reg_uac_t *reg) { - unsigned int slot; - reg_item_t *ri = NULL; + unsigned int slot1, slot2; + reg_item_t *it = NULL; + reg_item_t *prev = NULL; + int found = 0; - if(_reg_htable==NULL) + if (reg == NULL) { - LM_ERR("reg hash table not initialized\n"); + LM_ERR("bad parameter\n"); return -1; } - if(reg->auth_password.len>UAC_REG_MAX_PASSWD_SIZE) + /* by uuid */ + slot1 = reg_get_entry(reg->h_uuid, _reg_htable->htsize); + it = _reg_htable->entries[slot1].byuuid; + while (it) { - LM_ERR("password is too big: %d\n", reg->auth_password.len); - return -1; - } - if(reg->auth_proxy.len>UAC_REG_MAX_URI_SIZE) - { - LM_ERR("proxy uri is too big: %d\n", reg->auth_proxy.len); - return -1; + if (it->r == reg) + { + if (prev) + prev->next=it->next; + else + _reg_htable->entries[slot1].byuuid = it->next; + _reg_htable->entries[slot1].isize--; + shm_free(it); + found = 1; + break; + } + prev = it; + it = it->next; } - slot = reg_get_entry(reg->h_user, _reg_htable->htsize); - lock_get(&_reg_htable->entries[slot].lock); - ri = _reg_htable->entries[slot].byuser; - while(ri) { - if(ri->r->l_uuid.len == reg->l_uuid.len - && strncmp(ri->r->l_uuid.s, reg->l_uuid.s, reg->l_uuid.len)==0) + /* by user */ + prev = NULL; + slot2 = reg_get_entry(reg->h_user, _reg_htable->htsize); + if (slot2 != slot1) { + lock_get(&_reg_htable->entries[slot2].lock); + } + it = _reg_htable->entries[slot2].byuser; + while (it) + { + if (it->r == reg) { - /* record found */ - strncpy(ri->r->auth_password.s, reg->auth_password.s, reg->auth_password.len); - ri->r->auth_password.len = reg->auth_password.len; - ri->r->auth_password.s[reg->auth_password.len] = '\0'; - strncpy(ri->r->auth_proxy.s, reg->auth_proxy.s, reg->auth_proxy.len); - ri->r->auth_proxy.len = reg->auth_proxy.len; - ri->r->auth_proxy.s[reg->auth_proxy.len] = '\0'; - if(reg->flags & UAC_REG_DISABLED) - ri->r->flags |= UAC_REG_DISABLED; + if (prev) + prev->next=it->next; else - ri->r->flags &= ~UAC_REG_DISABLED; - lock_release(&_reg_htable->entries[slot].lock); - return 0; + _reg_htable->entries[slot2].byuser = it->next; + _reg_htable->entries[slot2].usize--; + shm_free(it); + break; } - ri = ri->next; + prev = it; + it = it->next; } - lock_release(&_reg_htable->entries[slot].lock); - return -1; + + shm_free(reg); + if (slot2 != slot1) { + lock_release(&_reg_htable->entries[slot2].lock); + } + lock_release(&_reg_htable->entries[slot1].lock); + + if (found) { + counter_add(regtotal, -1); + if(reg->flags & UAC_REG_ONLINE) + counter_add(regactive, -1); + if(reg->flags & UAC_REG_DISABLED) + counter_add(regdisabled, -1); + } + return 0; } -/** - * - */ + reg_uac_t *reg_ht_get_byuuid(str *uuid) { unsigned int hash; @@ -637,16 +654,19 @@ reg_uac_t *reg_ht_get_byuuid(str *uuid) hash = reg_compute_hash(uuid); slot = reg_get_entry(hash, _reg_htable->htsize); + lock_get(&_reg_htable->entries[slot].lock); it = _reg_htable->entries[slot].byuuid; while(it) { if((it->r->h_uuid==hash) && (it->r->l_uuid.len==uuid->len) && (strncmp(it->r->l_uuid.s, uuid->s, uuid->len)==0)) { + it->r->lock = &_reg_htable->entries[slot].lock; return it->r; } it = it->next; } + lock_release(&_reg_htable->entries[slot].lock); return NULL; } @@ -667,10 +687,11 @@ reg_uac_t *reg_ht_get_byuser(str *user, str *domain) hash = reg_compute_hash(user); slot = reg_get_entry(hash, _reg_htable->htsize); + lock_get(&_reg_htable->entries[slot].lock); it = _reg_htable->entries[slot].byuser; while(it) { - if((it->r->h_uuid==hash) && (it->r->l_username.len==user->len) + if((it->r->h_user==hash) && (it->r->l_username.len==user->len) && (strncmp(it->r->l_username.s, user->s, user->len)==0)) { if(domain!=NULL && domain->s!=NULL) @@ -678,17 +699,68 @@ reg_uac_t *reg_ht_get_byuser(str *user, str *domain) if((it->r->l_domain.len==domain->len) && (strncmp(it->r->l_domain.s, domain->s, domain->len)==0)) { + it->r->lock = &_reg_htable->entries[slot].lock; return it->r; } } else { + it->r->lock = &_reg_htable->entries[slot].lock; return it->r; } } it = it->next; } + lock_release(&_reg_htable->entries[slot].lock); return NULL; } +int reg_ht_get_byfilter(reg_uac_t **reg, str *attr, str *val) +{ + int i; + str *rval; + reg_item_t *it; + + /* try to use the hash table indices */ + if(attr->len==6 && strncmp(attr->s, "l_uuid", 6)==0) { + *reg = reg_ht_get_byuuid(val); + return *reg != NULL; + } + if(attr->len==10 && strncmp(attr->s, "l_username", 10)==0) { + *reg = reg_ht_get_byuser(val, NULL); + return *reg != NULL; + } + + /* check _all_ records */ + for(i=0; i<_reg_htable->htsize; i++) + { + lock_get(&_reg_htable->entries[i].lock); + /* walk through entries */ + it = _reg_htable->entries[i].byuuid; + while(it) + { + if(attr->len==10 && strncmp(attr->s, "r_username", 10)==0) { + rval = &it->r->r_username; + } else if(attr->len==13 && strncmp(attr->s, "auth_username", 13)==0) { + rval = &it->r->auth_username; + } else { + lock_release(&_reg_htable->entries[i].lock); + LM_ERR("unsupported filter attribute %.*s\n", attr->len, attr->s); + return -1; + } + + if(rval->len==val->len && strncmp(val->s, rval->s, val->len)==0) { + /* found */ + *reg = it->r; + (*reg)->lock = &_reg_htable->entries[i].lock; + return 1; + } + it = it->next; + } + lock_release(&_reg_htable->entries[i].lock); + } + *reg = NULL; + return 0; +} + int uac_reg_tmdlg(dlg_t *tmdlg, sip_msg_t *rpl) { if(tmdlg==NULL || rpl==NULL) @@ -807,13 +879,18 @@ void uac_reg_tm_callback( struct cell *t, int type, struct tmcb_params *ps) expires=0; if(c->expires==NULL || c->expires->body.len<=0) { - if(ps->rpl->expires!=NULL && ps->rpl->expires->body.len>0) - expires = atoi(ps->rpl->expires->body.s); + if(ps->rpl->expires!=NULL && parse_expires(ps->rpl->expires)==0) + expires = ((exp_body_t *)ps->rpl->expires->parsed)->val; } else { str2int(&c->expires->body, (unsigned int*)(&expires)); } ri->timer_expires = ri->timer_expires + expires; ri->flags |= UAC_REG_ONLINE; + if (reg_keep_callid && ps->rpl->callid->body.len < UAC_REG_TM_CALLID_SIZE) { + ri->callid.len = ps->rpl->callid->body.len; + memcpy(ri->callid.s, ps->rpl->callid->body.s, ri->callid.len); + str2int(&(get_cseq(ps->rpl)->number), &ri->cseq); + } goto done; } if (contact_iterator(&c, ps->rpl, c) < 0) @@ -930,8 +1007,27 @@ void uac_reg_tm_callback( struct cell *t, int type, struct tmcb_params *ps) } ri->flags |= UAC_REG_AUTHSENT; + lock_release(ri->lock); return; - } else { + } + + if (ps->code == 423) /* Interval too brief, retry with longer expiry */ + { + if (parse_headers(ps->rpl, HDR_EOH_F, 0) == -1) { + LM_ERR("failed to parse headers\n"); + goto error; + } + if(ps->rpl->min_expires!=NULL && parse_expires(ps->rpl->min_expires)==0) { + ri->expires = ((exp_body_t *)ps->rpl->min_expires->parsed)->val; + } else { + ri->expires *= 2; + } + LM_DBG("got 423 response while registering [%.*s], set new expires to %d\n", + ri->l_uuid.len, ri->l_uuid.s, ri->expires); + /* Retry will be done on next timer interval */ + goto done; + } else + { LM_ERR("got sip response %d while registering [%.*s]\n", ps->code, ri->l_uuid.len, ri->l_uuid.s); goto error; @@ -944,9 +1040,12 @@ void uac_reg_tm_callback( struct cell *t, int type, struct tmcb_params *ps) ri->flags |= UAC_REG_DISABLED; counter_inc(regdisabled); } + ri->cseq = 0; done: - if(ri) + if(ri) { ri->flags &= ~(UAC_REG_ONGOING|UAC_REG_AUTHSENT); + lock_release(ri->lock); + } shm_free(uuid); counter_inc(regactive); } @@ -963,6 +1062,7 @@ int uac_reg_update(reg_uac_t *reg, time_t tn) str s_turi; char b_hdrs[MAX_UACH_SIZE]; str s_hdrs; + dlg_t tmdlg; if(uac_tmb.t_request==NULL) return -1; @@ -984,8 +1084,8 @@ int uac_reg_update(reg_uac_t *reg, time_t tn) return 2; } } + reg->flags |= UAC_REG_INIT; } - reg->flags |= UAC_REG_INIT; if(reg->timer_expires > tn + reg_timer_interval + 3) return 3; @@ -997,7 +1097,6 @@ int uac_reg_update(reg_uac_t *reg, time_t tn) } reg->timer_expires = tn; reg->flags |= UAC_REG_ONGOING; - reg->flags &= ~UAC_REG_ONLINE; counter_add(regactive, -1); /* Take it out of the active pool while re-registering */ memcpy(uuid, reg->l_uuid.s, reg->l_uuid.len); uuid[reg->l_uuid.len] = '\0'; @@ -1027,12 +1126,34 @@ int uac_reg_update(reg_uac_t *reg, time_t tn) uac_r.cb = uac_reg_tm_callback; /* Callback parameter */ uac_r.cbp = (void*)uuid; - ret = uac_tmb.t_request(&uac_r, /* UAC Req */ - &s_ruri, /* Request-URI */ - &s_turi, /* To */ - &s_turi, /* From */ - (reg->auth_proxy.len)?®->auth_proxy:NULL /* outbound uri */ - ); + + if (reg_keep_callid && reg->flags & UAC_REG_ONLINE + && reg->cseq > 0 && reg->cseq < 2147483638 + && reg->callid.len > 0) + { + /* reregister, reuse callid and cseq */ + memset(&tmdlg, 0, sizeof(dlg_t)); + tmdlg.id.call_id = reg->callid; + tmdlg.loc_seq.value = reg->cseq; + tmdlg.loc_seq.is_set = 1; + tmdlg.rem_target = s_ruri; + tmdlg.loc_uri = s_turi; + tmdlg.rem_uri = s_turi; + tmdlg.state= DLG_CONFIRMED; + if(reg->auth_proxy.len) + tmdlg.dst_uri = reg->auth_proxy; + uac_r.dialog = &tmdlg; + + ret = uac_tmb.t_request_within(&uac_r); + } else { + ret = uac_tmb.t_request(&uac_r, /* UAC Req */ + &s_ruri, /* Request-URI */ + &s_turi, /* To */ + &s_turi, /* From */ + (reg->auth_proxy.len)?®->auth_proxy:NULL /* outbound uri */ + ); + } + reg->flags &= ~UAC_REG_ONLINE; if(ret<0) { @@ -1066,12 +1187,14 @@ void uac_reg_timer(unsigned int ticks) for(i=0; i<_reg_htable->htsize; i++) { /* walk through entries */ + lock_get(&_reg_htable->entries[i].lock); it = _reg_htable->entries[i].byuuid; while(it) { uac_reg_update(it->r, tn); it = it->next; } + lock_release(&_reg_htable->entries[i].lock); } if(_reg_htable_gc!=NULL) @@ -1086,17 +1209,42 @@ void uac_reg_timer(unsigned int ticks) #define reg_db_set_attr(attr, pos) do { \ if(!VAL_NULL(&RES_ROWS(db_res)[i].values[pos])) { \ - reg.attr.s = \ + reg->attr.s = \ (char*)(RES_ROWS(db_res)[i].values[pos].val.string_val); \ - reg.attr.len = strlen(reg.attr.s); \ - if(reg.attr.len == 0) { \ + reg->attr.len = strlen(reg->attr.s); \ + if(reg->attr.len == 0) { \ LM_ERR("empty value not allowed for column[%d]='%.*s' - ignoring record\n", \ pos, db_cols[pos]->len, db_cols[pos]->s); \ - goto nextrec; \ + return -1; \ } \ } \ } while(0); + +static inline int uac_reg_db_to_reg(reg_uac_t *reg, db1_res_t* db_res, int i, db_key_t *db_cols) +{ + memset(reg, 0, sizeof(reg_uac_t));; + /* check for NULL values ?!?! */ + reg_db_set_attr(l_uuid, 0); + reg_db_set_attr(l_username, 1); + reg_db_set_attr(l_domain, 2); + reg_db_set_attr(r_username, 3); + reg_db_set_attr(r_domain, 4); + /* realm may be empty */ + if(!VAL_NULL(&RES_ROWS(db_res)[i].values[5])) { + reg->realm.s = (char*)(RES_ROWS(db_res)[i].values[5].val.string_val); + reg->realm.len = strlen(reg->realm.s); + } + reg_db_set_attr(auth_username, 6); + reg_db_set_attr(auth_password, 7); + reg_db_set_attr(auth_proxy, 8); + reg->expires = (unsigned int)RES_ROWS(db_res)[i].values[9].val.int_val; + reg->flags = (unsigned int)RES_ROWS(db_res)[i].values[10].val.int_val; + reg->reg_delay = (unsigned int)RES_ROWS(db_res)[i].values[11].val.int_val; + return 0; +} + + /** * */ @@ -1191,35 +1339,13 @@ int uac_reg_load_db(void) do { for(i=0; iflags & (UAC_REG_ONGOING | UAC_REG_AUTHSENT)); + reg_ht_rm(cur_reg); } - } else { if(reg_ht_add(®)<0) { lock_release(_reg_htable_gc_lock); LM_ERR("Error adding reg to htable\n"); goto error; } + lock_release(_reg_htable_gc_lock); } - lock_release(_reg_htable_gc_lock); - -nextrec: reg_dbf.free_result(reg_db_con, db_res); reg_dbf.close(reg_db_con); - return 0; + return 1; error: if (reg_db_con) { @@ -1427,6 +1526,7 @@ int uac_reg_lookup(struct sip_msg *msg, str *src, pv_spec_t *dst, int mode) reg->l_uuid.len, reg->l_uuid.s); s_ruri.s = b_ruri; s_ruri.len = strlen(s_ruri.s); } + lock_release(reg->lock); memset(&val, 0, sizeof(pv_value_t)); val.flags |= PV_VAL_STR; val.rs = s_ruri; @@ -1436,6 +1536,50 @@ int uac_reg_lookup(struct sip_msg *msg, str *src, pv_spec_t *dst, int mode) return 1; } +/** + * + */ +int uac_reg_status(struct sip_msg *msg, str *src, int mode) +{ + struct sip_uri puri; + reg_uac_t *reg = NULL; + int ret; + + if(mode==0) + { + reg = reg_ht_get_byuuid(src); + if(reg==NULL) + { + LM_DBG("no uuid: %.*s\n", src->len, src->s); + return -1; + } + } else { + if(parse_uri(src->s, src->len, &puri)!=0) + { + LM_ERR("failed to parse uri\n"); + return -1; + } + reg = reg_ht_get_byuser(&puri.user, (reg_use_domain)?&puri.host:NULL); + if(reg==NULL) + { + LM_DBG("no user: %.*s\n", src->len, src->s); + return -1; + } + } + + if ((reg->flags & UAC_REG_ONLINE) && (reg->timer_expires > time(NULL))) + ret = 1; + else if (reg->flags & UAC_REG_ONGOING) + ret = -2; + else if (reg->flags & UAC_REG_DISABLED) + ret = -3; + else + ret = -99; + + lock_release(reg->lock); + return ret; +} + /** * */ @@ -1488,13 +1632,13 @@ int uac_reg_request_to(struct sip_msg *msg, str *src, unsigned int mode) init_run_actions_ctx(&ra_ctx); if (do_action(&ra_ctx, &act, msg) < 0) { LM_ERR("error while setting request uri\n"); - return -1; + goto error; } // Set auth_proxy ($du) if (set_dst_uri(msg, ®->auth_proxy) < 0) { LM_ERR("error while setting outbound proxy\n"); - return -1; + goto error; } memset(&val, 0, sizeof(pv_value_t)); @@ -1504,24 +1648,64 @@ int uac_reg_request_to(struct sip_msg *msg, str *src, unsigned int mode) val.rs = reg->realm; if(pv_set_spec_value(msg, &auth_realm_spec, 0, &val)!=0) { LM_ERR("error while setting auth_realm\n"); - return -1; + goto error; } // Set auth_username val.rs = reg->auth_username; if(pv_set_spec_value(msg, &auth_username_spec, 0, &val)!=0) { LM_ERR("error while setting auth_username\n"); - return -1; + goto error; } // Set auth_password val.rs = reg->auth_password; if(pv_set_spec_value(msg, &auth_password_spec, 0, &val)!=0) { LM_ERR("error while setting auth_password\n"); - return -1; + goto error; } + lock_release(reg->lock); return 1; + +error: + lock_release(reg->lock); + return -1; +} + +static int rpc_uac_reg_add_node_helper(rpc_t* rpc, void* ctx, reg_uac_t *reg, time_t tn) +{ + void* th; + str none = {"none", 4}; + + /* add entry node */ + if (rpc->add(ctx, "{", &th) < 0) + { + rpc->fault(ctx, 500, "Internal error creating rpc"); + return -1; + } + if (rpc->struct_add(th, "SSSSSSSSSdddddd", + "l_uuid", ®->l_uuid, + "l_username", ®->l_username, + "l_domain", ®->l_domain, + "r_username", ®->r_username, + "r_domain", ®->r_domain, + "realm", ®->realm, + "auth_username", ®->auth_username, + "auth_password", ®->auth_password, + "auth_proxy", (reg->auth_proxy.len)? + ®->auth_proxy:&none, + "expires", (int)reg->expires, + "flags", (int)reg->flags, + "diff_expires", (int)(reg->timer_expires - tn), + "timer_expires", (int)reg->timer_expires, + "reg_init", (int)reg->reg_init, + "reg_delay", (int)reg->reg_delay + )<0) { + rpc->fault(ctx, 500, "Internal error adding item"); + return -1; + } + return 0; } static const char* rpc_uac_reg_dump_doc[2] = { @@ -1533,8 +1717,6 @@ static void rpc_uac_reg_dump(rpc_t* rpc, void* ctx) { int i; reg_item_t *reg = NULL; - void* th; - str none = {"none", 4}; time_t tn; if(_reg_htable==NULL) @@ -1552,33 +1734,9 @@ static void rpc_uac_reg_dump(rpc_t* rpc, void* ctx) reg = _reg_htable->entries[i].byuuid; while(reg) { - /* add entry node */ - if (rpc->add(ctx, "{", &th) < 0) - { - rpc->fault(ctx, 500, "Internal error creating rpc"); - return; - } - if(rpc->struct_add(th, "SSSSSSSSSdddddd", - "l_uuid", ®->r->l_uuid, - "l_username", ®->r->l_username, - "l_domain", ®->r->l_domain, - "r_username", ®->r->r_username, - "r_domain", ®->r->r_domain, - "realm", ®->r->realm, - "auth_username", ®->r->auth_username, - "auth_password", ®->r->auth_password, - "auth_proxy", (reg->r->auth_proxy.len)? - ®->r->auth_proxy:&none, - "expires", (int)reg->r->expires, - "flags", (int)reg->r->flags, - "diff_expires", (int)(reg->r->timer_expires - tn), - "timer_expires", (int)reg->r->timer_expires, - "reg_init", (int)reg->r->reg_init, - "reg_delay", (int)reg->r->reg_delay - )<0) + if (rpc_uac_reg_add_node_helper(rpc, ctx, reg->r, tn)<0) { lock_release(&_reg_htable->entries[i].lock); - rpc->fault(ctx, 500, "Internal error adding item"); return; } reg = reg->next; @@ -1594,14 +1752,10 @@ static const char* rpc_uac_reg_info_doc[2] = { static void rpc_uac_reg_info(rpc_t* rpc, void* ctx) { - int i; - reg_item_t *reg = NULL; - void* th; - str none = {"none", 4}; - time_t tn; + reg_uac_t *reg = NULL; str attr = {0}; str val = {0}; - str *rval; + int ret; if(_reg_htable==NULL) { @@ -1611,85 +1765,37 @@ static void rpc_uac_reg_info(rpc_t* rpc, void* ctx) if(rpc->scan(ctx, "S.S", &attr, &val)<2) { - rpc->fault(ctx, 500, "Invalid Parameters"); + rpc->fault(ctx, 400, "Invalid Parameters"); return; } if(attr.len<=0 || attr.s==NULL || val.len<=0 || val.s==NULL) { LM_ERR("bad parameter values\n"); - rpc->fault(ctx, 500, "Invalid Parameter Values"); + rpc->fault(ctx, 400, "Invalid Parameter Values"); return; } - tn = time(NULL); - - for(i=0; i<_reg_htable->htsize; i++) - { - /* walk through entries */ - lock_get(&_reg_htable->entries[i].lock); - reg = _reg_htable->entries[i].byuuid; - while(reg) - { - if(attr.len==10 && strncmp(attr.s, "l_username", 10)==0) { - rval = ®->r->l_username; - } else if(attr.len==10 && strncmp(attr.s, "r_username", 10)==0) { - rval = ®->r->r_username; - } else if(attr.len==6 && strncmp(attr.s, "l_uuid", 6)==0) { - rval = ®->r->l_uuid; - } else if(attr.len==13 && strncmp(attr.s, "auth_username", 13)==0) { - rval = ®->r->auth_username; - } else { - lock_release(&_reg_htable->entries[i].lock); - LM_ERR("usupoorted filter attribute %.*s\n", attr.len, attr.s); - rpc->fault(ctx, 500, "Unsupported Filter Attribtue"); - return; - } - - if(rval->len==val.len && strncmp(val.s, rval->s, val.len)==0) { - /* add entry node */ - if (rpc->add(ctx, "{", &th) < 0) - { - rpc->fault(ctx, 500, "Internal error creating rpc"); - return; - } - if(rpc->struct_add(th, "SSSSSSSSSdddd", - "l_uuid", ®->r->l_uuid, - "l_username", ®->r->l_username, - "l_domain", ®->r->l_domain, - "r_username", ®->r->r_username, - "r_domain", ®->r->r_domain, - "realm", ®->r->realm, - "auth_username", ®->r->auth_username, - "auth_password", ®->r->auth_password, - "auth_proxy", (reg->r->auth_proxy.len)? - ®->r->auth_proxy:&none, - "expires", (int)reg->r->expires, - "flags", (int)reg->r->flags, - "diff_expires", (int)(reg->r->timer_expires - tn), - "timer_expires", (int)reg->r->timer_expires - )<0) - { - lock_release(&_reg_htable->entries[i].lock); - rpc->fault(ctx, 500, "Internal error adding item"); - return; - } - lock_release(&_reg_htable->entries[i].lock); - return; - } - reg = reg->next; - } - lock_release(&_reg_htable->entries[i].lock); + ret = reg_ht_get_byfilter(®, &attr, &val); + if (ret == 0) { + rpc->fault(ctx, 404, "Record not found"); + return; + } else if (ret < 0) { + rpc->fault(ctx, 400, "Unsupported filter attribute"); + return; } + + rpc_uac_reg_add_node_helper(rpc, ctx, reg, time(NULL)); + lock_release(reg->lock); + return; } static void rpc_uac_reg_update_flag(rpc_t* rpc, void* ctx, int mode, int fval) { - int i; - reg_item_t *reg = NULL; + reg_uac_t *reg = NULL; str attr = {0}; str val = {0}; - str *rval; + int ret; if(_reg_htable==NULL) { @@ -1699,53 +1805,34 @@ static void rpc_uac_reg_update_flag(rpc_t* rpc, void* ctx, int mode, int fval) if(rpc->scan(ctx, "S.S", &attr, &val)<2) { - rpc->fault(ctx, 500, "Invalid Parameters"); + rpc->fault(ctx, 400, "Invalid Parameters"); return; } if(attr.len<=0 || attr.s==NULL || val.len<=0 || val.s==NULL) { LM_ERR("bad parameter values\n"); - rpc->fault(ctx, 500, "Invalid Parameter Values"); + rpc->fault(ctx, 400, "Invalid Parameter Values"); return; } - for(i=0; i<_reg_htable->htsize; i++) - { - lock_get(&_reg_htable->entries[i].lock); - /* walk through entries */ - reg = _reg_htable->entries[i].byuuid; - while(reg) - { - if(attr.len==10 && strncmp(attr.s, "l_username", 10)==0) { - rval = ®->r->l_username; - } else if(attr.len==10 && strncmp(attr.s, "r_username", 10)==0) { - rval = ®->r->r_username; - } else if(attr.len==6 && strncmp(attr.s, "l_uuid", 6)==0) { - rval = ®->r->l_uuid; - } else if(attr.len==13 && strncmp(attr.s, "auth_username", 13)==0) { - rval = ®->r->auth_username; - } else { - lock_release(&_reg_htable->entries[i].lock); - LM_ERR("usupoorted filter attribute %.*s\n", attr.len, attr.s); - rpc->fault(ctx, 500, "Unsupported Filter Attribtue"); - return; - } + ret = reg_ht_get_byfilter(®, &attr, &val); + if (ret == 0) { + rpc->fault(ctx, 404, "Record not found"); + return; + } else if (ret < 0) { + rpc->fault(ctx, 400, "Unsupported filter attribute"); + return; + } - if(rval->len==val.len && strncmp(val.s, rval->s, val.len)==0) { - /* found */ - if(mode==1) { - reg->r->flags |= fval; - } else { - reg->r->flags &= ~fval; - } - reg->r->timer_expires = time(NULL) + 1; - lock_release(&_reg_htable->entries[i].lock); - return; - } - reg = reg->next; - } - lock_release(&_reg_htable->entries[i].lock); + if(mode==1) { + reg->flags |= fval; + } else { + reg->flags &= ~fval; } + reg->timer_expires = time(NULL) + 1; + + lock_release(reg->lock); + return; } static const char* rpc_uac_reg_enable_doc[2] = { @@ -1803,17 +1890,94 @@ static void rpc_uac_reg_refresh(rpc_t* rpc, void* ctx) if(rpc->scan(ctx, "S", &l_uuid)<1) { - rpc->fault(ctx, 500, "Invalid Parameters"); + rpc->fault(ctx, 400, "Invalid Parameters"); return; } ret = uac_reg_db_refresh(&l_uuid); - if(ret<0) { + if(ret==0) { + rpc->fault(ctx, 404, "Record not found"); + return; + } else if(ret<0) { rpc->fault(ctx, 500, "Failed to refresh record - check log messages"); return; } } +static const char* rpc_uac_reg_remove_doc[2] = { + "Remove a record from memory.", + 0 +}; + +static void rpc_uac_reg_remove(rpc_t* rpc, void* ctx) +{ + int ret; + str l_uuid; + reg_uac_t *reg; + + if(rpc->scan(ctx, "S", &l_uuid)<1) + { + rpc->fault(ctx, 400, "Invalid Parameters"); + return; + } + reg = reg_ht_get_byuuid(&l_uuid); + if (!reg) { + rpc->fault(ctx, 404, "Record not found"); + return; + } + + ret = reg_ht_rm(reg); + if(ret<0) { + rpc->fault(ctx, 500, "Failed to remove record - check log messages"); + return; + } +} + +static const char* rpc_uac_reg_add_doc[2] = { + "Add a record to memory.", + 0 +}; + +static void rpc_uac_reg_add(rpc_t* rpc, void* ctx) +{ + int ret; + reg_uac_t reg; + reg_uac_t *cur_reg; + + if(rpc->scan(ctx, "SSSSSSSSSddd", + ®.l_uuid, + ®.l_username, + ®.l_domain, + ®.r_username, + ®.r_domain, + ®.realm, + ®.auth_username, + ®.auth_password, + ®.auth_proxy, + ®.expires, + ®.flags, + ®.reg_delay + )<1) + { + rpc->fault(ctx, 400, "Invalid Parameters"); + return; + } + + cur_reg = reg_ht_get_byuuid(®.l_uuid); + if (cur_reg) { + lock_release(cur_reg->lock); + rpc->fault(ctx, 409, "uuid already exists"); + return; + } + + ret = reg_ht_add(®); + if(ret<0) { + rpc->fault(ctx, 500, "Failed to add record - check log messages"); + return; + } +} + + rpc_export_t uac_reg_rpc[] = { {"uac.reg_dump", rpc_uac_reg_dump, rpc_uac_reg_dump_doc, RET_ARRAY}, {"uac.reg_info", rpc_uac_reg_info, rpc_uac_reg_info_doc, 0}, @@ -1821,6 +1985,8 @@ rpc_export_t uac_reg_rpc[] = { {"uac.reg_disable", rpc_uac_reg_disable, rpc_uac_reg_disable_doc, 0}, {"uac.reg_reload", rpc_uac_reg_reload, rpc_uac_reg_reload_doc, 0}, {"uac.reg_refresh", rpc_uac_reg_refresh, rpc_uac_reg_refresh_doc, 0}, + {"uac.reg_remove", rpc_uac_reg_remove, rpc_uac_reg_remove_doc, 0}, + {"uac.reg_add", rpc_uac_reg_add, rpc_uac_reg_add_doc, 0}, {0, 0, 0, 0} }; diff --git a/modules/uac/uac_reg.h b/modules/uac/uac_reg.h index c5b37006862..ae3cca31f92 100644 --- a/modules/uac/uac_reg.h +++ b/modules/uac/uac_reg.h @@ -27,6 +27,8 @@ extern int reg_timer_interval; extern int reg_retry_interval; extern int reg_htable_size; extern int reg_fetch_rows; +extern int reg_keep_callid; +extern int reg_random_delay; extern str reg_contact_addr; extern str reg_db_url; extern str reg_db_table; @@ -50,5 +52,6 @@ void uac_reg_timer(unsigned int ticks); int uac_reg_init_rpc(void); int uac_reg_lookup(struct sip_msg *msg, str *src, pv_spec_t *dst, int mode); +int uac_reg_status(struct sip_msg *msg, str *src, int mode); int uac_reg_request_to(struct sip_msg *msg, str *src, unsigned int mode); #endif diff --git a/parser/case_min.h b/parser/case_min.h index 56231c3063d..e09f4acf729 100644 --- a/parser/case_min.h +++ b/parser/case_min.h @@ -31,22 +31,37 @@ #define CASE_MIN_H -#define min_se_CASE \ - if (LOWER_BYTE(*p) == 's') { \ - p++; \ - if (LOWER_BYTE(*p) == 'e') { \ - hdr->type = HDR_MIN_SE_T; \ - p++; \ - goto dc_end; \ - } \ - } - - -#define min_CASE \ - p += 4; \ - val = READ(p); \ - min_se_CASE; \ - goto other; \ +#define RES_CASE \ + switch(LOWER_DWORD(val)) { \ + case _res1_: /* "res:" */ \ + hdr->type = HDR_MIN_EXPIRES_T; \ + hdr->name.len = 11; \ + return (p + 4); \ + case _res2_: /* "res " */ \ + hdr->type = HDR_MIN_EXPIRES_T; \ + p+=4; \ + goto dc_end; \ + } + +#define MIN2_CASE \ + if (LOWER_BYTE(*p) == 's') { \ + p++; \ + if (LOWER_BYTE(*p) == 'e') { \ + hdr->type = HDR_MIN_SE_T; \ + p++; \ + goto dc_end; \ + } \ + } else if (LOWER_DWORD(val) == _expi_) { \ + p += 4; \ + val = READ(p); \ + RES_CASE; \ + } + +#define min_CASE \ + p += 4; \ + val = READ(p); \ + MIN2_CASE; \ + goto other; #endif /* CASE_MIN_H */ diff --git a/parser/hf.c b/parser/hf.c index 3f2f879da80..2ff94d0b61e 100644 --- a/parser/hf.c +++ b/parser/hf.c @@ -98,6 +98,10 @@ void clean_hdr_field(struct hdr_field* const hf) free_expires((exp_body_t**)h_parsed); break; + case HDR_MIN_EXPIRES_T: + free_expires((exp_body_t**)h_parsed); + break; + case HDR_FROM_T: free_to(hf->parsed); break; diff --git a/parser/hf.h b/parser/hf.h index 03b1ec7f5d3..0dc25c95c23 100644 --- a/parser/hf.h +++ b/parser/hf.h @@ -65,6 +65,7 @@ enum _hdr_types_t { HDR_CONTENTLENGTH_T /*!< Content-Length header field */, HDR_AUTHORIZATION_T /*!< Authorization header field */, HDR_EXPIRES_T /*!< Expires header field */, + HDR_MIN_EXPIRES_T /*!< Min-Expires header */, HDR_PROXYAUTH_T /*!< Proxy-Authorization hdr field */, HDR_SUPPORTED_T /*!< Supported header field */, HDR_REQUIRE_T /*!< Require header */, @@ -137,6 +138,7 @@ typedef unsigned long long hdr_flags_t; #define HDR_CONTENTLENGTH_F HDR_F_DEF(CONTENTLENGTH) #define HDR_AUTHORIZATION_F HDR_F_DEF(AUTHORIZATION) #define HDR_EXPIRES_F HDR_F_DEF(EXPIRES) +#define HDR_MIN_EXPIRES_F HDR_F_DEF(MIN_EXPIRES) #define HDR_PROXYAUTH_F HDR_F_DEF(PROXYAUTH) #define HDR_SUPPORTED_F HDR_F_DEF(SUPPORTED) #define HDR_REQUIRE_F HDR_F_DEF(REQUIRE) @@ -217,6 +219,7 @@ static inline int hdr_allocs_parse(struct hdr_field* hdr) case HDR_DIVERSION_T: case HDR_EVENT_T: case HDR_EXPIRES_T: + case HDR_MIN_EXPIRES_T: case HDR_FROM_T: case HDR_IDENTITY_INFO_T: case HDR_IDENTITY_T: diff --git a/parser/msg_parser.c b/parser/msg_parser.c index 21b3cc2f464..86897a90094 100644 --- a/parser/msg_parser.c +++ b/parser/msg_parser.c @@ -212,6 +212,7 @@ char* get_hdr_field(char* const buf, char* const end, struct hdr_field* const hd case HDR_MAXFORWARDS_T: case HDR_AUTHORIZATION_T: case HDR_EXPIRES_T: + case HDR_MIN_EXPIRES_T: case HDR_PROXYAUTH_T: case HDR_PROXYREQUIRE_T: case HDR_UNSUPPORTED_T: @@ -406,6 +407,10 @@ int parse_headers(struct sip_msg* const msg, const hdr_flags_t flags, const int if (msg->expires==0) msg->expires = hf; msg->parsed_flag|=HDR_EXPIRES_F; break; + case HDR_MIN_EXPIRES_T: + if (msg->min_expires==0) msg->min_expires = hf; + msg->parsed_flag|=HDR_MIN_EXPIRES_F; + break; case HDR_PROXYAUTH_T: if (msg->proxy_auth==0) msg->proxy_auth = hf; msg->parsed_flag|=HDR_PROXYAUTH_F; diff --git a/parser/msg_parser.h b/parser/msg_parser.h index c4e426a4131..84604a9b30d 100644 --- a/parser/msg_parser.h +++ b/parser/msg_parser.h @@ -318,6 +318,7 @@ typedef struct sip_msg { struct hdr_field* ppi; struct hdr_field* path; struct hdr_field* privacy; + struct hdr_field* min_expires; struct msg_body* body; diff --git a/sip_msg_clone.c b/sip_msg_clone.c index e736b7b6506..048ef77959a 100644 --- a/sip_msg_clone.c +++ b/sip_msg_clone.c @@ -442,6 +442,7 @@ struct sip_msg* sip_msg_shm_clone( struct sip_msg *org_msg, int *sip_msg_len, case HDR_CONTENTLENGTH_T: case HDR_RETRY_AFTER_T: case HDR_EXPIRES_T: + case HDR_MIN_EXPIRES_T: case HDR_SUPPORTED_T: case HDR_REQUIRE_T: case HDR_PROXYREQUIRE_T: @@ -772,6 +773,11 @@ struct sip_msg* sip_msg_shm_clone( struct sip_msg *org_msg, int *sip_msg_len, new_msg->expires = new_hdr; } break; + case HDR_MIN_EXPIRES_T: + if (!HOOK_SET(min_expires)) { + new_msg->min_expires = new_hdr; + } + break; case HDR_PROXYAUTH_T: if (!HOOK_SET(proxy_auth)) { new_msg->proxy_auth = new_hdr;