diff --git a/lib/sliblist.c b/lib/sliblist.c index 282b2f48fd9..0620dc43fa3 100644 --- a/lib/sliblist.c +++ b/lib/sliblist.c @@ -215,3 +215,35 @@ void slinkedl_list_destroy(slinkedl_list_t *list) return; } +int slinkedl_delete(slinkedl_list_t *list, slinkedl_match_f *func, void *data) +{ + slinkedl_element_t *prev = NULL; + slinkedl_element_t *element; + int ret = 0; + + if (!list || !func) + return 0; + + element = list->head; + while (element) { + ret = (*func)(element->data, data, NULL); + if (ret) { + if (prev) + prev->next = element->next; + else + list->head = element->next; + + /* fix tail if we removed the last element */ + if (element == list->tail) + list->tail = prev; + + list->dealloc(element); + return ret; + } + + prev = element; + element = element->next; + } + + return 0; /* not found */ +} diff --git a/lib/sliblist.h b/lib/sliblist.h index c82cb3be7ed..3878d00f037 100644 --- a/lib/sliblist.h +++ b/lib/sliblist.h @@ -157,6 +157,18 @@ int slinkedl_replace(slinkedl_list_t *list, slinkedl_match_f match, */ void *slinkedl_peek(slinkedl_list_t *list); +/** + * Delete from the list the first element that matches is found + * + * @psram lit The list to traverse. + * @param match_func The function used for matching the list element to delete + * @param data The data to be used by run_data function. + * @return The return code from last match_func call. + * @see slinkedl_match_f() + */ +int slinkedl_delete(slinkedl_list_t *list, slinkedl_match_f *match_func, void *data); + + /** * Destroy the list. * Any element in the list will be silently destroyed. diff --git a/mi/mi.h b/mi/mi.h index e5fb13d22d3..1ab44765dfd 100644 --- a/mi/mi.h +++ b/mi/mi.h @@ -27,7 +27,7 @@ #include "../str.h" #include "item.h" -#define MAX_MI_PARAMS 10 +#define MAX_MI_PARAMS 20 #define MAX_MI_RECIPES 48 /* async MI command */ diff --git a/modules/uac_registrant/doc/uac_registrant_admin.xml b/modules/uac_registrant/doc/uac_registrant_admin.xml index 80e98c845a3..ace02ea273d 100644 --- a/modules/uac_registrant/doc/uac_registrant_admin.xml +++ b/modules/uac_registrant/doc/uac_registrant_admin.xml @@ -521,6 +521,26 @@ modparam("uac_registrant", "state_column", "status") +
+ <varname>reregister_expiry_percentage</varname> (integer) + + Percentage describing how much sooner a RE-REGISTER needs to be send based on the Expiry. a 100 value means the RE-REGISTER will be send right on the edge of expiry ( old behavior ), which might lead to registration loss. a 90 value means the RE-REGISTER will be sent sooner , at 90% of the Expiry, etc. + + + + Default value is 100. + + + + Set <quote>reregister_expiry_percentage</quote> parameter + +... +modparam("uac_registrant", "reregister_expiry_percentage", 90) +... + + +
+
@@ -687,6 +707,271 @@ opensips-cli -x mi reg_force_register sip:alice@opensips.org sip:alice@127.0.0.
+
+ <function moreinfo="none">reg_upsert</function> + Inserts or updates the in-memory contents of the AOR/Contact/Registrar. No Database queries are done when calling this MI command, all parameters are passed via MI + Name: reg_upsert + Parameters: + + + aor - URI defining the address + + + contact - Contact URI + + + registrar - URI pointing to the remote registrar + + + proxy - URI of a registration proxy + + + third_party_registrant - 3rd party registrant + + + username - the username for auth purposes + + + + password - the password for auth purposes + + + binding_params - params to be added to the registration + + + expiry - number of seconds that the registration will be valid + + + forced_socket - opensips socket to send out the register out through + + + cluster_shtag - the sharing tag for this registration + + + state - 0 for enabled, 1 for disabled + + + MI FIFO Command Format: + +opensips-cli -x mi reg_upsert aor=sip:vlad@test.com contact=sip:test@localhost registrar=sip:127.0.0.1:5061 proxy="" third_party_registrant="" username="vlad" password="1234" binding_params="" expiry=60 forced_socket="" cluster_shtag="" state=0 + +
+ +
+ <function moreinfo="none">reg_delete</function> + Deletes the in-memory contents of the AOR/Contact/Registrar. No Database queries are done when calling this MI command, all parameters are passed via MI + Name: reg_delete + Parameters: + + + aor - URI defining the address + + + contact - Contact URI + + + registrar - URI pointing to the remote registrar + + + MI FIFO Command Format: + +opensips-cli -x mi reg_delete aor=sip:vlad@test.com contact=sip:test@localhost registrar=sip:127.0.0.1:5061 + +
+ + + +
+ Exported Events +
+ + <function moreinfo="none">E_REGISTRANT_REGISTERING</function> + + + This event is raised when the module sent the initial REGISTER and started the registration process. + + Parameters: + + + aor - the AOR + + + contact - the Contact + + + registrar - the Registrar + + +
+ +
+ + <function moreinfo="none">E_REGISTRANT_AUTHENTICATING</function> + + + This event is raised when the initial REGISTER has been challenged and a new REGISTER with credentials has been sent out. + + Parameters: + + + aor - the AOR + + + contact - the Contact + + + registrar - the Registrar + + +
+ +
+ + <function moreinfo="none">E_REGISTRANT_REGISTERED</function> + + + This event is raised when a REGISTER has been 200 OKd. + + Parameters: + + + aor - the AOR + + + contact - the Contact + + + registrar - the Registrar + + +
+ +
+ + <function moreinfo="none">E_REGISTRANT_REGISTER_TIMEOUT</function> + + + This event is raised when a REGISTER received no reply from the registrar. + + Parameters: + + + aor - the AOR + + + contact - the Contact + + + registrar - the Registrar + + +
+ +
+ + <function moreinfo="none">E_REGISTRANT_INTERNAL_ERROR</function> + + + This event is raised when a REGISTER procesing was stopped due to an internal OpenSIPS error. + + Parameters: + + + aor - the AOR + + + contact - the Contact + + + registrar - the Registrar + + +
+ +
+ + <function moreinfo="none">E_REGISTRANT_WRONG_CREDENTIALS</function> + + + This event is raised when a REGISTER with credentials was still rejected by the registrar + + Parameters: + + + aor - the AOR + + + contact - the Contact + + + registrar - the Registrar + + +
+ +
+ + <function moreinfo="none">E_REGISTRANT_REGISTRAR_ERROR</function> + + + This event is raised when a REGISTER is rejected by the registrar with a non-standard sip code. + + Parameters: + + + aor - the AOR + + + contact - the Contact + + + registrar - the Registrar + + +
+ +
+ + <function moreinfo="none">E_REGISTRANT_UNREGISTERING</function> + + + This event is raised when a de-REGISTER is sent by OpenSIPS. + + Parameters: + + + aor - the AOR + + + contact - the Contact + + + registrar - the Registrar + + +
+ +
+ + <function moreinfo="none">E_REGISTRANT_AUTHENTICATING_UNREGISTER</function> + + + This event is raised when a de-REGISTER is challenged and auth is sent by OpenSIPS. + + Parameters: + + + aor - the AOR + + + contact - the Contact + + + registrar - the Registrar + + +
+
diff --git a/modules/uac_registrant/reg_events.c b/modules/uac_registrant/reg_events.c new file mode 100644 index 00000000000..2fe3472fb16 --- /dev/null +++ b/modules/uac_registrant/reg_events.c @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2019 OpenSIPS Project + * + * This file is part of opensips, a free SIP server. + * + * opensips is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * opensips is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "../../evi/evi_params.h" +#include "../../evi/evi_modules.h" + +#include "reg_events.h" + +static str reg_event_aor = str_init("aor"); +static str reg_event_contact = str_init("contact"); +static str reg_event_registrar = str_init("registrar"); + +static str reg_registering_ev_name = str_init("E_REGISTRANT_REGISTERING"); +static event_id_t reg_registering_ev_id = EVI_ERROR; +static evi_params_p reg_registering_event_params; +static evi_param_p reg_registering_aor_p, reg_registering_contact_p, reg_registering_registrar_p; + +static str reg_authenticating_ev_name = str_init("E_REGISTRANT_AUTHENTICATING"); +static event_id_t reg_authenticating_ev_id = EVI_ERROR; +static evi_params_p reg_authenticating_event_params; +static evi_param_p reg_authenticating_aor_p, reg_authenticating_contact_p, reg_authenticating_registrar_p; + +static str reg_registered_ev_name = str_init("E_REGISTRANT_REGISTERED"); +static event_id_t reg_registered_ev_id = EVI_ERROR; +static evi_params_p reg_registered_event_params; +static evi_param_p reg_registered_aor_p, reg_registered_contact_p, reg_registered_registrar_p; + +static str reg_register_timeout_ev_name = str_init("E_REGISTRANT_REGISTER_TIMEOUT"); +static event_id_t reg_register_timeout_ev_id = EVI_ERROR; +static evi_params_p reg_register_timeout_event_params; +static evi_param_p reg_register_timeout_aor_p, reg_register_timeout_contact_p, reg_register_timeout_registrar_p; + +static str reg_internal_error_ev_name = str_init("E_REGISTRANT_INTERNAL_ERROR"); +static event_id_t reg_internal_error_ev_id = EVI_ERROR; +static evi_params_p reg_internal_error_event_params; +static evi_param_p reg_internal_error_aor_p, reg_internal_error_contact_p, reg_internal_error_registrar_p; + +static str reg_wrong_credentials_ev_name = str_init("E_REGISTRANT_WRONG_CREDENTIALS"); +static event_id_t reg_wrong_credentials_ev_id = EVI_ERROR; +static evi_params_p reg_wrong_credentials_event_params; +static evi_param_p reg_wrong_credentials_aor_p, reg_wrong_credentials_contact_p, reg_wrong_credentials_registrar_p; + +static str reg_registrar_error_ev_name = str_init("E_REGISTRANT_REGISTRAR_ERROR"); +static event_id_t reg_registrar_error_ev_id = EVI_ERROR; +static evi_params_p reg_registrar_error_event_params; +static evi_param_p reg_registrar_error_aor_p, reg_registrar_error_contact_p, reg_registrar_error_registrar_p; + +static str reg_unregistering_ev_name = str_init("E_REGISTRANT_UNREGISTERING"); +static event_id_t reg_unregistering_ev_id = EVI_ERROR; +static evi_params_p reg_unregistering_event_params; +static evi_param_p reg_unregistering_aor_p, reg_unregistering_contact_p, reg_unregistering_registrar_p; + +static str reg_authenticating_unregister_ev_name = str_init("E_REGISTRANT_AUTHENTICATING_UNREGISTER"); +static event_id_t reg_authenticating_unregister_ev_id = EVI_ERROR; +static evi_params_p reg_authenticating_unregister_event_params; +static evi_param_p reg_authenticating_unregister_aor_p, reg_authenticating_unregister_contact_p, reg_authenticating_unregister_registrar_p; + +#define EVI_PUBLISH_OR_FAIL(_ev_id, _ev_name) \ + do { \ + (_ev_id) = evi_publish_event((_ev_name)); \ + if ((_ev_id) == EVI_ERROR) { \ + LM_ERR("cannot register %.*s event\n", \ + (_ev_name).len, (_ev_name).s); \ + return -1; \ + } \ + } while (0) + +#define EVI_PARAMS_ALLOC_OR_FAIL(_params_ptr) \ + do { \ + (_params_ptr) = pkg_malloc(sizeof(evi_params_t)); \ + if ((_params_ptr) == NULL) { \ + LM_ERR("no more pkg mem\n"); \ + return -1; \ + } \ + memset((_params_ptr), 0, sizeof(evi_params_t)); \ + } while (0) + +#define EVI_PARAM_CREATE_OR_FAIL(_out_param, _params, _spec) \ + do { \ + (_out_param) = evi_param_create((_params), (_spec)); \ + if ((_out_param) == NULL) { \ + LM_ERR("Failed to create param \n"); \ + return -1; \ + } \ + } while (0) + +#define INIT_REG_EVENT(_prefix) \ + do { \ + EVI_PUBLISH_OR_FAIL(_prefix##_ev_id, _prefix##_ev_name); \ + EVI_PARAMS_ALLOC_OR_FAIL(_prefix##_event_params); \ + EVI_PARAM_CREATE_OR_FAIL(_prefix##_aor_p, \ + _prefix##_event_params, ®_event_aor); \ + EVI_PARAM_CREATE_OR_FAIL(_prefix##_contact_p, \ + _prefix##_event_params, ®_event_contact); \ + EVI_PARAM_CREATE_OR_FAIL(_prefix##_registrar_p, \ + _prefix##_event_params, ®_event_registrar); \ + } while (0) + +int init_registrant_events(void) +{ + INIT_REG_EVENT(reg_registering); + INIT_REG_EVENT(reg_authenticating); + INIT_REG_EVENT(reg_registered); + INIT_REG_EVENT(reg_register_timeout); + INIT_REG_EVENT(reg_internal_error); + INIT_REG_EVENT(reg_wrong_credentials); + INIT_REG_EVENT(reg_registrar_error); + INIT_REG_EVENT(reg_unregistering); + INIT_REG_EVENT(reg_authenticating_unregister); + + return 0; +} + +#define REG_EVI_SET_STR_OR_RETURN(_param, _str, _errmsg) \ + do { \ + if (evi_param_set_str((_param), (_str)) < 0) { \ + LM_ERR(_errmsg); \ + return; \ + } \ + } while (0) + +#define RAISE_REG_EVENT(_prefix, _rec) \ + do { \ + if (!evi_probe_event(_prefix##_ev_id)) \ + return; \ + REG_EVI_SET_STR_OR_RETURN(_prefix##_aor_p, \ + &(_rec)->td.rem_uri, \ + "cannot set AOR parameter\n"); \ + REG_EVI_SET_STR_OR_RETURN(_prefix##_contact_p, \ + &(_rec)->contact_uri, \ + "cannot set Contact parameter\n"); \ + REG_EVI_SET_STR_OR_RETURN(_prefix##_registrar_p, \ + &(_rec)->td.rem_target, \ + "cannot set Registrar parameter\n"); \ + if (evi_raise_event(_prefix##_ev_id, _prefix##_event_params) < 0) \ + LM_ERR("cannot raise %.*s event\n", \ + _prefix##_ev_name.len, \ + _prefix##_ev_name.s); \ + } while (0) + +void raise_registering_event(reg_record_t *rec) +{ + RAISE_REG_EVENT(reg_registering, rec); +} + +void raise_authenticating_event(reg_record_t *rec) +{ + RAISE_REG_EVENT(reg_authenticating, rec); +} + +void raise_registered_event(reg_record_t *rec) +{ + RAISE_REG_EVENT(reg_registered, rec); +} + +void raise_register_timeout_event(reg_record_t *rec) +{ + RAISE_REG_EVENT(reg_register_timeout, rec); +} + +void raise_internal_error_event(reg_record_t *rec) +{ + RAISE_REG_EVENT(reg_internal_error, rec); +} + +void raise_wrong_credentials_event(reg_record_t *rec) +{ + RAISE_REG_EVENT(reg_wrong_credentials, rec); +} + +void raise_registrar_error_event(reg_record_t *rec) +{ + RAISE_REG_EVENT(reg_registrar_error, rec); +} + +void raise_unregistering_event(reg_record_t *rec) +{ + RAISE_REG_EVENT(reg_unregistering, rec); +} + +void raise_authenticating_unregister_event(reg_record_t *rec) +{ + RAISE_REG_EVENT(reg_authenticating_unregister, rec); +} diff --git a/modules/uac_registrant/reg_events.h b/modules/uac_registrant/reg_events.h new file mode 100644 index 00000000000..6cd31267963 --- /dev/null +++ b/modules/uac_registrant/reg_events.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2025 OpenSIPS Project + * + * This file is part of opensips, a free SIP server. + * + * opensips is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * opensips is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef _UAC_REGISTRANT_EVENTS_H_ +#define _UAC_REGISTRANT_EVENTS_H_ + +#include "reg_records.h" + +int init_registrant_events(void); +void raise_registering_event(reg_record_t *rec); +void raise_authenticating_event(reg_record_t *rec); +void raise_registered_event(reg_record_t *rec); +void raise_register_timeout_event(reg_record_t *rec); +void raise_internal_error_event(reg_record_t *rec); +void raise_wrong_credentials_event(reg_record_t *rec); +void raise_registrar_error_event(reg_record_t *rec); +void raise_unregistering_event(reg_record_t *rec); +void raise_authenticating_unregister_event(reg_record_t *rec); + +#endif diff --git a/modules/uac_registrant/reg_records.c b/modules/uac_registrant/reg_records.c index 36b1ab38e79..4a92a059d0e 100644 --- a/modules/uac_registrant/reg_records.c +++ b/modules/uac_registrant/reg_records.c @@ -27,6 +27,7 @@ #include #include +#include "reg_events.h" #include "reg_records.h" extern unsigned int default_expires; @@ -131,10 +132,11 @@ int match_reload_record(void *e_data, void *data, void *n_data) !str_strcmp(&coords->registrar, &rec->td.rem_target)) { if (!(new_rec->flags®_ENABLED) && rec->flags®_ENABLED && rec->state == REGISTERED_STATE) { - if(send_unregister((unsigned long)coords->extra, rec, NULL, 0)==1) - rec->state = UNREGISTERING_STATE; - else - rec->state = INTERNAL_ERROR_STATE; + if(send_unregister((unsigned long)coords->extra, rec, NULL, 0)==1) { + reg_change_state(rec,UNREGISTERING_STATE); + } else { + reg_change_state(rec,INTERNAL_ERROR_STATE); + } } else if (new_rec->flags®_ENABLED && rec->flags®_ENABLED && rec->state == REGISTERED_STATE) { memcpy(new_rec->td.id.call_id.s, rec->td.id.call_id.s, @@ -144,6 +146,8 @@ int match_reload_record(void *e_data, void *data, void *n_data) new_rec->td.loc_seq.value = rec->td.loc_seq.value; new_rec->last_register_sent = rec->last_register_sent; new_rec->registration_timeout = rec->registration_timeout; + /* we are not migrating state here, just inheriting, + * do not call reg_change_state */ new_rec->state = rec->state; } @@ -350,3 +354,42 @@ void destroy_reg_htable(void) { } } +void reg_change_state(reg_record_t *rec, int new_state) +{ + rec->state = new_state; + switch (new_state) { + case NOT_REGISTERED_STATE: + /* NO-OP, just initializing */ + break; + case REGISTERING_STATE: + raise_registering_event(rec); + break; + case AUTHENTICATING_STATE: + raise_authenticating_event(rec); + break; + case REGISTERED_STATE: + raise_registered_event(rec); + break; + case REGISTER_TIMEOUT_STATE: + raise_register_timeout_event(rec); + break; + case INTERNAL_ERROR_STATE: + raise_internal_error_event(rec); + break; + case WRONG_CREDENTIALS_STATE: + raise_wrong_credentials_event(rec); + break; + case REGISTRAR_ERROR_STATE: + raise_registrar_error_event(rec); + break; + case UNREGISTERING_STATE: + raise_unregistering_event(rec); + break; + case AUTHENTICATING_UNREGISTER_STATE: + raise_authenticating_unregister_event(rec); + break; + default: + LM_ERR("Unhandled new state %d \n",new_state); + break; + } +} diff --git a/modules/uac_registrant/reg_records.h b/modules/uac_registrant/reg_records.h index a7b35cf3af4..340c541d826 100644 --- a/modules/uac_registrant/reg_records.h +++ b/modules/uac_registrant/reg_records.h @@ -122,4 +122,6 @@ int add_record(uac_reg_map_t *uac, str *now, unsigned int mode, record_coords_t *coords); void reg_print_record(reg_record_t *rec); +void reg_change_state(reg_record_t *rec, int new_state); + #endif diff --git a/modules/uac_registrant/registrant.c b/modules/uac_registrant/registrant.c index 9bce900d172..d9374728d5e 100644 --- a/modules/uac_registrant/registrant.c +++ b/modules/uac_registrant/registrant.c @@ -41,6 +41,7 @@ #include "reg_records.h" #include "reg_db_handler.h" #include "clustering.h" +#include "reg_events.h" #define UAC_REGISTRAR_URI_PARAM 1 @@ -102,6 +103,11 @@ static mi_response_t *mi_reg_disable(const mi_params_t *params, static mi_response_t *mi_reg_force_register(const mi_params_t *params, struct mi_handler *async_hdl); +static mi_response_t *mi_reg_upsert(const mi_params_t *params, + struct mi_handler *async_hdl); +static mi_response_t *mi_reg_delete(const mi_params_t *params, + struct mi_handler *async_hdl); + int send_register(unsigned int hash_index, reg_record_t *rec, str *auth_hdr); int send_unregister(unsigned int hash_index, reg_record_t *rec, str *auth_hdr, unsigned int all_contacts); @@ -114,6 +120,7 @@ uac_auth_api_t uac_auth_api; unsigned int default_expires = 3600; unsigned int timer_interval = 100; unsigned int failure_retry_interval = 0; +unsigned int reregister_exp_percentage=100; reg_table_t reg_htable = NULL; unsigned int reg_hsize = 1; @@ -159,6 +166,7 @@ static const param_export_t params[]= { {"forced_socket_column", STR_PARAM, &forced_socket_column.s}, {"cluster_shtag_column", STR_PARAM, &cluster_shtag_column.s}, {"state_column", STR_PARAM, &state_column.s}, + {"reregister_expiry_percentage", INT_PARAM, &reregister_exp_percentage}, {0,0,0} }; @@ -187,6 +195,14 @@ static const mi_export_t mi_cmds[] = { {mi_reg_force_register, {"aor", "contact", "registrar", 0}}, {EMPTY_MI_RECIPE}} }, + {"reg_upsert", 0, 0, 0, { + {mi_reg_upsert, {"aor", "contact", "registrar","proxy","third_party_registrant","username","password","binding_params","expiry","forced_socket","cluster_shtag","state", 0}}, + {EMPTY_MI_RECIPE}} + }, + { "reg_delete", 0, 0, 0, { + {mi_reg_delete, {"aor", "contact", "registrar", 0}}, + {EMPTY_MI_RECIPE}} + }, {EMPTY_MI_EXPORT} }; @@ -306,6 +322,16 @@ static int mod_init(void) return -1; } + if (init_registrant_events() < 0) { + LM_ERR("Failed to create the registrant events \n"); + return -1; + } + + if (reregister_exp_percentage <= 0 || reregister_exp_percentage > 100) { + LM_ERR("Earlier reregistration expiry percentage needs to be in the (0...100] range \n"); + return -1; + } + return 0; } @@ -426,7 +452,7 @@ int run_reg_tm_cback(void *e_data, void *data, void *r_data) case AUTHENTICATING_UNREGISTER_STATE: if (!(rec->flags®_ENABLED)) { /* succesfully unREGISTERED */ - rec->state = NOT_REGISTERED_STATE; + reg_change_state(rec,NOT_REGISTERED_STATE); rec->registration_timeout = 0; } else { /* registrant got enabled while waiting for a reply to @@ -438,10 +464,10 @@ int run_reg_tm_cback(void *e_data, void *data, void *r_data) new_call_id_ftag_4_record(rec, &str_now); if(send_register(cb_param->hash_index, rec, NULL)==1) { rec->last_register_sent = now; - rec->state = REGISTERING_STATE; + reg_change_state(rec,REGISTERING_STATE); } else { - rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:rec->expires) - timer_interval; - rec->state = INTERNAL_ERROR_STATE; + rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:(rec->expires*reregister_exp_percentage/100)) - timer_interval; + reg_change_state(rec,INTERNAL_ERROR_STATE); } } @@ -479,9 +505,9 @@ int run_reg_tm_cback(void *e_data, void *data, void *r_data) if (bindings_counter>1) { LM_DBG("got [%d] bindings\n", bindings_counter); if(send_unregister(cb_param->hash_index, rec, NULL, 1)==1) { - rec->state = UNREGISTERING_STATE; + reg_change_state(rec,UNREGISTERING_STATE); } else { - rec->state = INTERNAL_ERROR_STATE; + reg_change_state(rec,INTERNAL_ERROR_STATE); } break; } @@ -536,13 +562,13 @@ int run_reg_tm_cback(void *e_data, void *data, void *r_data) /* registrant got disabled while waiting for a reply to * a previous register */ if(send_unregister(cb_param->hash_index, rec, NULL, 0)==1) { - rec->state = UNREGISTERING_STATE; + reg_change_state(rec,UNREGISTERING_STATE); } else { - rec->state = INTERNAL_ERROR_STATE; + reg_change_state(rec,INTERNAL_ERROR_STATE); } } else { /* succesfully unREGISTERED */ - rec->state = NOT_REGISTERED_STATE; + reg_change_state(rec,NOT_REGISTERED_STATE); rec->registration_timeout = 0; } } else { @@ -557,14 +583,15 @@ int run_reg_tm_cback(void *e_data, void *data, void *r_data) new_call_id_ftag_4_record(rec, &str_now); if(send_register(cb_param->hash_index, rec, NULL)==1) { rec->last_register_sent = now; - rec->state = REGISTERING_STATE; + reg_change_state(rec,REGISTERING_STATE); } else { - rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:rec->expires) - timer_interval; - rec->state = INTERNAL_ERROR_STATE; + rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:(rec->expires*reregister_exp_percentage/100)) - timer_interval; + reg_change_state(rec,INTERNAL_ERROR_STATE); } } else { /* succesfully REGISTERED */ - rec->state = REGISTERED_STATE; + reg_change_state(rec,REGISTERED_STATE); + if (exp) rec->expires = exp; if (rec->expires <= timer_interval) { LM_ERR("Please decrease timer_interval=[%u]" @@ -572,7 +599,7 @@ int run_reg_tm_cback(void *e_data, void *data, void *r_data) timer_interval, rec->expires, rec->td.rem_uri.len, rec->td.rem_uri.s); } - rec->registration_timeout = now + rec->expires - timer_interval; + rec->registration_timeout = now + (rec->expires*reregister_exp_percentage/100) - timer_interval; } } @@ -589,8 +616,8 @@ int run_reg_tm_cback(void *e_data, void *data, void *r_data) if (rec->auth_user.s==NULL || rec->auth_user.len==0 || rec->auth_password.s==NULL || rec->auth_password.len==0) { LM_ERR("Credentials not provisioned\n"); - rec->state = WRONG_CREDENTIALS_STATE; - rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:rec->expires) - timer_interval; + reg_change_state(rec,WRONG_CREDENTIALS_STATE); + rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:(rec->expires*reregister_exp_percentage/100)) - timer_interval; /* action successfully completed on current list element */ return 1; /* exit list traversal */ } @@ -624,8 +651,8 @@ int run_reg_tm_cback(void *e_data, void *data, void *r_data) /* We already sent an authenticated REGISTER and we are still challanged! */ LM_WARN("Wrong credentials for [%.*s]\n", rec->td.rem_uri.len, rec->td.rem_uri.s); - rec->state = WRONG_CREDENTIALS_STATE; - rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:rec->expires) - timer_interval; + reg_change_state(rec,WRONG_CREDENTIALS_STATE); + rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:(rec->expires*reregister_exp_percentage/100)) - timer_interval; /* action successfully completed on current list element */ return 1; /* exit list traversal */ default: @@ -664,16 +691,16 @@ int run_reg_tm_cback(void *e_data, void *data, void *r_data) switch(rec->state) { case REGISTERING_STATE: if(send_register(cb_param->hash_index, rec, new_hdr)==1) { - rec->state = AUTHENTICATING_STATE; + reg_change_state(rec,AUTHENTICATING_STATE); } else { - rec->state = INTERNAL_ERROR_STATE; + reg_change_state(rec,INTERNAL_ERROR_STATE); } break; case UNREGISTERING_STATE: if(send_unregister(cb_param->hash_index, rec, new_hdr, 0)==1) { - rec->state = AUTHENTICATING_UNREGISTER_STATE; + reg_change_state(rec,AUTHENTICATING_UNREGISTER_STATE); } else { - rec->state = INTERNAL_ERROR_STATE; + reg_change_state(rec,INTERNAL_ERROR_STATE); } break; default: @@ -693,29 +720,30 @@ int run_reg_tm_cback(void *e_data, void *data, void *r_data) } if (0 == parse_min_expires(msg)) { rec->expires = (unsigned int)(long)msg->min_expires->parsed; - if(send_register(cb_param->hash_index, rec, NULL)==1) - rec->state = REGISTERING_STATE; - else - rec->state = INTERNAL_ERROR_STATE; + if(send_register(cb_param->hash_index, rec, NULL)==1) { + reg_change_state(rec,REGISTERING_STATE); + } else { + reg_change_state(rec,INTERNAL_ERROR_STATE); + } } else { - rec->state = REGISTRAR_ERROR_STATE; - rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:rec->expires) - timer_interval; + reg_change_state(rec,REGISTRAR_ERROR_STATE); + rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:(rec->expires*reregister_exp_percentage/100)) - timer_interval; } break; - case 408: /* Interval Too Brief */ - rec->state = REGISTER_TIMEOUT_STATE; - rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:rec->expires) - timer_interval; + case 408: /* No reply or Timeout */ + reg_change_state(rec,REGISTER_TIMEOUT_STATE); + rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:(rec->expires*reregister_exp_percentage/100)) - timer_interval; break; default: if(statuscode<400 && statuscode>=300) { LM_ERR("Redirection not implemented yet\n"); - rec->state = INTERNAL_ERROR_STATE; + reg_change_state(rec,INTERNAL_ERROR_STATE); } else { /* we got an error from the server */ - rec->state = REGISTRAR_ERROR_STATE; - rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:rec->expires) - timer_interval; + reg_change_state(rec,REGISTRAR_ERROR_STATE); + rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:(rec->expires*reregister_exp_percentage/100)) - timer_interval; } } @@ -723,13 +751,11 @@ int run_reg_tm_cback(void *e_data, void *data, void *r_data) /* action successfully completed on current list element */ return 1; /* exit list traversal */ done: - rec->state = INTERNAL_ERROR_STATE; - rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:rec->expires) - timer_interval; + reg_change_state(rec,INTERNAL_ERROR_STATE); + rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:(rec->expires*reregister_exp_percentage/100)) - timer_interval; return -1; /* exit list traversal */ } - - void reg_tm_cback(struct cell *t, int type, struct tmcb_params *ps) { reg_tm_cb_t *cb_param; @@ -761,8 +787,8 @@ void reg_tm_cback(struct cell *t, int type, struct tmcb_params *ps) /* Initialize slinkedl run traversal data */ tm_cback_data.t = t; - tm_cback_data.ps = ps; - tm_cback_data.cb_param = cb_param; + tm_cback_data.ps = ps; + tm_cback_data.cb_param = cb_param; tm_cback_data.now = now; lock_get(®_htable[cb_param->hash_index].lock); @@ -963,32 +989,32 @@ int run_timer_check(void *e_data, void *data, void *r_data) new_call_id_ftag_4_record(rec, s_now); if(send_register(i, rec, NULL)==1) { rec->last_register_sent = now; - rec->state = REGISTERING_STATE; + reg_change_state(rec,REGISTERING_STATE); } else { - rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:rec->expires) - timer_interval; - rec->state = INTERNAL_ERROR_STATE; + rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:(rec->expires*reregister_exp_percentage/100)) - timer_interval; + reg_change_state(rec,INTERNAL_ERROR_STATE); } } else { if(send_unregister(i, rec, NULL, 0)==1) { - rec->state = UNREGISTERING_STATE; + reg_change_state(rec,UNREGISTERING_STATE); } else { - rec->state = INTERNAL_ERROR_STATE; + reg_change_state(rec,INTERNAL_ERROR_STATE); } } break; case REGISTERED_STATE: /* check if we need to re-register */ - if (now < rec->registration_timeout) { + if (now + timer_interval < rec->registration_timeout) { break; } case NOT_REGISTERED_STATE: if (rec->flags®_ENABLED) { if(send_register(i, rec, NULL)==1) { rec->last_register_sent = now; - rec->state = REGISTERING_STATE; + reg_change_state(rec,REGISTERING_STATE); } else { - rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:rec->expires) - timer_interval; - rec->state = INTERNAL_ERROR_STATE; + rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:(rec->expires*reregister_exp_percentage/100)) - timer_interval; + reg_change_state(rec,INTERNAL_ERROR_STATE); } } break; @@ -1051,7 +1077,7 @@ static int cluster_shtag_check(void *e_data, void *data, void *r_data) 0==memcmp(rec->cluster_shtag.s, shtag_data->tag->s, shtag_data->tag->len)) { /* this record matches the shtag + cluster_id, so we need to de-active it */ LM_DBG("Moving record to NOT_REGISTERED_STATE\n"); - rec->state = NOT_REGISTERED_STATE; + reg_change_state(rec,NOT_REGISTERED_STATE); } @@ -1249,6 +1275,18 @@ int run_mi_reg_list_record(void *e_data, void *data, void *r_data) return 0; /* continue search */ } +int run_mi_reg_matching(void *e_data, void*data, void *unused) +{ + reg_record_t *rec = (reg_record_t*)e_data; + record_coords_t *coords = (record_coords_t *)data; + + if (!str_strcmp(&coords->contact, &rec->contact_uri) && + !str_strcmp(&coords->registrar, &rec->td.rem_target)) + return 1; + else + return 0; /* continue search */ +} + static mi_response_t *mi_get_coords(const mi_params_t *params, record_coords_t *coords) { if (get_mi_string_param(params, "aor", &coords->aor.s, &coords->aor.len) < 0) @@ -1347,7 +1385,7 @@ static mi_response_t *mi_reg_reload(const mi_params_t *params, reg_htable[i].s_list = NULL; } reg_htable[i].s_list = slinkedl_init(®_alloc, ®_free); - if (reg_htable[i].p_list == NULL) { + if (reg_htable[i].s_list == NULL) { LM_ERR("oom while allocating list\n"); err = 1; } @@ -1424,10 +1462,10 @@ int run_mi_reg_enable(void *e_data, void *data, void *r_data) if(send_register((unsigned long)coords->extra, rec, NULL)==1) { rec->last_register_sent = now; - rec->state = REGISTERING_STATE; + reg_change_state(rec,REGISTERING_STATE); } else { - rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:rec->expires) - timer_interval; - rec->state = INTERNAL_ERROR_STATE; + rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:(rec->expires*reregister_exp_percentage/100)) - timer_interval; + reg_change_state(rec,INTERNAL_ERROR_STATE); } } @@ -1450,10 +1488,11 @@ int run_mi_reg_disable(void *e_data, void *data, void *r_data) !str_strcmp(&coords->registrar, &rec->td.rem_target)) { if (rec->flags®_ENABLED) { if (rec->state == REGISTERED_STATE) { - if(send_unregister((unsigned long)coords->extra, rec, NULL, 0)==1) - rec->state = UNREGISTERING_STATE; - else - rec->state = INTERNAL_ERROR_STATE; + if(send_unregister((unsigned long)coords->extra, rec, NULL, 0)==1) { + reg_change_state(rec,UNREGISTERING_STATE); + } else { + reg_change_state(rec,INTERNAL_ERROR_STATE); + } } rec->flags &= ~REG_ENABLED; @@ -1564,3 +1603,294 @@ static mi_response_t *mi_reg_force_register(const mi_params_t *params, return init_mi_result_ok(); } +static mi_response_t *mi_reg_upsert(const mi_params_t *params, + struct mi_handler *async_hdl) +{ + uac_reg_map_t uac_param; + str aor; + str contact; + str registrar; + str proxy; + str third_party_registrant; + str username; + str password; + str binding_params; + int expiry; + str forced_socket; + str sharing_tag; + int state; + struct sip_uri uri; + param_hooks_t hooks; + param_t *c_params = NULL; + param_t *_param = NULL; + str _param_str = {NULL, 0}; + int reg_id_found = 0; + int sip_instance_found = 0; + char *p; + str s; + str now = {NULL, 0}; + int ret; + int len; + int mode = REG_DB_LOAD_RECORD; + record_coords_t coords; + + if (get_mi_string_param(params, "aor", &aor.s, &aor.len) < 0) + return init_mi_param_error(); + if (get_mi_string_param(params, "contact", + &contact.s, &contact.len) < 0) + return init_mi_param_error(); + if (get_mi_string_param(params, "registrar", + ®istrar.s, ®istrar.len) < 0) + return init_mi_param_error(); + if (get_mi_string_param(params, "proxy", + &proxy.s, &proxy.len) < 0) + return init_mi_param_error(); + if (get_mi_string_param(params, "third_party_registrant", + &third_party_registrant.s, &third_party_registrant.len) < 0) + return init_mi_param_error(); + if (get_mi_string_param(params, "username", + &username.s, &username.len) < 0) + return init_mi_param_error(); + if (get_mi_string_param(params, "password", + &password.s, &password.len) < 0) + return init_mi_param_error(); + if (get_mi_string_param(params, "binding_params", + &binding_params.s, &binding_params.len) < 0) + return init_mi_param_error(); + if (get_mi_int_param(params, "expiry",&expiry) < 0) { + return init_mi_param_error(); + } + if (get_mi_string_param(params, "forced_socket", + &forced_socket.s, &forced_socket.len) < 0) + return init_mi_param_error(); + if (get_mi_string_param(params, "cluster_shtag", + &sharing_tag.s, &sharing_tag.len) < 0) + return init_mi_param_error(); + if (get_mi_int_param(params, "state",&state) < 0) + return init_mi_param_error(); + + p = int2str((unsigned long)(time(0)), &len); + if (p && len>0) { + now.s = (char *)pkg_malloc(len); + if(now.s) { + memcpy(now.s, p, len); now.len = len; + } else { + return init_mi_error(503, MI_SSTR("Internal Error")); + } + } + + memset(&uac_param, 0, sizeof(uac_reg_map_t)); + + uac_param.registrar_uri = registrar; + if (parse_uri(uac_param.registrar_uri.s,uac_param.registrar_uri.len, &uri)<0) { + LM_ERR("cannot parse registrar uri [%.*s]\n", + uac_param.registrar_uri.len, + uac_param.registrar_uri.s); + + return init_mi_param_error(); + } + + if (uri.user.s && uri.user.len) { + LM_ERR("registrant uri must not have user [%.*s]\n", + uri.user.len, uri.user.s); + + return init_mi_param_error(); + } + + uac_param.proxy_uri = proxy; + if (uac_param.proxy_uri.len) { + if (parse_uri(uac_param.proxy_uri.s, + uac_param.proxy_uri.len, &uri)<0) { + LM_ERR("cannot parse proxy uri [%.*s]\n", + uac_param.proxy_uri.len, + uac_param.proxy_uri.s); + return init_mi_param_error(); + } + if (uri.user.s && uri.user.len) { + LM_ERR("proxy uri must not have user [%.*s]\n", + uri.user.len, uri.user.s); + return init_mi_param_error(); + } + } else { + uac_param.proxy_uri.s = NULL; + } + + uac_param.to_uri = aor; + if (!uac_param.to_uri.s) { + LM_ERR("empty AOR provided\n"); + return init_mi_param_error(); + } + if (parse_uri(uac_param.to_uri.s,uac_param.to_uri.len,&uri)<0) { + LM_ERR("cannot parse aor uri [%.*s]\n", + uac_param.to_uri.len, uac_param.to_uri.s); + return init_mi_param_error(); + } + uac_param.hash_code = core_hash(&uac_param.to_uri, NULL, reg_hsize); + + uac_param.from_uri = third_party_registrant; + if (uac_param.from_uri.len) { + if (parse_uri(uac_param.from_uri.s, + uac_param.from_uri.len, &uri)<0) { + LM_ERR("cannot parse third party registrant" + " uri [%.*s]\n", + uac_param.from_uri.len, + uac_param.from_uri.s); + return init_mi_param_error(); + } + } else { + uac_param.from_uri.s = NULL; + } + + uac_param.contact_uri = contact; + if (!uac_param.contact_uri.s) { + LM_ERR("empty CONTACT provided\n"); + return init_mi_param_error(); + } + if (parse_uri(uac_param.contact_uri.s, + uac_param.contact_uri.len, &uri)<0) { + LM_ERR("cannot parse contact uri [%.*s]\n", + uac_param.contact_uri.len, + uac_param.contact_uri.s); + return init_mi_param_error(); + } + + uac_param.auth_user = username; + uac_param.auth_password = password; + + uac_param.contact_params = binding_params; + if (uac_param.contact_params.len != 0) { + memset(&hooks, 0, sizeof(param_hooks_t)); + _param_str = uac_param.contact_params; + if (parse_params(&_param_str, CLASS_CONTACT, &hooks, &c_params) <0) { + LM_ERR("Bogus params [%.*s]\n", _param_str.len, _param_str.s); + free_params(c_params); + return init_mi_param_error(); + } + _param = c_params; + while (_param) { + LM_DBG("[%.*s][%.*s]\n", _param->name.len, _param->name.s, _param->body.len, _param->body.s); + if (reg_id_found == 0 && strncmp(_param->name.s, "reg-id", 6) == 0) + reg_id_found = 1; + if (sip_instance_found == 0 && strncmp(_param->name.s, "+sip.instance", 13) == 0) + sip_instance_found = 1; + _param = _param->next; + } + free_params(c_params); + if (reg_id_found && sip_instance_found) { + LM_DBG("special record\n"); + uac_param.flags |= FORCE_SINGLE_REGISTRATION; + } + } + + uac_param.expires = expiry; + if (uac_param.expires <= timer_interval) { + LM_ERR("Please decrease timer_interval=[%u]" + " - requested expires=[%u] to small for AOR=[%.*s]\n", + timer_interval, uac_param.expires, + uac_param.to_uri.len, uac_param.to_uri.s); + return init_mi_param_error(); + } + + /* Get the socket */ + if (forced_socket.len && forced_socket.s) { + uac_param.send_sock = parse_sock_info(&forced_socket); + if (uac_param.send_sock==NULL) { + LM_ERR("invalid forced socket [%.*s]\n", + forced_socket.len, forced_socket.s); + return init_mi_param_error(); + } + } + + if (sharing_tag.s && sharing_tag.len) { + uac_param.cluster_shtag = sharing_tag; + p = memchr(uac_param.cluster_shtag.s, '/', + uac_param.cluster_shtag.len); + if (!p) { + LM_ERR("Bad naming for sharing tag <%.*s>, " + " expected\n", + uac_param.cluster_shtag.len, + uac_param.cluster_shtag.s); + return init_mi_param_error(); + } + s.s = p + 1; + s.len = uac_param.cluster_shtag.s + uac_param.cluster_shtag.len - s.s; + trim_spaces_lr( s ); + uac_param.cluster_shtag.len = p - uac_param.cluster_shtag.s; + trim_spaces_lr( uac_param.cluster_shtag ); + /* get the cluster ID */ + if (str2int( &s, (unsigned int*)&uac_param.cluster_id)<0) { + LM_ERR("Invalid cluster id <%.*s> for sharing tag " + " <%.*s> \n",s.len, s.s, + uac_param.cluster_shtag.len, + uac_param.cluster_shtag.s); + return init_mi_param_error(); + } + } + + if (state == REG_DB_STATE_ENABLED) + uac_param.flags |= REG_ENABLED; + + LM_DBG("registrar=[%.*s] AOR=[%.*s] auth_user=[%.*s] " + "password=[%.*s] expire=[%d] proxy=[%.*s] " + "contact=[%.*s] third_party=[%.*s] " + "cluster_shtag=[%.*s/%d] state=[%d]\n", + uac_param.registrar_uri.len, uac_param.registrar_uri.s, + uac_param.to_uri.len, uac_param.to_uri.s, + uac_param.auth_user.len, uac_param.auth_user.s, + uac_param.auth_password.len, uac_param.auth_password.s, + uac_param.expires, + uac_param.proxy_uri.len, uac_param.proxy_uri.s, + uac_param.contact_uri.len, uac_param.contact_uri.s, + uac_param.from_uri.len, uac_param.from_uri.s, + uac_param.cluster_shtag.len, uac_param.cluster_shtag.s, + uac_param.cluster_id, + state); + + coords.aor = aor; + coords.contact = contact; + coords.registrar = registrar; + + lock_get(®_htable[uac_param.hash_code].lock); + ret = add_record(&uac_param, &now, mode, &coords); + lock_release(®_htable[uac_param.hash_code].lock); + if(ret<0) { + if (now.s) pkg_free(now.s); + return init_mi_error(503, MI_SSTR("Failed Upsert")); + } + + if (now.s) pkg_free(now.s); + return init_mi_result_ok(); +} + +static mi_response_t *mi_reg_delete(const mi_params_t *params, + struct mi_handler *async_hdl) +{ + mi_response_t *resp; + record_coords_t coords; + int rc; + unsigned int hash_code; + + if ((resp = mi_get_coords(params, &coords))) + return resp; + + hash_code = core_hash(&coords.aor, NULL, reg_hsize); + coords.extra = (void*)(unsigned long)hash_code; + + lock_get(®_htable[hash_code].lock); + rc = slinkedl_traverse(reg_htable[hash_code].p_list, + &run_mi_reg_disable, &coords, NULL); + + if (rc > 0) { + /* we found it, let's also delete it now */ + rc = slinkedl_delete(reg_htable[hash_code].p_list, + &run_mi_reg_matching, &coords); + } + lock_release(®_htable[hash_code].lock); + + if (rc < 0) + return NULL; + else if (rc == 0) + return init_mi_error(404, MI_SSTR("No such registrant")); + + return init_mi_result_ok(); +}