diff --git a/modules/dialog_ng/Makefile b/modules/dialog_ng/Makefile index c9165bcb284..227cef59331 100644 --- a/modules/dialog_ng/Makefile +++ b/modules/dialog_ng/Makefile @@ -12,4 +12,5 @@ SERLIBPATH=../../lib SER_LIBS+=$(SERLIBPATH)/kmi/kmi SER_LIBS+=$(SERLIBPATH)/srdb1/srdb1 SER_LIBS+=$(SERLIBPATH)/kcore/kcore +SER_LIBS+=$(SERLIBPATH)/srutils/srutils include ../../Makefile.modules diff --git a/modules/dialog_ng/dialog.c b/modules/dialog_ng/dialog.c index cdf54e45ca4..c37a631c952 100644 --- a/modules/dialog_ng/dialog.c +++ b/modules/dialog_ng/dialog.c @@ -55,8 +55,6 @@ static char* profiles_wv_s = NULL; static char* profiles_nv_s = NULL; int detect_spirals = 1; str dlg_extra_hdrs = {NULL, 0}; -int initial_cbs_inscript = 1; - str dlg_bridge_controller = str_init("sip:controller@kamailio.org"); str ruri_pvar_param = str_init("$ru"); @@ -792,7 +790,7 @@ static int w_dlg_terminate(struct sip_msg *msg, char *side, char *r) { } } - dlg = get_current_dialog(msg); + dlg = dlg_get_msg_dialog(msg); //dlg_get_ctx_dialog(); if (!dlg) { LM_DBG("Unable to find dialog for terminate\n"); diff --git a/modules/dialog_ng/dlg_cb.c b/modules/dialog_ng/dlg_cb.c index 3365e7b4c31..1dc820f8e9d 100644 --- a/modules/dialog_ng/dlg_cb.c +++ b/modules/dialog_ng/dlg_cb.c @@ -234,6 +234,7 @@ void run_load_callbacks( void ) void run_create_callbacks(struct dlg_cell *dlg, struct sip_msg *msg) { struct dlg_callback *cb; + LM_DBG("Running DLG_CREATED callbacks\n"); if (create_cbs==NULL || create_cbs==POINTER_CLOSED_MARKER || create_cbs->first==NULL) return; diff --git a/modules/dialog_ng/dlg_cb.h b/modules/dialog_ng/dlg_cb.h index a7ef40d7bd5..490256588cd 100644 --- a/modules/dialog_ng/dlg_cb.h +++ b/modules/dialog_ng/dlg_cb.h @@ -70,8 +70,6 @@ typedef int (*set_dlg_variable_f)( str* callid, str* ftag, str* ttag, typedef str* (*get_dlg_variable_f)( str *callid, str *ftag, str *ttag, str* key); -typedef struct dlg_cell* (*get_current_dlg_f)( struct sip_msg* msg); - #define DLGCB_LOADED (1<<0) #define DLGCB_CREATED (1<<1) #define DLGCB_FAILED (1<<2) diff --git a/modules/dialog_ng/dlg_db_handler.c b/modules/dialog_ng/dlg_db_handler.c index 226edb4c978..18aa3e94317 100644 --- a/modules/dialog_ng/dlg_db_handler.c +++ b/modules/dialog_ng/dlg_db_handler.c @@ -426,7 +426,7 @@ static int load_dialog_out_from_db(struct dlg_cell *dlg, str *did, int fetch_num GET_STR_VALUE(to_uri, values, DLGO_TO_URI_IDX, 1, 0); GET_STR_VALUE(to_tag, values, DLGO_TO_TAG_IDX, 1, 0); - dlg_out = build_new_dlg_out(dlg, &to_uri, &to_tag); + dlg_out = build_new_dlg_out(dlg, &to_uri, &to_tag, 0); if (!dlg_out) { LM_ERR("Error creating dlg_out cell\n"); @@ -534,7 +534,7 @@ static int load_dialog_info_from_db(int dlg_hash_size, int fetch_num_rows) } /*link the dialog*/ - link_dlg(dlg, 0); + link_dlg(dlg, 0, 0); GET_STR_VALUE(did, values, DLGI_DID_COL_IDX, 1, 0); update_dlg_did(dlg, &did); @@ -721,7 +721,7 @@ static int load_dialog_vars_from_db(int fetch_num_rows) if (dlg->h_id == VAL_INT(values+1)) { str key = { VAL_STR(values+2).s, strlen(VAL_STRING(values+2)) }; str value = { VAL_STR(values+3).s, strlen(VAL_STRING(values+3)) }; - set_dlg_variable_unsafe(dlg, &key, &value, 1); + set_dlg_variable_unsafe(dlg, &key, &value); break; } dlg = dlg->next; @@ -886,8 +886,14 @@ int update_dialog_vars_dbinfo(struct dlg_cell * cell, struct dlg_var * var) int update_dialog_out_dbinfo_unsafe(struct dlg_cell * cell) { + str x = {0,0}; + struct dlg_cell_out *dlg_out = cell->dlg_entry_out.first; - str x = {0,0}; + if (!dlg_out) { + LM_DBG("no out dialogs to update\n"); + return 0; + } + if(use_dialog_out_table()!=0) return -1; @@ -1044,8 +1050,9 @@ int update_dialog_dbinfo_unsafe(struct dlg_cell * cell) VAL_INT(GET_FIELD_IDX(values, DLGI_HASH_ENTRY_COL_IDX)) = cell->h_entry; VAL_INT(GET_FIELD_IDX(values, DLGI_HASH_ID_COL_IDX)) = cell->h_id; VAL_INT(GET_FIELD_IDX(values, DLGI_START_TIME_COL_IDX)) = cell->start_ts; - VAL_INT(GET_FIELD_IDX(values, DLGI_STATE_COL_IDX)) = cell->state; + VAL_INT(GET_FIELD_IDX(values, DLGI_STATE_COL_IDX)) = cell->state; VAL_INT(GET_FIELD_IDX(values, DLGI_TIMEOUT_COL_IDX)) = (unsigned int)( (unsigned int)time(0) + cell->tl.timeout - get_ticks() ); + VAL_INT(GET_FIELD_IDX(values, DLGI_TOROUTE_INDEX_COL_IDX)) = cell->toroute; SET_STR_VALUE(GET_FIELD_IDX(values, DLGI_CALLID_COL_IDX), cell->callid); SET_STR_VALUE(GET_FIELD_IDX(values, DLGI_DID_COL_IDX), cell->did); diff --git a/modules/dialog_ng/dlg_handlers.c b/modules/dialog_ng/dlg_handlers.c index 96f7d3eb799..e94be2035f0 100644 --- a/modules/dialog_ng/dlg_handlers.c +++ b/modules/dialog_ng/dlg_handlers.c @@ -39,7 +39,6 @@ static int default_timeout; /*!< default dialog timeout */ static int seq_match_mode; /*!< dlg_match mode */ static int shutdown_done = 0; /*!< 1 when destroy_dlg_handlers was called */ extern int detect_spirals; -extern int initial_cbs_inscript; int spiral_detected = -1; extern struct rr_binds d_rrb; /*!< binding to record-routing module */ @@ -91,7 +90,7 @@ void destroy_dlg_handlers(void) { * \param id dialog hash id * \return 0 on success, -1 on failure */ -static inline int add_dlg_rr_param(struct dlg_cell *dlg, struct sip_msg *req, unsigned int entry, unsigned int id) { +static inline int add_dlg_rr_param(struct sip_msg *req, unsigned int entry, unsigned int id) { static char buf[RR_DLG_PARAM_SIZE]; str s; int n; @@ -104,8 +103,6 @@ static inline int add_dlg_rr_param(struct dlg_cell *dlg, struct sip_msg *req, un p += rr_param.len; *(p++) = '='; - char *did = p; - n = RR_DLG_PARAM_SIZE - (p - buf); if (int2reverse_hex(&p, &n, entry) == -1) return -1; @@ -123,33 +120,29 @@ static inline int add_dlg_rr_param(struct dlg_cell *dlg, struct sip_msg *req, un return -1; } + return 0; +} +/*! + * \brief Unreference a dialog from tm callback (another wrapper) + * \param t transaction, unused + * \param type type of the entered callback + * \param param saved dialog structure in the callback + */ +static void unref_dlg_from_cb(struct cell* t, int type, struct tmcb_params *param) { + dlg_cell_t *dlg = NULL; + dlg_iuid_t *iuid = NULL; - //add the did into the dlg structure - int did_len = p - did; - - if (dlg->did.s) { - if (dlg->did.len < did_len) { - shm_free(dlg->did.s); - dlg->did.s = (char*) shm_malloc(did_len); - if (dlg->did.s == NULL) { - LM_ERR("failed to add did to dlg_cell struct\n"); - return -1; - } - } - } else { - dlg->did.s = (char*) shm_malloc(did_len); - if (dlg->did.s == NULL) { - LM_ERR("failed to add did to dlg_cell struct\n"); - return -1; - } - } - memcpy(dlg->did.s, did, did_len); - dlg->did.len = did_len; - - + LM_DBG("Unref dlg from callback called\n"); + iuid = (dlg_iuid_t*) (*param->param); + if (iuid == NULL) + return; - return 0; + dlg = dlg_get_by_iuid(iuid); + if (dlg == NULL) + return; + /* unref by 2: 1 set when adding in tm cb, 1 sent by dlg_get_by_iuid() */ + unref_dlg(dlg, 2); } /*! @@ -236,11 +229,11 @@ int populate_leg_info(struct dlg_cell *dlg, struct sip_msg *msg, if (leg == DLG_CALLER_LEG) dlg->from_rr_nb = skip_recs; - LM_DBG("route_set %.*s, contact %.*s, cseq %.*s and bind_addr %.*s\n", + LM_DBG("route_set %.*s, contact %.*s, cseq %.*s and bind_addr %.*s [%p]\n", rr_set.len, rr_set.s, contact.len, contact.s, cseq.len, cseq.s, msg->rcv.bind_address->sock_str.len, - msg->rcv.bind_address->sock_str.s); + msg->rcv.bind_address->sock_str.s, msg->rcv.bind_address); if (dlg_set_leg_info(dlg, tag, &rr_set, &contact, &cseq, callee_bind_address, leg) != 0) { LM_ERR("dlg_set_leg_info failed\n"); @@ -255,6 +248,40 @@ int populate_leg_info(struct dlg_cell *dlg, struct sip_msg *msg, return -1; } +/*! + * \brief Clone dialog internal unique id to shared memory + */ +dlg_iuid_t *dlg_get_iuid_shm_clone(dlg_cell_t *dlg) { + dlg_iuid_t *iuid = NULL; + + if (dlg == NULL) + return NULL; + + iuid = (dlg_iuid_t*) shm_malloc(sizeof (dlg_iuid_t)); + if (iuid == NULL) { + LM_ERR("failed to clone dialog iuid\n"); + return NULL; + } + + memset(iuid, 0, sizeof (dlg_iuid_t)); + iuid->h_entry = dlg->h_entry; + iuid->h_id = dlg->h_id; + + return iuid; +} + +/*! + * \brief Free dialog internal unique id stored in shared memory + */ +void dlg_iuid_sfree(void *iuid) { + if (iuid) { + LM_DBG("freeing dlg iuid [%u:%u] (%p)\n", + ((dlg_iuid_t*) iuid)->h_entry, + ((dlg_iuid_t*) iuid)->h_id, iuid); + shm_free(iuid); + } +} + /*! * \brief Function that executes BYE reply callbacks * \param t transaction, unused @@ -284,6 +311,29 @@ static void dlg_terminated_confirmed(struct cell* t, 0); } +static void dlg_cancel_confirmed(struct cell* t, + int type, + struct tmcb_params* params) { + if (!params || !params->req || !params->param) { + LM_ERR("invalid parameters!\n"); + return; + } + + struct dlg_cell* dlg = (struct dlg_cell*) *params->param; + + if (!dlg) { + LM_ERR("failed to get dialog from params!\n"); + return; + } + /* dialog termination confirmed (BYE reply) */ + run_dlg_callbacks(DLGCB_TERMINATED_CONFIRMED, + dlg, + params->req, + params->rpl, + DLG_DIR_UPSTREAM, + 0); +} + /*! * \brief Execute callback for the BYE request and register callback for the BYE reply * \param req request message @@ -319,952 +369,1323 @@ static void dlg_terminated(struct sip_msg* req, } } -static void unlink_dlgouts_from_cb(struct cell* t, int type, struct tmcb_params *param) { - struct dlg_cell *dlg = (struct dlg_cell *) (*param->param); +/*! + * \brief Execute callback for the BYE request and register callback for the BYE reply + * \param req request message + * \param dlg corresponding dialog + * \param dir message direction + */ +static void dlg_cancelled(struct sip_msg* req, + struct dlg_cell* dlg, + unsigned int dir) { + if (!req) { + LM_ERR("request is empty!"); + return; + } - if (!dlg) + if (!dlg) { + LM_ERR("dialog is empty!"); return; + } - if (t && t->fwded_totags && t->fwded_totags->tag.len > 0) { - LM_DBG("unlink_dlgouts_from_cb: transaction [%.*s] can now be removed IFF it has been marked for deletion\n", t->fwded_totags->tag.len, t->fwded_totags->tag.s); - dlg_remove_dlg_out_tag(dlg, &t->fwded_totags->tag); + /* dialog terminated (BYE) */ + run_dlg_callbacks(DLGCB_TERMINATED, dlg, req, NULL, dir, 0); + + /* register callback for the coresponding reply */ + LM_DBG("Registering tmcb for CANCEL confirmed\n"); + if (d_tmb.register_tmcb(req, + 0, + TMCB_RESPONSE_OUT, + dlg_cancel_confirmed, + (void*) dlg, + 0) <= 0) { + LM_ERR("cannot register response callback for CANCEL request\n"); + return; } } -/*! - * \brief Function that is registered as TM callback and called on replies - * - * Function that is registered as TM callback and called on replies. It - * parses the reply and set the appropriate event. This is then used to - * update the dialog state, run eventual dialog callbacks and save or - * update the necessary informations about the dialog. - * \see next_state_dlg - * \param t transaction, unused - * \param type type of the entered callback - * \param param saved dialog structure in the callback - */ -static void dlg_onreply(struct cell* t, int type, struct tmcb_params *param) { - struct dlg_cell *dlg; - struct dlg_cell_out *dlg_out = 0; +//static void unlink_dlgouts_from_cb(struct cell* t, int type, struct tmcb_params *param) { +// struct dlg_cell *dlg = (struct dlg_cell *) (*param->param); +// +// if (!dlg) +// return; - int new_state, old_state, unref, event; - str to_tag, to_uri; - struct sip_msg *req = param->req; - struct sip_msg *rpl = param->rpl; - struct dlg_entry_out* dlg_entry_out = 0; +// if (t && t->fwded_totags && t->fwded_totags->tag.len > 0) { +// LM_DBG("unlink_dlgouts_from_cb: transaction [%.*s] can now be removed IFF it has been marked for deletion\n", t->fwded_totags->tag.len, t->fwded_totags->tag.s); +// dlg_remove_dlg_out_tag(dlg, &t->fwded_totags->tag); +// } +//} - if (t && t->fwded_totags) - LM_DBG("ONREPLY CALL_BACK from TM received and type is [%i] and TO is [%.*s]\n", type, t->fwded_totags->tag.len, t->fwded_totags->tag.s); - else - LM_DBG("ONREPLY CALL_BACK from TM received and type is [%i]\n", type); +/*static void dlg_oncancel(struct cell* t, int type, struct tmcb_params *param) { + struct dlg_cell* dlg; + int unref = 0, old_state, new_state; + LM_DBG("RECEIVED CANCEL ON DIALOG......\n"); dlg = (struct dlg_cell *) (*param->param); if (shutdown_done || dlg == 0) return; - if (t) { - dlg->transaction = t; - } + struct sip_msg *req = param->req; + old_state = dlg->state; + LM_DBG("DLG dialog id is entry:id [%i:%i]\n", dlg->h_entry, dlg->h_id); + next_state_dlg(dlg, DLG_EVENT_REQCANCEL, &old_state, &new_state, &unref, 0); - LM_DBG("DLG dialogid is entry:id [%i:%i]\n", dlg->h_entry, dlg->h_id); + if (old_state != DLG_STATE_DELETED && new_state == DLG_STATE_DELETED) + dlg_cancelled(req, dlg, DLG_DIR_NONE); - if (type == TMCB_RESPONSE_FWDED) { - // The state does not change, but the msg is mutable in this callback - LM_DBG("TMCB_RESPONSE_FWDED from TM received"); - run_dlg_callbacks(DLGCB_RESPONSE_FWDED, dlg, req, rpl, DLG_DIR_UPSTREAM, 0); - return; - } + unref_dlg(dlg, unref); +}*/ + +/*! + * \brief Helper function that run dialog callbacks on forwarded requests + * \see dlg_seq_up_onreply + * \see dlg_seq_down_onreply + * \param t transaction, unused + * \param type type of the callback, should be TMCB_RESPONSE_FWDED + * \param param saved dialog structure inside the callback + * \param direction direction of the request + */ +static void dlg_seq_onreply_helper(struct cell* t, int type, + struct tmcb_params *param, const int direction) { + dlg_cell_t *dlg = NULL; + dlg_iuid_t *iuid = NULL; - if (type == TMCB_RESPONSE_OUT) { - LM_DBG("TMCB_RESPONSE_OUT\n"); + if (shutdown_done) + return; + iuid = (dlg_iuid_t*) (*param->param); + dlg = dlg_get_by_iuid(iuid); + if (dlg == 0) return; + + if (type == TMCB_RESPONSE_FWDED) { + run_dlg_callbacks(DLGCB_RESPONSE_WITHIN, + dlg, + param->req, + param->rpl, + direction, + 0); } + dlg_release(dlg); - if (type == TMCB_RESPONSE_READY) { - if (rpl == FAKED_REPLY) { - LM_DBG("Faked reply\n"); - return; - } + return; +} - // get to tag - LM_DBG("Extracting to-tag from reply"); - if (!rpl->to && ((parse_headers(rpl, HDR_TO_F, 0) < 0) || !rpl->to)) { - LM_ERR("bad reply or missing TO hdr :-/\n"); - to_tag.s = 0; - to_tag.len = 0; - } else { - //populate to uri for this branch. - to_uri = get_to(rpl)->uri; +/*! + * \brief Run dialog callbacks on forwarded requests in upstream direction + * \see dlg_seq_onreply_helper + * \param t transaction, unused + * \param type type of the callback, should be TMCB_RESPONSE_FWDED + * \param param saved dialog structure inside the callback + */ +static void dlg_seq_up_onreply(struct cell* t, int type, struct tmcb_params *param) { + return dlg_seq_onreply_helper(t, type, param, DLG_DIR_UPSTREAM); +} - to_tag = get_to(rpl)->tag_value; - if (to_tag.s == 0 || to_tag.len == 0) { - LM_ERR("missing TAG param in TO hdr :-/\n"); - to_tag.s = 0; - to_tag.len = 0; - //Here we assume that the transaction module timer will remove any early dialogs - return; - } +/*! + * \brief Run dialog callbacks on forwarded requests in downstream direction + * \see dlg_seq_onreply_helper + * \param t transaction, unused + * \param type type of the callback, should be TMCB_RESPONSE_FWDED + * \param param saved dialog structure inside the callback + */ +static void dlg_seq_down_onreply(struct cell* t, int type, struct tmcb_params *param) { + return dlg_seq_onreply_helper(t, type, param, DLG_DIR_DOWNSTREAM); +} + +/*! + * \brief Return the timeout for a dialog + * \param req SIP message + * \return value from timeout AVP if present or default timeout + */ +inline static int get_dlg_timeout(struct sip_msg *req) { + pv_value_t pv_val; + + if (timeout_avp) { + if (pv_get_spec_value(req, timeout_avp, &pv_val) == 0 && + pv_val.flags & PV_VAL_INT && pv_val.ri > 0) { + return pv_val.ri; } + LM_DBG("invalid AVP value, using default timeout\n"); + } + return default_timeout; +} - LM_DBG("Got to-tag from response: %.*s \n", to_tag.len, to_tag.s); +/*! + * \brief Helper function to get the necessary content from SIP message + * \param req SIP request + * \param callid found callid + * \param ftag found from tag + * \param ttag found to tag + * \param with_ttag flag set if to tag must be found for success + * \return 0 on success, -1 on failure + */ +static inline int pre_match_parse(struct sip_msg *req, str *callid, + str *ftag, str *ttag, int with_ttag) { + if (parse_headers(req, HDR_CALLID_F | HDR_TO_F, 0) < 0 || !req->callid || + !req->to) { + LM_ERR("bad request or missing CALLID/TO hdr :-/\n"); + return -1; } - if (type == TMCB_DESTROY) - event = DLG_EVENT_TDEL; - else if (param->code < 200) - event = DLG_EVENT_RPL1xx; - else if (param->code < 300) - event = DLG_EVENT_RPL2xx; - else - event = DLG_EVENT_RPL3xx; + if (get_to(req)->tag_value.len == 0) { + if (with_ttag == 1) { + /* out of dialog request with preloaded Route headers; ignore. */ + return -1; + } else { + ttag->s = NULL; + ttag->len = 0; + } + } else { + *ttag = get_to(req)->tag_value; + } - LM_DBG("Calling next_state_dlg and event is %i\n", event); - next_state_dlg(dlg, event, &old_state, &new_state, &unref, &to_tag); + if (parse_from_header(req) < 0 || get_from(req)->tag_value.len == 0) { + LM_ERR("failed to get From header\n"); + return -1; + } - if (type == TMCB_RESPONSE_READY) { - LM_DBG("Checking if there is an existing dialog_out entry with same to-tag"); + /* callid */ + *callid = req->callid->body; + trim(callid); + /* from tag */ + *ftag = get_from(req)->tag_value; + return 0; +} - dlg_entry_out = &dlg->dlg_entry_out; +static inline char* tm_type_to_string(int type) { + switch (type) { + case TMCB_REQUEST_IN: + return "TMCB_REQUEST_IN"; + case TMCB_RESPONSE_IN: + return "TMCB_RESPONSE_IN"; + case TMCB_E2EACK_IN: + return "TMCB_E2EACK_IN"; + case TMCB_REQUEST_PENDING: + return "TMCB_REQUEST_PENDING"; + case TMCB_REQUEST_FWDED: + return "TMCB_REQUEST_FWDED"; + case TMCB_RESPONSE_FWDED: + return "TMCB_RESPONSE_FWDED"; + case TMCB_ON_FAILURE_RO: + return "TMCB_ON_FAILURE_RO"; + case TMCB_ON_FAILURE: + return "TMCB_ON_FAILURE"; + case TMCB_REQUEST_OUT: + return "TMCB_REQUEST_OUT"; + case TMCB_RESPONSE_OUT: + return "TMCB_RESPONSE_OUT"; + case TMCB_LOCAL_COMPLETED: + return "TMCB_LOCAL_COMPLETED"; + case TMCB_LOCAL_RESPONSE_OUT: + return "TMCB_LOCAL_RESPONSE_OUT"; + case TMCB_ACK_NEG_IN: + return "TMCB_ACK_NEG_IN"; + case TMCB_REQ_RETR_IN: + return "TMCB_REQ_RETR_IN"; + case TMCB_LOCAL_RESPONSE_IN: + return "TMCB_LOCAL_RESPONSE_IN"; + case TMCB_LOCAL_REQUEST_IN: + return "TMCB_LOCAL_REQUEST_IN"; + case TMCB_DLG: + return "TMCB_DLG"; + case TMCB_DESTROY: + return "TMCB_DESTROY"; + case TMCB_E2ECANCEL_IN: + return "TMCB_E2ECANCEL_IN"; + case TMCB_E2EACK_RETR_IN: + return "TMCB_E2EACK_RETR_IN"; + case TMCB_RESPONSE_READY: + return "TMCB_RESPONSE_READY"; + case TMCB_DONT_ACK: + return "TMCB_DONT_ACK"; + case TMCB_REQUEST_SENT: + return "TMCB_REQUEST_SENT"; + case TMCB_RESPONSE_SENT: + return "TMCB_RESPONSE_SENT"; + case TMCB_ON_BRANCH_FAILURE: + return "TMCB_ON_BRANCH_FAILURE"; + case TMCB_ON_BRANCH_FAILURE_RO: + return "TMCB_ON_BRANCH_FAILURE_RO"; + case TMCB_MAX: + return "TMCB_MAX"; + } - lock_get(dlg->dlg_out_entries_lock); - dlg_out = dlg_entry_out->first; + return "UNKNOWN"; +} - LM_DBG("Scanning dlg_entry_out list for dlg_out"); - while (dlg_out) { - //Check if there is an already dialog_out entry with same To-tag - if (dlg_out->to_tag.len == to_tag.len && - memcmp(dlg_out->to_tag.s, to_tag.s, dlg_out->to_tag.len) == 0) { - //Found a dialog_out entry with same to_tag! - LM_DBG("Found dlg_out for to-tag: %.*s\n", dlg_out->to_tag.len, dlg_out->to_tag.s); - break; - } - dlg_out = dlg_out->next; - } - lock_release(dlg->dlg_out_entries_lock); - - if (!dlg_out) { - LM_DBG("No dlg_out entry found - creating a new dialog_out entry on dialog [%p]\n", dlg); - dlg_out = build_new_dlg_out(dlg, &to_uri, &to_tag); - - link_dlg_out(dlg, dlg_out, 0); +/*! + * \brief Unreference a dialog (small wrapper to take care of shutdown) + * \see unref_dlg + * \param dialog unreferenced dialog + */ +/*static void unreference_dialog(void *dialog) { + // if the dialog table is gone, it means the system is shutting down. + if (!dialog || !d_table) + return; + unref_dlg((struct dlg_cell*) dialog, 1); +}*/ - /* save callee's cseq, caller cseq, callee contact and callee record route*/ - if (populate_leg_info(dlg, rpl, t, DLG_CALLEE_LEG, &to_tag) != 0) { - LM_ERR("could not add further info to the dlg out\n"); - } +/*! + * \brief Parse the record-route parameter, to get dialog information back + * \param p start of parameter string + * \param end end of parameter string + * \param h_entry found dialog hash entry + * \param h_id found dialog hash id + * \return 0 on success, -1 on failure + */ +static inline int parse_dlg_rr_param(char *p, char *end, int *h_entry, int *h_id) { + char *s; - if (!dlg_out) { - LM_ERR("failed to create new dialog out structure\n"); - //TODO do something on this error! + for (s = p; p < end && *p != DLG_SEPARATOR; p++); + if (*p != DLG_SEPARATOR) { + LM_ERR("malformed rr param '%.*s'\n", (int) (long) (end - s), s); + return -1; + } - } - } else { - //This dlg_out already exists, update cseq and contact if present + if (reverse_hex2int(s, p - s, (unsigned int*) h_entry) < 0) { + LM_ERR("invalid hash entry '%.*s'\n", (int) (long) (p - s), s); + return -1; + } - LM_DBG("dlg_out entry found - updating cseq's for dialog out [%p] for to-tag [%.*s] \n", dlg_out, dlg_out->to_tag.len, dlg_out->to_tag.s); + if (reverse_hex2int(p + 1, end - (p + 1), (unsigned int*) h_id) < 0) { + LM_ERR("invalid hash id '%.*s'\n", (int) (long) (end - (p + 1)), p + 1); + return -1; + } - if ((!rpl->cseq && parse_headers(rpl, HDR_CSEQ_F, 0) < 0) || !rpl->cseq || - !rpl->cseq->parsed) { - LM_ERR("bad sip message or missing CSeq hdr :-/\n"); - } - dlg_update_cseq(dlg, DLG_CALLEE_LEG, &((get_cseq(rpl))->number), &(dlg_out->to_tag)); + return 0; +} +/*! + * \brief Register a transaction on a dialog + * \param t transaction + * \param type type of the entered callback + * \param param saved dialog structure in the callback + */ +/*static int store_dlg_in_tm(struct sip_msg* msg, + struct cell* t, + struct dlg_cell *dlg) { + if (!msg || msg == FAKED_REPLY || !t || !dlg) { + LM_ERR("invalid parameter msg(%p), t(%p), dlg(%p)\n", msg, t, dlg); + return -1; + } - /* extract the contact address to update if present*/ - if (!rpl->contact && (parse_headers(rpl, HDR_CONTACT_F, 0) < 0 || !rpl->contact)) { - LM_ERR("Can not update callee contact: bad sip message or missing Contact hdr\n"); - } - else if (parse_contact(rpl->contact) < 0 || - ((contact_body_t *) rpl->contact->parsed)->contacts == NULL || - ((contact_body_t *) rpl->contact->parsed)->contacts->next != NULL) { - LM_ERR("Can not update callee contact: bad Contact HDR\n"); - } - else - { - str contact; - contact = ((contact_body_t *) rpl->contact->parsed)->contacts->uri; - dlg_update_contact(dlg, DLG_CALLEE_LEG, &contact, &(dlg_out->to_tag)); - } + if (get_dialog_from_tm(t)) { + LM_NOTICE("dialog %p is already set for this transaction!\n", dlg); + return 1; + } - } + // facilitate referencing of dialog through TMCB_MAX + if (d_tmb.register_tmcb(msg, + t, + TMCB_MAX, + dlg_tmcb_dummy, + (void*) dlg, unreference_dialog) < 0) { + LM_ERR("failed cache in T the shortcut to dlg %p\n", dlg); + return -3; } - if (new_state == DLG_STATE_EARLY) { - if ((dlg->dflags & DLG_FLAG_INSERTED) == 0) { - dlg->dflags |= DLG_FLAG_NEW; - } else { - dlg->dflags |= DLG_FLAG_CHANGED; - } - if (dlg_db_mode == DB_MODE_REALTIME) - update_dialog_dbinfo(dlg); + // registering succeeded, we must increase the reference counter + ref_dlg(dlg, 1); - if (old_state != DLG_STATE_EARLY) { - counter_inc(dialog_ng_cnts_h.early); - } + return 0; +}*/ - run_dlg_callbacks(DLGCB_EARLY, dlg, req, rpl, DLG_DIR_UPSTREAM, 0); - return; +/*! + * \brief Callback to register a transaction on a dialog + * \param t transaction, unused + * \param type type of the entered callback + * \param param saved dialog structure in the callback + */ +/*static void store_dlg_in_tm_cb(struct cell* t, + int type, + struct tmcb_params *param) { + struct dlg_cell *dlg = (struct dlg_cell *) (*param->param); + + struct sip_msg* msg = param->rpl; + if (msg == NULL || msg == FAKED_REPLY) { + msg = param->req; } - LM_DBG("new state is %i and old state is %i\n", new_state, old_state); + store_dlg_in_tm(msg, t, dlg); +}*/ - if ((new_state == DLG_STATE_CONFIRMED) && (event == DLG_EVENT_RPL2xx)) { - LM_DBG("dialog %p confirmed \n", dlg); - //Remove all the other entries in dialog_out for the same dialog after TM expires the transaction - //(not before in order to absorb late in-early-dialog requests). +/*! + * \brief Update the saved CSEQ information in dialog from SIP message + * \param dlg updated dialog + * \param req SIP request + * \param dir direction of request, must DLG_DIR_UPSTREAM or DLG_DIR_DOWNSTREAM + * \return 0 on success, -1 on failure + */ +static inline int update_cseqs(struct dlg_cell *dlg, struct sip_msg *req, + unsigned int dir, str *to_tag) { + if ((!req->cseq && parse_headers(req, HDR_CSEQ_F, 0) < 0) || !req->cseq || + !req->cseq->parsed) { + LM_ERR("bad sip message or missing CSeq hdr :-/\n"); + return -1; + } - //remove all other dlg_out objects - if (dlg_out) { - if (d_tmb.register_tmcb(req, NULL, TMCB_DESTROY, unlink_dlgouts_from_cb, (void*) dlg, NULL) < 0) { - LM_ERR("failed to register deletion delay function\n"); - LM_DBG("Removing all other DLGs"); - dlg_remove_dlg_out(dlg_out, dlg, 0); - } else { - //mark the outs for deletion - dlg_remove_dlg_out(dlg_out, dlg, 1); + if (dir == DLG_DIR_UPSTREAM) { + return dlg_update_cseq(dlg, DLG_CALLEE_LEG, &((get_cseq(req))->number), to_tag); + } else if (dir == DLG_DIR_DOWNSTREAM) { + return dlg_update_cseq(dlg, DLG_CALLER_LEG, &((get_cseq(req))->number), to_tag); + } else { + LM_CRIT("dir is not set!\n"); + return -1; + } +} - } - } else { - LM_ERR("There is no dlg_out structure - this is bad\n"); - //TODO: add error handling here - } +/*! + * \brief Function that is registered as TM callback and called on requests + * \see dlg_new_dialog + * \param t transaction, used to created the dialog + * \param type type of the entered callback + * \param param saved dialog structure in the callback + */ +void dlg_onreq(struct cell* t, int type, struct tmcb_params *param) { + sip_msg_t *req = param->req; + dlg_cell_t *dlg = NULL; - /* set start time */ - dlg->start_ts = (unsigned int) (time(0)); + if (req->first_line.u.request.method_value == METHOD_BYE) { + _dlg_ctx.t = 1; + return; + } - /* save the settings to the database, - * if realtime saving mode configured- save dialog now - * else: the next time the timer will fire the update*/ - if ((dlg->dflags & DLG_FLAG_INSERTED) == 0) { - dlg->dflags |= DLG_FLAG_NEW; - } else { - dlg->dflags |= DLG_FLAG_CHANGED; - } - if (dlg_db_mode == DB_MODE_REALTIME) - update_dialog_dbinfo(dlg); - + if (req->first_line.u.request.method_value != METHOD_INVITE/* && req->first_line.u.request.method_value != METHOD_CANCEL*/) { + LM_DBG("Ignoring SIP Request: [%.*s] in dlg_onreq\n", req->first_line.u.request.method.len, req->first_line.u.request.method.s); + return; + } - if (0 != insert_dlg_timer(&dlg->tl, dlg->lifetime)) { - LM_CRIT("Unable to insert dlg %p [%u:%u] on event %d [%d->%d] " - "with clid '%.*s' and tags '%.*s' \n", - dlg, dlg->h_entry, dlg->h_id, event, old_state, new_state, - dlg->callid.len, dlg->callid.s, - dlg->from_tag.len, dlg->from_tag.s); + dlg = dlg_get_ctx_dialog(); + if (dlg != NULL) { + if (spiral_detected == 1) { + LM_DBG("Running DLGB_SPIRALED callback\n"); + run_dlg_callbacks(DLGCB_SPIRALED, dlg, + req, NULL, DLG_DIR_DOWNSTREAM, 0); + LM_DBG("This is a spiraled REQUEST\n"); + } else if (spiral_detected == 0) + run_create_callbacks(dlg, req); + } + if (dlg == NULL) { + if ((req->flags & dlg_flag) != dlg_flag) + return; + LM_DBG("dialog creation on config flag\n"); + dlg_new_dialog(req, t, 1); + dlg = dlg_get_ctx_dialog(); + } + if (dlg != NULL) { + if (dlg_set_tm_callbacks(t, req, dlg, spiral_detected) !=0) { + LM_ERR("Failed to register TM Callbacks for dialog... this is bad!\n"); + //the DLG_TM flag will not be set so at least we can clear the dialog later... } else { - ref_dlg(dlg, 1); + LM_DBG("dialog [%p] added to tm callbacks\n", dlg); } - - if (old_state == DLG_STATE_EARLY) - counter_add(dialog_ng_cnts_h.early, -1); - - counter_inc(dialog_ng_cnts_h.active); - run_dlg_callbacks(DLGCB_CONFIRMED, dlg, req, rpl, DLG_DIR_UPSTREAM, 0); + _dlg_ctx.t = 1; + dlg_release(dlg); + } else { + LM_ERR("Failed to create dialog.... this is bad\n"); + } +} - if (unref) unref_dlg(dlg, unref); +/*! + * \brief Function that is registered as RR callback for dialog tracking + * + * Function that is registered as RR callback for dialog tracking. It + * sets the appropriate events after the SIP method and run the state + * machine to update the dialog state. It updates then the saved + * dialogs and also the statistics. + * \param req SIP request + * \param route_params record-route parameter + * \param param unused + */ +void dlg_onroute(struct sip_msg* req, str *route_params, void *param) { + dlg_cell_t *dlg; + dlg_iuid_t *iuid; + str val, callid, ftag, ttag; + int h_entry, h_id, new_state, old_state, unref, event, timeout; + unsigned int dir; + int ret = 0; + + dlg = dlg_get_ctx_dialog(); + if (dlg != NULL) { + dlg_release(dlg); return; } - if (new_state == DLG_STATE_CONCURRENTLY_CONFIRMED && (old_state == DLG_STATE_CONFIRMED || old_state == DLG_STATE_CONCURRENTLY_CONFIRMED)) { - //This is a concurrently confirmed call - LM_DBG("This is a concurrently confirmed call."); - //Create a new Dialog ID token “X” - //Not sure how to do this so just going to use existing Did and add an X character to it - str new_did; - create_concurrent_did(dlg, &new_did); + /* skip initial requests - they may end up here because of the + * preloaded route */ + if ((!req->to && parse_headers(req, HDR_TO_F, 0) < 0) || !req->to) { + LM_ERR("bad request or missing TO hdr :-/\n"); + return; + } + if (get_to(req)->tag_value.len == 0) { + LM_DBG("No to tag header found --- ignoring... MESSAGE [%.*s\n", req->first_line.u.request.method.len, req->first_line.u.request.method.s); + return; + } - //assign new did to the created or updated dialog_out entry. - update_dlg_out_did(dlg_out, &new_did); + dlg = 0; + dir = DLG_DIR_NONE; - //Then, duplicate the dialog_in entry and set its Dialog ID value to new_did - //for now rather just create new dlg structure with the correct params - this should be fixed if future use requires + if (seq_match_mode != SEQ_MATCH_NO_ID) { + if (d_rrb.get_route_param(req, &rr_param, &val) != 0) { + LM_DBG("Route param '%.*s' not found\n", rr_param.len, rr_param.s); + if (seq_match_mode == SEQ_MATCH_STRICT_ID) + return; + } else { + LM_DBG("route param is '%.*s' (len=%d)\n", val.len, val.s, val.len); - struct dlg_cell *new_dlg = 0; - new_dlg = build_new_dlg(&(dlg->callid) /*callid*/, - &(dlg->from_uri) /*from uri*/, - &(dlg->from_tag)/*from_tag*/, - &(dlg->req_uri) /*r-uri*/); + if (parse_dlg_rr_param(val.s, val.s + val.len, &h_entry, &h_id) < 0) + return; - //assign new did to dlg_in - update_dlg_did(new_dlg, &new_did); + dlg = lookup_dlg(h_entry, h_id); + if (dlg == 0) { + LM_WARN("unable to find dialog for %.*s " + "with route param '%.*s' [%u:%u]\n", + req->first_line.u.request.method.len, + req->first_line.u.request.method.s, + val.len, val.s, h_entry, h_id); + if (seq_match_mode == SEQ_MATCH_STRICT_ID) + return; + } else { + if (pre_match_parse(req, &callid, &ftag, &ttag, 1) < 0) { + // lookup_dlg has incremented the ref count by 1 + unref_dlg(dlg, 1); + return; + } + if (match_dialog(dlg, &callid, &ftag, &ttag, &dir) == 0) { + LM_WARN("tight matching failed for %.*s with callid='%.*s'/%d, " + "ftag='%.*s'/%d, ttag='%.*s'/%d and direction=%d\n", + req->first_line.u.request.method.len, + req->first_line.u.request.method.s, + callid.len, callid.s, callid.len, + ftag.len, ftag.s, ftag.len, + ttag.len, ttag.s, ttag.len, dir); + LM_WARN("dialog identification elements are callid='%.*s'/%d, " + "caller tag='%.*s'/%d\n", + dlg->callid.len, dlg->callid.s, dlg->callid.len, + dlg->from_tag.len, dlg->from_tag.s, + dlg->from_tag.len); + // lookup_dlg has incremented the ref count by 1 + dlg_release(dlg); - if (new_dlg == 0) { - LM_ERR("failed to create new dialog\n"); + // Reset variables in order to do a lookup based on SIP-Elements. + dlg = 0; + dir = DLG_DIR_NONE; + + if (seq_match_mode == SEQ_MATCH_STRICT_ID) + return; + } + } + } + } + + if (dlg == 0) { + if (pre_match_parse(req, &callid, &ftag, &ttag, 1) < 0) + return; + /* TODO - try to use the RR dir detection to speed up here the + * search -bogdan */ + dlg = get_dlg(&callid, &ftag, &ttag, &dir); + if (!dlg) { + LM_DBG("Callid '%.*s' not found\n", + req->callid->body.len, req->callid->body.s); return; } + } - //link the new_dlg with dlg_out object - link_dlg_out(new_dlg, dlg_out, 0); + /* set current dialog - re-use ref increment from dlg_get() above */ + set_current_dialog(req, dlg); + _dlg_ctx.iuid.h_entry = dlg->h_entry; + _dlg_ctx.iuid.h_id = dlg->h_id; + + if (req->first_line.u.request.method_value != METHOD_ACK) { + iuid = dlg_get_iuid_shm_clone(dlg); + if (iuid != NULL) { + /* register callback for the replies of this request */ + if (d_tmb.register_tmcb(req, 0, TMCB_RESPONSE_IN | TMCB_ON_FAILURE, + dlg_onreply, (void*) iuid, dlg_iuid_sfree) < 0) { + LM_ERR("failed to register TMCB (3)\n"); + shm_free(iuid); + } + iuid = NULL; + } + } + // if (d_tmb.register_tmcb(req, NULL, TMCB_REQUEST_FWDED, + // store_dlg_in_tm_cb, (void*) dlg, NULL) < 0) { + // LM_ERR("failed to store dialog in transaction during dialog creation for later reference\n"); + // } + /* run state machine */ + switch (req->first_line.u.request.method_value) { + case METHOD_PRACK: + event = DLG_EVENT_REQPRACK; + break; + case METHOD_ACK: + event = DLG_EVENT_REQACK; + break; + case METHOD_BYE: + event = DLG_EVENT_REQBYE; + break; +// case METHOD_CANCEL: +// event = DLG_EVENT_REQCANCEL; +// break; + default: + event = DLG_EVENT_REQ; } - if (old_state != DLG_STATE_DELETED && new_state == DLG_STATE_DELETED) { - LM_DBG("dialog %p failed (negative reply)\n", dlg); - /* dialog setup not completed (3456XX) */ - run_dlg_callbacks(DLGCB_FAILED, dlg, req, rpl, DLG_DIR_UPSTREAM, 0); - /* do unref */ - if (unref) - unref_dlg(dlg, unref); + next_state_dlg(dlg, event, &old_state, &new_state, &unref, 0); + LM_DBG("unref after next state is %i\n", unref); + CURR_DLG_ID = req->id; + CURR_DLG_LIFETIME = (unsigned int) (time(0)) - dlg->start_ts; + CURR_DLG_STATUS = new_state; + + // dlg_run_event_route(dlg, req, old_state, new_state); - if (old_state == DLG_STATE_EARLY) - counter_add(dialog_ng_cnts_h.early, -1); - return; + /* delay deletion of dialog until transaction has died off in order + * to absorb in-air messages */ + if ((new_state == DLG_STATE_DELETED) && (old_state != DLG_STATE_DELETED)) { + iuid = dlg_get_iuid_shm_clone(dlg); + if (iuid != NULL) { + if (d_tmb.register_tmcb(req, NULL, TMCB_DESTROY, + unref_dlg_from_cb, (void*) iuid, dlg_iuid_sfree) < 0) { + LM_ERR("failed to register deletion delay function\n"); + shm_free(iuid); + } else { + ref_dlg(dlg, 1); + } + } +// unref_dlg(dlg, unref); } - if (unref) unref_dlg(dlg, unref); + // if (new_state == DLG_STATE_CONFIRMED && old_state != DLG_STATE_CONFIRMED) + // dlg_ka_add(dlg); - return; -} + if (event == DLG_EVENT_REQCANCEL && new_state == DLG_STATE_DELETED && + old_state != DLG_STATE_DELETED) { + LM_DBG("CANCEL successfully processed and old state was [%d]\n", old_state); + + ret = remove_dialog_timer(&dlg->tl); + if (ret < 0) { + LM_CRIT("unable to unlink the timer on dlg %p [%u:%u] " + "with clid '%.*s' and tags '%.*s'\n", + dlg, dlg->h_entry, dlg->h_id, + dlg->callid.len, dlg->callid.s, + dlg->from_tag.len, dlg->from_tag.s); -/*! - * \brief Helper function that run dialog callbacks on forwarded requests - * \see dlg_seq_up_onreply - * \see dlg_seq_down_onreply - * \param t transaction, unused - * \param type type of the callback, should be TMCB_RESPONSE_FWDED - * \param param saved dialog structure inside the callback - * \param direction direction of the request - */ -static void dlg_seq_onreply_helper(struct cell* t, int type, - struct tmcb_params *param, const int direction) { - struct dlg_cell *dlg; + } else if (ret > 0) { + LM_WARN("inconsitent dlg timer data on dlg %p [%u:%u] " + "with clid '%.*s' and tags '%.*s' \n", + dlg, dlg->h_entry, dlg->h_id, + dlg->callid.len, dlg->callid.s, + dlg->from_tag.len, dlg->from_tag.s); - dlg = (struct dlg_cell *) (*param->param); - if (shutdown_done || dlg == 0) - return; + } else { + unref++; + } + + dlg_terminated(req, dlg, dir); + unref_dlg(dlg, unref); + _dlg_ctx.cpid = my_pid(); + _dlg_ctx.expect_t = 1; + dlg_set_ctx_iuid(dlg); + + if (old_state == DLG_STATE_EARLY) { + counter_add(dialog_ng_cnts_h.early, -1); + } else if (old_state == DLG_STATE_CONFIRMED ||old_state == DLG_STATE_CONFIRMED_NA) { + counter_add(dialog_ng_cnts_h.active, -1); + } + + goto done; + } + + /* run actions for the transition */ + if (event == DLG_EVENT_REQBYE && new_state == DLG_STATE_DELETED && + old_state != DLG_STATE_DELETED) { + LM_DBG("BYE successfully processed\n"); + /* remove from timer */ + ret = remove_dialog_timer(&dlg->tl); + if (ret < 0) { + LM_CRIT("unable to unlink the timer on dlg %p [%u:%u] " + "with clid '%.*s' and tags '%.*s'\n", + dlg, dlg->h_entry, dlg->h_id, + dlg->callid.len, dlg->callid.s, + dlg->from_tag.len, dlg->from_tag.s); - if (type == TMCB_RESPONSE_FWDED) { - run_dlg_callbacks(DLGCB_RESPONSE_WITHIN, - dlg, - param->req, - param->rpl, - direction, - 0); - return; + } else if (ret > 0) { + LM_WARN("inconsitent dlg timer data on dlg %p [%u:%u] " + "with clid '%.*s' and tags '%.*s' \n", + dlg, dlg->h_entry, dlg->h_id, + dlg->callid.len, dlg->callid.s, + dlg->from_tag.len, dlg->from_tag.s); + + } else { + unref++; + } + /* dialog terminated (BYE) */ + dlg_terminated(req, dlg, dir); + unref_dlg(dlg, unref); + _dlg_ctx.cpid = my_pid(); + _dlg_ctx.expect_t = 1; + dlg_set_ctx_iuid(dlg); + + if (old_state == DLG_STATE_CONFIRMED ||old_state == DLG_STATE_CONFIRMED_NA) { + counter_add(dialog_ng_cnts_h.active, -1); + } + + goto done; } - return; -} + if (event == DLG_EVENT_REQCANCEL && new_state == DLG_STATE_DELETED && + old_state != DLG_STATE_DELETED) { + LM_DBG("CANCEL successfully processed\n"); + /* remove from timer */ + ret = remove_dialog_timer(&dlg->tl); + if (ret < 0) { + LM_CRIT("unable to unlink the timer on dlg %p [%u:%u] " + "with clid '%.*s' and tags '%.*s'\n", + dlg, dlg->h_entry, dlg->h_id, + dlg->callid.len, dlg->callid.s, + dlg->from_tag.len, dlg->from_tag.s); + + } else if (ret > 0) { + LM_WARN("inconsitent dlg timer data on dlg %p [%u:%u] " + "with clid '%.*s' and tags '%.*s' \n", + dlg, dlg->h_entry, dlg->h_id, + dlg->callid.len, dlg->callid.s, + dlg->from_tag.len, dlg->from_tag.s); -/*! - * \brief Run dialog callbacks on forwarded requests in upstream direction - * \see dlg_seq_onreply_helper - * \param t transaction, unused - * \param type type of the callback, should be TMCB_RESPONSE_FWDED - * \param param saved dialog structure inside the callback - */ -static void dlg_seq_up_onreply(struct cell* t, int type, struct tmcb_params *param) { - return dlg_seq_onreply_helper(t, type, param, DLG_DIR_UPSTREAM); -} + } else { + unref++; + } + /* dialog terminated (CANCEL) */ + dlg_cancelled(req, dlg, dir); +// unref_dlg(dlg, unref); + _dlg_ctx.cpid = my_pid(); + _dlg_ctx.expect_t = 1; + dlg_set_ctx_iuid(dlg); + counter_add(dialog_ng_cnts_h.early, -1); + goto done; + } -/*! - * \brief Run dialog callbacks on forwarded requests in downstream direction - * \see dlg_seq_onreply_helper - * \param t transaction, unused - * \param type type of the callback, should be TMCB_RESPONSE_FWDED - * \param param saved dialog structure inside the callback - */ -static void dlg_seq_down_onreply(struct cell* t, int type, struct tmcb_params *param) { - return dlg_seq_onreply_helper(t, type, param, DLG_DIR_DOWNSTREAM); -} + if ((event == DLG_EVENT_REQ || event == DLG_EVENT_REQACK) + && (new_state == DLG_STATE_CONFIRMED || new_state == DLG_STATE_EARLY)) { -/*! - * \brief Return the timeout for a dialog - * \param req SIP message - * \return value from timeout AVP if present or default timeout - */ -inline static int get_dlg_timeout(struct sip_msg *req) { - pv_value_t pv_val; + timeout = get_dlg_timeout(req); + if (timeout != default_timeout) { + dlg->lifetime = timeout; + } + // reset = !((dlg->iflags & DLG_IFLAG_TIMER_NORESET) || dlg_timeout_noreset); - if (timeout_avp) { - if (pv_get_spec_value(req, timeout_avp, &pv_val) == 0 && - pv_val.flags & PV_VAL_INT && pv_val.ri > 0) { - return pv_val.ri; + if ((new_state != DLG_STATE_EARLY) && (old_state != DLG_STATE_CONFIRMED/* || reset*/)) { + if (update_dlg_timer(&dlg->tl, dlg->lifetime) == -1) { + LM_ERR("failed to update dialog lifetime\n"); + } else { + dlg->dflags |= DLG_FLAG_CHANGED; + } + } + if (event != DLG_EVENT_REQACK) { + if (update_cseqs(dlg, req, dir, &ttag) != 0) { + LM_ERR("cseqs update failed\n"); + } else { + dlg->dflags |= DLG_FLAG_CHANGED; + } + } + if (dlg_db_mode == DB_MODE_REALTIME && (dlg->dflags & DLG_FLAG_CHANGED)) { + update_dialog_dbinfo(dlg); } - LM_DBG("invalid AVP value, using default timeout\n"); - } - return default_timeout; -} -/*! - * \brief Helper function to get the necessary content from SIP message - * \param req SIP request - * \param callid found callid - * \param ftag found from tag - * \param ttag found to tag - * \param with_ttag flag set if to tag must be found for success - * \return 0 on success, -1 on failure - */ -static inline int pre_match_parse(struct sip_msg *req, str *callid, - str *ftag, str *ttag, int with_ttag) { - if (parse_headers(req, HDR_CALLID_F | HDR_TO_F, 0) < 0 || !req->callid || - !req->to) { - LM_ERR("bad request or missing CALLID/TO hdr :-/\n"); - return -1; - } + if (old_state == DLG_STATE_CONFIRMED_NA) { + LM_DBG("confirming ACK successfully processed\n"); - if (get_to(req)->tag_value.len == 0) { - if (with_ttag == 1) { - /* out of dialog request with preloaded Route headers; ignore. */ - return -1; + /* confirming ACK request */ + run_dlg_callbacks(DLGCB_CONFIRMED, dlg, req, NULL, dir, 0); } else { - ttag->s = NULL; - ttag->len = 0; + LM_DBG("sequential request successfully processed\n"); + + /* within dialog request */ + run_dlg_callbacks(DLGCB_REQ_WITHIN, dlg, req, NULL, dir, 0); + + if ((event != DLG_EVENT_REQACK) && + (dlg->cbs.types) & DLGCB_RESPONSE_WITHIN) { + iuid = dlg_get_iuid_shm_clone(dlg); + if (iuid != NULL) { + /* register callback for the replies of this request */ + if (d_tmb.register_tmcb(req, 0, TMCB_RESPONSE_FWDED, + (dir == DLG_DIR_UPSTREAM) ? dlg_seq_down_onreply : + dlg_seq_up_onreply, + (void*) iuid, dlg_iuid_sfree) < 0) { + LM_ERR("failed to register TMCB (2)\n"); + shm_free(iuid); + } + } + } } - } else { - *ttag = get_to(req)->tag_value; } - if (parse_from_header(req) < 0 || get_from(req)->tag_value.len == 0) { - LM_ERR("failed to get From header\n"); - return -1; + if (new_state == DLG_STATE_CONFIRMED && old_state == DLG_STATE_CONFIRMED_NA) { + dlg->dflags |= DLG_FLAG_CHANGED; + if (dlg_db_mode == DB_MODE_REALTIME) + update_dialog_dbinfo(dlg); } - /* callid */ - *callid = req->callid->body; - trim(callid); - /* from tag */ - *ftag = get_from(req)->tag_value; - return 0; +done: + dlg_release(dlg); + return; } /*! - * \brief Function that is registered as TM callback and called on requests - * \see dlg_new_dialog - * \param t transaction, used to created the dialog + * \brief Function that is registered as TM callback and called on replies + * + * Function that is registered as TM callback and called on replies. It + * parses the reply and set the appropriate event. This is then used to + * update the dialog state, run eventual dialog callbacks and save or + * update the necessary informations about the dialog. + * \see next_state_dlg + * \param t transaction, unused * \param type type of the entered callback * \param param saved dialog structure in the callback */ -void dlg_onreq(struct cell* t, int type, struct tmcb_params *param) { +void dlg_onreply(struct cell* t, int type, struct tmcb_params *param) { + dlg_cell_t *dlg = NULL; + dlg_iuid_t *iuid = NULL; + struct dlg_cell_out *dlg_out = 0; + char *event_s; + + int new_state, old_state, unref, event; + str to_tag={0,0}, to_uri, branch={0,0}; struct sip_msg *req = param->req; + struct sip_msg *rpl = param->rpl; + struct dlg_entry_out* dlg_entry_out = 0; + + char* cb_type_s = tm_type_to_string(type); + + if (t && t->fwded_totags) + LM_DBG("ONREPLY CALL_BACK from TM received and type is [%i] = [%s] and TO is [%.*s]\n", type, cb_type_s, t->fwded_totags->tag.len, t->fwded_totags->tag.s); + else + LM_DBG("ONREPLY CALL_BACK from TM received and type is [%i] = [%s]\n", type, cb_type_s); - if ((req->flags & dlg_flag) != dlg_flag) + if (shutdown_done) return; - if (current_dlg_pointer != NULL) + + iuid = (dlg_iuid_t*) (*param->param); + dlg = dlg_get_by_iuid(iuid); + if (dlg == 0) return; - dlg_new_dialog(req, t, 1); -} - -/*! - * \brief Unreference a new dialog, helper function for dlg_onreq - * \see dlg_onreq - * \param dialog unreferenced dialog - */ -static void unref_new_dialog(void *dialog) { - struct tmcb_params p; - memset(&p, 0, sizeof (struct tmcb_params)); - p.param = (void*) &dialog; - dlg_onreply(0, TMCB_DESTROY, &p); -} + if (t) { + dlg->transaction = t; + } -/*! - * \brief Unreference a dialog (small wrapper to take care of shutdown) - * \see unref_dlg - * \param dialog unreferenced dialog - */ -static void unreference_dialog(void *dialog) { - // if the dialog table is gone, it means the system is shutting down. - if (!dialog || !d_table) - return; - unref_dlg((struct dlg_cell*) dialog, 1); -} + unref = 0; -/*! - * \brief Dummy callback just to keep the compiler happy - * \param t unused - * \param type unused - * \param param unused - */ -void dlg_tmcb_dummy(struct cell* t, int type, struct tmcb_params *param) { - return; -} + LM_DBG("DLG dialogid is entry:id [%i:%i]\n", dlg->h_entry, dlg->h_id); -/*! - * \brief Register a transaction on a dialog - * \param t transaction - * \param type type of the entered callback - * \param param saved dialog structure in the callback - */ -static int store_dlg_in_tm(struct sip_msg* msg, - struct cell* t, - struct dlg_cell *dlg) { - if (!msg || msg == FAKED_REPLY || !t || !dlg) { - LM_ERR("invalid parameter msg(%p), t(%p), dlg(%p)\n", msg, t, dlg); - return -1; + if (type & TMCB_E2ECANCEL_IN) { + LM_DBG("Received CANCEL...\n"); + goto done; } - - if (get_dialog_from_tm(t)) { - LM_NOTICE("dialog %p is already set for this transaction!\n", dlg); - return 1; + + if (type & TMCB_REQUEST_OUT) { + LM_DBG("Received Request out for request [%.*s]\n", param->send_buf.len, param->send_buf.s); + if (param->send_buf.s && (param->send_buf.len > 6) && !memcmp(param->send_buf.s, "CANCEL", 6)) { + LM_DBG("Request out sending CANCEL...\n"); + type = TMCB_E2ECANCEL_IN; + goto done; + } else { + LM_DBG("Ignoring request out as it's not a CANCEL\n"); + goto done; + } } - // facilitate referencing of dialog through TMCB_MAX - if (d_tmb.register_tmcb(msg, - t, - TMCB_MAX, - dlg_tmcb_dummy, - (void*) dlg, unreference_dialog) < 0) { - LM_ERR("failed cache in T the shortcut to dlg %p\n", dlg); - return -3; + if (type & (TMCB_RESPONSE_IN | TMCB_ON_FAILURE)) { + /* Set the dialog context so it is available in onreply_route and failure_route*/ + set_current_dialog(req, dlg); + dlg_set_ctx_iuid(dlg); + goto done; } - // registering succeeded, we must increase the reference counter - ref_dlg(dlg, 1); - - return 0; -} + if (type == TMCB_RESPONSE_FWDED) { + // The state does not change, but the msg is mutable in this callback + LM_DBG("TMCB_RESPONSE_FWDED from TM received"); + run_dlg_callbacks(DLGCB_RESPONSE_FWDED, dlg, req, rpl, DLG_DIR_UPSTREAM, 0); + goto done; + } -/*! - * \brief Callback to register a transaction on a dialog - * \param t transaction, unused - * \param type type of the entered callback - * \param param saved dialog structure in the callback - */ -static void store_dlg_in_tm_cb(struct cell* t, - int type, - struct tmcb_params *param) { - struct dlg_cell *dlg = (struct dlg_cell *) (*param->param); + if (type == TMCB_RESPONSE_READY) { + if (rpl == FAKED_REPLY) { + LM_DBG("Faked reply\n"); + //TODO - maybe we should run the state machine especially for things like cancel (ie early dialog.....) + goto done; + } - struct sip_msg* msg = param->rpl; - if (msg == NULL || msg == FAKED_REPLY) { - msg = param->req; - } + // get to tag + LM_DBG("Extracting to-tag from reply"); + if (!rpl->to && ((parse_headers(rpl, HDR_TO_F, 0) < 0) || !rpl->to)) { + LM_ERR("bad reply or missing TO hdr :-/\n"); + to_tag.s = 0; + to_tag.len = 0; + } else { + //populate to uri for this branch. + to_uri = get_to(rpl)->uri; - store_dlg_in_tm(msg, t, dlg); -} + to_tag = get_to(rpl)->tag_value; + if (to_tag.s == 0 || to_tag.len == 0) { + LM_ERR("missing TAG param in TO hdr :-/\n"); + to_tag.s = 0; + to_tag.len = 0; + //Here we assume that the transaction module timer will remove any early dialogs + //return; if we leave this then we have over-reffed dialogs + } + } -/*! - * \brief Create a new dialog from a sip message - * - * Create a new dialog from a SIP message, register a callback - * to keep track of the dialog with help of the tm module. - * This function is either called from the request callback, or - * from the dlg_manage function in the configuration script. - * \see dlg_onreq - * \see w_dlg_manage - * \param msg SIP message - * \param t transaction - * \return 0 on success, -1 on failure - */ -int dlg_new_dialog(struct sip_msg *req, struct cell *t, const int run_initial_cbs) { - struct dlg_cell *dlg; - str s; - str callid; - str ftag; - str ttag; - str req_uri; - unsigned int dir; + LM_DBG("Got to-tag from response: %.*s \n", to_tag.len, to_tag.s); + } - LM_DBG("starting dlg_new_dialog and method is [%.*s]\n", req->first_line.u.request.method.len, req->first_line.u.request.method.s); + if (type == TMCB_DESTROY) { + event = DLG_EVENT_TDEL; + event_s = "DLG_EVENT_TDEL"; +// } else if (type == TMCB_E2ECANCEL_IN) { +// event = DLG_EVENT_REQCANCEL; +// event_s = "DLG_EVENT_REQCANCEL"; + } else if (param->code < 200) { + event = DLG_EVENT_RPL1xx; + event_s = "DLG_EVENT_RPL1xx"; + } else if (param->code < 300) { + event = DLG_EVENT_RPL2xx; + event_s = "DLG_EVENT_RPL2xx"; + } else { + event = DLG_EVENT_RPL3xx; + event_s = "DLG_EVENT_RPL3xx"; + } - if (current_dlg_pointer != NULL) - return -1; + LM_DBG("Calling next_state_dlg and event is %i = %s\n", event, event_s); + next_state_dlg(dlg, event, &old_state, &new_state, &unref, &to_tag); - if (req->first_line.u.request.method_value == METHOD_CANCEL) - return -1; + if (type == TMCB_RESPONSE_READY) { + LM_DBG("Checking if there is an existing dialog_out entry with same to-tag"); + if (rpl == FAKED_REPLY) { + LM_DBG("Faked reply\n"); + goto done; + } - if (pre_match_parse(req, &callid, &ftag, &ttag, 0) < 0) { - LM_WARN("pre-matching failed\n"); - return -1; - } + // get to tag + LM_DBG("Extracting to-tag from reply"); + if (!rpl->to && ((parse_headers(rpl, HDR_TO_F, 0) < 0) || !rpl->to)) { + LM_ERR("bad reply or missing TO hdr :-/\n"); + to_tag.s = 0; + to_tag.len = 0; + } else { + //populate to uri for this branch. + to_uri = get_to(rpl)->uri; - if (ttag.s != 0 && ttag.len != 0) - return -1; + to_tag = get_to(rpl)->tag_value; + if (to_tag.s == 0 || to_tag.len == 0) { + LM_ERR("missing TAG param in TO hdr :-/\n"); + to_tag.s = 0; + to_tag.len = 0; + //Here we assume that the transaction module timer will remove any early dialogs + if (new_state == DLG_STATE_DELETED + && (old_state == DLG_STATE_UNCONFIRMED + || old_state == DLG_STATE_EARLY)) { + LM_DBG("dialog %p failed (negative reply)\n", dlg); + /* dialog setup not completed (3456XX) */ + run_dlg_callbacks(DLGCB_FAILED, dlg, req, rpl, DLG_DIR_UPSTREAM, 0); + /* do unref */ + if (unref) + unref_dlg(dlg, unref); + + if (old_state == DLG_STATE_EARLY) + counter_add(dialog_ng_cnts_h.early, -1); + } + goto done; + } + } + LM_DBG("Got to-tag from response: %.*s \n", to_tag.len, to_tag.s); - if (pv_printf_s(req, ruri_param_model, &req_uri) < 0) { - LM_ERR("error - cannot print the r-uri format\n"); - return -1; - } - trim(&req_uri); + dlg_entry_out = &dlg->dlg_entry_out; - if (detect_spirals) { - if (spiral_detected == 1) - return 0; + lock_get(dlg->dlg_out_entries_lock); + dlg_out = dlg_entry_out->first; - dir = DLG_DIR_NONE; + LM_DBG("Scanning dlg_entry_out list for dlg_out"); + while (dlg_out) { + //Check if there is an already dialog_out entry with same To-tag + if (dlg_out->to_tag.len == to_tag.len && + memcmp(dlg_out->to_tag.s, to_tag.s, dlg_out->to_tag.len) == 0) { + //Found a dialog_out entry with same to_tag! + LM_DBG("Found dlg_out for to-tag: %.*s\n", dlg_out->to_tag.len, dlg_out->to_tag.s); + break; + } + dlg_out = dlg_out->next; + } + lock_release(dlg->dlg_out_entries_lock); - dlg = get_dlg(&callid, &ftag, &ttag, &dir); - if (dlg) { - LM_DBG("Callid '%.*s' found, must be a spiraled request\n", - callid.len, callid.s); - spiral_detected = 1; + if (!dlg_out) { + if (rpl->via1->branch && (&rpl->via1->branch->value) && (rpl->via1->branch->value.len > 0)) { + branch = rpl->via1->branch->value; + } + + LM_DBG("No dlg_out entry found - creating a new dialog_out entry on dialog [%p]\n", dlg); + dlg_out = build_new_dlg_out(dlg, &to_uri, &to_tag, &branch); + + link_dlg_out(dlg, dlg_out, 0); - if (run_initial_cbs) - run_dlg_callbacks(DLGCB_SPIRALED, dlg, req, NULL, DLG_DIR_DOWNSTREAM, 0); + /* save callee's cseq, caller cseq, callee contact and callee record route*/ + if (populate_leg_info(dlg, rpl, t, DLG_CALLEE_LEG, &to_tag) != 0) { + LM_ERR("could not add further info to the dlg out\n"); + } - //Add did to rr header for all spiralled requested INVITEs - if (req->first_line.u.request.method_value == METHOD_INVITE) { - if (add_dlg_rr_param(dlg, req, dlg->h_entry, dlg->h_id) < 0) { - LM_ERR("failed to add RR param\n"); + if (!dlg_out) { + LM_ERR("failed to create new dialog out structure\n"); + goto done; + //TODO do something on this error! - } } + } else { + //This dlg_out already exists, update cseq and contact if present - // get_dlg has incremented the ref count by 1 - unref_dlg(dlg, 1); - goto finish; + LM_DBG("dlg_out entry found - updating cseq's for dialog out [%p] for to-tag [%.*s] \n", dlg_out, dlg_out->to_tag.len, dlg_out->to_tag.s); + + if ((!rpl->cseq && parse_headers(rpl, HDR_CSEQ_F, 0) < 0) || !rpl->cseq || + !rpl->cseq->parsed) { + LM_ERR("bad sip message or missing CSeq hdr :-/\n"); + } + dlg_update_cseq(dlg, DLG_CALLEE_LEG, &((get_cseq(rpl))->number), &(dlg_out->to_tag)); + + + /* extract the contact address to update if present*/ + if (!rpl->contact && (parse_headers(rpl, HDR_CONTACT_F, 0) < 0 || !rpl->contact)) { + LM_ERR("Can not update callee contact: bad sip message or missing Contact hdr\n"); + } else if (parse_contact(rpl->contact) < 0 || + ((contact_body_t *) rpl->contact->parsed)->contacts == NULL || + ((contact_body_t *) rpl->contact->parsed)->contacts->next != NULL) { + LM_ERR("Can not update callee contact: bad Contact HDR\n"); + } else { + str contact; + contact = ((contact_body_t *) rpl->contact->parsed)->contacts->uri; + dlg_update_contact(dlg, DLG_CALLEE_LEG, &contact, &(dlg_out->to_tag)); + } } } - spiral_detected = 0; + if (new_state == DLG_STATE_EARLY) { + if (dlg_db_mode == DB_MODE_REALTIME) + update_dialog_dbinfo(dlg); + run_dlg_callbacks(DLGCB_EARLY, dlg, req, rpl, DLG_DIR_UPSTREAM, 0); + if (old_state != DLG_STATE_EARLY) + counter_inc(dialog_ng_cnts_h.early); + goto done; + } + LM_DBG("new state is %i and old state is %i\n", new_state, old_state); - LM_DBG("Building new Dialog for call-id %.*s\n", callid.len, callid.s); - LM_DBG("SIP Method: %.*s \n", req->first_line.u.request.method.len, req->first_line.u.request.method.s); - dlg = build_new_dlg(&callid /*callid*/, - &(get_from(req)->uri) /*from uri*/, - &ftag/*from_tag*/, - &req_uri /*r-uri*/); + if (new_state == DLG_STATE_CONFIRMED_NA && + old_state != DLG_STATE_CONFIRMED_NA && old_state != DLG_STATE_CONFIRMED) { + // if ((new_state == DLG_STATE_CONFIRMED) && (event == DLG_EVENT_RPL2xx)) { + LM_DBG("dialog %p confirmed (No Ack) \n", dlg); - if (dlg == 0) { - LM_ERR("failed to create new dialog\n"); - return -1; - } + /* set start time */ + dlg->start_ts = (unsigned int) (time(0)); - /* save caller's tag, cseq, contact and record route*/ - if (populate_leg_info(dlg, req, t, DLG_CALLER_LEG, - &(get_from(req)->tag_value)) != 0) { - LM_ERR("could not add further info to the dialog\n"); - lock_destroy(dlg->dlg_out_entries_lock); - lock_dealloc(dlg->dlg_out_entries_lock); - shm_free(dlg); - return -1; - } + /* save the settings to the database, + * if realtime saving mode configured- save dialog now + * else: the next time the timer will fire the update*/ + if ((dlg->dflags & DLG_FLAG_INSERTED) == 0) { + dlg->dflags |= DLG_FLAG_NEW; + } else { + dlg->dflags |= DLG_FLAG_CHANGED; + } + if (dlg_db_mode == DB_MODE_REALTIME) + update_dialog_dbinfo(dlg); + + if (0 != insert_dlg_timer(&dlg->tl, dlg->lifetime)) { + LM_CRIT("Unable to insert dlg %p [%u:%u] on event %d [%d->%d] " + "with clid '%.*s' and tags '%.*s' \n", + dlg, dlg->h_entry, dlg->h_id, event, old_state, new_state, + dlg->callid.len, dlg->callid.s, + dlg->from_tag.len, dlg->from_tag.s); + } else { + ref_dlg(dlg, 1); + } - dlg->transaction = t; + /* dialog confirmed (ACK pending) */ + run_dlg_callbacks(DLGCB_CONFIRMED_NA, dlg, req, rpl, DLG_DIR_UPSTREAM, 0); - /* Populate initial varlist: */ - dlg->vars = get_local_varlist_pointer(req, 1); + if (old_state == DLG_STATE_EARLY) + counter_add(dialog_ng_cnts_h.early, -1); - link_dlg(dlg, 0); - if (run_initial_cbs) run_create_callbacks(dlg, req); + if (unref) unref_dlg(dlg, unref); - //Dialog will *always* use DID cookie so no need for match mode anymore - if (add_dlg_rr_param(dlg, req, dlg->h_entry, dlg->h_id) < 0) { - LM_ERR("failed to add RR param\n"); - goto error; - } + counter_inc(dialog_ng_cnts_h.active); - if (d_tmb.register_tmcb(req, t, - TMCB_RESPONSE_READY | TMCB_RESPONSE_FWDED | TMCB_RESPONSE_OUT, - dlg_onreply, (void*) dlg, unref_new_dialog) < 0) { - LM_ERR("failed to register TMCB\n"); - goto error; + if (unref) unref_dlg(dlg, unref); + goto done; } - // increase reference counter because of registered callback - ref_dlg(dlg, 1); - dlg->lifetime = get_dlg_timeout(req); - s.s = _dlg_ctx.to_route_name; - s.len = strlen(s.s); - dlg_set_toroute(dlg, &s); - dlg->sflags |= _dlg_ctx.flags; + if (new_state == DLG_STATE_CONCURRENTLY_CONFIRMED && (old_state == DLG_STATE_CONFIRMED || old_state == DLG_STATE_CONCURRENTLY_CONFIRMED)) { + //This is a concurrently confirmed call + LM_DBG("This is a concurrently confirmed call."); + //Create a new Dialog ID token “X” + //Not sure how to do this so just going to use existing Did and add an X character to it + str new_did; + create_concurrent_did(dlg, &new_did); - if (_dlg_ctx.to_bye != 0) - dlg->dflags |= DLG_FLAG_TOBYE; + //assign new did to the created or updated dialog_out entry. + update_dlg_out_did(dlg_out, &new_did); -finish: - if (t) { - // transaction exists ==> keep ref counter large enough to - // avoid premature cleanup and ensure proper dialog referencing - if (store_dlg_in_tm(req, t, dlg) < 0) { - LM_ERR("failed to store dialog in transaction\n"); - goto error; - } - } else { - // no transaction exists ==> postpone work until we see the - // request being forwarded statefully - if (d_tmb.register_tmcb(req, NULL, TMCB_REQUEST_FWDED, - store_dlg_in_tm_cb, (void*) dlg, NULL) < 0) { - LM_ERR("failed to register callback for storing dialog in transaction\n"); - goto error; - } - } + //Then, duplicate the dialog_in entry and set its Dialog ID value to new_did + //for now rather just create new dlg structure with the correct params - this should be fixed if future use requires - LM_DBG("Setting current dialog\n"); - set_current_dialog(req, dlg); - _dlg_ctx.dlg = dlg; - ref_dlg(dlg, 1); - return 0; + struct dlg_cell *new_dlg = 0; + new_dlg = build_new_dlg(&(dlg->callid) /*callid*/, + &(dlg->from_uri) /*from uri*/, + &(dlg->from_tag)/*from_tag*/, + &(dlg->req_uri) /*r-uri*/); -error: - LM_DBG("Error in build_new_dlg"); - if (!spiral_detected) - unref_dlg(dlg, 1); // undo ref regarding linking - return -1; -} + //assign new did to dlg_in + update_dlg_did(new_dlg, &new_did); -/*! - * \brief Parse the record-route parameter, to get dialog information back - * \param p start of parameter string - * \param end end of parameter string - * \param h_entry found dialog hash entry - * \param h_id found dialog hash id - * \return 0 on success, -1 on failure - */ -static inline int parse_dlg_rr_param(char *p, char *end, int *h_entry, int *h_id) { - char *s; + if (new_dlg == 0) { + LM_ERR("failed to create new dialog\n"); + goto done; + } - for (s = p; p < end && *p != DLG_SEPARATOR; p++); - if (*p != DLG_SEPARATOR) { - LM_ERR("malformed rr param '%.*s'\n", (int) (long) (end - s), s); - return -1; - } + //link the new_dlg with dlg_out object + link_dlg_out(new_dlg, dlg_out, 0); - if (reverse_hex2int(s, p - s, (unsigned int*) h_entry) < 0) { - LM_ERR("invalid hash entry '%.*s'\n", (int) (long) (p - s), s); - return -1; } - if (reverse_hex2int(p + 1, end - (p + 1), (unsigned int*) h_id) < 0) { - LM_ERR("invalid hash id '%.*s'\n", (int) (long) (end - (p + 1)), p + 1); - return -1; + if (new_state == DLG_STATE_DELETED + && (old_state == DLG_STATE_UNCONFIRMED + || old_state == DLG_STATE_EARLY)) { + LM_DBG("dialog %p failed (negative reply)\n", dlg); + /* dialog setup not completed (3456XX) */ + run_dlg_callbacks(DLGCB_FAILED, dlg, req, rpl, DLG_DIR_UPSTREAM, 0); + /* do unref */ + if (unref) + unref_dlg(dlg, unref); + + if (old_state == DLG_STATE_EARLY) + counter_add(dialog_ng_cnts_h.early, -1); + goto done; } - return 0; + if (unref) unref_dlg(dlg, unref); + +done: + /* unref due to dlg_get_by_iuid() */ + dlg_release(dlg); + return; } /*! - * \brief Update the saved CSEQ information in dialog from SIP message - * \param dlg updated dialog - * \param req SIP request - * \param dir direction of request, must DLG_DIR_UPSTREAM or DLG_DIR_DOWNSTREAM - * \return 0 on success, -1 on failure + * \brief Unreference a new dialog, helper function for dlg_onreq + * \see dlg_onreq + * \param dialog unreferenced dialog */ -static inline int update_cseqs(struct dlg_cell *dlg, struct sip_msg *req, - unsigned int dir, str *to_tag) { - if ((!req->cseq && parse_headers(req, HDR_CSEQ_F, 0) < 0) || !req->cseq || - !req->cseq->parsed) { - LM_ERR("bad sip message or missing CSeq hdr :-/\n"); - return -1; - } - - if (dir == DLG_DIR_UPSTREAM) { - return dlg_update_cseq(dlg, DLG_CALLEE_LEG, &((get_cseq(req))->number), to_tag); - } else if (dir == DLG_DIR_DOWNSTREAM) { - return dlg_update_cseq(dlg, DLG_CALLER_LEG, &((get_cseq(req))->number), to_tag); - } else { - LM_CRIT("dir is not set!\n"); - return -1; - } +/*static void unref_new_dialog(void *dialog) { + struct tmcb_params p; + + memset(&p, 0, sizeof (struct tmcb_params)); + p.param = (void*) &dialog; + dlg_onreply(0, TMCB_DESTROY, &p); +}*/ + +/*! + * \brief Dummy callback just to keep the compiler happy + * \param t unused + * \param type unused + * \param param unused + */ +void dlg_tmcb_dummy(struct cell* t, int type, struct tmcb_params *param) { + return; } /*! - * \brief Function that is registered as RR callback for dialog tracking + * \brief Create a new dialog from a sip message * - * Function that is registered as RR callback for dialog tracking. It - * sets the appropriate events after the SIP method and run the state - * machine to update the dialog state. It updates then the saved - * dialogs and also the statistics. - * \param req SIP request - * \param route_params record-route parameter - * \param param unused + * Create a new dialog from a SIP message, register a callback + * to keep track of the dialog with help of the tm module. + * This function is either called from the request callback, or + * from the dlg_manage function in the configuration script. + * \see dlg_onreq + * \see w_dlg_manage + * \param req SIP message + * \param t transaction + * \param run_initial_cbs if set zero, initial callbacks are not executed + * \return 0 on success, -1 on failure */ -void dlg_onroute(struct sip_msg* req, str *route_params, void *param) { +int dlg_new_dialog(struct sip_msg *req, struct cell *t, const int run_initial_cbs) { struct dlg_cell *dlg; - str val, callid, ftag, ttag; - int h_entry, h_id, new_state, old_state, unref, event, timeout; + str s; + str callid; + str ftag; + str ttag; + str req_uri; unsigned int dir; - int ret = 0; - - if (current_dlg_pointer != NULL) - return; + int mlock; - /* skip initial requests - they may end up here because of the - * preloaded route */ - if ((!req->to && parse_headers(req, HDR_TO_F, 0) < 0) || !req->to) { - LM_ERR("bad request or missing TO hdr :-/\n"); - return; - } - if (get_to(req)->tag_value.len == 0) - return; + if (req->first_line.u.request.method_value != METHOD_INVITE) + return -1; - dlg = 0; - dir = DLG_DIR_NONE; + LM_DBG("starting dlg_new_dialog and method is [%.*s]\n", req->first_line.u.request.method.len, req->first_line.u.request.method.s); - if (seq_match_mode != SEQ_MATCH_NO_ID) { - if (d_rrb.get_route_param(req, &rr_param, &val) != 0) { - LM_DBG("Route param '%.*s' not found\n", rr_param.len, rr_param.s); - if (seq_match_mode == SEQ_MATCH_STRICT_ID) - return; - } else { - LM_DBG("route param is '%.*s' (len=%d)\n", val.len, val.s, val.len); + if (pre_match_parse(req, &callid, &ftag, &ttag, 0) < 0) { + LM_WARN("pre-matching failed\n"); + return -1; + } - if (parse_dlg_rr_param(val.s, val.s + val.len, &h_entry, &h_id) < 0) - return; + if (ttag.s != 0 && ttag.len != 0) + return -1; - dlg = lookup_dlg(h_entry, h_id); - if (dlg == 0) { - LM_WARN("unable to find dialog for %.*s " - "with route param '%.*s' [%u:%u]\n", - req->first_line.u.request.method.len, - req->first_line.u.request.method.s, - val.len, val.s, h_entry, h_id); - if (seq_match_mode == SEQ_MATCH_STRICT_ID) - return; - } else { - if (pre_match_parse(req, &callid, &ftag, &ttag, 1) < 0) { - // lookup_dlg has incremented the ref count by 1 - unref_dlg(dlg, 1); - return; - } - if (match_dialog(dlg, &callid, &ftag, &ttag, &dir) == 0) { - LM_WARN("tight matching failed for %.*s with callid='%.*s'/%d, " - "ftag='%.*s'/%d, ttag='%.*s'/%d and direction=%d\n", - req->first_line.u.request.method.len, - req->first_line.u.request.method.s, - callid.len, callid.s, callid.len, - ftag.len, ftag.s, ftag.len, - ttag.len, ttag.s, ttag.len, dir); - LM_WARN("dialog identification elements are callid='%.*s'/%d, " - "caller tag='%.*s'/%d\n", - dlg->callid.len, dlg->callid.s, dlg->callid.len, - dlg->from_tag.len, dlg->from_tag.s, - dlg->from_tag.len); - // lookup_dlg has incremented the ref count by 1 - unref_dlg(dlg, 1); + if (pv_printf_s(req, ruri_param_model, &req_uri) < 0) { + LM_ERR("error - cannot print the r-uri format\n"); + return -1; + } + trim(&req_uri); - // Reset variables in order to do a lookup based on SIP-Elements. - dlg = 0; - dir = DLG_DIR_NONE; + mlock = 1; + dlg = search_dlg(&callid, &ftag, &ttag, &dir); + if (detect_spirals) { + if (spiral_detected == 1) + return 0; - if (seq_match_mode == SEQ_MATCH_STRICT_ID) - return; + dir = DLG_DIR_NONE; + if (dlg) { + mlock = 0; + if (dlg->state != DLG_STATE_DELETED) { + LM_DBG("Callid '%.*s' found, must be a spiraled request\n", + callid.len, callid.s); + spiral_detected = 1; + + if (run_initial_cbs) + run_dlg_callbacks(DLGCB_SPIRALED, dlg, req, NULL, DLG_DIR_DOWNSTREAM, 0); + + //Add did to rr header for all spiralled requested INVITEs + if (req->first_line.u.request.method_value == METHOD_INVITE) { + if (add_dlg_rr_param(req, dlg->h_entry, dlg->h_id) < 0) { + LM_ERR("failed to add RR param\n"); + } } +// dlg_release(dlg); //this is a fix so that we register for terminating callbacks too (caters for spiralled invites on same P/S-CSCF) + _dlg_ctx.cpid = my_pid(); + _dlg_ctx.iuid.h_entry = dlg->h_entry; + _dlg_ctx.iuid.h_id = dlg->h_id; + set_current_dialog(req, dlg); + dlg_release(dlg); + return 0; } + // get_dlg has incremented the ref count by 1 + dlg_release(dlg); } } + spiral_detected = 0; - if (dlg == 0) { - if (pre_match_parse(req, &callid, &ftag, &ttag, 1) < 0) - return; - /* TODO - try to use the RR dir detection to speed up here the - * search -bogdan */ - dlg = get_dlg(&callid, &ftag, &ttag, &dir); - if (!dlg) { - LM_DBG("Callid '%.*s' not found\n", - req->callid->body.len, req->callid->body.s); - return; - } - } - /* set current dialog - re-use ref increment from dlg_get() above */ - set_current_dialog(req, dlg); - _dlg_ctx.dlg = dlg; + LM_DBG("Building new Dialog for call-id %.*s\n", callid.len, callid.s); + LM_DBG("SIP Method: %.*s \n", req->first_line.u.request.method.len, req->first_line.u.request.method.s); + dlg = build_new_dlg(&callid /*callid*/, + &(get_from(req)->uri) /*from uri*/, + &ftag/*from_tag*/, + &req_uri /*r-uri*/); - if (d_tmb.register_tmcb(req, NULL, TMCB_REQUEST_FWDED, - store_dlg_in_tm_cb, (void*) dlg, NULL) < 0) { - LM_ERR("failed to store dialog in transaction during dialog creation for later reference\n"); + if (dlg == 0) { + if (likely(mlock == 1)) dlg_hash_release(&callid); + LM_ERR("failed to create new dialog\n"); + return -1; } - /* run state machine */ - switch (req->first_line.u.request.method_value) { - case METHOD_PRACK: - event = DLG_EVENT_REQPRACK; - break; - case METHOD_ACK: - event = DLG_EVENT_REQACK; - break; - case METHOD_BYE: - event = DLG_EVENT_REQBYE; - break; - default: - event = DLG_EVENT_REQ; + /* save caller's tag, cseq, contact and record route*/ + if (populate_leg_info(dlg, req, t, DLG_CALLER_LEG, + &(get_from(req)->tag_value)) != 0) { + LM_ERR("could not add further info to the dialog\n"); + lock_destroy(dlg->dlg_out_entries_lock); + lock_dealloc(dlg->dlg_out_entries_lock); + shm_free(dlg); + return -1; } - next_state_dlg(dlg, event, &old_state, &new_state, &unref, 0); + /* Populate initial varlist: */ + dlg->vars = get_local_varlist_pointer(req, 1); - LM_DBG("unref after next state is %i\n", unref); - CURR_DLG_ID = req->id; - CURR_DLG_LIFETIME = (unsigned int) (time(0)) - dlg->start_ts; - CURR_DLG_STATUS = new_state; + /* if search_dlg() returned NULL, slot was kept locked */ + link_dlg(dlg, 0, mlock); + if (likely(mlock == 1)) dlg_hash_release(&callid); - /* run actions for the transition */ - if (event == DLG_EVENT_REQBYE && new_state == DLG_STATE_DELETED && - old_state != DLG_STATE_DELETED) { - LM_DBG("BYE successfully processed\n"); - /* remove from timer */ - ret = remove_dialog_timer(&dlg->tl); - if (ret < 0) { - LM_CRIT("unable to unlink the timer on dlg %p [%u:%u] " - "with clid '%.*s' and tags '%.*s'\n", - dlg, dlg->h_entry, dlg->h_id, - dlg->callid.len, dlg->callid.s, - dlg->from_tag.len, dlg->from_tag.s); + dlg->lifetime = get_dlg_timeout(req); + s.s = _dlg_ctx.to_route_name; + s.len = strlen(s.s); + dlg_set_toroute(dlg, &s); + dlg->sflags |= _dlg_ctx.flags; + // dlg->iflags |= _dlg_ctx.iflags; - } else if (ret > 0) { - LM_WARN("inconsitent dlg timer data on dlg %p [%u:%u] " - "with clid '%.*s' and tags '%.*s' \n", - dlg, dlg->h_entry, dlg->h_id, - dlg->callid.len, dlg->callid.s, - dlg->from_tag.len, dlg->from_tag.s); + // if (dlg_send_bye != 0 || _dlg_ctx.to_bye != 0) + // dlg->iflags |= DLG_IFLAG_TIMEOUTBYE; - } else { - unref++; - } - /* dialog terminated (BYE) */ - dlg_terminated(req, dlg, dir); - unref_dlg(dlg, unref); - counter_add(dialog_ng_cnts_h.active, -1); - counter_inc(dialog_ng_cnts_h.processed); - return; + if (run_initial_cbs) run_create_callbacks(dlg, req); + + /* first INVITE seen (dialog created, unconfirmed) */ + if (seq_match_mode != SEQ_MATCH_NO_ID && + add_dlg_rr_param(req, dlg->h_entry, dlg->h_id) < 0) { + LM_ERR("failed to add RR param\n"); + goto error; } - if ((event == DLG_EVENT_REQ || event == DLG_EVENT_REQACK) - && (new_state == DLG_STATE_CONFIRMED || new_state==DLG_STATE_EARLY)) { + counter_inc(dialog_ng_cnts_h.processed); - timeout = get_dlg_timeout(req); - if (timeout != default_timeout) { - dlg->lifetime = timeout; - } - if (update_dlg_timer(&dlg->tl, dlg->lifetime) == -1) { - LM_ERR("failed to update dialog lifetime\n"); - } - if (update_cseqs(dlg, req, dir, &ttag) != 0) { - LM_ERR("cseqs update failed\n"); - } else { - dlg->dflags |= DLG_FLAG_CHANGED; - } + _dlg_ctx.cpid = my_pid(); + _dlg_ctx.iuid.h_entry = dlg->h_entry; + _dlg_ctx.iuid.h_id = dlg->h_id; + set_current_dialog(req, dlg); - if(dlg_db_mode==DB_MODE_REALTIME && (dlg->dflags&DLG_FLAG_CHANGED)) { - update_dialog_dbinfo(dlg); - } + LM_DBG("New dialog created\n"); - if (old_state != DLG_STATE_CONFIRMED) { - LM_DBG("confirming ACK successfully processed\n"); + return 0; - /* confirming ACK request */ - run_dlg_callbacks(DLGCB_CONFIRMED, dlg, req, NULL, dir, 0); - } else { - LM_DBG("sequential request successfully processed\n"); +error: + if (!spiral_detected) + unref_dlg(dlg, 1); // undo ref regarding linking + return -1; - /* within dialog request */ - run_dlg_callbacks(DLGCB_REQ_WITHIN, dlg, req, NULL, dir, 0); +} - if ((event != DLG_EVENT_REQACK) && - (dlg->cbs.types) & DLGCB_RESPONSE_WITHIN) { - /* ref the dialog as registered into the transaction callback. - * unref will be done when the callback will be destroyed */ - ref_dlg(dlg, 1); - /* register callback for the replies of this request */ - if (d_tmb.register_tmcb(req, 0, TMCB_RESPONSE_FWDED, - (dir == DLG_DIR_UPSTREAM) ? dlg_seq_down_onreply : dlg_seq_up_onreply, - (void*) dlg, unreference_dialog) < 0) { - LM_ERR("failed to register TMCB (2)\n"); - unref_dlg(dlg, 1); - } - } +/*! + * \brief add dlg structure to tm callbacks + * \param t current transaction + * \param req current sip request + * \param dlg current dialog + * \param smode if the sip request was spiraled + * \return 0 on success, -1 on failure + */ +int dlg_set_tm_callbacks(tm_cell_t *t, sip_msg_t *req, dlg_cell_t *dlg, + int smode) { + dlg_iuid_t *iuid = NULL; + if (t == NULL) + return -1; + + if (smode == 0) { +// if (smode == 1) { +// LM_DBG("Setting up TM callbacks for spiralled request on T (%p)\n", t); +// } + iuid = dlg_get_iuid_shm_clone(dlg); + if (iuid == NULL) { + LM_ERR("failed to create dialog unique id clone\n"); + goto error; + } + if (d_tmb.register_tmcb(req, t, + TMCB_RESPONSE_IN | TMCB_RESPONSE_READY | TMCB_RESPONSE_FWDED | TMCB_ON_FAILURE | TMCB_E2ECANCEL_IN | TMCB_REQUEST_OUT, + dlg_onreply, (void*) iuid, dlg_iuid_sfree) < 0) { + LM_ERR("failed to register TMCB\n"); + goto error; } + } else { + LM_DBG("Not registering for TM callbacks because this is a spiraled request and we should have already registered callbacks on this transaction....\n"); } - if (new_state == DLG_STATE_CONFIRMED && old_state != DLG_STATE_CONFIRMED) { - dlg->dflags |= DLG_FLAG_CHANGED; - - if(dlg_db_mode == DB_MODE_REALTIME) - update_dialog_dbinfo(dlg); - } + dlg->dflags |= DLG_FLAG_TM; - return; + return 0; +error: + dlg_iuid_sfree(iuid); + return -1; } /*! @@ -1272,46 +1693,60 @@ void dlg_onroute(struct sip_msg* req, str *route_params, void *param) { * \param tl dialog timer list */ void dlg_ontimeout(struct dlg_tl *tl) { - struct dlg_cell *dlg; + dlg_cell_t *dlg; int new_state, old_state, unref; - struct sip_msg *fmsg; + sip_msg_t *fmsg; /* get the dialog tl payload */ dlg = ((struct dlg_cell*) ((char *) (tl) - (unsigned long) (&((struct dlg_cell*) 0)->tl))); - if (dlg->toroute > 0 && dlg->toroute < main_rt.entries - && main_rt.rlist[dlg->toroute] != NULL) { - fmsg = faked_msg_next(); - if (exec_pre_script_cb(fmsg, REQUEST_CB_TYPE) > 0) { - dlg_set_ctx_dialog(dlg); - LM_DBG("executing route %d on timeout\n", dlg->toroute); - set_route_type(REQUEST_ROUTE); - run_top_route(main_rt.rlist[dlg->toroute], fmsg, 0); - dlg_set_ctx_dialog(0); - exec_post_script_cb(fmsg, REQUEST_CB_TYPE); + /* mark dialog as expired */ + dlg->dflags |= DLG_FLAG_EXPIRED; + + if (dlg->state == DLG_STATE_CONFIRMED_NA + || dlg->state == DLG_STATE_CONFIRMED) { + if (dlg->toroute > 0 && dlg->toroute < main_rt.entries + && main_rt.rlist[dlg->toroute] != NULL) { + fmsg = faked_msg_next(); + if (exec_pre_script_cb(fmsg, REQUEST_CB_TYPE) > 0) { + ref_dlg(dlg, 1); + dlg_set_ctx_iuid(dlg); + LM_DBG("executing route %d on timeout\n", dlg->toroute); + set_route_type(REQUEST_ROUTE); + run_top_route(main_rt.rlist[dlg->toroute], fmsg, 0); + dlg_reset_ctx_iuid(); + exec_post_script_cb(fmsg, REQUEST_CB_TYPE); + unref_dlg(dlg, 1); + } } - } - if (dlg->state == DLG_STATE_CONFIRMED) { - dlg_bye_all(dlg, NULL); + // if (dlg->iflags & DLG_IFLAG_TIMEOUTBYE) { //TODO return therse flags + if (dlg_bye_all(dlg, NULL) < 0) + unref_dlg(dlg, 1); + /* run event route for end of dlg */ + // dlg_run_event_route(dlg, NULL, dlg->state, DLG_STATE_DELETED); //TODO replace + unref_dlg(dlg, 1); + counter_inc(dialog_ng_cnts_h.expired); return; + // } } next_state_dlg(dlg, DLG_EVENT_REQBYE, &old_state, &new_state, &unref, 0); + // dlg_run_event_route(dlg, NULL, old_state, new_state); //TODO replace if (new_state == DLG_STATE_DELETED && old_state != DLG_STATE_DELETED) { LM_WARN("timeout for dlg with CallID '%.*s' and tags '%.*s'\n", dlg->callid.len, dlg->callid.s, dlg->from_tag.len, dlg->from_tag.s); - /* dialog timeout */ - run_dlg_callbacks(DLGCB_EXPIRED, dlg, NULL, NULL, DLG_DIR_NONE, 0); - unref_dlg(dlg, unref + 1); - counter_add(dialog_ng_cnts_h.active, -1); - counter_inc(dialog_ng_cnts_h.expired); - counter_inc(dialog_ng_cnts_h.processed); + /* dialog timeout */ + run_dlg_callbacks(DLGCB_EXPIRED, dlg, NULL, NULL, DLG_DIR_NONE, 0); + + unref_dlg(dlg, unref + 1); + counter_add(dialog_ng_cnts_h.active, -1); + counter_inc(dialog_ng_cnts_h.expired); } else { unref_dlg(dlg, 1); } @@ -1401,20 +1836,22 @@ void internal_print_all_dlg(struct dlg_cell *dlg) { LM_DBG("Dialog first request cseq: %.*s\n", dlg->first_req_cseq.len, dlg->first_req_cseq.s); LM_DBG("Dialog caller route set: %.*s\n", dlg->caller_route_set.len, dlg->caller_route_set.s); LM_DBG("Dialog lifetime: %d\n", dlg->lifetime); - LM_DBG("Dialog bind_address: %.*s\n", dlg->caller_bind_addr->sock_str.len, dlg->caller_bind_addr->sock_str.s); + LM_DBG("Dialog bind_address: %.*s\n", dlg->caller_bind_addr ? dlg->caller_bind_addr->sock_str.len : 0, dlg->caller_bind_addr ? dlg->caller_bind_addr->sock_str.s : 0); + LM_DBG("Dialog-creating transaction pointer [%p]\n", dlg->transaction); dlg_out = d_entry_out->first; while (dlg_out) { LM_DBG("----------"); - LM_DBG("Dialog out h_entry:h_id = [%u : %u]\n", dlg_out->h_entry, dlg_out->h_id); + LM_DBG("Dialog out h_entry:h_id = [%u : %u]\n", dlg_out->h_entry, dlg_out->h_id); LM_DBG("Dialog out did: %.*s\n", dlg_out->did.len, dlg_out->did.s); LM_DBG("Dialog out to_tag: %.*s\n", dlg_out->to_tag.len, dlg_out->to_tag.s); LM_DBG("Dialog out caller cseq: %.*s\n", dlg_out->caller_cseq.len, dlg_out->caller_cseq.s); LM_DBG("Dialog out callee cseq: %.*s\n", dlg_out->callee_cseq.len, dlg_out->callee_cseq.s); LM_DBG("Dialog out callee contact: %.*s\n", dlg_out->callee_contact.len, dlg_out->callee_contact.s); LM_DBG("Dialog out callee route set: %.*s\n", dlg_out->callee_route_set.len, dlg_out->callee_route_set.s); + LM_DBG("Dialog out branch: %.*s\n", dlg_out->branch.len, dlg_out->branch.s); LM_DBG("Dialog out state (deleted): %i\n", dlg_out->deleted); LM_DBG("----------"); @@ -1455,29 +1892,28 @@ void print_all_dlgs() { } -struct dlg_cell *dlg_get_msg_dialog(sip_msg_t *msg) -{ - struct dlg_cell *dlg = NULL; - str callid; - str ftag; - str ttag; - unsigned int dir; - - /* Retrieve the current dialog */ - dlg = dlg_get_ctx_dialog(); - if (dlg != NULL ) - return dlg; - - if (pre_match_parse(msg, &callid, &ftag, &ttag, 0) < 0) - return NULL ; - dir = DLG_DIR_NONE; - dlg = get_dlg(&callid, &ftag, &ttag, &dir); - if (dlg == NULL ) { - LM_DBG("dlg with callid '%.*s' not found\n", - msg->callid->body.len, msg->callid->body.s); - return NULL ; - } - return dlg; +struct dlg_cell *dlg_get_msg_dialog(sip_msg_t *msg) { + struct dlg_cell *dlg = NULL; + str callid; + str ftag; + str ttag; + unsigned int dir; + + /* Retrieve the current dialog */ + dlg = dlg_get_ctx_dialog(); + if (dlg != NULL) + return dlg; + + if (pre_match_parse(msg, &callid, &ftag, &ttag, 0) < 0) + return NULL; + dir = DLG_DIR_NONE; + dlg = get_dlg(&callid, &ftag, &ttag, &dir); + if (dlg == NULL) { + LM_DBG("dlg with callid '%.*s' not found\n", + msg->callid->body.len, msg->callid->body.s); + return NULL; + } + return dlg; } diff --git a/modules/dialog_ng/dlg_handlers.h b/modules/dialog_ng/dlg_handlers.h index 6ba5a512446..6aec00c5441 100644 --- a/modules/dialog_ng/dlg_handlers.h +++ b/modules/dialog_ng/dlg_handlers.h @@ -90,6 +90,15 @@ void destroy_dlg_handlers(void); int populate_leg_info( struct dlg_cell *dlg, struct sip_msg *msg, struct cell* t, unsigned int leg, str *tag); +/*! + * \brief Clone dialog internal unique id to shared memory + */ +dlg_iuid_t *dlg_get_iuid_shm_clone(dlg_cell_t *dlg); + +/*! + * \brief Free dialog internal unique id stored in shared memory + */ +void dlg_iuid_sfree(void *iuid); /*! * \brief Function that is registered as TM callback and called on requests @@ -99,6 +108,7 @@ int populate_leg_info( struct dlg_cell *dlg, struct sip_msg *msg, */ void dlg_onreq(struct cell* t, int type, struct tmcb_params *param); +void dlg_onreply(struct cell* t, int type, struct tmcb_params *param); /*! * \brief Function that is registered as RR callback for dialog tracking @@ -136,6 +146,16 @@ void dlg_ontimeout( struct dlg_tl *tl); */ int dlg_new_dialog(struct sip_msg *msg, struct cell *t, const int run_initial_cbs); +/*! + * \brief add dlg structure to tm callbacks + * \param t current transaction + * \param req current sip request + * \param dlg current dialog + * \param smode if the sip request was spiraled + * \return 0 on success, -1 on failure + */ +int dlg_set_tm_callbacks(tm_cell_t *t, sip_msg_t *req, dlg_cell_t *dlg, + int smode); /*! * \brief Function that returns the dialog lifetime as pseudo-variable diff --git a/modules/dialog_ng/dlg_hash.c b/modules/dialog_ng/dlg_hash.c index 3a28a0b6f8e..725114ca871 100644 --- a/modules/dialog_ng/dlg_hash.c +++ b/modules/dialog_ng/dlg_hash.c @@ -17,6 +17,7 @@ #include "dlg_profile.h" #include "dlg_handlers.h" #include "dlg_db_handler.h" +#include #define MAX_LDG_LOCKS 2048 #define MIN_LDG_LOCKS 2 @@ -69,6 +70,24 @@ static int dlg_hash_size_out = 4096; }\ }while(0) +inline static int backtrace2str(char* buf, int size) +{ + void* bt[32]; + int bt_size, i; + char** bt_strs; + + bt_size=backtrace(bt, sizeof(bt)/sizeof(bt[0])); + bt_strs=backtrace_symbols(bt, bt_size); + if (bt_strs){ + /*if (bt_size>16) bt_size=16;*/ /* go up only 12 entries */ + for (i=0; i< bt_size; i++){ + /* try to isolate only the function name*/ + LM_DBG("BACKTRACE: %s\n", bt_strs[i]); + } + } + return 0; +} + /*! * \brief Initialize the global dialog table * \param size size of the table @@ -197,9 +216,7 @@ inline void destroy_dlg(struct dlg_cell *dlg) { LM_DBG("About to run dlg callback for destroy\n"); run_dlg_callbacks(DLGCB_DESTROY, dlg, NULL, NULL, DLG_DIR_NONE, 0); LM_DBG("DONE: About to run dlg callback for destroy\n"); - if (dlg == get_current_dlg_pointer()) - reset_current_dlg_pointer(); - + if (dlg->cbs.first) destroy_dlg_callbacks_list(dlg->cbs.first); @@ -468,14 +485,14 @@ int dlg_set_leg_info(struct dlg_cell *dlg, str* tag, str *rr, str *contact, * \param to_tag - dialog to_tag * \return created dlg_out structure on success, NULL otherwise */ -struct dlg_cell_out* build_new_dlg_out(struct dlg_cell *dlg, str* to_uri, str* to_tag) { +struct dlg_cell_out* build_new_dlg_out(struct dlg_cell *dlg, str* to_uri, str* to_tag, str* branch) { struct dlg_cell_out *dlg_out; int len; char *p; //len = sizeof (struct dlg_cell_out) +dlg->did.len + to_tag->len + to_uri->len; - len = sizeof (struct dlg_cell_out) +to_tag->len + to_uri->len; + len = sizeof (struct dlg_cell_out) +to_tag->len + to_uri->len + branch->len; dlg_out = (struct dlg_cell_out*) shm_malloc(len); if (dlg_out == 0) { @@ -489,10 +506,12 @@ struct dlg_cell_out* build_new_dlg_out(struct dlg_cell *dlg, str* to_uri, str* t p = (char*) (dlg_out + 1); - //dlg_out->did.s = p; - //dlg_out->did.len = dlg->did.len; - //memcpy(p, dlg->did.s, dlg->did.len); - //p += dlg->did.len; + if (branch->len > 0) { + dlg_out->branch.s = p; + dlg_out->branch.len = branch->len; + memcpy(p, branch->s, branch->len); + p += branch->len; + } dlg_out->to_uri.s = p; dlg_out->to_uri.len = to_uri->len; @@ -697,61 +716,51 @@ int dlg_update_cseq(struct dlg_cell * dlg, unsigned int leg, str *cseq, str *to_ //compare the to_tag passed parameter to all the dlg_out to_tag entry of the dlg parameter (There could be multiple) while (dlg_out) { - LM_DBG("Searching out dialog with to_tag '%.*s' (looking for '%.*s')\n", dlg_out->to_tag.len, dlg_out->to_tag.s, to_tag->len, to_tag->s); if (dlg_out->to_tag.len == to_tag->len && memcmp(dlg_out->to_tag.s, to_tag->s, dlg_out->to_tag.len) == 0) { //this parameter matches we have found the dlg_out to update the cseq if (leg == DLG_CALLER_LEG) { - LM_DBG("Update Caller\n"); //update caller cseq if (dlg_out->caller_cseq.s) { - if (dlg_out->caller_cseq.len != cseq->len) { + if (dlg_out->caller_cseq.len < cseq->len) { shm_free(dlg_out->caller_cseq.s); dlg_out->caller_cseq.s = (char*) shm_malloc(cseq->len); if (dlg_out->caller_cseq.s == NULL) goto error; dlg_out->caller_cseq.len = cseq->len; + memcpy(dlg_out->caller_cseq.s, cseq->s, cseq->len); } - LM_DBG("Updating CSeq...\n"); - memcpy(dlg_out->caller_cseq.s, cseq->s, cseq->len); - LM_DBG("CSeq is now '%.*s' (%p)\n", dlg_out->caller_cseq.len, dlg_out->caller_cseq.s, dlg_out->caller_cseq.s); } else { dlg_out->caller_cseq.s = (char*) shm_malloc(cseq->len); if (dlg_out->caller_cseq.s == NULL) goto error; dlg_out->caller_cseq.len = cseq->len; - LM_DBG("Updating CSeq...\n"); memcpy(dlg_out->caller_cseq.s, cseq->s, cseq->len); - LM_DBG("CSeq is now '%.*s' (%p)\n", dlg_out->caller_cseq.len, dlg_out->caller_cseq.s, dlg_out->caller_cseq.s); } + } else if (leg == DLG_CALLEE_LEG) { - LM_DBG("Update Callee\n"); //update callee cseq if (dlg_out->callee_cseq.s) { - if (dlg_out->callee_cseq.len != cseq->len) { + if (dlg_out->callee_cseq.len < cseq->len) { shm_free(dlg_out->callee_cseq.s); dlg_out->callee_cseq.s = (char*) shm_malloc(cseq->len); if (dlg_out->callee_cseq.s == NULL) goto error; dlg_out->callee_cseq.len = cseq->len; + memcpy(dlg_out->callee_cseq.s, cseq->s, cseq->len); } - LM_DBG("Updating CSeq...\n"); - memcpy(dlg_out->callee_cseq.s, cseq->s, cseq->len); - LM_DBG("CSeq is now '%.*s' (%p)\n", dlg_out->caller_cseq.len, dlg_out->caller_cseq.s, dlg_out->caller_cseq.s); } else { dlg_out->callee_cseq.s = (char*) shm_malloc(cseq->len); if (dlg_out->callee_cseq.s == NULL) goto error; dlg_out->callee_cseq.len = cseq->len; - - LM_DBG("Updating CSeq...\n"); memcpy(dlg_out->callee_cseq.s, cseq->s, cseq->len); - LM_DBG("CSeq is now '%.*s' (%p)\n", dlg_out->caller_cseq.len, dlg_out->caller_cseq.s, dlg_out->caller_cseq.s); } + } } dlg_out = dlg_out->next; @@ -862,6 +871,7 @@ struct dlg_cell * lookup_dlg(unsigned int h_entry, unsigned int h_id) { for (dlg = d_entry->first; dlg; dlg = dlg->next) { if (dlg->h_id == h_id) { +// backtrace2str(0,0); ref_dlg_unsafe(dlg, 1); dlg_unlock(d_table, d_entry); LM_DBG("dialog id=%u found on entry %u\n", h_id, h_entry); @@ -883,27 +893,32 @@ struct dlg_cell * lookup_dlg(unsigned int h_entry, unsigned int h_id) { * \param ftag from tag * \param ttag to tag * \param dir direction + * \param mode let hash table slot locked if dialog is not found * \return dialog structure on success, NULL on failure */ static inline struct dlg_cell * internal_get_dlg(unsigned int h_entry, - str *callid, str *ftag, str *ttag, unsigned int *dir) { + str *callid, str *ftag, str *ttag, unsigned int *dir, unsigned int mode) { struct dlg_cell *dlg; - struct dlg_entry *d_entry; - - d_entry = &(d_table->entries[h_entry]); - - dlg_lock(d_table, d_entry); - - for (dlg = d_entry->first; dlg; dlg = dlg->next) { - /* Check callid / fromtag / totag */ - if (match_dialog(dlg, callid, ftag, ttag, dir) == 1) { - ref_dlg_unsafe(dlg, 1); - dlg_unlock(d_table, d_entry); - return dlg; - } - } + struct dlg_entry *d_entry; + + d_entry = &(d_table->entries[h_entry]); + + dlg_lock( d_table, d_entry); + + for( dlg = d_entry->first ; dlg ; dlg = dlg->next ) { + /* Check callid / fromtag / totag */ + if (match_dialog( dlg, callid, ftag, ttag, dir)==1) { + ref_dlg_unsafe(dlg, 1); + dlg_unlock( d_table, d_entry); + LM_DBG("dialog callid='%.*s' found on entry %u, dir=%d\n", + callid->len, callid->s,h_entry,*dir); + return dlg; + } + } - dlg_unlock(d_table, d_entry); + if(likely(mode==0)) dlg_unlock( d_table, d_entry); + LM_DBG("no dialog callid='%.*s' found\n", callid->len, callid->s); + return 0; return 0; } @@ -926,13 +941,13 @@ static inline struct dlg_cell * internal_get_dlg(unsigned int h_entry, */ struct dlg_cell * get_dlg(str *callid, str *ftag, str *ttag, unsigned int *dir) { struct dlg_cell *dlg; + unsigned int he; - if ((dlg = internal_get_dlg(core_hash(callid, 0, - d_table->size), callid, ftag, ttag, dir)) == 0 && - (dlg = internal_get_dlg(core_hash(callid, ttag->len - ? ttag : 0, d_table->size), callid, ftag, ttag, dir)) == 0) { - LM_DBG("no dialog callid='%.*s' found\n", callid->len, callid->s); + he = core_hash(callid, 0, d_table->size); + dlg = internal_get_dlg(he, callid, ftag, ttag, dir, 0); + if (dlg == 0) { + LM_DBG("no dialog callid='%.*s' found\n", callid->len, callid->s); return 0; } return dlg; @@ -968,21 +983,23 @@ void link_dlg_out(struct dlg_cell *dlg, struct dlg_cell_out *dlg_out, int n) { LM_DBG("Done: link_dlg_out\n"); return; } - /*! * \brief Link a dialog structure * \param dlg dialog * \param n extra increments for the reference counter + * \param mode link in safe mode (0 - lock slot; 1 - don't) */ -void link_dlg(struct dlg_cell *dlg, int n) { +void link_dlg(struct dlg_cell *dlg, int n, int mode) { struct dlg_entry *d_entry; - LM_DBG("Linking new dialog with h_entry: %u", dlg->h_entry); d_entry = &(d_table->entries[dlg->h_entry]); - dlg_lock(d_table, d_entry); + if (unlikely(mode == 0)) dlg_lock(d_table, d_entry); - dlg->h_id = d_entry->next_id++; + /* keep id 0 for special cases */ + dlg->h_id = 1 + d_entry->next_id++; + if (dlg->h_id == 0) dlg->h_id = 1; + LM_DBG("linking dialog [%u:%u]\n", dlg->h_entry, dlg->h_id); if (d_entry->first == 0) { d_entry->first = d_entry->last = dlg; } else { @@ -993,11 +1010,12 @@ void link_dlg(struct dlg_cell *dlg, int n) { ref_dlg_unsafe(dlg, 1 + n); - dlg_unlock(d_table, d_entry); - + if (unlikely(mode == 0)) dlg_unlock(d_table, d_entry); return; } + + /*! * \brief Refefence a dialog with locking * \see ref_dlg_unsafe @@ -1011,10 +1029,12 @@ void ref_dlg(struct dlg_cell *dlg, unsigned int cnt) { d_entry = &(d_table->entries[dlg->h_entry]); dlg_lock(d_table, d_entry); +// backtrace2str(0, 0); ref_dlg_unsafe(dlg, cnt); dlg_unlock(d_table, d_entry); } + /*! * \brief Unreference a dialog with locking * \see unref_dlg_unsafe @@ -1028,6 +1048,7 @@ void unref_dlg(struct dlg_cell *dlg, unsigned int cnt) { d_entry = &(d_table->entries[dlg->h_entry]); dlg_lock(d_table, d_entry); +// backtrace2str(0, 0); unref_dlg_unsafe(dlg, cnt, d_entry); dlg_unlock(d_table, d_entry); } @@ -1062,8 +1083,6 @@ static inline void log_next_state_dlg(const int event, const struct dlg_cell * d void next_state_dlg(struct dlg_cell *dlg, int event, int *old_state, int *new_state, int *unref, str * to_tag) { struct dlg_entry *d_entry; - - d_entry = &(d_table->entries[dlg->h_entry]); @@ -1084,27 +1103,28 @@ void next_state_dlg(struct dlg_cell *dlg, int event, switch (dlg->state) { case DLG_STATE_UNCONFIRMED: case DLG_STATE_EARLY: - if (to_tag) { - LM_DBG("Going to check if there is another active branch - we only change state to DELETED if there are no other active branches\n"); - while (dlg_out) { - if (dlg_out->to_tag.len != to_tag->len || memcmp(dlg_out->to_tag.s, to_tag->s, dlg_out->to_tag.len) != 0) { - if(dlg_out->deleted != 1) { - LM_DBG("Found a dlg_out that is not for this event and is not in state deleted, therefore there is another active branch\n"); - delete = 0; - //we should delete this dlg_out tho... - dlg_out->deleted=1; - } - } - dlg_out = dlg_out->next; - } - } - if(delete) { - dlg->state = DLG_STATE_DELETED; - unref_dlg_unsafe(dlg, 1, d_entry); - *unref = 1; - } +// if (to_tag) { +// LM_DBG("Going to check if there is another active branch - we only change state to DELETED if there are no other active branches\n"); +// while (dlg_out) { +// if (dlg_out->to_tag.len == to_tag->len && memcmp(dlg_out->to_tag.s, to_tag->s, dlg_out->to_tag.len) == 0) { +// dlg_out->deleted=1; +// } else { +// if (dlg_out->deleted != 1) { +// LM_DBG("Found a dlg_out (to-tag: [%.*s]) that is not for this event and is not in state deleted, therefore there is another active branch\n", to_tag->len, to_tag->s); +// delete = 0; +// } +// } +// dlg_out = dlg_out->next; +// } +// } + if (delete) { + dlg->state = DLG_STATE_DELETED; + unref_dlg_unsafe(dlg, 1, d_entry); + *unref = 1; + } break; case DLG_STATE_CONFIRMED: + case DLG_STATE_CONFIRMED_NA: unref_dlg_unsafe(dlg, 1, d_entry); break; case DLG_STATE_DELETED: @@ -1128,18 +1148,6 @@ void next_state_dlg(struct dlg_cell *dlg, int event, switch (dlg->state) { case DLG_STATE_UNCONFIRMED: case DLG_STATE_EARLY: - if (to_tag) { - LM_DBG("Going to check if there is another active branch - we only change state to DELETED if there are no other active branches\n"); - while (dlg_out) { - if (dlg_out->to_tag.len != to_tag->len || memcmp(dlg_out->to_tag.s, to_tag->s, dlg_out->to_tag.len) != 0) { - if(dlg_out->deleted != 1) { - LM_DBG("Found a dlg_out that is not for this event and is not in state deleted, therefore there is another active branch\n"); - delete = 0; - } - } - dlg_out = dlg_out->next; - } - } if(delete) { dlg->state = DLG_STATE_DELETED; *unref = 1; @@ -1163,11 +1171,10 @@ void next_state_dlg(struct dlg_cell *dlg, int event, ref_dlg_unsafe(dlg, 1); case DLG_STATE_UNCONFIRMED: case DLG_STATE_EARLY: - dlg->state = DLG_STATE_CONFIRMED; - //TODO: check that the callbacks for confirmed are run + dlg->state = DLG_STATE_CONFIRMED_NA; break; + case DLG_STATE_CONFIRMED_NA: case DLG_STATE_CONFIRMED: - //check the to_tag passed parameter exists if (to_tag) { //compare the to_tag passed parameter to the dlg_out to_tag entry of the dlg parameter (There should be only 1 dlg_out entry) @@ -1191,19 +1198,13 @@ void next_state_dlg(struct dlg_cell *dlg, int event, //The parameter does not match so this is a concurrently confirmed call //dlg->state = DLG_STATE_CONCURRENTLY_CONFIRMED; } - } else { - //to_tag parameter does not exist so break - break; } + break; case DLG_STATE_CONCURRENTLY_CONFIRMED: - //check the to_tag passed parameter exists - if (to_tag) { - //compare the to_tag passed parameter to all the dlg_out to_tag entry of the dlg parameter (There could be multiple) while (dlg_out) { - if (dlg_out->to_tag.len == to_tag->len && memcmp(dlg_out->to_tag.s, to_tag->s, dlg_out->to_tag.len) == 0) { //this parameter matches the existing dlg_out and is therefore a retransmission found = 1; @@ -1214,12 +1215,10 @@ void next_state_dlg(struct dlg_cell *dlg, int event, //The parameter does not match so this is another concurrently confirmed call (we would have breaked by now if it matched) dlg->state = DLG_STATE_CONCURRENTLY_CONFIRMED; } - } else { //to_tag parameter does not exist so break break; } - break; default: log_next_state_dlg(event, dlg); @@ -1227,6 +1226,9 @@ void next_state_dlg(struct dlg_cell *dlg, int event, break; case DLG_EVENT_REQACK: switch (dlg->state) { + case DLG_STATE_CONFIRMED_NA: + dlg->state = DLG_STATE_CONFIRMED; + break; case DLG_STATE_CONFIRMED: break; case DLG_STATE_DELETED: @@ -1249,6 +1251,8 @@ void next_state_dlg(struct dlg_cell *dlg, int event, log_next_state_dlg(event, dlg); } break; + + break; case DLG_EVENT_REQPRACK: switch (dlg->state) { case DLG_STATE_EARLY: @@ -1276,6 +1280,13 @@ void next_state_dlg(struct dlg_cell *dlg, int event, dlg->from_tag.len, dlg->from_tag.s); } *new_state = dlg->state; + + /* remove the dialog from profiles when is not no longer active */ + if (*new_state == DLG_STATE_DELETED && dlg->profile_links != NULL + && *old_state != *new_state) { + destroy_linkers(dlg->profile_links); + dlg->profile_links = NULL; + } dlg_unlock(d_table, d_entry); @@ -1757,6 +1768,53 @@ struct mi_root * mi_print_dlgs_ctx(struct mi_root *cmd_tree, void *param) { return NULL; } +/*! + * \brief Search dialog that corresponds to CallId, From Tag and To Tag + * + * Get dialog that correspond to CallId, From Tag and To Tag. + * See RFC 3261, paragraph 4. Overview of Operation: + * "The combination of the To tag, From tag, and Call-ID completely + * defines a peer-to-peer SIP relationship between [two UAs] and is + * referred to as a dialog." + * Note that the caller is responsible for decrementing (or reusing) + * the reference counter by one again if a dialog has been found. + * If the dialog is not found, the hash slot is left locked, to allow + * linking the structure of a new dialog. + * \param callid callid + * \param ftag from tag + * \param ttag to tag + * \param dir direction + * \return dialog structure on success, NULL on failure (and slot locked) + */ +dlg_cell_t* search_dlg( str *callid, str *ftag, str *ttag, unsigned int *dir) +{ + struct dlg_cell *dlg; + unsigned int he; + + he = core_hash(callid, 0, d_table->size); + dlg = internal_get_dlg(he, callid, ftag, ttag, dir, 1); + + if (dlg == 0) { + LM_DBG("dialog with callid='%.*s' not found\n", callid->len, callid->s); + return 0; + } + return dlg; +} + +/*! + * \brief Release hash table slot by call-id + * \param callid call-id value + */ +void dlg_hash_release(str *callid) +{ + unsigned int he; + struct dlg_entry *d_entry; + + he = core_hash(callid, 0, d_table->size); + d_entry = &(d_table->entries[he]); + dlg_unlock(d_table, d_entry); +} + /*! * \brief decrement dialog ref counter by 1 * \see dlg_unref @@ -1810,3 +1868,22 @@ char* state_to_char(unsigned int state) { return "Unknown"; } } + +/*! + * \brief Search a dialog in the global list by iuid + * + * Note that the caller is responsible for decrementing (or reusing) + * the reference counter by one again if a dialog has been found. + * \param diuid internal unique id per dialog + * \return dialog structure on success, NULL on failure + */ +dlg_cell_t* dlg_get_by_iuid(dlg_iuid_t *diuid) +{ + if(diuid==NULL) + return NULL; + if(diuid->h_id==0) + return NULL; + /* dlg ref counter is increased by next line */ + return lookup_dlg(diuid->h_entry, diuid->h_id); +} + diff --git a/modules/dialog_ng/dlg_hash.h b/modules/dialog_ng/dlg_hash.h index 2a5fe5d6308..8d78a513c6b 100644 --- a/modules/dialog_ng/dlg_hash.h +++ b/modules/dialog_ng/dlg_hash.h @@ -55,8 +55,9 @@ #define DLG_STATE_UNCONFIRMED 1 /*!< unconfirmed dialog */ #define DLG_STATE_EARLY 2 /*!< early dialog */ #define DLG_STATE_CONFIRMED 4 /*!< confirmed dialog */ -#define DLG_STATE_DELETED 5 /*!< deleted dialog */ -#define DLG_STATE_CONCURRENTLY_CONFIRMED 6 /*!< confirmed concurrent dailogs */ +#define DLG_STATE_CONFIRMED_NA 5 /*!< confirmed dialog without a ACK yet */ +#define DLG_STATE_DELETED 6 /*!< deleted dialog */ +#define DLG_STATE_CONCURRENTLY_CONFIRMED 7 /*!< confirmed concurrent dailogs */ /* events for dialog processing */ #define DLG_EVENT_TDEL 1 /*!< transaction was destroyed */ @@ -66,7 +67,8 @@ #define DLG_EVENT_REQPRACK 5 /*!< PRACK request */ #define DLG_EVENT_REQACK 6 /*!< ACK request */ #define DLG_EVENT_REQBYE 7 /*!< BYE request */ -#define DLG_EVENT_REQ 8 /*!< other requests */ +#define DLG_EVENT_REQCANCEL 8 /*!< CANCEL request */ +#define DLG_EVENT_REQ 9 /*!< other requests */ /* dialog flags */ #define DLG_FLAG_NEW (1<<0) /*!< new dialog */ @@ -77,10 +79,14 @@ #define DLG_FLAG_CALLEEBYE (1<<5) /*!< bye from callee */ #define DLG_FLAG_LOCALDLG (1<<6) /*!< local dialog, unused */ #define DLG_FLAG_CHANGED_VARS (1<<7) /*!< dialog-variables changed */ +#define DLG_FLAG_HASCANCEL (1<<8) /*!< cancel was received */ /* dialog-variable flags (in addition to dialog-flags) */ -#define DLG_FLAG_DEL (1<<8) /*!< delete this var */ -#define DLG_FLAG_INSERTED (1<<9) /*!< DLG already written to DB - could have been put in by early media or confirmed */ +#define DLG_FLAG_DEL (1<<9) /*!< delete this var */ +#define DLG_FLAG_INSERTED (1<<10) /*!< DLG already written to DB - could have been put in by early media or confirmed */ +#define DLG_FLAG_TM (1<<11) /*!< dialog is set in transaction */ +#define DLG_FLAG_EXPIRED (1<<12)/*!< dialog is expired */ + #define DLG_CALLER_LEG 0 /*!< attribute that belongs to a caller leg */ #define DLG_CALLEE_LEG 1 /*!< attribute that belongs to a callee leg */ @@ -89,6 +95,12 @@ #define DLG_DIR_DOWNSTREAM 1 /*!< dialog has downstream direction */ #define DLG_DIR_UPSTREAM 2 /*!< dialog has upstream direction */ +/*! internal unique ide per dialog */ +typedef struct dlg_iuid { + unsigned int h_id; /*!< id in the hash table entry (seq nr in slot) */ + unsigned int h_entry; /*!< index of hash table entry (the slot number) */ +} dlg_iuid_t; + /*! entries in the main dialog table */ struct dlg_entry_out { struct dlg_cell_out *first; /*!< dialog list */ @@ -97,7 +109,7 @@ struct dlg_entry_out { }; /*! entries in the dialog list */ -struct dlg_cell { +typedef struct dlg_cell { volatile int ref; /*!< reference counter */ struct dlg_cell *next; /*!< next entry in the list */ struct dlg_cell *prev; /*!< previous entry in the list */ @@ -127,13 +139,14 @@ struct dlg_cell { struct cell *transaction; /*!< ptr to associated transaction for this dialog TM module cell ptr */ gen_lock_t *dlg_out_entries_lock; /*!< lock for dialog_out linked list */ unsigned int from_rr_nb; /*!< information from record routing */ -}; +} dlg_cell_t; struct dlg_cell_out { struct dlg_cell_out *next; /*!< next entry in the list */ struct dlg_cell_out *prev; /*!< previous entry in the list */ unsigned int h_id; /*!< id of the hash table entry */ unsigned int h_entry; /*!< number of hash entry */ + str branch; str did; str to_uri; /*!< to uri */ str to_tag; /*!< to tags of callee*/ @@ -147,27 +160,24 @@ struct dlg_cell_out { }; /*! entries in the main dialog table */ -struct dlg_entry { +typedef struct dlg_entry { struct dlg_cell *first; /*!< dialog list */ struct dlg_cell *last; /*!< optimisation, end of the dialog list */ unsigned int next_id; /*!< next id */ unsigned int lock_idx; /*!< lock index */ -}; +} dlg_entry_t; /*! main dialog table */ -struct dlg_table { +typedef struct dlg_table { unsigned int size; /*!< size of the dialog table */ struct dlg_entry *entries; /*!< dialog hash table */ unsigned int locks_no; /*!< number of locks */ gen_lock_set_t *locks; /*!< lock table */ -}; +} dlg_table_t; /*! global dialog table */ extern struct dlg_table *d_table; -/*! point to the current dialog */ -extern struct dlg_cell *current_dlg_pointer; - /*! * \brief Set a dialog lock @@ -286,6 +296,15 @@ int dlg_update_contact(struct dlg_cell * dlg, unsigned int leg, str *contact, st */ int dlg_set_toroute(struct dlg_cell *dlg, str *route); +/*! + * \brief Search and return dialog in the global list by iuid + * + * Note that the caller is responsible for decrementing (or reusing) + * the reference counter by one again if a dialog has been found. + * \param diuid internal unique id per dialog + * \return dialog structure on success, NULL on failure + */ +dlg_cell_t* dlg_get_by_iuid(dlg_iuid_t *diuid); /*! * \brief Lookup a dialog in the global list @@ -298,6 +317,31 @@ int dlg_set_toroute(struct dlg_cell *dlg, str *route); */ struct dlg_cell* lookup_dlg(unsigned int h_entry, unsigned int h_id); +/*! + * \brief Search dialog that corresponds to CallId, From Tag and To Tag + * + * Get dialog that correspond to CallId, From Tag and To Tag. + * See RFC 3261, paragraph 4. Overview of Operation: + * "The combination of the To tag, From tag, and Call-ID completely + * defines a peer-to-peer SIP relationship between [two UAs] and is + * referred to as a dialog." + * Note that the caller is responsible for decrementing (or reusing) + * the reference counter by one again if a dialog has been found. + * If the dialog is not found, the hash slot is left locked, to allow + * linking the structure of a new dialog. + * \param callid callid + * \param ftag from tag + * \param ttag to tag + * \param dir direction + * \return dialog structure on success, NULL on failure (and slot locked) + */ +dlg_cell_t* search_dlg(str *callid, str *ftag, str *ttag, unsigned int *dir); + +/*! + * \brief Release hash table slot by call-id + * \param callid call-id value + */ +void dlg_hash_release(str *callid); /*! * \brief Get dialog that correspond to CallId, From Tag and To Tag @@ -322,8 +366,9 @@ struct dlg_cell* get_dlg(str *callid, str *ftag, str *ttag, unsigned int *dir); * \brief Link a dialog structure * \param dlg dialog * \param n extra increments for the reference counter + * \param mode link in safe mode (0 - lock slot; 1 - don't) */ -void link_dlg(struct dlg_cell *dlg, int n); +void link_dlg(struct dlg_cell *dlg, int n, int mode); void link_dlg_out(struct dlg_cell *dlg, struct dlg_cell_out *dlg_out, int n); @@ -406,6 +451,7 @@ static inline int match_dialog(struct dlg_cell *dlg, str *callid, if (d_entry_out->first == 0) { //there are no dialog out entries yet + LM_DBG("No dlg outs yet...\n"); if (*dir == DLG_DIR_DOWNSTREAM) { if (dlg->callid.len == callid->len && dlg->from_tag.len == ftag->len && @@ -441,14 +487,14 @@ static inline int match_dialog(struct dlg_cell *dlg, str *callid, LM_DBG("No match found\n"); } } else { - + LM_DBG("searching dlg_outs\n"); //there is a dialog out entry if (*dir == DLG_DIR_DOWNSTREAM) { if (dlg->callid.len == callid->len && dlg->from_tag.len == ftag->len && strncmp(dlg->callid.s, callid->s, callid->len) == 0 && strncmp(dlg->from_tag.s, ftag->s, ftag->len) == 0) { - //now need to scroll thought d_out_entries to see if to_tag matches! + //now need to scroll through d_out_entries to see if to_tag matches! dlg_out = d_entry_out->first; while (dlg_out) { if (dlg_out->to_tag.len == ttag->len && @@ -511,6 +557,8 @@ static inline int match_dialog(struct dlg_cell *dlg, str *callid, } dlg_out = dlg_out->next; } + *dir = DLG_DIR_UPSTREAM; + return 1; } else LM_DBG("no match tags: "); @@ -561,7 +609,7 @@ int mi_print_dlg(struct mi_node *rpl, struct dlg_cell *dlg, int with_context); * \return created dlg_out structure on success, NULL otherwise */ -struct dlg_cell_out* build_new_dlg_out(struct dlg_cell *dlg, str *to_uri, str* to_tag); +struct dlg_cell_out* build_new_dlg_out(struct dlg_cell *dlg, str *to_uri, str* to_tag, str* branch); /*! * \brief Remove all dlg_out entries from dlg structure expect that identified as dlg_do_not_remove diff --git a/modules/dialog_ng/dlg_profile.c b/modules/dialog_ng/dlg_profile.c index 98c26e40070..28daa2b92b7 100644 --- a/modules/dialog_ng/dlg_profile.c +++ b/modules/dialog_ng/dlg_profile.c @@ -43,7 +43,7 @@ #include "dlg_hash.h" #include "dlg_handlers.h" #include "dlg_profile.h" - +#include "dlg_var.h" /*! size of dialog profile hash */ #define PROFILE_HASH_SIZE 16 @@ -53,9 +53,7 @@ extern struct tm_binds d_tmb; /*! global dialog message id */ static unsigned int current_dlg_msg_id = 0 ; - -/*! global dialog */ -struct dlg_cell *current_dlg_pointer = NULL ; +static unsigned int current_dlg_msg_pid = 0 ; /*! pending dialog links */ static struct dlg_profile_link *current_pending_linkers = NULL; @@ -67,16 +65,8 @@ static struct dlg_profile_table *profiles = NULL; static struct dlg_profile_table* new_dlg_profile( str *name, unsigned int size, unsigned int has_value); +static sruid_t _dlg_profile_sruid; -struct dlg_cell *get_current_dlg_pointer(void) -{ - return current_dlg_pointer; -} - -void reset_current_dlg_pointer(void) -{ - current_dlg_pointer = NULL; -} /*! * \brief Add profile definitions to the global list @@ -311,18 +301,26 @@ void destroy_linkers(struct dlg_profile_link *linker) */ int profile_cleanup( struct sip_msg *msg, unsigned int flags, void *param ) { - current_dlg_msg_id = 0; - if (current_dlg_pointer) { - unref_dlg( current_dlg_pointer, 1); - current_dlg_pointer = NULL; - } - if (current_pending_linkers) { - destroy_linkers(current_pending_linkers); - current_pending_linkers = NULL; - } + dlg_cell_t *dlg; + + current_dlg_msg_id = 0; + current_dlg_msg_pid = 0; + dlg = dlg_get_ctx_dialog(); + if (dlg != NULL) { + if (dlg->dflags & DLG_FLAG_TM) { + unref_dlg(dlg, 1); + } else { + /* dialog didn't make it to tm */ + unref_dlg(dlg, 2); + } + } + if (current_pending_linkers) { + destroy_linkers(current_pending_linkers); + current_pending_linkers = NULL; + } - /* need to return non-zero - 0 will break the exec of the request */ - return 1; + /* need to return non-zero - 0 will break the exec of the request */ + return 1; } @@ -345,36 +343,6 @@ struct dlg_cell* get_dialog_from_tm(struct cell *t) return NULL; } -/*! - * \brief Get the current dialog for a message, if exists - * \param msg SIP message - * \return NULL if called in REQUEST_ROUTE, pointer to dialog ctx otherwise - */ -struct dlg_cell *get_current_dialog(struct sip_msg *msg) -{ - - if (is_route_type(REQUEST_ROUTE|BRANCH_ROUTE)) { - LM_DBG("Get Current Dialog: Route type is REQUEST ROUTE or BRANCH ROUTE"); - LM_DBG("Get Current Dialog: SIP Method - %.*s", msg->first_line.u.request.method.len, msg->first_line.u.request.method.s); - /* use the per-process static holder */ - if (msg->id==current_dlg_msg_id){ - LM_DBG("Message Id [%i] equals current dlg msg id [%i] - returning current dlg pointer", msg->id, current_dlg_msg_id); - return current_dlg_pointer; - } - LM_DBG("Message Id [%i] not equal to current point dlg id [%i] - returning null", msg->id, current_dlg_msg_id); - current_dlg_pointer = NULL; - current_dlg_msg_id = msg->id; - destroy_linkers(current_pending_linkers); - current_pending_linkers = NULL; - return NULL; - } else { - /* use current transaction to get dialog */ - LM_DBG("Route type is not REQUEST ROUTE or brancg route - getting from tm"); - return get_dialog_from_tm(d_tmb.t_gett()); - } -} - - /*! * \brief Calculate the hash profile from a dialog * \see core_hash @@ -449,34 +417,33 @@ static void link_dlg_profile(struct dlg_profile_link *linker, struct dlg_cell *d * \param msg SIP message * \param dlg dialog cell */ -void set_current_dialog(struct sip_msg *msg, struct dlg_cell *dlg) +void set_current_dialog(sip_msg_t *msg, dlg_cell_t *dlg) { - struct dlg_profile_link *linker; - struct dlg_profile_link *tlinker; - - /* if linkers are not from current request, just discard them */ - if (msg->id!=current_dlg_msg_id) { - current_dlg_msg_id = msg->id; - destroy_linkers(current_pending_linkers); - } else { - /* add the linker, one be one, to the dialog */ - linker = current_pending_linkers; - while (linker) { - tlinker = linker; - linker = linker->next; - /* process tlinker */ - tlinker->next = NULL; - link_dlg_profile( tlinker, dlg); - } - } - current_pending_linkers = NULL; - current_dlg_pointer = dlg; - - /* do not increase reference counter here, let caller handle it - * (yes, this is somewhat ugly) */ + struct dlg_profile_link *linker; + struct dlg_profile_link *tlinker; + + LM_DBG("setting current dialog [%u:%u]\n", dlg->h_entry, dlg->h_id); + /* if linkers are not from current request, just discard them */ + if (msg->id!=current_dlg_msg_id || msg->pid!=current_dlg_msg_pid) { + current_dlg_msg_id = msg->id; + current_dlg_msg_pid = msg->pid; + destroy_linkers(current_pending_linkers); + } else { + /* add the linker, one by one, to the dialog */ + linker = current_pending_linkers; + while (linker) { + tlinker = linker; + linker = linker->next; + /* process tlinker */ + tlinker->next = NULL; + link_dlg_profile( tlinker, dlg); + } + } + current_pending_linkers = NULL; } + /*! * \brief Set a dialog profile * \param msg SIP message @@ -486,11 +453,11 @@ void set_current_dialog(struct sip_msg *msg, struct dlg_cell *dlg) */ int set_dlg_profile(struct sip_msg *msg, str *value, struct dlg_profile_table *profile) { - struct dlg_cell *dlg; - struct dlg_profile_link *linker; + dlg_cell_t *dlg = NULL; + dlg_profile_link_t *linker; /* get current dialog */ - dlg = get_current_dialog(msg); + dlg = dlg_get_msg_dialog(msg); if (dlg==NULL && !is_route_type(REQUEST_ROUTE)) { LM_CRIT("BUG - dialog not found in a non REQUEST route (%d)\n", @@ -503,12 +470,13 @@ int set_dlg_profile(struct sip_msg *msg, str *value, struct dlg_profile_table *p sizeof(struct dlg_profile_link) + (profile->has_value?value->len:0) ); if (linker==NULL) { LM_ERR("no more shm memory\n"); - return -1; + goto error; } memset(linker, 0, sizeof(struct dlg_profile_link)); - /* set backpointer to profile */ + /* set backpointers to profile and linker (itself) */ linker->profile = profile; + linker->hash_linker.linker = linker; /* set the value */ if (profile->has_value) { @@ -516,17 +484,37 @@ int set_dlg_profile(struct sip_msg *msg, str *value, struct dlg_profile_table *p memcpy( linker->hash_linker.value.s, value->s, value->len); linker->hash_linker.value.len = value->len; } + sruid_next_safe(&_dlg_profile_sruid); + strcpy(linker->hash_linker.puid, _dlg_profile_sruid.uid.s); + linker->hash_linker.puid_len = _dlg_profile_sruid.uid.len; if (dlg!=NULL) { /* add linker directly to the dialog and profile */ link_dlg_profile( linker, dlg); } else { + /* if existing linkers are not from current request, just discard them */ + if (msg->id!=current_dlg_msg_id || msg->pid!=current_dlg_msg_pid) { + current_dlg_msg_id = msg->id; + current_dlg_msg_pid = msg->pid; + destroy_linkers(current_pending_linkers); + current_pending_linkers = NULL; + } /* no dialog yet -> set linker as pending */ + if (msg->id!=current_dlg_msg_id || msg->pid!=current_dlg_msg_pid) { + current_dlg_msg_id = msg->id; + current_dlg_msg_pid = msg->pid; + destroy_linkers(current_pending_linkers); + } + linker->next = current_pending_linkers; current_pending_linkers = linker; } + dlg_release(dlg); return 0; +error: + dlg_release(dlg); + return -1; } @@ -540,16 +528,21 @@ int set_dlg_profile(struct sip_msg *msg, str *value, struct dlg_profile_table *p int unset_dlg_profile(struct sip_msg *msg, str *value, struct dlg_profile_table *profile) { - struct dlg_cell *dlg; - struct dlg_profile_link *linker; - struct dlg_profile_link *linker_prev; - struct dlg_entry *d_entry; + dlg_cell_t *dlg; + dlg_profile_link_t *linker; + dlg_profile_link_t *linker_prev; + dlg_entry_t *d_entry; + + if (is_route_type(REQUEST_ROUTE)) { + LM_ERR("dialog delete profile cannot be used in request route\n"); + return -1; + } /* get current dialog */ - dlg = get_current_dialog(msg); + dlg = dlg_get_msg_dialog(msg); - if (dlg==NULL || is_route_type(REQUEST_ROUTE)) { - LM_CRIT("BUG - dialog NULL or del_profile used in request route\n"); + if (dlg==NULL) { + LM_WARN("dialog is NULL for delete profile\n"); return -1; } @@ -572,6 +565,7 @@ int unset_dlg_profile(struct sip_msg *msg, str *value, } } dlg_unlock( d_table, d_entry); + dlg_release(dlg); return -1; found: @@ -586,6 +580,7 @@ int unset_dlg_profile(struct sip_msg *msg, str *value, dlg_unlock( d_table, d_entry); /* remove linker from profile table and free it */ destroy_linkers(linker); + dlg_release(dlg); return 1; } @@ -602,10 +597,11 @@ int is_dlg_in_profile(struct sip_msg *msg, struct dlg_profile_table *profile, struct dlg_cell *dlg; struct dlg_profile_link *linker; struct dlg_entry *d_entry; + int ret; LM_DBG("Getting current dialog"); /* get current dialog */ - dlg = get_current_dialog(msg); + dlg = dlg_get_msg_dialog(msg); if (dlg == NULL) { LM_DBG("Error: Current dlg is null"); @@ -614,6 +610,7 @@ int is_dlg_in_profile(struct sip_msg *msg, struct dlg_profile_table *profile, } LM_DBG("Current dlg found"); + ret = -1; /* check the dialog linkers */ d_entry = &d_table->entries[dlg->h_entry]; dlg_lock( d_table, d_entry); @@ -624,13 +621,15 @@ int is_dlg_in_profile(struct sip_msg *msg, struct dlg_profile_table *profile, if (profile->has_value == 0) { LM_DBG("Profile has value is zero returning true"); dlg_unlock( d_table, d_entry); - return 1; + ret = 1; + goto done; } else if (value && value->len == linker->hash_linker.value.len && memcmp(value->s, linker->hash_linker.value.s, value->len) == 0) { LM_DBG("Profile has value equal to passed value returning true"); dlg_unlock( d_table, d_entry); - return 1; + ret = 1; + goto done; } /* allow further search - maybe the dialog is inserted twice in * the same profile, but with different values -bogdan @@ -638,7 +637,10 @@ int is_dlg_in_profile(struct sip_msg *msg, struct dlg_profile_table *profile, } } dlg_unlock( d_table, d_entry); - return -1; + +done: + dlg_release(dlg); + return ret; } @@ -688,9 +690,15 @@ unsigned int get_profile_size(struct dlg_profile_table *profile, str *value) * Determine if message is in a dialog currently being tracked */ int is_known_dlg(struct sip_msg *msg) { - if(get_current_dialog(msg) == NULL) + dlg_cell_t *dlg; + + dlg = dlg_get_msg_dialog(msg); + + if(dlg == NULL) return -1; + dlg_release(dlg); + return 1; } diff --git a/modules/dialog_ng/dlg_profile.h b/modules/dialog_ng/dlg_profile.h index 4fc0cbb3137..12f51e2c2a1 100644 --- a/modules/dialog_ng/dlg_profile.h +++ b/modules/dialog_ng/dlg_profile.h @@ -34,6 +34,8 @@ #include "../../locking.h" #include "../../str.h" #include "../../modules/tm/h_table.h" +#include "../../lib/srutils/srjson.h" +#include "../../lib/srutils/sruid.h" @@ -46,21 +48,26 @@ /*! dialog profile hash list */ -struct dlg_profile_hash { +typedef struct dlg_profile_hash { str value; /*!< hash value */ struct dlg_cell *dlg; /*!< dialog cell */ + char puid[SRUID_SIZE]; + int puid_len; + time_t expires; + int flags; + struct dlg_profile_link *linker; struct dlg_profile_hash *next; struct dlg_profile_hash *prev; unsigned int hash; /*!< position in the hash table */ -}; +} dlg_profile_hash_t; /*! list with links to dialog profiles */ -struct dlg_profile_link { +typedef struct dlg_profile_link { struct dlg_profile_hash hash_linker; struct dlg_profile_link *next; struct dlg_profile_table *profile; -}; +} dlg_profile_link_t; /*! dialog profile entry */ @@ -80,15 +87,8 @@ struct dlg_profile_table { struct dlg_profile_table *next; }; - -struct dlg_cell *get_current_dlg_pointer(void); - -void reset_current_dlg_pointer(void); - struct dlg_cell* get_dialog_from_tm(struct cell *t); -struct dlg_cell *get_current_dialog(struct sip_msg *msg); - /*! * \brief Add profile definitions to the global list * \see new_dlg_profile diff --git a/modules/dialog_ng/dlg_req_within.c b/modules/dialog_ng/dlg_req_within.c index 06ce0cbfccf..68fdb2a727e 100644 --- a/modules/dialog_ng/dlg_req_within.c +++ b/modules/dialog_ng/dlg_req_within.c @@ -94,7 +94,7 @@ dlg_t * build_dlg_t(struct dlg_cell * cell, int dir) { memset(td, 0, sizeof (dlg_t)); if (dir == DLG_CALLER_LEG) { - cseq = dlg_out->callee_cseq; + cseq = cell->first_req_cseq; route_set = cell->caller_route_set; contact = cell->caller_contact; td->rem_uri = cell->from_uri; @@ -103,7 +103,7 @@ dlg_t * build_dlg_t(struct dlg_cell * cell, int dir) { td->id.loc_tag = dlg_out->to_tag; td->send_sock = cell->caller_bind_addr; } else { - cseq = dlg_out->caller_cseq; + cseq = dlg_out->callee_cseq; route_set = dlg_out->callee_route_set; contact = dlg_out->callee_contact; td->rem_uri = dlg_out->to_uri; @@ -122,8 +122,6 @@ dlg_t * build_dlg_t(struct dlg_cell * cell, int dir) { td->loc_seq.value = loc_seq; td->loc_seq.is_set = 1; - LM_DBG("CSeq is '%.*s' (%i)\n", cseq.len, cseq.s, loc_seq); - /*route set*/ if (route_set.s && route_set.len) { diff --git a/modules/dialog_ng/dlg_var.c b/modules/dialog_ng/dlg_var.c index fa7e04ac8b9..323d7efc4f2 100644 --- a/modules/dialog_ng/dlg_var.c +++ b/modules/dialog_ng/dlg_var.c @@ -22,10 +22,12 @@ */ #include "../../route.h" +#include "../../pvapi.h" #include "dlg_var.h" #include "dlg_hash.h" #include "dlg_profile.h" +#include "dlg_handlers.h" #include "dlg_db_handler.h" dlg_ctx_t _dlg_ctx; @@ -103,7 +105,7 @@ struct dlg_var * get_local_varlist_pointer(struct sip_msg *msg, int clear_pointe } /* Adds, updates and deletes dialog variables */ -int set_dlg_variable_unsafe(struct dlg_cell *dlg, str *key, str *val, int new) +int set_dlg_variable_unsafe(struct dlg_cell *dlg, str *key, str *val) { struct dlg_var * var = NULL; struct dlg_var * it; @@ -149,10 +151,9 @@ int set_dlg_variable_unsafe(struct dlg_cell *dlg, str *key, str *val, int new) } /* not found: */ - if (!var) { - LM_ERR("dialog variable <%.*s> does not exist in variable list\n", key->len, key->s); - return -1; + LM_DBG("dialog variable <%.*s> does not exist in variable list\n", key->len, key->s); + return 1; } /* insert a new one at the beginning of the list */ @@ -283,12 +284,12 @@ int set_dlg_variable(struct dlg_cell *dlg, str *key, str *val) if( !val) { - if (set_dlg_variable_unsafe(dlg, key, NULL, 1)!=0) { + if (set_dlg_variable_unsafe(dlg, key, NULL)!=0) { LM_ERR("failed to delete dialog variable <%.*s>\n", key->len,key->s); goto error; } } else { - if (set_dlg_variable_unsafe(dlg, key, val, 1)!=0) { + if (set_dlg_variable_unsafe(dlg, key, val)!=0) { LM_ERR("failed to store dialog values <%.*s>\n",key->len,key->s); goto error; } @@ -311,16 +312,20 @@ int set_dlg_variable(struct dlg_cell *dlg, str *key, str *val) int pv_get_dlg_variable(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { - struct dlg_cell *dlg; + + dlg_cell_t *dlg; str * value; + str spv; - if (param==NULL || param->pvn.type!=PV_NAME_INTSTR || param->pvn.u.isname.type!=AVP_NAME_STR || param->pvn.u.isname.name.s.s==NULL) { + if (param==NULL || param->pvn.type!=PV_NAME_INTSTR + || param->pvn.u.isname.type!=AVP_NAME_STR + || param->pvn.u.isname.name.s.s==NULL) { LM_CRIT("BUG - bad parameters\n"); return -1; } - /* Retrieve the current dialog */ - dlg=get_current_dialog( msg); + /* Retrieve the dialog for current message */ + dlg=dlg_get_msg_dialog( msg); if (dlg) { /* Lock the dialog */ @@ -330,26 +335,51 @@ int pv_get_dlg_variable(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) get_local_varlist_pointer(msg, 0); } + /* dcm: todo - the value should be cloned for safe usage */ value = get_dlg_variable_unsafe(dlg, ¶m->pvn.u.isname.name.s); + spv.s = NULL; + if(value) { + spv.len = pv_get_buffer_size(); + if(spv.lenlen+1) { + LM_ERR("pv buffer too small (%d) - needed %d\n", spv.len, value->len); + } else { + spv.s = pv_get_buffer(); + strncpy(spv.s, value->s, value->len); + spv.len = value->len; + spv.s[spv.len] = '\0'; + } + } + print_lists(dlg); /* unlock dialog */ - if (dlg) dlg_unlock(d_table, &(d_table->entries[dlg->h_entry])); + if (dlg) { + dlg_unlock(d_table, &(d_table->entries[dlg->h_entry])); + dlg_release(dlg); + } - if (value) - return pv_get_strval(msg, param, res, value); + if (spv.s) + return pv_get_strval(msg, param, res, &spv); - return 0; + return pv_get_null(msg, param, res); } int pv_set_dlg_variable(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val) { - struct dlg_cell *dlg; + dlg_cell_t *dlg = NULL; + int ret = -1; + + if (param==NULL || param->pvn.type!=PV_NAME_INTSTR + || param->pvn.u.isname.type!=AVP_NAME_STR + || param->pvn.u.isname.name.s.s==NULL ) { + LM_CRIT("BUG - bad parameters\n"); + goto error; + } - /* Retrieve the current dialog */ - dlg=get_current_dialog( msg); + /* Retrieve the dialog for current message */ + dlg=dlg_get_msg_dialog( msg); if (dlg) { /* Lock the dialog */ @@ -359,18 +389,16 @@ int pv_set_dlg_variable(struct sip_msg* msg, pv_param_t *param, int op, pv_value get_local_varlist_pointer(msg, 0); } - if (param==NULL || param->pvn.type!=PV_NAME_INTSTR || param->pvn.u.isname.type!=AVP_NAME_STR || param->pvn.u.isname.name.s.s==NULL ) { - LM_CRIT("BUG - bad parameters\n"); - return -1; - } - if (val==NULL || val->flags&(PV_VAL_NONE|PV_VAL_NULL|PV_VAL_EMPTY)) { /* if NULL, remove the value */ - if (set_dlg_variable_unsafe(dlg, ¶m->pvn.u.isname.name.s, NULL, 1)!=0) { - LM_ERR("failed to delete dialog variable <%.*s>\n", param->pvn.u.isname.name.s.len,param->pvn.u.isname.name.s.s); + ret = set_dlg_variable_unsafe(dlg, ¶m->pvn.u.isname.name.s, NULL); + if(ret!= 0) { /* unlock dialog */ - if (dlg) dlg_unlock(d_table, &(d_table->entries[dlg->h_entry])); - return -1; + if (dlg) { + dlg_unlock(d_table, &(d_table->entries[dlg->h_entry])); + dlg_release(dlg); + } + return ret; } } else { /* if value, must be string */ @@ -378,19 +406,19 @@ int pv_set_dlg_variable(struct sip_msg* msg, pv_param_t *param, int op, pv_value LM_ERR("non-string values are not supported\n"); /* unlock dialog */ if (dlg) dlg_unlock(d_table, &(d_table->entries[dlg->h_entry])); - return -1; + goto error; } - if (set_dlg_variable_unsafe(dlg, ¶m->pvn.u.isname.name.s, &val->rs, 1)!=0) { - LM_ERR("failed to store dialog values <%.*s>\n",param->pvn.u.isname.name.s.len,param->pvn.u.isname.name.s.s); + ret = set_dlg_variable_unsafe(dlg, ¶m->pvn.u.isname.name.s, &val->rs); + if(ret!= 0) { /* unlock dialog */ if (dlg) dlg_unlock(d_table, &(d_table->entries[dlg->h_entry])); - return -1; + goto error; } } /* unlock dialog */ if (dlg) { - dlg->dflags &= DLG_FLAG_CHANGED_VARS; + dlg->dflags |= DLG_FLAG_CHANGED_VARS; dlg_unlock(d_table, &(d_table->entries[dlg->h_entry])); if ( dlg_db_mode==DB_MODE_REALTIME ) update_dialog_dbinfo(dlg); @@ -398,7 +426,11 @@ int pv_set_dlg_variable(struct sip_msg* msg, pv_param_t *param, int op, pv_value } print_lists(dlg); + dlg_release(dlg); return 0; +error: + dlg_release(dlg); + return -1; } int pv_get_dlg_ctx(struct sip_msg *msg, pv_param_t *param, @@ -749,14 +781,21 @@ int pv_parse_dlg_name(pv_spec_p sp, str *in) return -1; } -void dlg_set_ctx_dialog(struct dlg_cell *dlg) +void dlg_set_ctx_iuid(dlg_cell_t *dlg) +{ + _dlg_ctx.iuid.h_entry = dlg->h_entry; + _dlg_ctx.iuid.h_id = dlg->h_id; +} + +void dlg_reset_ctx_iuid(void) { - _dlg_ctx.dlg = dlg; + _dlg_ctx.iuid.h_entry = 0; + _dlg_ctx.iuid.h_id = 0; } -struct dlg_cell* dlg_get_ctx_dialog(void) +dlg_cell_t* dlg_get_ctx_dialog(void) { - return _dlg_ctx.dlg; + return dlg_get_by_iuid(&_dlg_ctx.iuid); } dlg_ctx_t* dlg_get_dlg_ctx(void) diff --git a/modules/dialog_ng/dlg_var.h b/modules/dialog_ng/dlg_var.h index 270b6050828..d9f72356715 100644 --- a/modules/dialog_ng/dlg_var.h +++ b/modules/dialog_ng/dlg_var.h @@ -30,23 +30,28 @@ #define DLG_TOROUTE_SIZE 32 /*! dialog context */ typedef struct _dlg_ctx { - int on; - unsigned int flags; - int to_route; - char to_route_name[DLG_TOROUTE_SIZE]; - int to_bye; - int timeout; - struct dlg_cell *dlg; - int set; - unsigned int dir; + int on; + unsigned int flags; + unsigned int iflags; + int to_route; + char to_route_name[DLG_TOROUTE_SIZE]; + int to_bye; + int timeout; + struct dlg_cell *dlg; + dlg_iuid_t iuid; + int cpid; + int set; + unsigned int dir; + int t; /* set to 1 if tm req in callback executed */ + int expect_t; /* set to 1 if expects that t is set after config */ } dlg_ctx_t; /* A dialog-variable */ struct dlg_var { - str key; - str value; - unsigned int vflags; /*!< internal variable flags */ - struct dlg_var *next; + str key; + str value; + unsigned int vflags; /*!< internal variable flags */ + struct dlg_var *next; }; str * api_get_dlg_variable(str *callid, str *ftag, str *ttag, str *key); @@ -65,24 +70,25 @@ int pv_set_dlg_variable(struct sip_msg* msg, pv_param_t *param, int op, pv_value struct dlg_var * get_local_varlist_pointer(struct sip_msg *msg, int clear_pointer); /* Adds, updates and deletes dialog variables */ -int set_dlg_variable_unsafe(struct dlg_cell *dlg, str *key, str *val, int new); +int set_dlg_variable_unsafe(struct dlg_cell *dlg, str *key, str *val); extern dlg_ctx_t _dlg_ctx; -int pv_get_dlg_ctx(struct sip_msg *msg, pv_param_t *param, - pv_value_t *res); +int pv_get_dlg_ctx(struct sip_msg *msg, pv_param_t *param, + pv_value_t *res); int pv_set_dlg_ctx(struct sip_msg* msg, pv_param_t *param, - int op, pv_value_t *val); + int op, pv_value_t *val); int pv_parse_dlg_ctx_name(pv_spec_p sp, str *in); -int pv_get_dlg(struct sip_msg *msg, pv_param_t *param, - pv_value_t *res); +int pv_get_dlg(struct sip_msg *msg, pv_param_t *param, + pv_value_t *res); int pv_parse_dlg_name(pv_spec_p sp, str *in); int dlg_cfg_cb(struct sip_msg *foo, unsigned int flags, void *bar); -void dlg_set_ctx_dialog(struct dlg_cell *dlg); -struct dlg_cell* dlg_get_ctx_dialog(void); +void dlg_set_ctx_iuid(dlg_cell_t *dlg); +void dlg_reset_ctx_iuid(void); +dlg_cell_t* dlg_get_ctx_dialog(void); dlg_ctx_t* dlg_get_dlg_ctx(void); diff --git a/modules/ims_charging/Makefile b/modules/ims_charging/Makefile index 988e6d6d4af..035cd68be84 100644 --- a/modules/ims_charging/Makefile +++ b/modules/ims_charging/Makefile @@ -14,6 +14,7 @@ SERLIBPATH=../../lib SER_LIBS+=$(SERLIBPATH)/kcore/kcore SER_LIBS+=$(SERLIBPATH)/ims/kamailio_ims SER_LIBS+=$(SERLIBPATH)/srdb1/srdb1 +LIBS+=-lm ifneq ($(OS),darwin) LIBS += -lrt diff --git a/modules/ims_charging/Ro_data.c b/modules/ims_charging/Ro_data.c index c15ba9f42a4..801a358a269 100644 --- a/modules/ims_charging/Ro_data.c +++ b/modules/ims_charging/Ro_data.c @@ -124,7 +124,7 @@ ims_information_t * new_ims_information(event_type_t * event_type, time_stamps_t if (outgoing_trunk_id && outgoing_trunk_id->s) str_dup_ptr(x->outgoing_trunk_id, *outgoing_trunk_id, pkg); - if (pani && pani->s) { + if (pani && pani->s && (pani->len > 0)) { str_dup_ptr(x->access_network_info, *pani, pkg); } diff --git a/modules/ims_charging/dialog.c b/modules/ims_charging/dialog.c index 6ba4d3ec6f8..b9f77fe53d2 100644 --- a/modules/ims_charging/dialog.c +++ b/modules/ims_charging/dialog.c @@ -10,97 +10,108 @@ struct cdp_binds cdpb; extern usrloc_api_t ul; extern int ro_db_mode; extern char *domain; +extern struct dlg_binds dlgb; -void dlg_reply(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) { - struct sip_msg *reply; - struct ro_session* session = 0; - struct ro_session_entry* ro_session_entry; - time_t now = time(0); - time_t time_since_last_event; +void dlg_callback_received(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) { + LM_DBG("Received dialog callback event [%d]\n", type); + switch (type) { + case DLGCB_CONFIRMED: + dlg_answered(dlg, type, _params); + break; + case DLGCB_TERMINATED: + case DLGCB_FAILED: + case DLGCB_EXPIRED: + dlg_terminated(dlg, type, _params); + break; + default: + LM_WARN("Received unknown dialog callback [%d]\n", type); + } +} - LM_DBG("dlg_reply callback entered\n"); - - if (!_params) { - return; - } - - reply = _params->rpl; - if (!reply) { - LM_WARN("dlg_reply has no SIP reply associated.\n"); - return; - } +void dlg_answered(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) { + struct sip_msg *reply; + struct ro_session* session = 0; + struct ro_session_entry* ro_session_entry; + time_t now = get_current_time_micro(); + time_t time_since_last_event; - if (reply != FAKED_REPLY && reply->REPLY_STATUS == 200) { - LM_DBG("Call answered on dlg [%p] - search for Ro Session and initialise timers.\n", dlg); - - session = (struct ro_session*)*_params->param; - if (!session) { - LM_ERR("Ro Session object is NULL...... aborting\n"); - return; - } + LM_DBG("dlg_reply callback entered\n"); - ro_session_entry = &(ro_session_table->entries[session->h_entry]); + if (!_params) { + return; + } - ro_session_lock(ro_session_table, ro_session_entry); + session = (struct ro_session*) *_params->param; + if (!session) { + LM_ERR("Ro Session object is NULL...... aborting\n"); + return; + } + + LM_DBG("Call answered on dlg [%p] - search for Ro Session [%p]\n", dlg, session); - if (session->active) { - LM_CRIT("Why the heck am i receiving a double confirmation of the dialog? Ignoring... "); - ro_session_unlock(ro_session_table, ro_session_entry); - return; - } - - time_since_last_event = now - session->last_event_timestamp; - session->start_time = session->last_event_timestamp = now; - session->event_type = answered; - session->active = 1; - + ro_session_entry = &(ro_session_table->entries[session->h_entry]); - /* check to make sure that the validity of the credit is enough for the bundle */ - int ret = 0; - if (session->reserved_secs < (session->valid_for - time_since_last_event)) { - if (session->reserved_secs > ro_timer_buffer/*TIMEOUTBUFFER*/) { - ret = insert_ro_timer(&session->ro_tl, session->reserved_secs - ro_timer_buffer); //subtract 5 seconds so as to get more credit before we run out - } else { - ret = insert_ro_timer(&session->ro_tl, session->reserved_secs); - } - } else { - if (session->valid_for > ro_timer_buffer) { - ret = insert_ro_timer(&session->ro_tl, session->valid_for - ro_timer_buffer); //subtract 5 seconds so as to get more credit before we run out - } else { - ret = insert_ro_timer(&session->ro_tl, session->valid_for); - } - } + ro_session_lock(ro_session_table, ro_session_entry); + if (session->active) { + LM_CRIT("Why the heck am i receiving a double confirmation of the dialog? Ignoring... "); + ro_session_unlock(ro_session_table, ro_session_entry); + return; + } else if (session->active < 0) { //session has already been terminated - we can't reactivate... + LM_WARN("Received an answer after terminating dialog.... ignoring\n"); + ro_session_unlock(ro_session_table, ro_session_entry); + return; + } - if (ret != 0) { - LM_CRIT("unable to insert timer for Ro Session [%.*s]\n", session->ro_session_id.len, session->ro_session_id.s); - } else { - ref_ro_session_unsafe(session, 1); // lock already acquired - } + time_since_last_event = (now - session->last_event_timestamp)/1000000; + session->start_time = session->last_event_timestamp = now; + session->event_type = answered; + session->active = 1; - if (ro_db_mode == DB_MODE_REALTIME) { - session->flags |= RO_SESSION_FLAG_CHANGED; - if (update_ro_dbinfo_unsafe(session) != 0) { - LM_ERR("Failed to update ro_session in database... continuing\n"); - }; - } - - ro_session_unlock(ro_session_table, ro_session_entry); - AAASession* cdp_session = cdpb.AAAGetCCAccSession(session->ro_session_id); - if (!cdp_session) { - LM_ERR("could not find find CC App CDP session for session [%.*s]\n", session->ro_session_id.len, session->ro_session_id.s); -// ro_session_unlock(ro_session_table, ro_session_entry); - return; - } - -// ro_session_unlock(ro_session_table, ro_session_entry); + /* check to make sure that the validity of the credit is enough for the bundle */ + int ret = 0; + LM_DBG("we were granted %d seconds (valud for %d seconds) and it's been %d seconds since we requested\n", (int)session->reserved_secs, (int)session->valid_for, (int)time_since_last_event); + if (session->reserved_secs < (session->valid_for - time_since_last_event)) { + if (session->reserved_secs > ro_timer_buffer/*TIMEOUTBUFFER*/) { + ret = insert_ro_timer(&session->ro_tl, session->reserved_secs - ro_timer_buffer); //subtract 5 seconds so as to get more credit before we run out + } else { + ret = insert_ro_timer(&session->ro_tl, session->reserved_secs); + } + } else { + if (session->valid_for > ro_timer_buffer) { + ret = insert_ro_timer(&session->ro_tl, session->valid_for - ro_timer_buffer); //subtract 5 seconds so as to get more credit before we run out + } else { + ret = insert_ro_timer(&session->ro_tl, session->valid_for); + } + } + + + if (ret != 0) { + LM_CRIT("unable to insert timer for Ro Session [%.*s]\n", session->ro_session_id.len, session->ro_session_id.s); + } else { + ref_ro_session_unsafe(session, 1); // lock already acquired + } + + if (ro_db_mode == DB_MODE_REALTIME) { + session->flags |= RO_SESSION_FLAG_CHANGED; + if (update_ro_dbinfo_unsafe(session) != 0) { + LM_ERR("Failed to update ro_session in database... continuing\n"); + }; + } + + ro_session_unlock(ro_session_table, ro_session_entry); + + AAASession* cdp_session = cdpb.AAAGetCCAccSession(session->ro_session_id); + if (!cdp_session) { + LM_ERR("could not find find CC App CDP session for session [%.*s]\n", session->ro_session_id.len, session->ro_session_id.s); + return; + } + + cdpb.AAAStartChargingCCAccSession(cdp_session); + cdpb.AAASessionsUnlock(cdp_session->hash); + - cdpb.AAAStartChargingCCAccSession(cdp_session); - cdpb.AAASessionsUnlock(cdp_session->hash); - -// unref_ro_session(session, 1); DONT need this anymore because we don't do lookup so no addition to ref counter - } } void dlg_terminated(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) { @@ -155,20 +166,18 @@ void dlg_terminated(struct dlg_cell *dlg, int type, struct dlg_cb_params *_param int ret = remove_ro_timer(&ro_session->ro_tl); if (ret < 0) { LM_CRIT("unable to unlink the timer on ro_session %p [%.*s]\n", - ro_session, ro_session->cdp_session_id.len, ro_session->cdp_session_id.s); + ro_session, ro_session->ro_session_id.len, ro_session->ro_session_id.s); } else if (ret > 0) { LM_WARN("inconsistent ro timer data on ro_session %p [%.*s]\n", - ro_session, ro_session->cdp_session_id.len, ro_session->cdp_session_id.s); + ro_session, ro_session->ro_session_id.len, ro_session->ro_session_id.s); } else { unref++; } } - if (ro_session->event_type != unknown) { - LM_DBG("Sending CCR STOP on Ro_Session [%p], as it is in '%d' state\n", ro_session, ro_session->event_type); - send_ccr_stop(ro_session); - ro_session->active = 0; - } + LM_DBG("Sending CCR STOP on Ro_Session [%p]\n", ro_session); + send_ccr_stop(ro_session); + ro_session->active = -1; //deleted.... terminated .... if (ro_db_mode == DB_MODE_REALTIME) { ro_session->flags |= RO_SESSION_FLAG_DELETED; @@ -216,7 +225,7 @@ void remove_dlg_data_from_contact(struct dlg_cell *dlg, int type, struct dlg_cb_ if (ul.get_impurecord(domain_t, &impu_data->identity, &implicit_impurecord) != 0) { LM_DBG("usrloc does not have imprecord for implicity IMPU, ignore\n"); }else { - if (ul.get_ucontact(implicit_impurecord, &impu_data->contact, &callid, &path, 0/*cseq*/, &ucontact) != 0) { //contact does not exist + if (ul.get_ucontact(&impu_data->contact, &callid, &path, 0/*cseq*/, &ucontact) != 0) { //contact does not exist LM_DBG("This contact: <%.*s> is not in usrloc, ignore - NOTE: You need S-CSCF usrloc set to match_mode CONTACT_PORT_IP_ONLY\n", impu_data->contact.len, impu_data->contact.s); } else {//contact exists so add dialog data to it ul.remove_dialog_data_from_contact(ucontact, dlg->h_entry, dlg->h_id); @@ -226,6 +235,9 @@ void remove_dlg_data_from_contact(struct dlg_cell *dlg, int type, struct dlg_cb_ ul.unlock_udomain(domain_t, &impu_data->identity); free_impu_data(impu_data); } + + //we referenced the dialog when we registered for callbacks on it... + dlgb.release_dlg(dlg); } void add_dlg_data_to_contact(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) { @@ -258,7 +270,7 @@ void add_dlg_data_to_contact(struct dlg_cell *dlg, int type, struct dlg_cb_param if (ul.get_impurecord(domain_t, &impu_data->identity, &implicit_impurecord) != 0) { LM_DBG("usrloc does not have imprecord for implicity IMPU, ignore\n"); }else { - if (ul.get_ucontact(implicit_impurecord, &impu_data->contact, &callid, &path, 0/*cseq*/, &ucontact) != 0) { //contact does not exist + if (ul.get_ucontact(&impu_data->contact, &callid, &path, 0/*cseq*/, &ucontact) != 0) { //contact does not exist LM_DBG("This contact: <%.*s> is not in usrloc, ignore - NOTE: You need S-CSCF usrloc set to match_mode CONTACT_PORT_IP_ONLY\n", impu_data->contact.len, impu_data->contact.s); } else {//contact exists so add dialog data to it ul.add_dialog_data_to_contact(ucontact, dlg->h_entry, dlg->h_id); diff --git a/modules/ims_charging/dialog.h b/modules/ims_charging/dialog.h index 7c537181ab4..e64b4d99e37 100644 --- a/modules/ims_charging/dialog.h +++ b/modules/ims_charging/dialog.h @@ -8,8 +8,9 @@ extern int ro_timer_buffer; +void dlg_callback_received(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params); void dlg_terminated(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params); -void dlg_reply(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params); +void dlg_answered(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params); void add_dlg_data_to_contact(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params); void remove_dlg_data_from_contact(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params); diff --git a/modules/ims_charging/ims_ro.c b/modules/ims_charging/ims_ro.c index d2ceb0203ad..2d6c84af1aa 100644 --- a/modules/ims_charging/ims_ro.c +++ b/modules/ims_charging/ims_ro.c @@ -1,5 +1,6 @@ #include "mod.h" +#include #include "../../parser/msg_parser.h" #include "../../parser/parse_uri.h" #include "../../sr_module.h" @@ -67,11 +68,11 @@ static int get_mac_avp_value(struct sip_msg *msg, str *value); void credit_control_session_callback(int event, void* session) { switch (event) { - case AUTH_EV_SESSION_DROP: - LM_DBG("Received notification of CC App session drop - we must free the generic data\n"); - break; - default: - LM_DBG("Received unhandled event [%d] in credit control session callback from CDP\n", event); + case AUTH_EV_SESSION_DROP: + LM_DBG("Received notification of CC App session drop - we must free the generic data\n"); + break; + default: + LM_DBG("Received unhandled event [%d] in credit control session callback from CDP\n", event); } } @@ -85,8 +86,8 @@ struct sip_msg * trans_get_request_from_current_reply() { struct cell *t; t = tmb.t_gett(); if (!t || t == (void*) - 1) { - LM_ERR("trans_get_request_from_current_reply: Reply without transaction\n"); - return 0; + LM_ERR("trans_get_request_from_current_reply: Reply without transaction\n"); + return 0; } if (t) return t->uas.request; else return 0; @@ -105,24 +106,24 @@ struct sip_msg * trans_get_request_from_current_reply() { * @returns 1 on success or 0 on failure */ static inline int Ro_add_avp_list(AAA_AVP_LIST *list, char *d, int len, int avp_code, - int flags, int vendorid, int data_do, const char *func) { + int flags, int vendorid, int data_do, const char *func) { AAA_AVP *avp; if (vendorid != 0) flags |= AAA_AVP_FLAG_VENDOR_SPECIFIC; avp = cdpb.AAACreateAVP(avp_code, flags, vendorid, d, len, data_do); if (!avp) { - LM_ERR("%s: Failed creating avp\n", func); - return 0; + LM_ERR("%s: Failed creating avp\n", func); + return 0; } if (list->tail) { - avp->prev = list->tail; - avp->next = 0; - list->tail->next = avp; - list->tail = avp; + avp->prev = list->tail; + avp->next = 0; + list->tail->next = avp; + list->tail = avp; } else { - list->head = avp; - list->tail = avp; - avp->next = 0; - avp->prev = 0; + list->head = avp; + list->tail = avp; + avp->next = 0; + avp->prev = 0; } return 1; @@ -200,20 +201,18 @@ inline int Ro_add_multiple_service_credit_Control_stop(AAAMessage *msg, int used /* if we must Used-Service-Unit */ if (used_unit >= 0) { - set_4bytes(x, used_unit); - Ro_add_avp_list(&used_list, x, 4, AVP_CC_Time, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__); - used_group = cdpb.AAAGroupAVPS(used_list); - cdpb.AAAFreeAVPList(&used_list); - Ro_add_avp_list(&mscc_list, used_group.s, used_group.len, AVP_Used_Service_Unit, AAA_AVP_FLAG_MANDATORY, 0, AVP_FREE_DATA, __FUNCTION__); + set_4bytes(x, used_unit); + Ro_add_avp_list(&used_list, x, 4, AVP_CC_Time, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__); + used_group = cdpb.AAAGroupAVPS(used_list); + cdpb.AAAFreeAVPList(&used_list); + Ro_add_avp_list(&mscc_list, used_group.s, used_group.len, AVP_Used_Service_Unit, AAA_AVP_FLAG_MANDATORY, 0, AVP_FREE_DATA, __FUNCTION__); } set_4bytes(x, active_service_identifier); Ro_add_avp_list(&mscc_list, x, 4, AVP_Service_Identifier, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__); - if (active_rating_group >= 0) { - set_4bytes(x, active_rating_group); - Ro_add_avp_list(&mscc_list, x, 4, AVP_Rating_Group, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__); - } + set_4bytes(x, active_rating_group); + Ro_add_avp_list(&mscc_list, x, 4, AVP_Rating_Group, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__); used_group = cdpb.AAAGroupAVPS(mscc_list); cdpb.AAAFreeAVPList(&mscc_list); @@ -247,18 +246,16 @@ inline int Ro_add_multiple_service_credit_Control(AAAMessage *msg, unsigned int set_4bytes(x, active_service_identifier); Ro_add_avp_list(&mscc_list, x, 4, AVP_Service_Identifier, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__); - if (active_rating_group >= 0) { - set_4bytes(x, active_rating_group); - Ro_add_avp_list(&mscc_list, x, 4, AVP_Rating_Group, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__); - } + set_4bytes(x, active_rating_group); + Ro_add_avp_list(&mscc_list, x, 4, AVP_Rating_Group, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__); /* if we must Used-Service-Unit */ if (used_unit >= 0) { - set_4bytes(x, used_unit); - Ro_add_avp_list(&used_list, x, 4, AVP_CC_Time, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__); - used_group = cdpb.AAAGroupAVPS(used_list); - cdpb.AAAFreeAVPList(&used_list); - Ro_add_avp_list(&mscc_list, used_group.s, used_group.len, AVP_Used_Service_Unit, AAA_AVP_FLAG_MANDATORY, 0, AVP_FREE_DATA, __FUNCTION__); + set_4bytes(x, used_unit); + Ro_add_avp_list(&used_list, x, 4, AVP_CC_Time, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__); + used_group = cdpb.AAAGroupAVPS(used_list); + cdpb.AAAFreeAVPList(&used_list); + Ro_add_avp_list(&mscc_list, used_group.s, used_group.len, AVP_Used_Service_Unit, AAA_AVP_FLAG_MANDATORY, 0, AVP_FREE_DATA, __FUNCTION__); } group = cdpb.AAAGroupAVPS(mscc_list); @@ -308,12 +305,12 @@ inline int Ro_add_vendor_specific_appid(AAAMessage *msg, unsigned int vendor_id, Ro_add_avp_list(&list, x, 4, AVP_Vendor_Id, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__); if (auth_id) { - set_4bytes(x, auth_id); - Ro_add_avp_list(&list, x, 4, AVP_Auth_Application_Id, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__); + set_4bytes(x, auth_id); + Ro_add_avp_list(&list, x, 4, AVP_Auth_Application_Id, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__); } if (acct_id) { - set_4bytes(x, acct_id); - Ro_add_avp_list(&list, x, 4, AVP_Acct_Application_Id, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__); + set_4bytes(x, acct_id); + Ro_add_avp_list(&list, x, 4, AVP_Acct_Application_Id, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__); } group = cdpb.AAAGroupAVPS(list); @@ -324,41 +321,41 @@ inline int Ro_add_vendor_specific_appid(AAAMessage *msg, unsigned int vendor_id, } int get_sip_header_info(struct sip_msg * req, - struct sip_msg * reply, - int32_t * acc_record_type, - str * sip_method, - str * event, uint32_t * expires, - str * callid, str * asserted_id_uri, str * to_uri) { + struct sip_msg * reply, + int32_t * acc_record_type, + str * sip_method, + str * event, uint32_t * expires, + str * callid, str * asserted_id_uri, str * to_uri) { sip_method->s = req->first_line.u.request.method.s; sip_method->len = req->first_line.u.request.method.len; if (strncmp(sip_method->s, "INVITE", 6) == 0) - *acc_record_type = AAA_ACCT_START; + *acc_record_type = AAA_ACCT_START; else if (strncmp(sip_method->s, "BYE", 3) == 0) - *acc_record_type = AAA_ACCT_STOP; + *acc_record_type = AAA_ACCT_STOP; else - *acc_record_type = AAA_ACCT_EVENT; + *acc_record_type = AAA_ACCT_EVENT; *event = cscf_get_event(req); *expires = cscf_get_expires_hdr(req, 0); *callid = cscf_get_call_id(req, NULL); if ((*asserted_id_uri = cscf_get_asserted_identity(req, 0)).len == 0) { - LM_DBG("No P-Asserted-Identity hdr found. Using From hdr"); + LM_DBG("No P-Asserted-Identity hdr found. Using From hdr"); - if (!cscf_get_from_uri(req, asserted_id_uri)) { - LM_ERR("Error assigning P-Asserted-Identity using From hdr"); - goto error; - } + if (!cscf_get_from_uri(req, asserted_id_uri)) { + LM_ERR("Error assigning P-Asserted-Identity using From hdr"); + goto error; + } } *to_uri = req->first_line.u.request.uri; LM_DBG("retrieved sip info : sip_method %.*s acc_record_type %i, event %.*s expires %u " - "call_id %.*s from_uri %.*s to_uri %.*s\n", - sip_method->len, sip_method->s, *acc_record_type, event->len, event->s, *expires, - callid->len, callid->s, asserted_id_uri->len, asserted_id_uri->s, to_uri->len, to_uri->s); + "call_id %.*s from_uri %.*s to_uri %.*s\n", + sip_method->len, sip_method->s, *acc_record_type, event->len, event->s, *expires, + callid->len, callid->s, asserted_id_uri->len, asserted_id_uri->s, to_uri->len, to_uri->s); return 1; error: @@ -369,9 +366,9 @@ int get_ims_charging_info(struct sip_msg *req, struct sip_msg * reply, str * ici LM_DBG("get ims charging info\n"); if (req) - cscf_get_p_charging_vector(req, icid, orig_ioi, term_ioi); + cscf_get_p_charging_vector(req, icid, orig_ioi, term_ioi); if (reply) - cscf_get_p_charging_vector(reply, icid, orig_ioi, term_ioi); + cscf_get_p_charging_vector(reply, icid, orig_ioi, term_ioi); return 1; } @@ -379,9 +376,9 @@ int get_ims_charging_info(struct sip_msg *req, struct sip_msg * reply, str * ici int get_timestamps(struct sip_msg * req, struct sip_msg * reply, time_t * req_timestamp, time_t * reply_timestamp) { if (reply) - *reply_timestamp = time(NULL); + *reply_timestamp = time(NULL); if (req) - *req_timestamp = time(NULL); + *req_timestamp = time(NULL); return 1; } @@ -391,7 +388,7 @@ int get_timestamps(struct sip_msg * req, struct sip_msg * reply, time_t * req_ti */ Ro_CCR_t * dlg_create_ro_session(struct sip_msg * req, struct sip_msg * reply, AAASession ** authp, int dir, str asserted_identity, - str called_asserted_identity, str subscription_id, int subscription_id_type, str* incoming_trunk_id, str *outgoing_trunk_id, str* pani) { + str called_asserted_identity, str subscription_id, int subscription_id_type, str* incoming_trunk_id, str *outgoing_trunk_id, str* pani) { Ro_CCR_t * ro_ccr_data = 0; AAASession * auth = NULL; @@ -410,28 +407,28 @@ Ro_CCR_t * dlg_create_ro_session(struct sip_msg * req, struct sip_msg * reply, A *authp = 0; if (!get_sip_header_info(req, reply, &acc_record_type, &sip_method, &event, &expires, &callid, &from_uri, &to_uri)) - goto error; + goto error; user_name.s = subscription_id.s; user_name.len = subscription_id.len; /* if(!get_ims_charging_info(req, reply, &icid, &orig_ioi, &term_ioi)) - goto error; + goto error; */ LM_DBG("retrieved ims charging info icid:[%.*s] orig_ioi:[%.*s] term_ioi:[%.*s]\n", - icid.len, icid.s, orig_ioi.len, orig_ioi.s, term_ioi.len, term_ioi.s); + icid.len, icid.s, orig_ioi.len, orig_ioi.s, term_ioi.len, term_ioi.s); if (!get_timestamps(req, reply, &req_timestamp, &reply_timestamp)) - goto error; + goto error; if (!(event_type = new_event_type(&sip_method, &event, &expires))) - goto error; + goto error; if (!(time_stamps = new_time_stamps(&req_timestamp, NULL, &reply_timestamp, NULL))) - goto error; + goto error; if (!(ims_info = new_ims_information(event_type, time_stamps, &callid, &callid, &asserted_identity, &called_asserted_identity, &icid, - &orig_ioi, &term_ioi, dir, incoming_trunk_id, outgoing_trunk_id, pani))) - goto error; + &orig_ioi, &term_ioi, dir, incoming_trunk_id, outgoing_trunk_id, pani))) + goto error; event_type = 0; time_stamps = 0; @@ -442,26 +439,26 @@ Ro_CCR_t * dlg_create_ro_session(struct sip_msg * req, struct sip_msg * reply, A ro_ccr_data = new_Ro_CCR(acc_record_type, &user_name, ims_info, &subscr); if (!ro_ccr_data) { - LM_ERR("dlg_create_ro_session: no memory left for generic\n"); - goto out_of_memory; + LM_ERR("dlg_create_ro_session: no memory left for generic\n"); + goto out_of_memory; } ims_info = 0; if (strncmp(req->first_line.u.request.method.s, "INVITE", 6) == 0) { - //create CDP CC Accounting session - auth = cdpb.AAACreateCCAccSession(credit_control_session_callback, 1/*is_session*/, NULL); //must unlock session hash when done - LM_DBG("Created Ro Session with id Session ID [%.*s]\n", auth->id.len, auth->id.s); - //save_session = auth->id; + //create CDP CC Accounting session + auth = cdpb.AAACreateCCAccSession(credit_control_session_callback, 1/*is_session*/, NULL); //must unlock session hash when done + LM_DBG("Created Ro Session with id Session ID [%.*s]\n", auth->id.len, auth->id.s); + //save_session = auth->id; } /*if (strncmp(req->first_line.u.request.method.s, "BYE", 3) == 0) { - auth = cdp_avp->cdp->AAAGetAuthSession(save_session); + auth = cdp_avp->cdp->AAAGetAuthSession(save_session); }*/ if (!auth) { - LM_ERR("unable to create the Ro Session\n"); - goto error; + LM_ERR("unable to create the Ro Session\n"); + goto error; } *authp = auth; @@ -469,7 +466,7 @@ Ro_CCR_t * dlg_create_ro_session(struct sip_msg * req, struct sip_msg * reply, A out_of_memory: error : - time_stamps_free(time_stamps); + time_stamps_free(time_stamps); event_type_free(event_type); ims_information_free(ims_info); Ro_free_CCR(ro_ccr_data); @@ -478,17 +475,17 @@ Ro_CCR_t * dlg_create_ro_session(struct sip_msg * req, struct sip_msg * reply, A } int sip_create_ro_ccr_data(struct sip_msg * msg, int dir, Ro_CCR_t ** ro_ccr_data, AAASession ** auth, str asserted_identity, str called_asserted_identity, - str subscription_id, int subscription_id_type, str* incoming_trunk_id, str* outgoing_trunk_id, str* pani) { + str subscription_id, int subscription_id_type, str* incoming_trunk_id, str* outgoing_trunk_id, str* pani) { if (msg->first_line.type == SIP_REQUEST) { - /*end of session*/ - if (strncmp(msg->first_line.u.request.method.s, "INVITE", 6) == 0) { - if (!(*ro_ccr_data = dlg_create_ro_session(msg, NULL, auth, dir, asserted_identity, called_asserted_identity, subscription_id, - subscription_id_type, incoming_trunk_id, outgoing_trunk_id, pani))) - goto error; - } + /*end of session*/ + if (strncmp(msg->first_line.u.request.method.s, "INVITE", 6) == 0) { + if (!(*ro_ccr_data = dlg_create_ro_session(msg, NULL, auth, dir, asserted_identity, called_asserted_identity, subscription_id, + subscription_id_type, incoming_trunk_id, outgoing_trunk_id, pani))) + goto error; + } } else { - goto error; //We only support Request (INVITE) messages on this interface + goto error; //We only support Request (INVITE) messages on this interface } return 1; @@ -523,46 +520,46 @@ void send_ccr_interim(struct ro_session* ro_session, unsigned int used, unsigned event_type = new_event_type(&sip_method, &sip_event, 0); LM_DBG("Sending interim CCR request for (usage:new) [%i:%i] seconds for user [%.*s] using session id [%.*s] active rating group [%d] active service identifier [%d] incoming_trunk_id [%.*s] outgoing_trunk_id [%.*s]\n", - used, - reserve, - ro_session->asserted_identity.len, ro_session->asserted_identity.s, - ro_session->ro_session_id.len, ro_session->ro_session_id.s, - ro_session->rating_group, ro_session->service_identifier, - ro_session->incoming_trunk_id.len, ro_session->incoming_trunk_id.s, - ro_session->outgoing_trunk_id.len, ro_session->outgoing_trunk_id.s); + used, + reserve, + ro_session->asserted_identity.len, ro_session->asserted_identity.s, + ro_session->ro_session_id.len, ro_session->ro_session_id.s, + ro_session->rating_group, ro_session->service_identifier, + ro_session->incoming_trunk_id.len, ro_session->incoming_trunk_id.s, + ro_session->outgoing_trunk_id.len, ro_session->outgoing_trunk_id.s); req_timestamp = time(0); if (!(time_stamps = new_time_stamps(&req_timestamp, NULL, NULL, NULL))) - goto error; + goto error; if (!(ims_info = new_ims_information(event_type, time_stamps, &ro_session->callid, &ro_session->callid, &ro_session->asserted_identity, - &ro_session->called_asserted_identity, 0, 0, 0, ro_session->direction, &ro_session->incoming_trunk_id, &ro_session->outgoing_trunk_id, &ro_session->pani))) - goto error; + &ro_session->called_asserted_identity, 0, 0, 0, ro_session->direction, &ro_session->incoming_trunk_id, &ro_session->outgoing_trunk_id, &ro_session->pani))) + goto error; LM_DBG("Created IMS information\n"); event_type = 0; if (ro_session->direction == RO_ORIG_DIRECTION) { - subscr.id = ro_session->asserted_identity; + subscr.id = ro_session->asserted_identity; } else if (ro_session->direction == RO_TERM_DIRECTION) { - subscr.id = ro_session->called_asserted_identity; + subscr.id = ro_session->called_asserted_identity; } else { - LM_CRIT("don't know what to do in unknown mode - should we even get here\n"); - goto error; + LM_CRIT("don't know what to do in unknown mode - should we even get here\n"); + goto error; } //getting subscription id type if (strncasecmp(subscr.id.s, "tel:", 4) == 0) { - subscr.type = Subscription_Type_MSISDN; - // Strip "tel:": - subscr.id.s += 4; - subscr.id.len -= 4; + subscr.type = Subscription_Type_MSISDN; + // Strip "tel:": + subscr.id.s += 4; + subscr.id.len -= 4; } else { - subscr.type = Subscription_Type_IMPU; //default is END_USER_SIP_URI + subscr.type = Subscription_Type_IMPU; //default is END_USER_SIP_URI } user_name.s = subscr.id.s; @@ -572,53 +569,53 @@ void send_ccr_interim(struct ro_session* ro_session, unsigned int used, unsigned ro_ccr_data = new_Ro_CCR(acc_record_type, &user_name, ims_info, &subscr); if (!ro_ccr_data) { - LM_ERR("no memory left for generic\n"); - goto error; + LM_ERR("no memory left for generic\n"); + goto error; } ims_info = NULL; auth = cdpb.AAAGetCCAccSession(ro_session->ro_session_id); if (!auth) { - LM_DBG("Diameter Auth Session has timed out.... creating a new one.\n"); - /* lets try and recreate this session */ - //TODO: make a CC App session auth = cdpb.AAASession(ro_session->auth_appid, ro_session->auth_session_type, ro_session->ro_session_id); //TODO: would like this session to last longer (see session timeout in cdp - //BUG("Oh shit, session timed out and I don't know how to create a new one."); + LM_DBG("Diameter Auth Session has timed out.... creating a new one.\n"); + /* lets try and recreate this session */ + //TODO: make a CC App session auth = cdpb.AAASession(ro_session->auth_appid, ro_session->auth_session_type, ro_session->ro_session_id); //TODO: would like this session to last longer (see session timeout in cdp + //BUG("Oh shit, session timed out and I don't know how to create a new one."); - auth = cdpb.AAAMakeSession(ro_session->auth_appid, ro_session->auth_session_type, ro_session->ro_session_id); //TODO: would like this session to last longer (see session timeout in cdp - if (!auth) - goto error; + auth = cdpb.AAAMakeSession(ro_session->auth_appid, ro_session->auth_session_type, ro_session->ro_session_id); //TODO: would like this session to last longer (see session timeout in cdp + if (!auth) + goto error; } //don't send INTERIM record if session is not in OPEN state (it could already be waiting for a previous response, etc) if (auth->u.cc_acc.state != ACC_CC_ST_OPEN) { - LM_WARN("ignoring interim update on CC session not in correct state, currently in state [%d]\n", auth->u.cc_acc.state); - goto error; + LM_WARN("ignoring interim update on CC session not in correct state, currently in state [%d]\n", auth->u.cc_acc.state); + goto error; } if (!(ccr = Ro_new_ccr(auth, ro_ccr_data))) - goto error; + goto error; if (!Ro_add_vendor_specific_appid(ccr, IMS_vendor_id_3GPP, IMS_Ro, 0/*acct id*/)) { - LM_ERR("Problem adding Vendor specific ID\n"); + LM_ERR("Problem adding Vendor specific ID\n"); } ro_session->hop_by_hop += 1; if (!Ro_add_cc_request(ccr, RO_CC_INTERIM, ro_session->hop_by_hop)) { - LM_ERR("Problem adding CC-Request data\n"); + LM_ERR("Problem adding CC-Request data\n"); } if (!Ro_add_event_timestamp(ccr, time(NULL))) { - LM_ERR("Problem adding Event-Timestamp data\n"); + LM_ERR("Problem adding Event-Timestamp data\n"); } - if (!Ro_add_user_equipment_info(ccr, AVP_EPC_User_Equipment_Info_Type_MAC, ro_session->avp_value.mac)) { - LM_ERR("Problem adding User-Equipment data\n"); + if (!Ro_add_user_equipment_info(ccr, AVP_EPC_User_Equipment_Info_Type_MAC, ro_session->mac)) { + LM_ERR("Problem adding User-Equipment data\n"); } if (!Ro_add_subscription_id(ccr, subscr.type, &(subscr.id))) { - LM_ERR("Problem adding Subscription ID data\n"); + LM_ERR("Problem adding Subscription ID data\n"); } if (!Ro_add_multiple_service_credit_Control(ccr, interim_request_credits/*INTERIM_CREDIT_REQ_AMOUNT*/, used, ro_session->rating_group, ro_session->service_identifier)) { - LM_ERR("Problem adding Multiple Service Credit Control data\n"); + LM_ERR("Problem adding Multiple Service Credit Control data\n"); } LM_DBG("Sending CCR Diameter message.\n"); @@ -626,13 +623,13 @@ void send_ccr_interim(struct ro_session* ro_session, unsigned int used, unsigned cdpb.AAASessionsUnlock(auth->hash); if (ro_forced_peer.len > 0) { - ret = cdpb.AAASendMessageToPeer(ccr, &ro_forced_peer, resume_on_interim_ccr, (void *) i_req); + ret = cdpb.AAASendMessageToPeer(ccr, &ro_forced_peer, resume_on_interim_ccr, (void *) i_req); } else { - ret = cdpb.AAASendMessage(ccr, resume_on_interim_ccr, (void *) i_req); + ret = cdpb.AAASendMessage(ccr, resume_on_interim_ccr, (void *) i_req); } if (ret != 1) { - goto error; + goto error; } // cdpb.AAASessionsUnlock(auth->hash); @@ -644,14 +641,14 @@ void send_ccr_interim(struct ro_session* ro_session, unsigned int used, unsigned LM_ERR("error trying to reserve interim credit\n"); if (ro_ccr_data) - Ro_free_CCR(ro_ccr_data); + Ro_free_CCR(ro_ccr_data); if (ccr) - cdpb.AAAFreeMessage(&ccr); + cdpb.AAAFreeMessage(&ccr); if (auth) { - cdpb.AAASessionsUnlock(auth->hash); - cdpb.AAADropCCAccSession(auth); + cdpb.AAASessionsUnlock(auth->hash); + cdpb.AAADropCCAccSession(auth); } shm_free(i_req); @@ -672,36 +669,36 @@ static void resume_on_interim_ccr(int is_timeout, void *param, AAAMessage *cca, Ro_CCA_t * ro_cca_data = NULL; if (is_timeout) { - counter_inc(ims_charging_cnts_h.ccr_timeouts); - LM_ERR("Transaction timeout - did not get CCA\n"); - goto error; + counter_inc(ims_charging_cnts_h.ccr_timeouts); + LM_ERR("Transaction timeout - did not get CCA\n"); + goto error; } counter_add(ims_charging_cnts_h.ccr_response_time, elapsed_msecs); counter_inc(ims_charging_cnts_h.ccr_replies_received); if (!i_req) { - LM_ERR("This is so wrong: ro session is NULL\n"); - goto error; + LM_ERR("This is so wrong: ro session is NULL\n"); + goto error; } if (cca == NULL) { - LM_ERR("Error reserving credit for CCA.\n"); - goto error; + LM_ERR("Error reserving credit for CCA.\n"); + goto error; } ro_cca_data = Ro_parse_CCA_avps(cca); if (ro_cca_data == NULL) { - LM_ERR("Could not parse CCA message response.\n"); - goto error; + LM_ERR("Could not parse CCA message response.\n"); + goto error; } if (ro_cca_data->resultcode != 2001) { - LM_ERR("Got bad CCA result code [%d] - reservation failed", ro_cca_data->resultcode); - goto error; + LM_ERR("Got bad CCA result code [%d] - reservation failed", ro_cca_data->resultcode); + goto error; } else { - LM_DBG("Valid CCA response with time chunk of [%i] and validity [%i].\n", ro_cca_data->mscc->granted_service_unit->cc_time, ro_cca_data->mscc->validity_time); + LM_DBG("Valid CCA response with time chunk of [%i] and validity [%i].\n", ro_cca_data->mscc->granted_service_unit->cc_time, ro_cca_data->mscc->validity_time); } i_req->new_credit = ro_cca_data->mscc->granted_service_unit->cc_time; @@ -709,7 +706,7 @@ static void resume_on_interim_ccr(int is_timeout, void *param, AAAMessage *cca, i_req->is_final_allocation = 0; if (ro_cca_data->mscc->final_unit_action && (ro_cca_data->mscc->final_unit_action->action == 0)) - i_req->is_final_allocation = 1; + i_req->is_final_allocation = 1; Ro_free_CCA(ro_cca_data); cdpb.AAAFreeMessage(&cca); @@ -719,21 +716,28 @@ static void resume_on_interim_ccr(int is_timeout, void *param, AAAMessage *cca, error: if (ro_cca_data) - Ro_free_CCA(ro_cca_data); + Ro_free_CCA(ro_cca_data); if (!is_timeout && cca) { - cdpb.AAAFreeMessage(&cca); + cdpb.AAAFreeMessage(&cca); } if (i_req) { - i_req->credit_valid_for = 0; - i_req->new_credit = 0; + i_req->credit_valid_for = 0; + i_req->new_credit = 0; } success: resume_ro_session_ontimeout(i_req); } +long get_current_time_micro() { + struct timeval tv; + + gettimeofday(&tv, 0); + return tv.tv_sec*1000000 + tv.tv_usec; +} + void send_ccr_stop(struct ro_session *ro_session) { AAASession * auth = 0; Ro_CCR_t * ro_ccr_data = 0; @@ -742,15 +746,15 @@ void send_ccr_stop(struct ro_session *ro_session) { int32_t acc_record_type; subscription_id_t subscr; time_stamps_t *time_stamps; - unsigned int used = 0; + long used = 0; str user_name = {0, 0}; int ret = 0; if (ro_session->event_type != pending) { - used = time(0) - ro_session->last_event_timestamp; + used = rint((get_current_time_micro() - ro_session->last_event_timestamp)/(float)1000000); } - counter_add(ims_charging_cnts_h.billed_secs, used); + counter_add(ims_charging_cnts_h.billed_secs, (int)used); event_type_t *event_type; @@ -761,44 +765,42 @@ void send_ccr_stop(struct ro_session *ro_session) { event_type = new_event_type(&sip_method, &sip_event, 0); - LM_DBG("Sending stop CCR request for (usage) [%i] seconds for user [%.*s] using session id [%.*s] active rating group [%d] active service identifier [%d] incoming_trunk_id [%.*s] outgoing_trunk_id [%.*s]\n", - used, - ro_session->asserted_identity.len, ro_session->asserted_identity.s, - ro_session->ro_session_id.len, ro_session->ro_session_id.s, - ro_session->rating_group, ro_session->service_identifier, - ro_session->incoming_trunk_id.len, ro_session->incoming_trunk_id.s, - ro_session->outgoing_trunk_id.len, ro_session->outgoing_trunk_id.s); + LM_DBG("Sending stop CCR request for (usage) [%i] seconds for user [%.*s] using session id [%.*s] active rating group [%d] active service identifier [%d] incoming_trunk_id [%.*s] outgoing_trunk_id [%.*s] pani [%.*s]\n", + (int)used, + ro_session->asserted_identity.len, ro_session->asserted_identity.s, + ro_session->ro_session_id.len, ro_session->ro_session_id.s, + ro_session->rating_group, ro_session->service_identifier, + ro_session->incoming_trunk_id.len, ro_session->incoming_trunk_id.s, + ro_session->outgoing_trunk_id.len, ro_session->outgoing_trunk_id.s, + ro_session->pani.len, ro_session->pani.s); - req_timestamp = time(0); + req_timestamp = get_current_time_micro(); if (!(time_stamps = new_time_stamps(&req_timestamp, NULL, NULL, NULL))) - goto error0; + goto error0; if (!(ims_info = new_ims_information(event_type, time_stamps, &ro_session->callid, &ro_session->callid, &ro_session->asserted_identity, - &ro_session->called_asserted_identity, 0, 0, 0, ro_session->direction, &ro_session->incoming_trunk_id, &ro_session->outgoing_trunk_id, &ro_session->pani))) - goto error0; + &ro_session->called_asserted_identity, 0, 0, 0, ro_session->direction, &ro_session->incoming_trunk_id, &ro_session->outgoing_trunk_id, &ro_session->pani))) + goto error0; event_type = 0; if (ro_session->direction == RO_ORIG_DIRECTION) { - subscr.id = ro_session->asserted_identity; + subscr.id = ro_session->asserted_identity; } else if (ro_session->direction == RO_TERM_DIRECTION) { - subscr.id = ro_session->called_asserted_identity; + subscr.id = ro_session->called_asserted_identity; } else { - LM_CRIT("don't know what to do in unknown mode - should we even get here\n"); - goto error0; + LM_CRIT("don't know what to do in unknown mode - should we even get here\n"); + goto error0; } //getting subscription id type if (strncasecmp(subscr.id.s, "tel:", 4) == 0) { - subscr.type = Subscription_Type_MSISDN; - // Strip "tel:": - subscr.id.s += 4; - subscr.id.len -= 4; + subscr.type = Subscription_Type_MSISDN; } else { - subscr.type = Subscription_Type_IMPU; //default is END_USER_SIP_URI + subscr.type = Subscription_Type_IMPU; //default is END_USER_SIP_URI } user_name.s = subscr.id.s; @@ -809,8 +811,8 @@ void send_ccr_stop(struct ro_session *ro_session) { ro_ccr_data = new_Ro_CCR(acc_record_type, &user_name, ims_info, &subscr); if (!ro_ccr_data) { - LM_ERR("send_ccr_stop: no memory left for generic\n"); - goto error0; + LM_ERR("send_ccr_stop: no memory left for generic\n"); + goto error0; } ims_info = 0; @@ -819,58 +821,58 @@ void send_ccr_stop(struct ro_session *ro_session) { auth = cdpb.AAAGetCCAccSession(ro_session->ro_session_id); if (!auth) { - LM_DBG("Diameter Auth Session has timed out.... creating a new one.\n"); - /* lets try and recreate this session */ - auth = cdpb.AAAMakeSession(ro_session->auth_appid, ro_session->auth_session_type, ro_session->ro_session_id); //TODO: would like this session to last longer (see session timeout in cdp - if (!auth) - goto error1; + LM_DBG("Diameter Auth Session has timed out.... creating a new one.\n"); + /* lets try and recreate this session */ + auth = cdpb.AAAMakeSession(ro_session->auth_appid, ro_session->auth_session_type, ro_session->ro_session_id); //TODO: would like this session to last longer (see session timeout in cdp + if (!auth) + goto error1; } if (!(ccr = Ro_new_ccr(auth, ro_ccr_data))) - goto error1; + goto error1; LM_DBG("Created new CCR\n"); if (!Ro_add_vendor_specific_appid(ccr, IMS_vendor_id_3GPP, IMS_Ro, 0)) { - LM_ERR("Problem adding Vendor specific ID\n"); + LM_ERR("Problem adding Vendor specific ID\n"); } ro_session->hop_by_hop += 1; if (!Ro_add_cc_request(ccr, RO_CC_STOP, ro_session->hop_by_hop)) { - LM_ERR("Problem adding CC-Request data\n"); + LM_ERR("Problem adding CC-Request data\n"); } if (!Ro_add_event_timestamp(ccr, time(NULL))) { - LM_ERR("Problem adding Event-Timestamp data\n"); + LM_ERR("Problem adding Event-Timestamp data\n"); } - if (!Ro_add_user_equipment_info(ccr, AVP_EPC_User_Equipment_Info_Type_MAC, ro_session->avp_value.mac)) { - LM_ERR("Problem adding User-Equipment data\n"); + if (!Ro_add_user_equipment_info(ccr, AVP_EPC_User_Equipment_Info_Type_MAC, ro_session->mac)) { + LM_ERR("Problem adding User-Equipment data\n"); } if (!Ro_add_subscription_id(ccr, subscr.type, &subscr.id)) { - LM_ERR("Problem adding Subscription ID data\n"); + LM_ERR("Problem adding Subscription ID data\n"); } if (!Ro_add_multiple_service_credit_Control_stop(ccr, used, ro_session->rating_group, ro_session->service_identifier)) { - LM_ERR("Problem adding Multiple Service Credit Control data\n"); + LM_ERR("Problem adding Multiple Service Credit Control data\n"); } if (!Ro_add_termination_cause(ccr, TERM_CAUSE_LOGOUT)) { - LM_ERR("problem add Termination cause AVP to STOP record.\n"); + LM_ERR("problem add Termination cause AVP to STOP record.\n"); } cdpb.AAASessionsUnlock(auth->hash); if (ro_forced_peer.len > 0) { - ret = cdpb.AAASendMessageToPeer(ccr, &ro_forced_peer, resume_on_termination_ccr, NULL); + ret = cdpb.AAASendMessageToPeer(ccr, &ro_forced_peer, resume_on_termination_ccr, NULL); } else { - ret = cdpb.AAASendMessage(ccr, resume_on_termination_ccr, NULL); + ret = cdpb.AAASendMessage(ccr, resume_on_termination_ccr, NULL); } if (ret != 1) { - goto error1; + goto error1; } Ro_free_CCR(ro_ccr_data); @@ -884,8 +886,8 @@ void send_ccr_stop(struct ro_session *ro_session) { Ro_free_CCR(ro_ccr_data); if (auth) { - cdpb.AAASessionsUnlock(auth->hash); - cdpb.AAADropCCAccSession(auth); + cdpb.AAASessionsUnlock(auth->hash); + cdpb.AAADropCCAccSession(auth); } error0: @@ -897,31 +899,31 @@ static void resume_on_termination_ccr(int is_timeout, void *param, AAAMessage *c Ro_CCA_t *ro_cca_data = NULL; if (is_timeout) { - counter_inc(ims_charging_cnts_h.ccr_timeouts); - LM_ERR("Transaction timeout - did not get CCA\n"); - goto error; + counter_inc(ims_charging_cnts_h.ccr_timeouts); + LM_ERR("Transaction timeout - did not get CCA\n"); + goto error; } counter_inc(ims_charging_cnts_h.ccr_replies_received); counter_add(ims_charging_cnts_h.ccr_response_time, elapsed_msecs); if (!cca) { - LM_ERR("Error in termination CCR.\n"); - return; + LM_ERR("Error in termination CCR.\n"); + return; } ro_cca_data = Ro_parse_CCA_avps(cca); if (ro_cca_data == NULL) { - LM_DBG("Could not parse CCA message response.\n"); - return; + LM_DBG("Could not parse CCA message response.\n"); + return; } if (ro_cca_data->resultcode != 2001) { - LM_ERR("Got bad CCA result code for STOP record - [%d]\n", ro_cca_data->resultcode); - goto error; + LM_ERR("Got bad CCA result code for STOP record - [%d]\n", ro_cca_data->resultcode); + goto error; } else { - LM_DBG("Valid CCA response for STOP record\n"); + LM_DBG("Valid CCA response for STOP record\n"); } counter_inc(ims_charging_cnts_h.successful_final_ccrs); @@ -929,7 +931,7 @@ static void resume_on_termination_ccr(int is_timeout, void *param, AAAMessage *c error: Ro_free_CCA(ro_cca_data); if (!is_timeout && cca) { - cdpb.AAAFreeMessage(&cca); + cdpb.AAAFreeMessage(&cca); } } @@ -945,7 +947,7 @@ static void resume_on_termination_ccr(int is_timeout, void *param, AAAMessage *c * @returns #CSCF_RETURN_BREAK if OK, #CSCF_RETURN_ERROR on error */ int Ro_Send_CCR(struct sip_msg *msg, struct dlg_cell *dlg, int dir, int reservation_units, str* incoming_trunk_id, str* outgoing_trunk_id, - str* pani, cfg_action_t* action, unsigned int tindex, unsigned int tlabel) { + str* pani, cfg_action_t* action, unsigned int tindex, unsigned int tlabel) { str session_id = {0, 0}, called_asserted_identity = {0, 0}, subscription_id = {0, 0}, @@ -972,105 +974,102 @@ int Ro_Send_CCR(struct sip_msg *msg, struct dlg_cell *dlg, int dir, int reservat int sdp_stream_num = 0; LM_DBG("Sending initial CCR request for reservation_units [%d] incoming_trunk_id [%.*s] outgoing_trunk_id [%.*s]\n", - reservation_units, - incoming_trunk_id->len, incoming_trunk_id->s, - outgoing_trunk_id->len, outgoing_trunk_id->s); + reservation_units, + incoming_trunk_id->len, incoming_trunk_id->s, + outgoing_trunk_id->len, outgoing_trunk_id->s); ssd = shm_malloc(sizeof (struct session_setup_data)); // lookup structure used to load session info from cdp callback on CCA if (!ssd) { - LM_ERR("no more shm mem\n"); - goto error; + LM_ERR("no more shm mem\n"); + goto error; } //getting asserted identity if ((asserted_identity = cscf_get_asserted_identity(msg, 0)).len == 0) { - LM_DBG("No P-Asserted-Identity hdr found. Using From hdr for asserted_identity"); - asserted_identity = dlg->from_uri; + LM_DBG("No P-Asserted-Identity hdr found. Using From hdr for asserted_identity"); + asserted_identity = dlg->from_uri; } //getting called asserted identity if ((called_asserted_identity = cscf_get_public_identity_from_called_party_id(msg, &h)).len == 0) { - LM_DBG("No P-Called-Identity hdr found. Using request URI for called_asserted_identity"); - called_asserted_identity = cscf_get_public_identity_from_requri(msg); - free_called_asserted_identity = 1; + LM_DBG("No P-Called-Identity hdr found. Using request URI for called_asserted_identity"); + called_asserted_identity = cscf_get_public_identity_from_requri(msg); + free_called_asserted_identity = 1; } if (dir == RO_ORIG_DIRECTION) { - subscription_id.s = asserted_identity.s; - subscription_id.len = asserted_identity.len; + subscription_id.s = asserted_identity.s; + subscription_id.len = asserted_identity.len; } else if (dir == RO_TERM_DIRECTION) { - subscription_id.s = called_asserted_identity.s; - subscription_id.len = called_asserted_identity.len; + subscription_id.s = called_asserted_identity.s; + subscription_id.len = called_asserted_identity.len; } else { - LM_CRIT("don't know what to do in unknown mode - should we even get here\n"); - goto error; + LM_CRIT("don't know what to do in unknown mode - should we even get here\n"); + goto error; } //getting subscription id type if (strncasecmp(subscription_id.s, "tel:", 4) == 0) { - subscription_id_type = Subscription_Type_MSISDN; - // Strip "tel:": - subscription_id.s += 4; - subscription_id.len -= 4; + subscription_id_type = Subscription_Type_MSISDN; } else { - subscription_id_type = Subscription_Type_IMPU; //default is END_USER_SIP_URI + subscription_id_type = Subscription_Type_IMPU; //default is END_USER_SIP_URI } str mac = {0, 0}; if (get_mac_avp_value(msg, &mac) != 0) - LM_DBG(RO_MAC_AVP_NAME" was not set. Using default."); + LM_DBG(RO_MAC_AVP_NAME" was not set. Using default."); //by default we use voice service id and rate group //then we check SDP - if we find video then we use video service id and rate group LM_DBG("Setting default SID to %d and RG to %d for voice", - voice_service_identifier, voice_rating_group); + voice_service_identifier, voice_rating_group); active_service_identifier = voice_service_identifier; active_rating_group = voice_rating_group; //check SDP - if there is video then set default to video, if not set it to audio if (parse_sdp(msg) < 0) { - LM_ERR("Unable to parse req SDP\n"); - goto error; + LM_ERR("Unable to parse req SDP\n"); + goto error; } msg_sdp_session = get_sdp_session(msg, 0); if (!msg_sdp_session) { - LM_ERR("Missing SDP session information from rpl\n"); + LM_ERR("Missing SDP session information from rpl\n"); } else { - for (;;) { - msg_sdp_stream = get_sdp_stream(msg, 0, sdp_stream_num); - if (!msg_sdp_stream) { - //LM_ERR("Missing SDP stream information\n"); - break; - } - - int intportA = atoi(msg_sdp_stream->port.s); - if (intportA != 0 && strncasecmp(msg_sdp_stream->media.s, "video", 5) == 0) { - LM_DBG("This SDP has a video component and src ports not equal to 0 - so we set default SID to %d and RG to %d for video", - video_service_identifier, video_rating_group); - active_service_identifier = video_service_identifier; - active_rating_group = video_rating_group; - break; - } - - sdp_stream_num++; - } + for (;;) { + msg_sdp_stream = get_sdp_stream(msg, 0, sdp_stream_num); + if (!msg_sdp_stream) { + //LM_ERR("Missing SDP stream information\n"); + break; + } + + int intportA = atoi(msg_sdp_stream->port.s); + if (intportA != 0 && strncasecmp(msg_sdp_stream->media.s, "video", 5) == 0) { + LM_DBG("This SDP has a video component and src ports not equal to 0 - so we set default SID to %d and RG to %d for video", + video_service_identifier, video_rating_group); + active_service_identifier = video_service_identifier; + active_rating_group = video_rating_group; + break; + } + + sdp_stream_num++; + } } free_sdp((sdp_info_t**) (void*) &msg->body); //create a session object without auth and diameter session id - we will add this later. new_session = build_new_ro_session(dir, 0, 0, &session_id, &dlg->callid, - &asserted_identity, &called_asserted_identity, &mac, dlg->h_entry, dlg->h_id, - reservation_units, 0, active_rating_group, active_service_identifier, incoming_trunk_id, outgoing_trunk_id, pani); + &asserted_identity, &called_asserted_identity, &mac, dlg->h_entry, dlg->h_id, + reservation_units, 0, active_rating_group, active_service_identifier, incoming_trunk_id, outgoing_trunk_id, pani); if (!new_session) { - LM_ERR("Couldn't create new Ro Session - this is BAD!\n"); - goto error; + LM_ERR("Couldn't create new Ro Session - this is BAD!\n"); + goto error; } ssd->action = action; @@ -1079,83 +1078,84 @@ int Ro_Send_CCR(struct sip_msg *msg, struct dlg_cell *dlg, int dir, int reservat ssd->ro_session = new_session; if (!sip_create_ro_ccr_data(msg, dir, &ro_ccr_data, &cc_acc_session, asserted_identity, called_asserted_identity, subscription_id, subscription_id_type, incoming_trunk_id, outgoing_trunk_id, pani)) - goto error; + goto error; if (!ro_ccr_data) - goto error; + goto error; if (!cc_acc_session) - goto error; + goto error; if (!(ccr = Ro_new_ccr(cc_acc_session, ro_ccr_data))) - goto error; + goto error; if (!Ro_add_vendor_specific_appid(ccr, IMS_vendor_id_3GPP, IMS_Ro, 0)) { - LM_ERR("Problem adding Vendor specific ID\n"); - goto error; + LM_ERR("Problem adding Vendor specific ID\n"); + goto error; } if (!Ro_add_cc_request(ccr, cc_event_type, cc_event_number)) { - LM_ERR("Problem adding CC-Request data\n"); - goto error; + LM_ERR("Problem adding CC-Request data\n"); + goto error; } if (!Ro_add_event_timestamp(ccr, time(NULL))) { - LM_ERR("Problem adding Event-Timestamp data\n"); - goto error; + LM_ERR("Problem adding Event-Timestamp data\n"); + goto error; } if (!Ro_add_user_equipment_info(ccr, AVP_EPC_User_Equipment_Info_Type_MAC, mac)) { - LM_ERR("Problem adding User-Equipment data\n"); - goto error; + LM_ERR("Problem adding User-Equipment data\n"); + goto error; } if (!Ro_add_subscription_id(ccr, subscription_id_type, &subscription_id)) { - LM_ERR("Problem adding Subscription ID data\n"); - goto error; + LM_ERR("Problem adding Subscription ID data\n"); + goto error; } if (!Ro_add_multiple_service_credit_Control(ccr, reservation_units, -1, active_rating_group, active_service_identifier)) { - LM_ERR("Problem adding Multiple Service Credit Control data\n"); - goto error; + LM_ERR("Problem adding Multiple Service Credit Control data\n"); + goto error; } - + /* before we send, update our session object with CC App session ID and data */ new_session->auth_appid = cc_acc_session->application_id; new_session->auth_session_type = cc_acc_session->type; new_session->ro_session_id.s = (char*) shm_malloc(cc_acc_session->id.len); + if (!new_session->ro_session_id.s) { + LM_ERR("no more shm mem\n"); + goto error; + } + new_session->ro_session_id.len = cc_acc_session->id.len; memcpy(new_session->ro_session_id.s, cc_acc_session->id.s, cc_acc_session->id.len); - LM_DBG("new CC Ro Session ID: [%.*s]\n", cc_acc_session->id.len, cc_acc_session->id.s); + LM_DBG("new CC Ro Session ID: [%.*s] stored in shared memory address [%p]\n", cc_acc_session->id.len, cc_acc_session->id.s, new_session); LM_DBG("Sending CCR Diameter message.\n"); cdpb.AAASessionsUnlock(cc_acc_session->hash); if (ro_forced_peer.len > 0) { - LM_DBG("Sending message with Peer\n"); - ret = cdpb.AAASendMessageToPeer(ccr, &ro_forced_peer, resume_on_initial_ccr, (void *) ssd); + LM_DBG("Sending message with Peer\n"); + ret = cdpb.AAASendMessageToPeer(ccr, &ro_forced_peer, resume_on_initial_ccr, (void *) ssd); } else { - LM_DBG("Sending message without Peer and realm is [%.*s]\n", ccr->dest_realm->data.len, ccr->dest_realm->data.s); - ret = cdpb.AAASendMessage(ccr, resume_on_initial_ccr, (void *) ssd); + LM_DBG("Sending message without Peer and realm is [%.*s]\n", ccr->dest_realm->data.len, ccr->dest_realm->data.s); + ret = cdpb.AAASendMessage(ccr, resume_on_initial_ccr, (void *) ssd); } if (ret != 1) { - LM_ERR("Failed to send Diameter CCR\n"); - goto error; + LM_ERR("Failed to send Diameter CCR\n"); + goto error; } Ro_free_CCR(ro_ccr_data); - //TODO: if the following fail, we should clean up the Ro session....... - if (dlgb.register_dlgcb(dlg, /* DLGCB_RESPONSE_FWDED */ DLGCB_CONFIRMED, dlg_reply, (void*) new_session, NULL) != 0) { - LM_CRIT("cannot register callback for dialog confirmation\n"); - goto error; - } + LM_DBG("Registering for callbacks on Dialog [%p] and charging session [%p]\n", dlg, new_session); - if (dlgb.register_dlgcb(dlg, DLGCB_TERMINATED | DLGCB_FAILED | DLGCB_EXPIRED /*| DLGCB_DESTROY */ - , dlg_terminated, (void*) new_session, NULL) != 0) { - LM_CRIT("cannot register callback for dialog termination\n"); - goto error; + //TODO: if the following fail, we should clean up the Ro session....... + if (dlgb.register_dlgcb(dlg, DLGCB_TERMINATED | DLGCB_FAILED | DLGCB_EXPIRED | DLGCB_CONFIRMED, dlg_callback_received, (void*) new_session, NULL) != 0) { + LM_CRIT("cannot register callback for dialog confirmation\n"); + goto error; } counter_inc(ims_charging_cnts_h.initial_ccrs); @@ -1164,17 +1164,18 @@ int Ro_Send_CCR(struct sip_msg *msg, struct dlg_cell *dlg, int dir, int reservat return RO_RETURN_BREAK; error: + LM_DBG("Trying to reserve credit on initial INVITE failed.\n"); + if (free_called_asserted_identity) shm_free(called_asserted_identity.s); // shm_malloc in cscf_get_public_identity_from_requri Ro_free_CCR(ro_ccr_data); if (cc_acc_session) { - cdpb.AAASessionsUnlock(cc_acc_session->hash); - cdpb.AAADropSession(cc_acc_session); + cdpb.AAASessionsUnlock(cc_acc_session->hash); + cdpb.AAADropSession(cc_acc_session); } if (ssd) - shm_free(ssd); + shm_free(ssd); - LM_DBG("Trying to reserve credit on initial INVITE failed.\n"); return RO_RETURN_ERROR; } @@ -1185,32 +1186,32 @@ static void resume_on_initial_ccr(int is_timeout, void *param, AAAMessage *cca, int error_code = RO_RETURN_ERROR; if (is_timeout) { - counter_inc(ims_charging_cnts_h.ccr_timeouts); - LM_ERR("Transaction timeout - did not get CCA\n"); - error_code = RO_RETURN_ERROR; - goto error0; + counter_inc(ims_charging_cnts_h.ccr_timeouts); + LM_ERR("Transaction timeout - did not get CCA\n"); + error_code = RO_RETURN_ERROR; + goto error0; } counter_inc(ims_charging_cnts_h.ccr_replies_received); counter_add(ims_charging_cnts_h.ccr_response_time, elapsed_msecs); if (!cca) { - LM_ERR("Error reserving credit for CCA.\n"); - error_code = RO_RETURN_ERROR; - goto error0; + LM_ERR("Error reserving credit for CCA.\n"); + error_code = RO_RETURN_ERROR; + goto error0; } if (!ssd) { - LM_ERR("Session lookup data is NULL.\n"); - error_code = RO_RETURN_ERROR; - goto error0; + LM_ERR("Session lookup data is NULL.\n"); + error_code = RO_RETURN_ERROR; + goto error0; } // we make sure the transaction exists if (tmb.t_lookup_ident(&t, ssd->tindex, ssd->tlabel) < 0) { - LM_ERR("t_continue: transaction not found\n"); - error_code = RO_RETURN_ERROR; - goto error0; + LM_ERR("t_continue: transaction not found\n"); + error_code = RO_RETURN_ERROR; + goto error0; } // we bring the list of AVPs of the transaction to the current context @@ -1224,28 +1225,28 @@ static void resume_on_initial_ccr(int is_timeout, void *param, AAAMessage *cca, ro_cca_data = Ro_parse_CCA_avps(cca); if (!ro_cca_data) { - LM_ERR("Could not parse CCA message response.\n"); - error_code = RO_RETURN_ERROR; - goto error0; + LM_ERR("Could not parse CCA message response.\n"); + error_code = RO_RETURN_ERROR; + goto error0; } if (ro_cca_data->resultcode != 2001) { - LM_ERR("Got bad CCA result code - reservation failed"); - error_code = RO_RETURN_FALSE; - goto error1; + LM_ERR("Got bad CCA result code - reservation failed"); + error_code = RO_RETURN_FALSE; + goto error1; } LM_DBG("Valid CCA response with time chunk of [%i] and validity [%i]\n", - ro_cca_data->mscc->granted_service_unit->cc_time, - ro_cca_data->mscc->validity_time); - - if (ro_cca_data->mscc->granted_service_unit->cc_time <=0 ) { + ro_cca_data->mscc->granted_service_unit->cc_time, + ro_cca_data->mscc->validity_time); + + if (ro_cca_data->mscc->granted_service_unit->cc_time <= 0) { LM_DBG("got zero GSU.... reservation failed"); - error_code = RO_RETURN_FALSE; - goto error1; + error_code = RO_RETURN_FALSE; + goto error1; } - ssd->ro_session->last_event_timestamp = time(0); + ssd->ro_session->last_event_timestamp = get_current_time_micro(); ssd->ro_session->event_type = pending; ssd->ro_session->reserved_secs = ro_cca_data->mscc->granted_service_unit->cc_time; ssd->ro_session->valid_for = ro_cca_data->mscc->validity_time; @@ -1258,10 +1259,10 @@ static void resume_on_initial_ccr(int is_timeout, void *param, AAAMessage *cca, link_ro_session(ssd->ro_session, 1); /* create extra ref for the fact that dialog has a handle in the callbacks */ if (ro_db_mode == DB_MODE_REALTIME) { - ssd->ro_session->flags |= RO_SESSION_FLAG_NEW; - if (update_ro_dbinfo(ssd->ro_session) != 0) { - LM_ERR("Failed to update ro_session in database... continuing\n"); - }; + ssd->ro_session->flags |= RO_SESSION_FLAG_NEW; + if (update_ro_dbinfo(ssd->ro_session) != 0) { + LM_ERR("Failed to update ro_session in database... continuing\n"); + }; } unref_ro_session(ssd->ro_session, 1); /* release our reference */ @@ -1269,7 +1270,7 @@ static void resume_on_initial_ccr(int is_timeout, void *param, AAAMessage *cca, create_cca_return_code(RO_RETURN_TRUE); if (t) - tmb.unref_cell(t); + tmb.unref_cell(t); tmb.t_continue(ssd->tindex, ssd->tlabel, ssd->action); shm_free(ssd); @@ -1287,11 +1288,11 @@ static void resume_on_initial_ccr(int is_timeout, void *param, AAAMessage *cca, create_cca_return_code(error_code); if (!is_timeout && cca) { - cdpb.AAAFreeMessage(&cca); + cdpb.AAAFreeMessage(&cca); } if (t) - tmb.unref_cell(t); + tmb.unref_cell(t); tmb.t_continue(ssd->tindex, ssd->tlabel, ssd->action); shm_free(ssd); @@ -1301,9 +1302,9 @@ void remove_aaa_session(str *session_id) { AAASession *session; if ((session = cdpb.AAAGetCCAccSession(*session_id))) { - LM_DBG("Found AAA CC App Auth session to delete.\n"); - cdpb.AAASessionsUnlock(session->hash); - cdpb.AAADropCCAccSession(session); + LM_DBG("Found AAA CC App Auth session to delete.\n"); + cdpb.AAASessionsUnlock(session->hash); + cdpb.AAADropCCAccSession(session); } } @@ -1311,11 +1312,11 @@ int get_direction_as_int(str* direction) { char* p = direction->s; if (direction->len > 0 && p) { - if (p[0] == 'O' || p[0] == 'o') { - return RO_ORIG_DIRECTION; - } else if (p[0] == 'T' || p[0] == 't') { - return RO_TERM_DIRECTION; - } + if (p[0] == 'O' || p[0] == 'o') { + return RO_ORIG_DIRECTION; + } else if (p[0] == 'T' || p[0] == 't') { + return RO_TERM_DIRECTION; + } } return RO_UNKNOWN_DIRECTION; } @@ -1331,29 +1332,29 @@ static int create_cca_return_code(int result) { avp_val.s.len = 1; switch (result) { - case RO_RETURN_FALSE: - avp_val.s.s = RO_RETURN_FALSE_STR; - break; - case RO_RETURN_ERROR: - avp_val.s.s = RO_RETURN_ERROR_STR; - break; - default: - if (result >= 0) - break; + case RO_RETURN_FALSE: + avp_val.s.s = RO_RETURN_FALSE_STR; + break; + case RO_RETURN_ERROR: + avp_val.s.s = RO_RETURN_ERROR_STR; + break; + default: + if (result >= 0) + break; - LM_ERR("Unknown result code: %d", result); - avp_val.s.s = "??"; + LM_ERR("Unknown result code: %d", result); + avp_val.s.s = "??"; } if (result < 0) - avp_val.s.len = 2; + avp_val.s.len = 2; rc = add_avp(AVP_NAME_STR | AVP_VAL_STR, avp_name, avp_val); if (rc < 0) - LM_ERR("Couldn't create ["RO_AVP_CCA_RETURN_CODE"] AVP\n"); + LM_ERR("Couldn't create ["RO_AVP_CCA_RETURN_CODE"] AVP\n"); else - LM_DBG("Created AVP ["RO_AVP_CCA_RETURN_CODE"] successfully: value=[%d]\n", result); + LM_DBG("Created AVP ["RO_AVP_CCA_RETURN_CODE"] successfully: value=[%d]\n", result); return 1; } @@ -1366,9 +1367,9 @@ static int get_mac_avp_value(struct sip_msg *msg, str *value) { pv_parse_spec2(&mac_avp_name_str, &avp_spec, 1); if (pv_get_spec_value(msg, &avp_spec, &val) != 0 || val.rs.len == 0) { - value->s = "00:00:00:00:00:00"; - value->len = sizeof ("00:00:00:00:00:00") - 1; - return -1; + value->s = "00:00:00:00:00:00"; + value->len = sizeof ("00:00:00:00:00:00") - 1; + return -1; } *value = val.rs; diff --git a/modules/ims_charging/ims_ro.h b/modules/ims_charging/ims_ro.h index b18d0f8f93b..361bd8f57ae 100644 --- a/modules/ims_charging/ims_ro.h +++ b/modules/ims_charging/ims_ro.h @@ -3,6 +3,7 @@ #include "../../mod_fix.h" #include "../cdp/diameter_api.h" +#include "../dialog_ng/dlg_hash.h" #include "ro_session_hash.h" struct interim_ccr { @@ -16,6 +17,7 @@ void credit_control_session_callback(int event, void* session); void remove_aaa_session(str *session_id); int Ro_Send_CCR(struct sip_msg *msg, struct dlg_cell *dlg, int dir, int reservation_units, str *incoming_trunk_id, str *outgoing_trunk_id, str *enb_cell_id, cfg_action_t* action, unsigned int tindex, unsigned int tlabel); +long get_current_time_micro(); void send_ccr_interim(struct ro_session* ro_session, unsigned int used, unsigned int reserve); void send_ccr_stop(struct ro_session *ro_session); int get_direction_as_int(str* direction); diff --git a/modules/ims_charging/mod.c b/modules/ims_charging/mod.c index a0d94b22ad4..48f050b292c 100644 --- a/modules/ims_charging/mod.c +++ b/modules/ims_charging/mod.c @@ -172,7 +172,6 @@ int fix_parameters() { static int mod_init(void) { int n; - load_dlg_f load_dlg; load_tm_f load_tm; bind_usrloc_t bind_usrloc; @@ -189,13 +188,6 @@ static int mod_init(void) { if (load_tm(&tmb) == -1) goto error; - if (!(load_dlg = (load_dlg_f) find_export("load_dlg", 0, 0))) { /* bind to dialog module */ - LM_ERR("can not import load_dlg. This module requires Kamailio dialog module.\n"); - } - if (load_dlg(&dlgb) == -1) { - goto error; - } - if (load_cdp_api(&cdpb) != 0) { /* load the CDP API */ LM_ERR("can't load CDP API\n"); goto error; @@ -211,6 +203,16 @@ static int mod_init(void) { LM_ERR("can't load CDP_AVP API\n"); goto error; } + + bind_usrloc = (bind_usrloc_t) find_export("ul_bind_usrloc", 1, 0); + if (!bind_usrloc) { + LM_ERR("can't bind usrloc\n"); + return -1; + } + + if (bind_usrloc(&ul) < 0) { + return -1; + } /* init timer lists*/ if (init_ro_timer(ro_session_ontimeout) != 0) { @@ -239,15 +241,7 @@ static int mod_init(void) { return -1; } - bind_usrloc = (bind_usrloc_t) find_export("ul_bind_usrloc", 1, 0); - if (!bind_usrloc) { - LM_ERR("can't bind usrloc\n"); - return -1; - } - if (bind_usrloc(&ul) < 0) { - return -1; - } /*Register for callback of URECORD being deleted - so we can send a SAR*/ @@ -491,21 +485,7 @@ static int w_ro_ccr(struct sip_msg *msg, char* c_route_name, char* c_direction, ret = RO_RETURN_ERROR; goto done; } - - - //reg for callbacks on confirmed and terminated - if (dlgb.register_dlgcb(dlg, /* DLGCB_RESPONSE_FWDED */ DLGCB_CONFIRMED, add_dlg_data_to_contact, (void*)impu_data ,NULL ) != 0) { - LM_CRIT("cannot register callback for dialog confirmation\n"); - ret = RO_RETURN_ERROR; - goto done; - } - if (dlgb.register_dlgcb(dlg, DLGCB_TERMINATED | DLGCB_FAILED | DLGCB_EXPIRED /*| DLGCB_DESTROY */, remove_dlg_data_from_contact, (void*)impu_data, NULL ) != 0) { - LM_CRIT("cannot register callback for dialog termination\n"); - ret = RO_RETURN_ERROR; - goto done; - } - send_ccr: //check if we need to send_ccr - @@ -565,6 +545,7 @@ static int w_ro_ccr(struct sip_msg *msg, char* c_route_name, char* c_direction, done: if(free_contact) shm_free(contact.s);// shm_malloc in cscf_get_public_identity_from_requri + dlgb.release_dlg(dlg); return ret; } diff --git a/modules/ims_charging/ro_db_handler.c b/modules/ims_charging/ro_db_handler.c index f66292de763..4eac7228adc 100644 --- a/modules/ims_charging/ro_db_handler.c +++ b/modules/ims_charging/ro_db_handler.c @@ -180,8 +180,8 @@ int update_ro_dbinfo_unsafe(struct ro_session* ro_session) { db_set_int_val(values, DIRECTION_COL_IDX, ro_session->direction); db_set_str_val(values, ASSERTED_ID_COL_IDX, &ro_session->asserted_identity); db_set_str_val(values, CALLEE_COL_IDX, &ro_session->called_asserted_identity); - db_set_datetime_val(values, START_TIME_COL_IDX, ro_session->start_time); - db_set_datetime_val(values, LAST_EVENT_TS_COL_IDX, ro_session->last_event_timestamp); + db_set_datetime_val(values, START_TIME_COL_IDX, ro_session->start_time/1000000); + db_set_datetime_val(values, LAST_EVENT_TS_COL_IDX, ro_session->last_event_timestamp/1000000); db_set_int_val(values, RESERVED_SECS_COL_IDX, ro_session->reserved_secs); db_set_int_val(values, VALID_FOR_COL_IDX, ro_session->valid_for); db_set_int_val(values, STATE_COL_IDX, ro_session->active); @@ -216,8 +216,8 @@ int update_ro_dbinfo_unsafe(struct ro_session* ro_session) { db_set_int_val(values, DIRECTION_COL_IDX - 1, ro_session->direction); db_set_str_val(values, ASSERTED_ID_COL_IDX - 1, &ro_session->asserted_identity); db_set_str_val(values, CALLEE_COL_IDX - 1, &ro_session->called_asserted_identity); - db_set_datetime_val(values, START_TIME_COL_IDX - 1, ro_session->start_time); - db_set_datetime_val(values, LAST_EVENT_TS_COL_IDX - 1, ro_session->last_event_timestamp); + db_set_datetime_val(values, START_TIME_COL_IDX - 1, ro_session->start_time/1000000); + db_set_datetime_val(values, LAST_EVENT_TS_COL_IDX - 1, ro_session->last_event_timestamp/1000000); db_set_int_val(values, RESERVED_SECS_COL_IDX - 1, ro_session->reserved_secs); db_set_int_val(values, VALID_FOR_COL_IDX - 1, ro_session->valid_for); db_set_int_val(values, STATE_COL_IDX - 1, ro_session->active); diff --git a/modules/ims_charging/ro_session_hash.c b/modules/ims_charging/ro_session_hash.c index b78d827f58a..9c2866cf8ef 100644 --- a/modules/ims_charging/ro_session_hash.c +++ b/modules/ims_charging/ro_session_hash.c @@ -6,6 +6,7 @@ */ #include "ro_session_hash.h" +#include "ims_ro.h" #define MAX_ROSESSION_LOCKS 2048 #define MIN_ROSESSION_LOCKS 2 @@ -20,6 +21,8 @@ struct ro_session_table *ro_session_table = 0; */ void link_ro_session(struct ro_session *ro_session, int n) { struct ro_session_entry *ro_session_entry; + + LM_DBG("Linking Ro session [%.*s] into entries hash index [%d]", ro_session->ro_session_id.len, ro_session->ro_session_id.s, ro_session->h_entry); ro_session_entry = &(ro_session_table->entries[ro_session->h_entry]); @@ -126,6 +129,15 @@ int init_ro_session_table(unsigned int size) { return -1; } +int put_ro_session_on_wait(struct ro_session* session) { + LM_DBG("Putting Ro session [%p] - [%.*s] on wait queue for deletion\n", session, session->ro_session_id.len, session->ro_session_id.s); + session->event_type = delayed_delete; + session->last_event_timestamp = get_current_time_micro(); + insert_ro_timer(&session->ro_tl, 120); + + return 0; +} + /*! * \brief Destroy an ro_session and free memory * \param ro_session destroyed Ro Session @@ -140,7 +152,6 @@ inline void destroy_ro_session(struct ro_session *ro_session) { shm_free(ro_session->ro_session_id.s); } - shm_free(ro_session); } @@ -180,7 +191,7 @@ struct ro_session* build_new_ro_session(int direction, int auth_appid, int auth_ int active_rating_group, int active_service_identifier, str *incoming_trunk_id, str *outgoing_trunk_id, str *pani){ LM_DBG("Building Ro Session **********"); char *p; - unsigned int len = session_id->len + callid->len + asserted_identity->len + called_asserted_identity->len + mac->len + + unsigned int len = /*session_id->len + */callid->len + asserted_identity->len + called_asserted_identity->len + mac->len + incoming_trunk_id->len + outgoing_trunk_id->len + pani->len + sizeof (struct ro_session); struct ro_session *new_ro_session = (struct ro_session*) shm_malloc(len); @@ -203,7 +214,7 @@ struct ro_session* build_new_ro_session(int direction, int auth_appid, int auth_ new_ro_session->auth_appid = auth_appid; new_ro_session->auth_session_type = auth_session_type; - new_ro_session->ro_tl.next = new_ro_session->ro_tl.prev; + new_ro_session->ro_tl.next = new_ro_session->ro_tl.prev;// = 0; new_ro_session->ro_tl.timeout = 0; //requested_secs; new_ro_session->reserved_secs = requested_secs; @@ -227,11 +238,6 @@ struct ro_session* build_new_ro_session(int direction, int auth_appid, int auth_ memcpy(p, callid->s, callid->len); p += callid->len; - new_ro_session->ro_session_id.s = p; - new_ro_session->ro_session_id.len = session_id->len; - memcpy(p, session_id->s, session_id->len); - p += session_id->len; - new_ro_session->asserted_identity.s = p; new_ro_session->asserted_identity.len = asserted_identity->len; memcpy(p, asserted_identity->s, asserted_identity->len); @@ -252,8 +258,8 @@ struct ro_session* build_new_ro_session(int direction, int auth_appid, int auth_ memcpy(p, outgoing_trunk_id->s, outgoing_trunk_id->len); p += outgoing_trunk_id->len; - new_ro_session->avp_value.mac.s = p; - new_ro_session->avp_value.mac.len = mac->len; + new_ro_session->mac.s = p; + new_ro_session->mac.len = mac->len; memcpy(p, mac->s, mac->len); p += mac->len; diff --git a/modules/ims_charging/ro_session_hash.h b/modules/ims_charging/ro_session_hash.h index e481f886cad..dcd79e5f262 100644 --- a/modules/ims_charging/ro_session_hash.h +++ b/modules/ims_charging/ro_session_hash.h @@ -23,26 +23,25 @@ #define MAX_PANI_LEN 100 enum ro_session_event_type { - unknown = 0, pending, answered, no_more_credit, - unknown_error + delayed_delete, + unknown_error, }; struct diameter_avp_value { - str mac; + str mac; }; //used to pass data into dialog callbacks + struct impu_data { str identity; str contact; } impu_data_t; - struct ro_session { - str cdp_session_id; volatile int ref; int direction; struct ro_session* next; @@ -69,7 +68,7 @@ struct ro_session { int auth_session_type; int active; unsigned int flags; - struct diameter_avp_value avp_value; + str mac; int rating_group; int service_identifier; }; @@ -141,7 +140,7 @@ extern struct ro_session_table *ro_session_table; if ((_ro_session)->ref<=0) { \ unlink_unsafe_ro_session( _ro_session_entry, _ro_session);\ LM_DBG("ref <=0 for ro_session %p\n",_ro_session);\ - destroy_ro_session(_ro_session);\ + put_ro_session_on_wait(_ro_session);\ }\ }while(0) @@ -193,9 +192,9 @@ void link_ro_session(struct ro_session *ro_session, int n); void remove_aaa_session(str *session_id); -struct ro_session* build_new_ro_session(int direction, int auth_appid, int auth_session_type, str *session_id, str *callid, str *asserted_identity, str* called_asserted_identity, - str* mac, unsigned int dlg_h_entry, unsigned int dlg_h_id, unsigned int requested_secs, unsigned int validity_timeout, - int active_rating_group, int active_service_identifier, str *incoming_trunk_id, str *outgoing_trunk_id, str *pani); +struct ro_session* build_new_ro_session(int direction, int auth_appid, int auth_session_type, str *session_id, str *callid, str *asserted_identity, str* called_asserted_identity, + str* mac, unsigned int dlg_h_entry, unsigned int dlg_h_id, unsigned int requested_secs, unsigned int validity_timeout, + int active_rating_group, int active_service_identifier, str *incoming_trunk_id, str *outgoing_trunk_id, str *pani); /*! * \brief Refefence a ro_session with locking @@ -217,6 +216,8 @@ struct ro_session* lookup_ro_session(unsigned int h_entry, str *callid, int dire void free_impu_data(struct impu_data *impu_data); +int put_ro_session_on_wait(struct ro_session* session); + #endif /* RO_SESSION_HASH_H */ diff --git a/modules/ims_charging/ro_timer.c b/modules/ims_charging/ro_timer.c index d1d036dd5fe..4e1c268ea0b 100644 --- a/modules/ims_charging/ro_timer.c +++ b/modules/ims_charging/ro_timer.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "../../mem/shm_mem.h" #include "../dialog_ng/dlg_load.h" @@ -97,10 +98,12 @@ static inline void insert_ro_timer_unsafe(struct ro_tl *tl) { } LM_DBG("inserting %p for %d\n", tl, tl->timeout); + LM_DBG("BEFORE ptr [%p], ptr->next [%p], ptr->next->prev [%p]", ptr, ptr->next, ptr->next->prev); tl->prev = ptr; tl->next = ptr->next; tl->prev->next = tl; tl->next->prev = tl; + LM_DBG("AFTER tl->prev [%p], tl->next [%p]", tl->prev, tl->next); } /*! @@ -147,7 +150,7 @@ static inline void remove_ro_timer_unsafe(struct ro_tl *tl) { */ int remove_ro_timer(struct ro_tl *tl) { lock_get(roi_timer->lock); - + if (tl->prev == NULL && tl->timeout == 0) { lock_release(roi_timer->lock); return 1; @@ -159,7 +162,7 @@ int remove_ro_timer(struct ro_tl *tl) { lock_release(roi_timer->lock); return -1; } - LM_DBG("TIMER REMOVED"); + LM_DBG("TIMER [%p] REMOVED\n", tl); remove_ro_timer_unsafe(tl); tl->next = NULL; tl->prev = NULL; @@ -179,6 +182,7 @@ int remove_ro_timer(struct ro_tl *tl) { int update_ro_timer(struct ro_tl *tl, int timeout) { lock_get(roi_timer->lock); + LM_DBG("Updating ro timer [%p] with timeout [%d]\n", tl, timeout); if (tl->next) { if (tl->prev == 0) { lock_release(roi_timer->lock); @@ -204,6 +208,8 @@ static inline struct ro_tl* get_expired_ro_sessions(unsigned int time) { lock_get(roi_timer->lock); + LM_DBG("my ticks are [%d]\n", time); + if (roi_timer->first.next == &(roi_timer->first) || roi_timer->first.next->timeout > time) { lock_release(roi_timer->lock); return 0; @@ -257,217 +263,206 @@ void ro_timer_routine(unsigned int ticks, void * attr) { } void resume_ro_session_ontimeout(struct interim_ccr *i_req) { - time_t now = time(0); - time_t used_secs; - struct ro_session_entry *ro_session_entry = NULL; - int call_terminated = 0; - - if (!i_req) { - LM_ERR("This is so wrong: i_req is NULL\n"); - return; - } - - ro_session_entry = &(ro_session_table->entries[i_req->ro_session->h_entry]); - ro_session_lock(ro_session_table, ro_session_entry); - LM_DBG("credit=%d credit_valid_for=%d", i_req->new_credit, i_req->credit_valid_for); - - used_secs = now - i_req->ro_session->last_event_timestamp; - - /* check to make sure diameter server is giving us sane values */ - if (i_req->new_credit > i_req->credit_valid_for) { - LM_WARN("That's weird, Diameter server gave us credit with a lower validity period :D. Setting reserved time to validity period instead \n"); - i_req->new_credit = i_req->credit_valid_for; - } - - if (i_req->new_credit > 0) { - //now insert the new timer - i_req->ro_session->last_event_timestamp = time(0); - i_req->ro_session->event_type = answered; - i_req->ro_session->valid_for = i_req->credit_valid_for; - - int ret = 0; - if (i_req->is_final_allocation) { - LM_DBG("This is a final allocation and call will end in %i seconds\n", i_req->new_credit); - i_req->ro_session->event_type = no_more_credit; - ret = insert_ro_timer(&i_req->ro_session->ro_tl, i_req->new_credit); - } - else { - int timer_timeout = i_req->new_credit; - - if (i_req->new_credit > ro_timer_buffer /*TIMEOUTBUFFER*/) { - - // We haven't finished using our 1st block of units, and we need to set the timer to - // (new_credit - ro_timer_buffer[5 secs]) to ensure we get new credit before our previous - // reservation is exhausted. This will only be done the first time, because the timer - // will always be fired 5 seconds before we run out of time thanks to this operation - - if ((now - i_req->ro_session->start_time) /* call time */ < i_req->ro_session->reserved_secs) - timer_timeout = i_req->new_credit - ro_timer_buffer; - else - timer_timeout = i_req->new_credit; - } - - ret = insert_ro_timer(&i_req->ro_session->ro_tl, timer_timeout); - - } - - // update to the new block of units we got - i_req->ro_session->reserved_secs = i_req->new_credit; - - if (ret != 0) { - LM_CRIT("unable to insert timer for Ro Session [%.*s]\n", - i_req->ro_session->ro_session_id.len, i_req->ro_session->ro_session_id.s); - } - else { - ref_ro_session_unsafe(i_req->ro_session, 1); - } - - if (ro_db_mode == DB_MODE_REALTIME) { - i_req->ro_session->flags |= RO_SESSION_FLAG_CHANGED; - if (update_ro_dbinfo_unsafe(i_req->ro_session) != 0) { - LM_ERR("Failed to update Ro session in DB... continuing\n"); - } - } - } - else { - /* just put the timer back in with however many seconds are left (if any!!! in which case we need to kill */ - /* also update the event type to no_more_credit to save on processing the next time we get here */ - i_req->ro_session->event_type = no_more_credit; - int whatsleft = i_req->ro_session->reserved_secs - used_secs; - if (whatsleft <= 0) { - // TODO we need to handle this situation more precisely. - // in case CCR times out, we get a call shutdown but the error message assumes it was due to a lack of credit. - // - LM_WARN("Immediately killing call due to no more credit *OR* no CCA received (timeout) after reservation request\n"); - - // - // we need to unlock the session or else we might get a deadlock on dlg_terminated() dialog callback. - // Do not unref the session because it will be made inside the dlg_terminated() function. - // - - //unref_ro_session_unsafe(i_req->ro_session, 1, ro_session_entry); - ro_session_unlock(ro_session_table, ro_session_entry); - - dlgb.lookup_terminate_dlg(i_req->ro_session->dlg_h_entry, i_req->ro_session->dlg_h_id, NULL ); - call_terminated = 1; - } - else { - LM_DBG("No more credit for user - letting call run out of money in [%i] seconds", whatsleft); - int ret = insert_ro_timer(&i_req->ro_session->ro_tl, whatsleft); - if (ret != 0) { - LM_CRIT("unable to insert timer for Ro Session [%.*s]\n", - i_req->ro_session->ro_session_id.len, i_req->ro_session->ro_session_id.s); - } - else { - ref_ro_session_unsafe(i_req->ro_session, 1); - } - } - } - - // - // if call was forcefully terminated, the lock was released before dlgb.lookup_terminate_dlg() function call. - // - if (!call_terminated) { - unref_ro_session_unsafe(i_req->ro_session, 1, ro_session_entry);//unref from the initial timer that fired this event. - ro_session_unlock(ro_session_table, ro_session_entry); - } - - shm_free(i_req); - LM_DBG("Exiting async ccr interim nicely"); + time_t now = get_current_time_micro(); + long used_secs; + struct ro_session_entry *ro_session_entry = NULL; + int call_terminated = 0; + + if (!i_req) { + LM_ERR("This is so wrong: i_req is NULL\n"); + return; + } + + ro_session_entry = &(ro_session_table->entries[i_req->ro_session->h_entry]); + ro_session_lock(ro_session_table, ro_session_entry); + LM_DBG("credit=%d credit_valid_for=%d", i_req->new_credit, i_req->credit_valid_for); + + used_secs = rint((now - i_req->ro_session->last_event_timestamp) / (float) 1000000); + + /* check to make sure diameter server is giving us sane values */ + if (i_req->new_credit > i_req->credit_valid_for) { + LM_WARN("That's weird, Diameter server gave us credit with a lower validity period :D. Setting reserved time to validity period instead \n"); + i_req->new_credit = i_req->credit_valid_for; + } + + if (i_req->new_credit > 0) { + //now insert the new timer + i_req->ro_session->last_event_timestamp = get_current_time_micro(); + i_req->ro_session->event_type = answered; + i_req->ro_session->valid_for = i_req->credit_valid_for; + + int ret = 0; + if (i_req->is_final_allocation) { + LM_DBG("This is a final allocation and call will end in %i seconds\n", i_req->new_credit); + i_req->ro_session->event_type = no_more_credit; + ret = insert_ro_timer(&i_req->ro_session->ro_tl, i_req->new_credit); + } else { + int timer_timeout = i_req->new_credit; + + if (i_req->new_credit > ro_timer_buffer /*TIMEOUTBUFFER*/) { + + // We haven't finished using our 1st block of units, and we need to set the timer to + // (new_credit - ro_timer_buffer[5 secs]) to ensure we get new credit before our previous + // reservation is exhausted. This will only be done the first time, because the timer + // will always be fired 5 seconds before we run out of time thanks to this operation + timer_timeout = i_req->new_credit - ro_timer_buffer; + } + + ret = insert_ro_timer(&i_req->ro_session->ro_tl, timer_timeout); + + } + + // update to the new block of units we got + i_req->ro_session->reserved_secs = i_req->new_credit; + + if (ret != 0) { + LM_CRIT("unable to insert timer for Ro Session [%.*s]\n", + i_req->ro_session->ro_session_id.len, i_req->ro_session->ro_session_id.s); + } else { + ref_ro_session_unsafe(i_req->ro_session, 1); + } + + if (ro_db_mode == DB_MODE_REALTIME) { + i_req->ro_session->flags |= RO_SESSION_FLAG_CHANGED; + if (update_ro_dbinfo_unsafe(i_req->ro_session) != 0) { + LM_ERR("Failed to update Ro session in DB... continuing\n"); + } + } + } else { + /* just put the timer back in with however many seconds are left (if any!!! in which case we need to kill */ + /* also update the event type to no_more_credit to save on processing the next time we get here */ + i_req->ro_session->event_type = no_more_credit; + int whatsleft = i_req->ro_session->reserved_secs - used_secs; + if (whatsleft <= 0) { + // TODO we need to handle this situation more precisely. + // in case CCR times out, we get a call shutdown but the error message assumes it was due to a lack of credit. + // + LM_WARN("Immediately killing call due to no more credit *OR* no CCA received (timeout) after reservation request\n"); + + // + // we need to unlock the session or else we might get a deadlock on dlg_terminated() dialog callback. + // Do not unref the session because it will be made inside the dlg_terminated() function. + // + + //unref_ro_session_unsafe(i_req->ro_session, 1, ro_session_entry); + ro_session_unlock(ro_session_table, ro_session_entry); + + dlgb.lookup_terminate_dlg(i_req->ro_session->dlg_h_entry, i_req->ro_session->dlg_h_id, NULL); + call_terminated = 1; + } else { + LM_DBG("No more credit for user - letting call run out of money in [%i] seconds", whatsleft); + int ret = insert_ro_timer(&i_req->ro_session->ro_tl, whatsleft); + if (ret != 0) { + LM_CRIT("unable to insert timer for Ro Session [%.*s]\n", + i_req->ro_session->ro_session_id.len, i_req->ro_session->ro_session_id.s); + } else { + ref_ro_session_unsafe(i_req->ro_session, 1); + } + } + } + + // + // if call was forcefully terminated, the lock was released before dlgb.lookup_terminate_dlg() function call. + // + if (!call_terminated) { + unref_ro_session_unsafe(i_req->ro_session, 1, ro_session_entry); //unref from the initial timer that fired this event. + ro_session_unlock(ro_session_table, ro_session_entry); + } + + shm_free(i_req); + LM_DBG("Exiting async ccr interim nicely"); } /* this is the function called when a we need to request more funds/credit. We need to try and reserve more credit. * If we cant we need to put a new timer to kill the call at the appropriate time */ void ro_session_ontimeout(struct ro_tl *tl) { - time_t now, used_secs, call_time; - - LM_DBG("We have a fired timer [p=%p] and tl=[%i].\n", tl, tl->timeout); - - /* find the session id for this timer*/ - struct ro_session* ro_session = ((struct ro_session*) ((char *) (tl) - (unsigned long) (&((struct ro_session*) 0)->ro_tl))); - - if (!ro_session) { - LM_ERR("Can't find a session. This is bad"); - return; - } - - LM_DBG("event-type=%d", ro_session->event_type); - -// if (!ro_session->active) { -// LM_ALERT("Looks like this session was terminated while requesting more units"); -// goto exit; -// return; -// } - - switch (ro_session->event_type) { - case answered: - now = time(0); - used_secs = now - ro_session->last_event_timestamp; - call_time = now - ro_session->start_time; - - counter_add(ims_charging_cnts_h.billed_secs, used_secs); - - if (ro_session->callid.s != NULL - && ro_session->dlg_h_entry >= 0 - && ro_session->dlg_h_id > 0 - && ro_session->ro_session_id.s != NULL) - { - LM_DBG("Found a session to re-apply for timing [%.*s] and user is [%.*s]\n", - ro_session->ro_session_id.len, - ro_session->ro_session_id.s, - ro_session->asserted_identity.len, - ro_session->asserted_identity.s); - - LM_DBG("Call session has been active for %i seconds. The last reserved secs was [%i] and the last event was [%i seconds] ago", - (unsigned int) call_time, - (unsigned int) ro_session->reserved_secs, - (unsigned int) used_secs); - - LM_DBG("Call session [p=%p]: we will now make a request for another [%i] of credit with a usage of [%i] seconds from the last bundle.\n", - ro_session, - interim_request_credits/* new reservation request amount */, - (unsigned int) used_secs/* charged seconds from previous reservation */); - - // Apply for more credit. - // - // The function call will return immediately and we will receive the reply asynchronously via a callback - send_ccr_interim(ro_session, (unsigned int) used_secs, interim_request_credits); - return; - } - else { - LM_ERR("Hmmm, the session we have either doesn't have all the data or something else has gone wrong.\n"); - /* put the timer back so the call will be killed according to previous timeout. */ - ro_session->event_type = unknown_error; - int ret = insert_ro_timer(&ro_session->ro_tl, - ro_session->reserved_secs - used_secs); - if (ret != 0) { - LM_CRIT("unable to insert timer for Ro Session [%.*s]\n", - ro_session->ro_session_id.len, ro_session->ro_session_id.s); - } - else { - ref_ro_session_unsafe(ro_session, 1); - } - LM_ERR("Immediately killing call due to unknown error\n"); - } - - break; - default: - LM_ERR("Diameter call session - event [%d]\n", ro_session->event_type); - - if (ro_session->event_type == no_more_credit) - LM_INFO("Call/session must be ended - no more funds.\n"); - else if (ro_session->event_type == unknown_error) - LM_ERR("last event caused an error. We will now tear down this session.\n"); - } - - - counter_inc(ims_charging_cnts_h.killed_calls); - - //unref_ro_session_unsafe(ro_session, 1, ro_session_entry); //unref from the initial timer that fired this event. -// ro_session_unlock(ro_session_table, ro_session_entry); - - dlgb.lookup_terminate_dlg(ro_session->dlg_h_entry, ro_session->dlg_h_id, NULL); - return; + time_t now, call_time; + long used_secs; + + LM_DBG("We have a fired timer [p=%p] and tl=[%i].\n", tl, tl->timeout); + + /* find the session id for this timer*/ + struct ro_session* ro_session = ((struct ro_session*) ((char *) (tl) - (unsigned long) (&((struct ro_session*) 0)->ro_tl))); + LM_DBG("offset for ro_tl is [%lu] and ro_session id is [%.*s]\n", (unsigned long) (&((struct ro_session*) 0)->ro_tl), ro_session->ro_session_id.len, ro_session->ro_session_id.s); + + if (!ro_session) { + LM_ERR("Can't find a session. This is bad"); + return; + } + + LM_DBG("event-type=%d", ro_session->event_type); + + // if (!ro_session->active) { + // LM_ALERT("Looks like this session was terminated while requesting more units"); + // goto exit; + // return; + // } + + switch (ro_session->event_type) { + case answered: + now = get_current_time_micro(); + used_secs = rint((now - ro_session->last_event_timestamp) / (float) 1000000); + call_time = rint((now - ro_session->start_time) / (float) 1000000); + + counter_add(ims_charging_cnts_h.billed_secs, used_secs); + + if (ro_session->callid.s != NULL + && ro_session->dlg_h_entry >= 0 + && ro_session->dlg_h_id > 0 + && ro_session->ro_session_id.s != NULL) { + LM_DBG("Found a session to re-apply for timing [%.*s] and user is [%.*s]\n", + ro_session->ro_session_id.len, + ro_session->ro_session_id.s, + ro_session->asserted_identity.len, + ro_session->asserted_identity.s); + + LM_DBG("Call session has been active for %i seconds. The last reserved secs was [%i] and the last event was [%i seconds] ago", + (unsigned int) call_time, + (unsigned int) ro_session->reserved_secs, + (unsigned int) used_secs); + + LM_DBG("Call session [p=%p]: we will now make a request for another [%i] of credit with a usage of [%i] seconds from the last bundle.\n", + ro_session, + interim_request_credits/* new reservation request amount */, + (unsigned int) used_secs/* charged seconds from previous reservation */); + + // Apply for more credit. + // + // The function call will return immediately and we will receive the reply asynchronously via a callback + send_ccr_interim(ro_session, (unsigned int) used_secs, interim_request_credits); + return; + } else { + LM_ERR("Hmmm, the session we have either doesn't have all the data or something else has gone wrong.\n"); + /* put the timer back so the call will be killed according to previous timeout. */ + ro_session->event_type = unknown_error; + int ret = insert_ro_timer(&ro_session->ro_tl, + (ro_session->reserved_secs - used_secs) / 1000000); + if (ret != 0) { + LM_CRIT("unable to insert timer for Ro Session [%.*s]\n", + ro_session->ro_session_id.len, ro_session->ro_session_id.s); + } else { + ref_ro_session_unsafe(ro_session, 1); + return; + } + LM_ERR("Immediately killing call due to unknown error\n"); + } + break; + case delayed_delete: + destroy_ro_session(ro_session); + return; + default: + LM_ERR("Diameter call session - event [%d]\n", ro_session->event_type); + + if (ro_session->event_type == no_more_credit) + LM_INFO("Call/session must be ended - no more funds.\n"); + else if (ro_session->event_type == unknown_error) + LM_ERR("last event caused an error. We will now tear down this session.\n"); + } + + counter_inc(ims_charging_cnts_h.killed_calls); + + dlgb.lookup_terminate_dlg(ro_session->dlg_h_entry, ro_session->dlg_h_id, NULL); + return; } diff --git a/modules/ims_qos/cdpeventprocessor.c b/modules/ims_qos/cdpeventprocessor.c index 88c71cc4b81..360cff13988 100644 --- a/modules/ims_qos/cdpeventprocessor.c +++ b/modules/ims_qos/cdpeventprocessor.c @@ -173,6 +173,7 @@ void cdp_cb_event_process() { cdp_cb_event_t *ev; udomain_t* domain; pcontact_t* pcontact; + pcontact_info_t contact_info; struct pcontact_info ci; memset(&ci, 0, sizeof (struct pcontact_info)); @@ -248,8 +249,19 @@ void cdp_cb_event_process() { LM_DBG("Unable to register usrloc domain....aborting\n"); return; } - ul.lock_udomain(domain, &p_session_data->registration_aor, &p_session_data->ip, p_session_data->recv_port); - if (ul.get_pcontact(domain, &p_session_data->registration_aor, &p_session_data->ip, p_session_data->recv_port, &pcontact) != 0) { + ul.lock_udomain(domain, &p_session_data->via_host, p_session_data->via_port, p_session_data->via_proto); + + contact_info.received_host = p_session_data->ip; + contact_info.received_port = p_session_data->recv_port; + contact_info.received_proto = p_session_data->recv_proto; + contact_info.searchflag = (1 << SEARCH_RECEIVED); + + contact_info.via_host = p_session_data->via_host; + contact_info.via_port = p_session_data->via_port; + contact_info.via_prot = p_session_data->via_proto; + contact_info.aor = p_session_data->registration_aor; + + if (ul.get_pcontact(domain, &contact_info, &pcontact) != 0) { LM_DBG("no contact found for terminated Rx reg session..... ignoring\n"); } else { LM_DBG("Updating contact [%.*s] after Rx reg session terminated, setting state to PCONTACT_DEREG_PENDING_PUBLISH\n", pcontact->aor.len, pcontact->aor.s); @@ -257,7 +269,7 @@ void cdp_cb_event_process() { ci.num_service_routes = 0; ul.update_pcontact(domain, &ci, pcontact); } - ul.unlock_udomain(domain, &p_session_data->registration_aor, &p_session_data->ip, p_session_data->recv_port); + ul.unlock_udomain(domain, &p_session_data->via_host, p_session_data->via_port, p_session_data->via_proto); counter_add(ims_qos_cnts_h.active_registration_rx_sessions, -1); } } else { diff --git a/modules/ims_qos/mod.c b/modules/ims_qos/mod.c index d8266453930..56be1d85b41 100644 --- a/modules/ims_qos/mod.c +++ b/modules/ims_qos/mod.c @@ -589,7 +589,7 @@ static int w_rx_aar(struct sip_msg *msg, char *route, char* dir, char *c_id, int str s_id; struct hdr_field *h=0; struct dlg_cell* dlg = 0; - + cfg_action_t* cfg_action = 0; saved_transaction_t* saved_t_data = 0; //data specific to each contact's AAR async call char* direction = dir; @@ -622,7 +622,6 @@ static int w_rx_aar(struct sip_msg *msg, char *route, char* dir, char *c_id, int //We don't ever do AAR on request for calling scenario... if (msg->first_line.type != SIP_REPLY) { LM_DBG("Can't do AAR for call session in request\n"); - result = CSCF_RETURN_FALSE; return result; } @@ -635,19 +634,17 @@ static int w_rx_aar(struct sip_msg *msg, char *route, char* dir, char *c_id, int } //we dont apply QoS if its not a reply to an INVITE! or UPDATE or PRACK! - if ((t->method.len == 5 && memcmp(t->method.s, "PRACK", 5) == 0) - || (t->method.len == 6 && (memcmp(t->method.s, "INVITE", 6) == 0 - || memcmp(t->method.s, "UPDATE", 6) == 0))) { - if (cscf_get_content_length(msg) == 0 - || cscf_get_content_length(t->uas.request) == 0) { - LM_DBG("No SDP offer answer -> therefore we can not do Rx AAR"); - //goto aarna; //AAR na if we dont have offer/answer pair - result = CSCF_RETURN_FALSE; - return result; - } + if ((t->method.len == 5 && memcmp(t->method.s, "PRACK", 5) == 0) + || (t->method.len == 6 && (memcmp(t->method.s, "INVITE", 6) == 0 + || memcmp(t->method.s, "UPDATE", 6) == 0))) { + if (cscf_get_content_length(msg) == 0 + || cscf_get_content_length(t->uas.request) == 0) { + LM_DBG("No SDP offer answer -> therefore we can not do Rx AAR"); + //goto aarna; //AAR na if we dont have offer/answer pair + return result; + } } else { LM_DBG("Message is not response to INVITE, PRACK or UPDATE -> therefore we do not Rx AAR"); - result = CSCF_RETURN_FALSE; return result; } @@ -882,7 +879,13 @@ static int w_rx_aar(struct sip_msg *msg, char *route, char* dir, char *c_id, int LM_DBG("Attached CDP auth session [%.*s] for Rx to dialog in %s mode\n", auth_session->id.len, auth_session->id.s, direction); } else { LM_DBG("Update AAR session for this dialog in mode %s\n", direction); - saved_t_data->aar_update = 1;//this is an update aar - we set this so on async_aar we know this is an update and act accordingly + //check if this is triggered by a 183 - if so break here as its probably a re-transmit + if((msg->first_line).u.reply.statuscode == 183) { + LM_ERR("Received a 183 for a diameter session that already exists - just going to ignore this\n"); + cdpb.AAASessionsUnlock(auth_session->hash); + goto ignore; + } + saved_t_data->aar_update = 1;//this is an update aar - we set this so on async_aar we know this is an update and act accordingly } LM_DBG("Suspending SIP TM transaction\n"); @@ -909,6 +912,7 @@ static int w_rx_aar(struct sip_msg *msg, char *route, char* dir, char *c_id, int error: LM_ERR("Error trying to send AAR (calling)\n"); +ignore: if (saved_t_data) free_saved_transaction_global_data(saved_t_data); //only free global data if no AARs were sent. if one was sent we have to rely on the callback (CDP) to free //otherwise the callback will segfault @@ -924,6 +928,7 @@ static int w_rx_aar_register(struct sip_msg *msg, char* route, char* str1, char* contact_t* c; struct hdr_field* h; pcontact_t* pcontact; + pcontact_info_t contact_info; contact_body_t* cb = 0; AAASession* auth; rx_authsessiondata_t* rx_regsession_data_p; @@ -935,6 +940,11 @@ static int w_rx_aar_register(struct sip_msg *msg, char* route, char* str1, char* saved_transaction_t* saved_t_data = 0; //data specific to each contact's AAR async call str recv_ip; int recv_port; + unsigned short recv_proto; + + struct via_body* vb; + unsigned short via_port; + unsigned short via_proto; if (fixup_get_svalue(msg, (gparam_t*) route, &route_name) != 0) { LM_ERR("no async route block for assign_server_unreg\n"); @@ -1001,6 +1011,10 @@ static int w_rx_aar_register(struct sip_msg *msg, char* route, char* str1, char* } } + vb = cscf_get_ue_via(msg); + via_port = vb->port?vb->port:5060; + via_proto = vb->proto; + //before we continue, make sure we have a transaction to work with (viz. cdp async) t = tmb.t_gett(); if (t == NULL || t == T_UNDEFINED) { @@ -1053,17 +1067,27 @@ static int w_rx_aar_register(struct sip_msg *msg, char* route, char* str1, char* recv_ip.s = ip_addr2a(&msg->rcv.src_ip); recv_ip.len = strlen(ip_addr2a(&msg->rcv.src_ip)); recv_port = msg->rcv.src_port; + recv_proto = msg->rcv.proto; + LM_DBG("Message received IP address is: [%.*s]\n", recv_ip.len, recv_ip.s); - uint16_t ip_version = AF_INET; //TODO IPv6!!!? + LM_DBG("Message via is [%d://%.*s:%d]\n", vb->proto, vb->host.len, vb->host.s, via_port); lock_get(saved_t_data->lock); //we lock here to make sure we send all requests before processing replies asynchronously for (h = msg->contact; h; h = h->next) { if (h->type == HDR_CONTACT_T && h->parsed) { for (c = ((contact_body_t*) h->parsed)->contacts; c; c = c->next) { - ul.lock_udomain(domain_t, &c->uri, &recv_ip, recv_port); - if (ul.get_pcontact(domain_t, &c->uri, &recv_ip, recv_port, &pcontact) != 0) { - LM_DBG("This contact does not exist in PCSCF usrloc - error in cfg file\n"); - ul.unlock_udomain(domain_t, &c->uri, &recv_ip, recv_port); + ul.lock_udomain(domain_t, &vb->host, vb->port, vb->proto); + contact_info.aor = c->uri; + contact_info.via_host = vb->host; + contact_info.via_port = vb->port; + contact_info.via_prot = vb->proto; + contact_info.searchflag = SEARCH_NORMAL; + contact_info.received_host.s = 0; + contact_info.received_host.len = 0; + + if (ul.get_pcontact(domain_t, &contact_info, &pcontact) != 0) { + LM_ERR("This contact does not exist in PCSCF usrloc - error in cfg file\n"); + ul.unlock_udomain(domain_t, &vb->host, vb->port, vb->proto); lock_release(saved_t_data->lock); goto error; } else if (pcontact->reg_state == PCONTACT_REG_PENDING @@ -1087,10 +1111,10 @@ static int w_rx_aar_register(struct sip_msg *msg, char* route, char* str1, char* is_rereg = 1; } else { LM_DBG("Creating new Rx session for contact <%.*s>\n", pcontact->aor.len, pcontact->aor.s); - int ret = create_new_regsessiondata(domain_t->name, &pcontact->aor, &recv_ip, ip_version, recv_port, &rx_regsession_data_p); + int ret = create_new_regsessiondata(domain_t->name, &pcontact->aor, &recv_ip, AF_INET /* TODO: IPv6 support */, recv_port, recv_proto, &vb->host, vb->port, vb->proto, &rx_regsession_data_p); if (!ret) { LM_ERR("Unable to create regsession data parcel for rx_session_id [%.*s]...Aborting\n", pcontact->rx_session_id.len, pcontact->rx_session_id.s); - ul.unlock_udomain(domain_t, &c->uri, &recv_ip, recv_port); + ul.unlock_udomain(domain_t, &vb->host, vb->port, vb->proto); if (rx_regsession_data_p) { shm_free(rx_regsession_data_p); rx_regsession_data_p = 0; @@ -1105,7 +1129,7 @@ static int w_rx_aar_register(struct sip_msg *msg, char* route, char* str1, char* shm_free(rx_regsession_data_p); rx_regsession_data_p = 0; } - ul.unlock_udomain(domain_t, &c->uri, &recv_ip, recv_port); + ul.unlock_udomain(domain_t, &vb->host, via_port, via_proto); if (auth) cdpb.AAASessionsUnlock(auth->hash); if (rx_regsession_data_p) { shm_free(rx_regsession_data_p); @@ -1118,7 +1142,7 @@ static int w_rx_aar_register(struct sip_msg *msg, char* route, char* str1, char* } //we are ready to send the AAR async. lets save the local data data - int local_data_len = sizeof (saved_transaction_local_t) + c->uri.len + auth->id.len; + int local_data_len = sizeof (saved_transaction_local_t) + c->uri.len + auth->id.len + vb->host.len + recv_ip.len; local_data = shm_malloc(local_data_len); if (!local_data) { LM_ERR("unable to alloc memory for local data, trying to send AAR Register\n"); @@ -1141,6 +1165,21 @@ static int w_rx_aar_register(struct sip_msg *msg, char* route, char* str1, char* memcpy(p, auth->id.s, auth->id.len); p += auth->id.len; + local_data->via_host.s = p; + local_data->via_host.len = vb->host.len; + memcpy(p, vb->host.s, vb->host.len); + p += vb->host.len; + + local_data->recv_host.s = p; + local_data->recv_host.len = recv_ip.len; + memcpy(p, recv_ip.s, recv_ip.len); + p += recv_ip.len; + + local_data->via_port = via_port; + local_data->via_proto = via_proto; + local_data->recv_port = recv_port; + local_data->recv_proto = recv_proto; + if (p != (((char*) local_data) + local_data_len)) { LM_CRIT("buffer overflow\n"); free_saved_transaction_data(local_data); @@ -1153,7 +1192,7 @@ static int w_rx_aar_register(struct sip_msg *msg, char* route, char* str1, char* //ret = rx_send_aar_register(msg, auth, &puri.host, &ip_version, &c->uri, local_data); //returns a locked rx auth object ret = rx_send_aar_register(msg, auth, local_data); //returns a locked rx auth object - ul.unlock_udomain(domain_t, &c->uri, &recv_ip, recv_port); + ul.unlock_udomain(domain_t, &vb->host, via_port, via_proto); if (!ret) { LM_ERR("Failed to send AAR\n"); @@ -1168,7 +1207,7 @@ static int w_rx_aar_register(struct sip_msg *msg, char* route, char* str1, char* } else { //contact exists - this is a re-registration, for now we just ignore this LM_DBG("This contact exists and is not in state REGISTER PENDING - we assume re (or de) registration and ignore\n"); - ul.unlock_udomain(domain_t, &c->uri, &recv_ip, recv_port); + ul.unlock_udomain(domain_t, &vb->host, via_port, via_proto); //now we loop for any other contacts. } } diff --git a/modules/ims_qos/rx_aar.c b/modules/ims_qos/rx_aar.c index 4871dc5bd51..13d91ac3bea 100644 --- a/modules/ims_qos/rx_aar.c +++ b/modules/ims_qos/rx_aar.c @@ -98,8 +98,6 @@ void async_aar_callback(int is_timeout, void *param, AAAMessage *aaa, long elaps struct cell *t = 0; unsigned int cdp_result; int result = CSCF_RETURN_ERROR; - AAASession *auth = 0; - rx_authsessiondata_t* p_session_data = 0; LM_DBG("Received AAR callback\n"); saved_transaction_t* data = (saved_transaction_t*) param; @@ -147,29 +145,8 @@ void async_aar_callback(int is_timeout, void *param, AAAMessage *aaa, long elaps LM_DBG("Auth session ID [%.*s]", aaa->sessionId->data.len, aaa->sessionId->data.s); if(!data->aar_update) { - LM_DBG("This is an AAA response to an initial AAR - active_media_rx_sessions"); - - //need to set Rx auth data to say this session has been successfully opened - //This is used elsewhere to prevent acting on termination events when the session has not been opened - //getting auth session - auth = cdpb.AAAGetAuthSession(aaa->sessionId->data); - if (!auth) { - LM_DBG("Could not get Auth Session for session id: [%.*s]\n", aaa->sessionId->data.len, aaa->sessionId->data.s); - goto error; - } - //getting session data - p_session_data = (rx_authsessiondata_t*) auth->u.auth.generic_data; - if (!p_session_data) { - LM_DBG("Could not get session data on Auth Session for session id: [%.*s]\n", aaa->sessionId->data.len, aaa->sessionId->data.s); - if (auth) cdpb.AAASessionsUnlock(auth->hash); - goto error; - } - - LM_DBG("Setting session_has_been_opened and incrementing active_media_rx_sessions\n"); - p_session_data->session_has_been_opened = 1; + LM_DBG("This is an AAA response to an initial AAR"); counter_inc(ims_qos_cnts_h.active_media_rx_sessions); - - if (auth) cdpb.AAASessionsUnlock(auth->hash); str * passed_rx_session_id = shm_malloc(sizeof (struct _str)); passed_rx_session_id->s = 0; @@ -216,6 +193,7 @@ void async_aar_reg_callback(int is_timeout, void *param, AAAMessage *aaa, long e AAASession *auth = 0; rx_authsessiondata_t* p_session_data = 0; int result = CSCF_RETURN_ERROR; + pcontact_info_t contact_info; LM_DBG("Received AAR callback\n"); saved_transaction_local_t* local_data = (saved_transaction_local_t*) param; @@ -275,7 +253,7 @@ void async_aar_reg_callback(int is_timeout, void *param, AAAMessage *aaa, long e if (cdp_result >= 2000 && cdp_result < 3000) { counter_inc(ims_qos_cnts_h.successful_registration_aars); - if (is_rereg) { + if (is_rereg) { LM_DBG("this is a re-registration, therefore we don't need to do anything except know that the the subscription was successful\n"); result = CSCF_RETURN_TRUE; create_return_code(result); @@ -296,7 +274,7 @@ void async_aar_reg_callback(int is_timeout, void *param, AAAMessage *aaa, long e if (auth) cdpb.AAASessionsUnlock(auth->hash); goto error; } - p_session_data->session_has_been_opened = 1; + p_session_data->session_has_been_opened = 1; counter_inc(ims_qos_cnts_h.active_registration_rx_sessions); if (auth) cdpb.AAASessionsUnlock(auth->hash); @@ -307,10 +285,22 @@ void async_aar_reg_callback(int is_timeout, void *param, AAAMessage *aaa, long e local_data->auth_session_id.len, local_data->auth_session_id.s); LM_DBG("Registering for Usrloc callbacks on DELETE\n"); - ul.lock_udomain(domain_t, &local_data->contact, &p_session_data->ip, p_session_data->recv_port); - if (ul.get_pcontact(domain_t, &local_data->contact, &p_session_data->ip, p_session_data->recv_port, &pcontact) != 0) { - LM_ERR("Shouldn't get here, can find contact....\n"); - ul.unlock_udomain(domain_t, &local_data->contact, &p_session_data->ip, p_session_data->recv_port); + ul.lock_udomain(domain_t, &local_data->via_host, local_data->via_port, local_data->via_proto); + + contact_info.received_host = local_data->recv_host; + contact_info.received_port = local_data->recv_port; + contact_info.received_proto = local_data->recv_proto; + contact_info.searchflag = (1 << SEARCH_RECEIVED); + + + contact_info.aor = local_data->contact; + contact_info.via_host = local_data->via_host; + contact_info.via_port = local_data->via_port; + contact_info.via_prot = local_data->via_proto; + + if (ul.get_pcontact(domain_t, &contact_info, &pcontact) != 0) { + LM_ERR("Shouldn't get here, can't find contact....\n"); + ul.unlock_udomain(domain_t, &local_data->via_host, local_data->via_port, local_data->via_proto); goto error; } @@ -320,7 +310,7 @@ void async_aar_reg_callback(int is_timeout, void *param, AAAMessage *aaa, long e * */ if (ul.update_rx_regsession(domain_t, &local_data->auth_session_id, pcontact) != 0) { LM_ERR("unable to update pcontact......\n"); - ul.unlock_udomain(domain_t, &local_data->contact, &p_session_data->ip, p_session_data->recv_port); + ul.unlock_udomain(domain_t, &local_data->via_host, local_data->via_port, local_data->via_proto); goto error; } memset(&ci, 0, sizeof (struct pcontact_info)); @@ -332,7 +322,7 @@ void async_aar_reg_callback(int is_timeout, void *param, AAAMessage *aaa, long e //register for callbacks on contact ul.register_ulcb(pcontact, PCSCF_CONTACT_DELETE | PCSCF_CONTACT_EXPIRE, callback_pcscf_contact_cb, NULL); - ul.unlock_udomain(domain_t, &local_data->contact, &p_session_data->ip, p_session_data->recv_port); + ul.unlock_udomain(domain_t, &local_data->via_host, local_data->via_port, local_data->via_proto); result = CSCF_RETURN_TRUE; } else { LM_DBG("Received negative reply from PCRF for AAR Request\n"); @@ -526,7 +516,7 @@ int rx_send_aar_update_no_video(AAASession* auth) { char x[4]; int ret = 0; - str ip; + str recv_ip; uint16_t ip_version; //we get ip and identifier for the auth session data @@ -534,7 +524,7 @@ int rx_send_aar_update_no_video(AAASession* auth) { p_session_data = (rx_authsessiondata_t*) auth->u.auth.generic_data; identifier = p_session_data->identifier; identifier_type = p_session_data->identifier_type; - ip = p_session_data->ip; + recv_ip = p_session_data->ip; ip_version = p_session_data->ip_version; aar = cdpb.AAACreateRequest(IMS_Rx, IMS_AAR, Flag_Proxyable, auth); @@ -630,9 +620,9 @@ int rx_send_aar_update_no_video(AAASession* auth) { add_media_components_using_current_flow_description(aar, p_session_data); - LM_DBG("Adding framed ip address [%.*s]\n", ip.len, ip.s); + LM_DBG("Adding framed ip address [%.*s]\n", recv_ip.len, recv_ip.s); /* Add Framed IP address AVP*/ - if (!rx_add_framed_ip_avp(&aar->avpList, ip, ip_version)) { + if (!rx_add_framed_ip_avp(&aar->avpList, recv_ip, ip_version)) { LM_ERR("Unable to add framed IP AVP\n"); goto error; } diff --git a/modules/ims_qos/rx_aar.h b/modules/ims_qos/rx_aar.h index 3fc1e92fdf6..1214b8400d2 100644 --- a/modules/ims_qos/rx_aar.h +++ b/modules/ims_qos/rx_aar.h @@ -76,7 +76,13 @@ typedef struct saved_transaction { typedef struct saved_transaction_local { int is_rereg; - str contact; + str contact; /* contact AOR */ + str via_host; /* host address of UE - first via for REQUEST, last via for REPLY */ + unsigned short via_port; /* port of UE based on VIA */ + unsigned short via_proto; /* via proto */ + str recv_host; /* host address of UE - first via for REQUEST, last via for REPLY */ + unsigned short recv_port; /* port of UE based on VIA */ + unsigned short recv_proto; /* via proto */ str auth_session_id; saved_transaction_t* global_data; } saved_transaction_local_t; diff --git a/modules/ims_qos/rx_authdata.c b/modules/ims_qos/rx_authdata.c index bc99492ed07..cbce4ef264c 100644 --- a/modules/ims_qos/rx_authdata.c +++ b/modules/ims_qos/rx_authdata.c @@ -68,9 +68,9 @@ #include "../../lib/ims/ims_getters.h" #include "mod.h" -int create_new_regsessiondata(str* domain, str* aor, str *ip, int ip_version, int recv_port, rx_authsessiondata_t** session_data) { +int create_new_regsessiondata(str* domain, str* aor, str *ip, int ip_version, int recv_port, unsigned short recv_proto, str *via_host, unsigned short via_port, unsigned short via_proto, rx_authsessiondata_t** session_data) { - int len = (domain->len + 1) + aor->len + ip->len + sizeof(rx_authsessiondata_t); + int len = (domain->len + 1) + ip->len + aor->len + via_host->len + sizeof(rx_authsessiondata_t); rx_authsessiondata_t* p_session_data = shm_malloc(len); if (!p_session_data) { LM_ERR("no more shm memory\n"); @@ -82,8 +82,12 @@ int create_new_regsessiondata(str* domain, str* aor, str *ip, int ip_version, in p_session_data->must_terminate_dialog = 0; /*irrelevent for reg session data this will always be 0 */ p_session_data->session_has_been_opened = 0; /*0 has not been opened 1 has been opened*/ - p_session_data->ip_version = ip_version; - p_session_data->recv_port = recv_port; + p_session_data->ip_version = ip_version; + p_session_data->via_port = via_port; + p_session_data->via_proto = via_proto; + + p_session_data->recv_port = recv_port; + p_session_data->recv_proto = recv_proto; char* p = (char*)(p_session_data + 1); p_session_data->domain.s = p; @@ -97,10 +101,15 @@ int create_new_regsessiondata(str* domain, str* aor, str *ip, int ip_version, in p_session_data->registration_aor.len = aor->len; p += aor->len; - p_session_data->ip.s = p; + p_session_data->ip.s = p; memcpy(p, ip->s, ip->len); p_session_data->ip.len = ip->len; p += ip->len; + + p_session_data->via_host.s = p; + memcpy(p, via_host->s, via_host->len); + p_session_data->via_host.len = via_host->len; + p += via_host->len; if (p != (((char*)p_session_data) + len)) { LM_ERR("buffer over/underflow\n"); @@ -338,7 +347,7 @@ void free_callsessiondata(rx_authsessiondata_t* session_data) { if(!session_data){ return; } - + LM_DBG("Freeing session data for [%.*s]\n", session_data->via_host.len, session_data->via_host.s); LM_DBG("Destroy current flow description\n"); free_flow_description(session_data, 1); diff --git a/modules/ims_qos/rx_authdata.h b/modules/ims_qos/rx_authdata.h index 88141677e55..b186cd0e215 100644 --- a/modules/ims_qos/rx_authdata.h +++ b/modules/ims_qos/rx_authdata.h @@ -81,9 +81,13 @@ typedef struct rx_authsessiondata { str ttag; str identifier; int identifier_type; + str via_host; /* UE host as fetched from Via (first for REQUEST, last for REPLY) */ + unsigned short via_port; /* UE port as fetched from Via (first for REQUEST, last for REPLY) */ + unsigned short via_proto; /* UE proto as fetched from Via (first for REQUEST, last for REPLY) */ str ip; - int recv_port; int ip_version; + int recv_port; + unsigned short recv_proto; //for registration session int subscribed_to_signaling_path_status; // 0 not subscribed 1 is subscribed int session_has_been_opened; // 0 has not been opened 1 has been opened @@ -94,7 +98,7 @@ typedef struct rx_authsessiondata { flow_description_t *first_new_flow_description; } rx_authsessiondata_t; -int create_new_regsessiondata(str* domain, str* aor, str *ip, int ip_version, int recv_port, rx_authsessiondata_t** session_data); +int create_new_regsessiondata(str* domain, str* aor, str *ip, int ip_version, int recv_port, unsigned short recv_proto, str *via_host, unsigned short via_port, unsigned short via_proto, rx_authsessiondata_t** session_data); int create_new_callsessiondata(str* callid, str* ftag, str* ttag, str* identifier, int identifier_type, str *ip, int ip_version, rx_authsessiondata_t** session_data); void free_callsessiondata(rx_authsessiondata_t* session_data); diff --git a/modules/ims_registrar_pcscf/lookup.c b/modules/ims_registrar_pcscf/lookup.c deleted file mode 100644 index b0f7ae23679..00000000000 --- a/modules/ims_registrar_pcscf/lookup.c +++ /dev/null @@ -1,107 +0,0 @@ -/* - * $Id$ - * - * Copyright (C) 2012 Smile Communications, jason.penton@smilecoms.com - * Copyright (C) 2012 Smile Communications, richard.good@smilecoms.com - * - * The initial version of this code was written by Dragos Vingarzan - * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the - * Fruanhofer Institute. It was and still is maintained in a separate - * branch of the original SER. We are therefore migrating it to - * Kamailio/SR and look forward to maintaining it from here on out. - * 2011/2012 Smile Communications, Pty. Ltd. - * ported/maintained/improved by - * Jason Penton (jason(dot)penton(at)smilecoms.com and - * Richard Good (richard(dot)good(at)smilecoms.com) as part of an - * effort to add full IMS support to Kamailio/SR using a new and - * improved architecture - * - * NB: Alot of this code was originally part of OpenIMSCore, - * FhG Fokus. - * Copyright (C) 2004-2006 FhG Fokus - * Thanks for great work! This is an effort to - * break apart the various CSCF functions into logically separate - * components. We hope this will drive wider use. We also feel - * that in this way the architecture is more complete and thereby easier - * to manage in the Kamailio/SR environment - * - * This file is part of Kamailio, a free SIP server. - * - * Kamailio 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 - * - * Kamailio 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 -#include "reg_mod.h" -#include "lookup.h" - -extern usrloc_api_t ul; - -/*! \brief - * Lookup contact in the database and rewrite Request-URI - * \return: 1 : contacts found and returned - * -1 : not found - * -2 : error - */ -int lookup_transport(struct sip_msg* _m, udomain_t* _d, str* _uri) { - str uri; - pcontact_t* pcontact; - char tmp[MAX_URI_SIZE]; - str received_host = {0,0}; - str tmp_s; - int ret = 1; - - if (_m->new_uri.s) uri = _m->new_uri; - else uri = _m->first_line.u.request.uri; - - //now lookup in usrloc - ul.lock_udomain(_d, &uri, &received_host, _m->rcv.src_port); - if (ul.get_pcontact(_d, &uri, &received_host, _m->rcv.src_port, &pcontact) != 0) { //need to insert new contact - LM_WARN("received request for contact that we don't know about\n"); - ret = -1; - goto done; - } - - if (pcontact->received_proto != _m->rcv.proto) { - reset_dst_uri(_m); - memset(tmp, 0, MAX_URI_SIZE); - switch (pcontact->received_proto) { - case PROTO_TCP: - snprintf(tmp, MAX_URI_SIZE, "%.*s;transport=tcp", pcontact->aor.len, pcontact->aor.s); - break; - case PROTO_UDP: - snprintf(tmp, MAX_URI_SIZE, "%.*s;transport=udp", pcontact->aor.len, pcontact->aor.s); - break; - default: - LM_WARN("unsupported transport [%d]\n", pcontact->received_proto); - ret = -2; - goto done; - } - - tmp_s.s = tmp; - tmp_s.len = strlen(tmp); - if (set_dst_uri(_m, &tmp_s) < 0) { - LM_ERR("failed to set dst_uri for terminating UE\n"); - ret = -2; - goto done; - } - LM_DBG("Changed dst URI transport for UE to [%.*s]\n", tmp_s.len, tmp_s.s); - } - -done: - ul.unlock_udomain(_d, &uri, &received_host, _m->rcv.src_port); - return ret; -} - diff --git a/modules/ims_registrar_pcscf/lookup.h b/modules/ims_registrar_pcscf/lookup.h deleted file mode 100644 index 746e15c023d..00000000000 --- a/modules/ims_registrar_pcscf/lookup.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * $Id$ - * - * Copyright (C) 2012 Smile Communications, jason.penton@smilecoms.com - * Copyright (C) 2012 Smile Communications, richard.good@smilecoms.com - * - * The initial version of this code was written by Dragos Vingarzan - * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the - * Fruanhofer Institute. It was and still is maintained in a separate - * branch of the original SER. We are therefore migrating it to - * Kamailio/SR and look forward to maintaining it from here on out. - * 2011/2012 Smile Communications, Pty. Ltd. - * ported/maintained/improved by - * Jason Penton (jason(dot)penton(at)smilecoms.com and - * Richard Good (richard(dot)good(at)smilecoms.com) as part of an - * effort to add full IMS support to Kamailio/SR using a new and - * improved architecture - * - * NB: Alot of this code was originally part of OpenIMSCore, - * FhG Fokus. - * Copyright (C) 2004-2006 FhG Fokus - * Thanks for great work! This is an effort to - * break apart the various CSCF functions into logically separate - * components. We hope this will drive wider use. We also feel - * that in this way the architecture is more complete and thereby easier - * to manage in the Kamailio/SR environment - * - * This file is part of Kamailio, a free SIP server. - * - * Kamailio 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 - * - * Kamailio 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 LOOKUP_H -#define LOOKUP_H - -#include "../../parser/msg_parser.h" -#include "../../modules/ims_usrloc_pcscf/usrloc.h" - - -/*! \brief - * Lookup a contact in ims_usrloc_pcscf and rewrite R-URI if found and received request transport is different to contact transport - */ -int lookup_transport(struct sip_msg* _m, udomain_t* _d, str* _uri); - -#endif /* LOOKUP_H */ - diff --git a/modules/ims_registrar_pcscf/notify.c b/modules/ims_registrar_pcscf/notify.c index 44089a9e421..f6010b46145 100644 --- a/modules/ims_registrar_pcscf/notify.c +++ b/modules/ims_registrar_pcscf/notify.c @@ -72,356 +72,434 @@ #define RESULT_ERROR -1 #define RESULT_CONTACTS_FOUND 1 +#define RESULT_TERMINATED_SUCCESS 1 + + extern usrloc_api_t ul; extern time_t time_now; -int process_contact(udomain_t * _d, int expires, str contact_uri, str * received_host, int received_port, int contact_state) { - - pcontact_t* pcontact; - - struct pcontact_info ci; - - int local_time_now; - int ret = RESULT_CONTACTS_FOUND; - - pcscf_act_time(); - local_time_now = time_now; - - //get contact - //if does not exist then add it - //if it does exist check if state it terminated - if so delete it, if not update it - - memset(&ci, 0, sizeof(struct pcontact_info)); - ci.num_public_ids=0; - ci.num_service_routes=0; - - expires = local_time_now + expires; //turn expires into correct time since epoch format - LM_DBG("Changed expires to format time since the epoch: %d", expires); - ci.expires=expires; - ci.reg_state = PCONTACT_REGISTERED; - - - ul.lock_udomain(_d, &contact_uri, received_host, received_port); - - if (ul.get_pcontact(_d, &contact_uri, received_host, received_port, &pcontact) != 0) { //contact does not exist - if (contact_state == STATE_TERMINATED) { - LM_DBG("This contact: <%.*s> is in state terminated and is not in usrloc, ignore\n", contact_uri.len, contact_uri.s); - ret = RESULT_CONTACTS_FOUND; - goto done; - } - LM_DBG("This contact: <%.*s> is in state active and is not in usrloc so adding it to usrloc, expires: %d which is in %d seconds\n", contact_uri.len, contact_uri.s, expires, expires-local_time_now); - if (ul.insert_pcontact(_d, &contact_uri, &ci, &pcontact) != 0) { - LM_ERR("Failed inserting new pcontact\n"); - ret = RESULT_ERROR; - goto done; - } else { - //register for callbacks on this contact so we can send PUBLISH to SCSCF should status change - LM_DBG("registering for UL callback\n"); - ul.register_ulcb(pcontact, PCSCF_CONTACT_DELETE | PCSCF_CONTACT_EXPIRE, callback_pcscf_contact_cb, NULL); - } - } else {//contact exists - if (contact_state == STATE_TERMINATED) { - //delete contact - LM_DBG("This contact <%.*s> is in state terminated and is in usrloc so removing it from usrloc\n", contact_uri.len, contact_uri.s); - if (ul.delete_pcontact(_d, &contact_uri, received_host, received_port, pcontact) != 0) { - LM_DBG("failed to delete pcscf contact <%.*s> - not a problem this may have been removed by de registration", contact_uri.len, contact_uri.s); - } - }else {//state is active - //update this contact - LM_DBG("This contact: <%.*s> is in state active and is in usrloc so just updating - old expires: %li, new expires: %i which is in %i seconds\n", contact_uri.len, contact_uri.s, - pcontact->expires, - expires, - expires-local_time_now); - if (ul.update_pcontact(_d, &ci, pcontact) != 0) { - LM_ERR("failed to update pcscf contact\n"); - ret = RESULT_ERROR; - goto done; - } - pcontact->expires = expires; - } - } - -done: - ul.unlock_udomain(_d, &contact_uri, received_host, received_port); - return ret; -} +int process_contact(udomain_t * _d, int expires, str contact_uri, int contact_state) { + char bufport[5], *rest, *sep, *val, *port, *trans; + pcontact_t* pcontact; + struct pcontact_info ci; + struct sip_uri puri, uri; + unsigned int received_proto, received_port_len; + int local_time_now, rest_len, val_len, has_alias; + int ret = RESULT_CONTACTS_FOUND; + + pcscf_act_time(); + local_time_now = time_now; + + has_alias = 0; + + //get contact + //if does not exist then add it + //if it does exist check if state it terminated - if so delete it, if not update it + + memset(&ci, 0, sizeof (struct pcontact_info)); + ci.num_public_ids = 0; + ci.num_service_routes = 0; + + LM_DBG("Processing contact using contact from NOTIFY [%.*s]\n", contact_uri.len, contact_uri.s); + if (parse_uri(contact_uri.s, contact_uri.len, &puri) < 0) { + LM_DBG("Error parsing Contact URI <%.*s>\n", contact_uri.len, contact_uri.s); + return RESULT_ERROR; + } + + expires = local_time_now + expires; //turn expires into correct time since epoch format + LM_DBG("Changed expires to format time since the epoch: %d", expires); + ci.expires = expires; + ci.reg_state = PCONTACT_REGISTERED; + + + ul.lock_udomain(_d, &puri.host, puri.port_no, puri.proto); + ci.aor = contact_uri; + ci.via_host = puri.host; + ci.via_prot = puri.proto; + ci.via_port = puri.port_no; + /* parse the uri in the NOTIFY */ + if (parse_uri(contact_uri.s, contact_uri.len, &uri) != 0) { + LM_ERR("Unable to parse contact in SIP notify [%.*s]\n", contact_uri.len, contact_uri.s); + return RESULT_ERROR; + } + /*check for alias - NAT */ + rest = uri.sip_params.s; + rest_len = uri.sip_params.len; + + while (rest_len >= ALIAS_LEN) { + if (strncmp(rest, ALIAS, ALIAS_LEN) == 0) { + has_alias = 1; + break; + } + sep = memchr(rest, 59 /* ; */, rest_len); + if (sep == NULL) { + LM_DBG("no alias param\n"); + break; + } else { + rest_len = rest_len - (sep - rest + 1); + rest = sep + 1; + } + } + + if (has_alias) { + val = rest + ALIAS_LEN; + val_len = rest_len - ALIAS_LEN; + port = memchr(val, 126 /* ~ */, val_len); + if (port == NULL) { + LM_ERR("no '~' in alias param value\n"); + return RESULT_ERROR; + } + port++; + // received_port = atoi(port); + trans = memchr(port, 126 /* ~ */, val_len - (port - val)); + if (trans == NULL) { + LM_ERR("no second '~' in alias param value\n"); + return RESULT_ERROR; + } + + received_port_len = trans - port; + + trans = trans + 1; + received_proto = *trans - 48 /* char 0 */; + + memcpy(bufport, port, received_port_len); + bufport[received_port_len]=0; + + ci.received_host.s = val; + ci.received_host.len = port - val - 1; + LM_DBG("Setting received host in search to [%.*s]\n", ci.received_host.len, ci.received_host.s); + ci.received_port = atoi(bufport); + LM_DBG("Setting received port in search to %d\n", ci.received_port); + ci.received_proto = received_proto; + LM_DBG("Setting received proto in search to %d\n", ci.received_proto); + ci.searchflag = (1 << SEARCH_RECEIVED); + } else { + LM_DBG("Contact in NOTIFY does not have an alias....\n"); + } + + if (ul.get_pcontact(_d, &ci, &pcontact) != 0) { //contact does not exist + if (contact_state == STATE_TERMINATED) { + LM_DBG("This contact: <%.*s> is in state terminated and is not in usrloc, ignore\n", contact_uri.len, contact_uri.s); + ret = RESULT_CONTACTS_FOUND; + goto done; + } + LM_WARN("This contact: <%.*s> is in state active and is not in usrloc - must be another contact on a different P so going to ignore\n", contact_uri.len, contact_uri.s); + // LM_DBG("This contact: <%.*s> is in state active and is not in usrloc so adding it to usrloc, expires: %d which is in %d seconds\n", contact_uri.len, contact_uri.s, expires, expires-local_time_now); + // if (ul.insert_pcontact(_d, &contact_uri, &ci, &pcontact) != 0) { + // LM_ERR("Failed inserting new pcontact\n"); + // ret = RESULT_ERROR; + // goto done; + // } else { + // //register for callbacks on this contact so we can send PUBLISH to SCSCF should status change + // LM_DBG("registering for UL callback\n"); + // ul.register_ulcb(pcontact, PCSCF_CONTACT_DELETE | PCSCF_CONTACT_EXPIRE, callback_pcscf_contact_cb, NULL); + // } + } else {//contact exists + if (contact_state == STATE_TERMINATED) { + //delete contact + LM_DBG("This contact <%.*s> is in state terminated and is in usrloc so removing it from usrloc\n", contact_uri.len, contact_uri.s); + if (ul.delete_pcontact(_d, pcontact) != 0) { + LM_DBG("failed to delete pcscf contact <%.*s> - not a problem this may have been removed by de registration", contact_uri.len, contact_uri.s); + } + /*TODO_LATEST - put this back */ + } else {//state is active + //update this contact + LM_DBG("This contact: <%.*s> is in state active and is in usrloc so just updating - old expires: %li, new expires: %i which is in %i seconds\n", contact_uri.len, contact_uri.s, + pcontact->expires, + expires, + expires - local_time_now); + if (ul.update_pcontact(_d, &ci, pcontact) != 0) { + LM_ERR("failed to update pcscf contact\n"); + ret = RESULT_ERROR; + goto done; + } + pcontact->expires = expires; + } + } + +done: + ul.unlock_udomain(_d, &puri.host, puri.port_no, puri.proto); + return ret; +} int reginfo_parse_state(char * s) { - if (s == NULL) { - return STATE_UNKNOWN; - } - switch (strlen(s)) { - case 6: - if (strncmp(s, "active", 6) == 0) return STATE_ACTIVE; - break; - case 10: - if (strncmp(s, "terminated", 10) == 0) return STATE_TERMINATED; - break; - default: - LM_ERR("Unknown State %s\n", s); - return STATE_UNKNOWN; - } - LM_ERR("Unknown State %s\n", s); - return STATE_UNKNOWN; + if (s == NULL) { + return STATE_UNKNOWN; + } + switch (strlen(s)) { + case 6: + if (strncmp(s, "active", 6) == 0) return STATE_ACTIVE; + break; + case 10: + if (strncmp(s, "terminated", 10) == 0) return STATE_TERMINATED; + break; + default: + LM_ERR("Unknown State %s\n", s); + return STATE_UNKNOWN; + } + LM_ERR("Unknown State %s\n", s); + return STATE_UNKNOWN; } int reginfo_parse_event(char * s) { - if (s == NULL) { - return EVENT_UNKNOWN; - } - switch (strlen(s)) { - case 7: - if (strncmp(s, "created", 7) == 0) return EVENT_CREATED; - if (strncmp(s, "expired", 7) == 0) return EVENT_EXPIRED; - break; - case 9: - if (strncmp(s, "refreshed", 9) == 0) return EVENT_CREATED; - break; - case 10: - if (strncmp(s, "registered", 10) == 0) return EVENT_REGISTERED; - if (strncmp(s, "terminated", 10) == 0) return EVENT_TERMINATED; - break; - case 11: - if (strncmp(s, "deactivated", 11) == 0) return EVENT_DEACTIVATED; - break; - case 12: - if (strncmp(s, "unregistered", 12) == 0) return EVENT_UNREGISTERED; - break; - default: - LM_ERR("Unknown Event %s\n", s); - return EVENT_UNKNOWN; - } - LM_ERR("Unknown Event %s\n", s); - return EVENT_UNKNOWN; + if (s == NULL) { + return EVENT_UNKNOWN; + } + switch (strlen(s)) { + case 7: + if (strncmp(s, "created", 7) == 0) return EVENT_CREATED; + if (strncmp(s, "expired", 7) == 0) return EVENT_EXPIRED; + break; + case 9: + if (strncmp(s, "refreshed", 9) == 0) return EVENT_CREATED; + break; + case 10: + if (strncmp(s, "registered", 10) == 0) return EVENT_REGISTERED; + if (strncmp(s, "terminated", 10) == 0) return EVENT_TERMINATED; + break; + case 11: + if (strncmp(s, "deactivated", 11) == 0) return EVENT_DEACTIVATED; + break; + case 12: + if (strncmp(s, "unregistered", 12) == 0) return EVENT_UNREGISTERED; + break; + default: + LM_ERR("Unknown Event %s\n", s); + return EVENT_UNKNOWN; + } + LM_ERR("Unknown Event %s\n", s); + return EVENT_UNKNOWN; } xmlNodePtr xmlGetNodeByName(xmlNodePtr parent, const char *name) { - xmlNodePtr cur = parent; - xmlNodePtr match = NULL; - while (cur) { - if (xmlStrcasecmp(cur->name, (unsigned char*)name) == 0) - return cur; - match = xmlGetNodeByName(cur->children, name); - if (match) - return match; - cur = cur->next; - } - return NULL; + xmlNodePtr cur = parent; + xmlNodePtr match = NULL; + while (cur) { + if (xmlStrcasecmp(cur->name, (unsigned char*) name) == 0) + return cur; + match = xmlGetNodeByName(cur->children, name); + if (match) + return match; + cur = cur->next; + } + return NULL; } char * xmlGetAttrContentByName(xmlNodePtr node, const char *name) { - xmlAttrPtr attr = node->properties; - while (attr) { - if (xmlStrcasecmp(attr->name, (unsigned char*)name) == 0) - return (char*)xmlNodeGetContent(attr->children); - attr = attr->next; - } - return NULL; + xmlAttrPtr attr = node->properties; + while (attr) { + if (xmlStrcasecmp(attr->name, (unsigned char*) name) == 0) + return (char*) xmlNodeGetContent(attr->children); + attr = attr->next; + } + return NULL; } int process_body(struct sip_msg* msg, str notify_body, udomain_t * domain) { - xmlDocPtr doc= NULL; - xmlNodePtr doc_root = NULL, registrations = NULL, contacts = NULL, uris = NULL; - str aor = {0, 0}; - str callid = {0, 0}; - str contact_uri = {0, 0}; - str received = {0,0}; - str path = {0,0}; - str user_agent = {0, 0}; - int reg_state, contact_state, event, expires, result, final_result = RESULT_ERROR; - char * expires_char, * cseq_char; - int cseq = 0; - pv_elem_t *presentity_uri_pv; - - doc = xmlParseMemory(notify_body.s, notify_body.len); - if(doc== NULL) { - LM_ERR("Error while parsing the xml body message, Body is:\n%.*s\n", - notify_body.len, notify_body.s); - return -1; - } - doc_root = xmlGetNodeByName(doc->children, "reginfo"); - if(doc_root == NULL) { - LM_ERR("while extracting the reginfo node\n"); - goto error; - } - registrations = doc_root->children; - while (registrations) { - /* Only process registration sub-items */ - if (xmlStrcasecmp(registrations->name, BAD_CAST "registration") != 0) - goto next_registration; - reg_state = reginfo_parse_state(xmlGetAttrContentByName(registrations, "state")); - if (reg_state == STATE_UNKNOWN) { - LM_ERR("No state for this registration!\n"); - goto next_registration; - } - aor.s = xmlGetAttrContentByName(registrations, "aor"); - if (aor.s == NULL) { - LM_ERR("No AOR for this registration!\n"); - goto next_registration; - } - aor.len = strlen(aor.s); - LM_DBG("AOR %.*s has reg_state \"%d\"\n", aor.len, aor.s, reg_state); - - if (reg_state == STATE_TERMINATED) { - //TODO we if there is a IMPU record state here we should delete all contacts associated to it - //Right now we do it go through all the contacts - LM_DBG("AOR %.*s is in state terminated so unsubscribing from reginfo\n", aor.len, aor.s); - if(pv_parse_format(&aor, &presentity_uri_pv)<0) { - LM_ERR("wrong format[%.*s] - failed unsubscribing to reginfo\n",aor.len, aor.s); - } - reginfo_subscribe_real(msg, presentity_uri_pv, 0, 0); - pv_elem_free_all(presentity_uri_pv); - } - - /* Now lets process the Contact's from this Registration: */ - contacts = registrations->children; - while (contacts) { - if (xmlStrcasecmp(contacts->name, BAD_CAST "contact") != 0) - goto next_contact; - callid.s = xmlGetAttrContentByName(contacts, "callid"); - if (callid.s == NULL) { - LM_DBG("No Call-ID for this contact!\n"); - callid.len = 0; - } else { - callid.len = strlen(callid.s); - LM_DBG("contact has callid <%.*s>\n", callid.len, callid.s); - } - - received.s = xmlGetAttrContentByName(contacts, "received"); - if (received.s == NULL) { - LM_DBG("No received for this contact!\n"); - received.len = 0; - } else { - received.len = strlen(received.s); - LM_DBG("contact has received <%.*s>\n", received.len, received.s); - } - - path.s = xmlGetAttrContentByName(contacts, "path"); - if (path.s == NULL) { - LM_DBG("No path for this contact!\n"); - path.len = 0; - } else { - path.len = strlen(path.s); - LM_DBG("contact has path <%.*s>\n", path.len, path.s); - } - - user_agent.s = xmlGetAttrContentByName(contacts, "user_agent"); - if (user_agent.s == NULL) { - LM_DBG("No user_agent for this contact!\n"); - user_agent.len = 0; - } else { - user_agent.len = strlen(user_agent.s); - LM_DBG("contact has user_agent <%.*s>\n", user_agent.len, user_agent.s); - } - event = reginfo_parse_event(xmlGetAttrContentByName(contacts, "event")); - if (event == EVENT_UNKNOWN) { - LM_ERR("No event for this contact - going to next contact!\n"); - goto next_contact; - } - expires_char = xmlGetAttrContentByName(contacts, "expires"); - if (expires_char == NULL) { - LM_ERR("No expires for this contact - going to next contact!\n"); - goto next_contact; - } - expires = atoi(expires_char); - if (expires < 0) { - LM_ERR("No valid expires for this contact - going to next contact!\n"); - goto next_contact; - } - - contact_state = reginfo_parse_state(xmlGetAttrContentByName(contacts, "state")); - if (contact_state == STATE_UNKNOWN) { - LM_ERR("No state for this contact - going to next contact!\n"); - goto next_contact; - } - - LM_DBG("Contact state %d: Event \"%d\", expires %d\n", contact_state, event, expires); - - - - cseq_char = xmlGetAttrContentByName(contacts, "cseq"); - if (cseq_char == NULL) { - LM_DBG("No cseq for this contact!\n"); - } else { - cseq = atoi(cseq_char); - if (cseq < 0) { - LM_DBG("No valid cseq for this contact!\n"); - } - } - - /* Now lets process the URI's from this Contact: */ - uris = contacts->children; - while (uris) { - if (xmlStrcasecmp(uris->name, BAD_CAST "uri") != 0) - goto next_uri; - contact_uri.s = (char*)xmlNodeGetContent(uris); - if (contact_uri.s == NULL) { - LM_ERR("No URI for this contact - going to next registration!\n"); - goto next_registration; - } - contact_uri.len = strlen(contact_uri.s); - LM_DBG("Contact: %.*s\n", - contact_uri.len, contact_uri.s); - - /* Add to Usrloc: */ - result = process_contact(domain, expires, contact_uri, 0/*we don't have the recv ip*/, 0 /*we don't have the recv port*/, contact_state); - - /* Process the result */ - if (final_result != RESULT_CONTACTS_FOUND) final_result = result; + xmlDocPtr doc = NULL; + xmlNodePtr doc_root = NULL, registrations = NULL, contacts = NULL, uris = NULL; + str aor = {0, 0}; + str callid = {0, 0}; + str contact_uri = {0, 0}; + str received = {0, 0}; + str path = {0, 0}; + str user_agent = {0, 0}; + int reg_state, contact_state, event, expires, result, final_result = RESULT_ERROR; + char * expires_char, * cseq_char; + int cseq = 0; + pv_elem_t *presentity_uri_pv; + + doc = xmlParseMemory(notify_body.s, notify_body.len); + if (doc == NULL) { + LM_ERR("Error while parsing the xml body message, Body is:\n%.*s\n", + notify_body.len, notify_body.s); + return -1; + } + doc_root = xmlGetNodeByName(doc->children, "reginfo"); + if (doc_root == NULL) { + LM_ERR("while extracting the reginfo node\n"); + goto error; + } + registrations = doc_root->children; + while (registrations) { + /* Only process registration sub-items */ + if (xmlStrcasecmp(registrations->name, BAD_CAST "registration") != 0) + goto next_registration; + reg_state = reginfo_parse_state(xmlGetAttrContentByName(registrations, "state")); + if (reg_state == STATE_UNKNOWN) { + LM_ERR("No state for this registration!\n"); + goto next_registration; + } + aor.s = xmlGetAttrContentByName(registrations, "aor"); + if (aor.s == NULL) { + LM_ERR("No AOR for this registration!\n"); + goto next_registration; + } + aor.len = strlen(aor.s); + LM_DBG("AOR %.*s has reg_state \"%d\"\n", aor.len, aor.s, reg_state); + + if (reg_state == STATE_TERMINATED) { + //TODO we if there is a IMPU record state here we should delete all contacts associated to it + //Right now we do it go through all the contacts + + LM_DBG("AOR %.*s is in state terminated so unsubscribing from reginfo\n", aor.len, aor.s); + //we return a successful result here even if no contacts + final_result = RESULT_TERMINATED_SUCCESS; + + if (pv_parse_format(&aor, &presentity_uri_pv) < 0) { + LM_ERR("wrong format[%.*s] - failed unsubscribing to reginfo\n", aor.len, aor.s); + } + reginfo_subscribe_real(msg, presentity_uri_pv, 0, 0); + pv_elem_free_all(presentity_uri_pv); + } + + /* Now lets process the Contact's from this Registration: */ + contacts = registrations->children; + while (contacts) { + if (xmlStrcasecmp(contacts->name, BAD_CAST "contact") != 0) + goto next_contact; + callid.s = xmlGetAttrContentByName(contacts, "callid"); + if (callid.s == NULL) { + LM_DBG("No Call-ID for this contact!\n"); + callid.len = 0; + } else { + callid.len = strlen(callid.s); + LM_DBG("contact has callid <%.*s>\n", callid.len, callid.s); + } + + received.s = xmlGetAttrContentByName(contacts, "received"); + if (received.s == NULL) { + LM_DBG("No received for this contact!\n"); + received.len = 0; + } else { + received.len = strlen(received.s); + LM_DBG("contact has received <%.*s>\n", received.len, received.s); + } + + path.s = xmlGetAttrContentByName(contacts, "path"); + if (path.s == NULL) { + LM_DBG("No path for this contact!\n"); + path.len = 0; + } else { + path.len = strlen(path.s); + LM_DBG("contact has path <%.*s>\n", path.len, path.s); + } + + user_agent.s = xmlGetAttrContentByName(contacts, "user_agent"); + if (user_agent.s == NULL) { + LM_DBG("No user_agent for this contact!\n"); + user_agent.len = 0; + } else { + user_agent.len = strlen(user_agent.s); + LM_DBG("contact has user_agent <%.*s>\n", user_agent.len, user_agent.s); + } + event = reginfo_parse_event(xmlGetAttrContentByName(contacts, "event")); + if (event == EVENT_UNKNOWN) { + LM_ERR("No event for this contact - going to next contact!\n"); + goto next_contact; + } + expires_char = xmlGetAttrContentByName(contacts, "expires"); + if (expires_char == NULL) { + LM_ERR("No expires for this contact - going to next contact!\n"); + goto next_contact; + } + expires = atoi(expires_char); + if (expires < 0) { + LM_ERR("No valid expires for this contact - going to next contact!\n"); + goto next_contact; + } + + contact_state = reginfo_parse_state(xmlGetAttrContentByName(contacts, "state")); + if (contact_state == STATE_UNKNOWN) { + LM_ERR("No state for this contact - going to next contact!\n"); + goto next_contact; + } + + LM_DBG("Contact state %d: Event \"%d\", expires %d\n", contact_state, event, expires); + + + + cseq_char = xmlGetAttrContentByName(contacts, "cseq"); + if (cseq_char == NULL) { + LM_DBG("No cseq for this contact!\n"); + } else { + cseq = atoi(cseq_char); + if (cseq < 0) { + LM_DBG("No valid cseq for this contact!\n"); + } + } + + /* Now lets process the URI's from this Contact: */ + uris = contacts->children; + while (uris) { + if (xmlStrcasecmp(uris->name, BAD_CAST "uri") != 0) + goto next_uri; + contact_uri.s = (char*) xmlNodeGetContent(uris); + if (contact_uri.s == NULL) { + LM_ERR("No URI for this contact - going to next registration!\n"); + goto next_registration; + } + contact_uri.len = strlen(contact_uri.s); + LM_DBG("Contact: %.*s\n", + contact_uri.len, contact_uri.s); + + /* Add to Usrloc: */ + result = process_contact(domain, expires, contact_uri, contact_state); + + /* Process the result */ + if (final_result != RESULT_CONTACTS_FOUND) final_result = result; next_uri: - uris = uris->next; - } + uris = uris->next; + } next_contact: - contacts = contacts->next; - } - + contacts = contacts->next; + } + next_registration: - // if (ul_record) ul.release_urecord(ul_record); - /* Unlock the domain for this AOR: */ - //if (aor_key.len > 0) - // ul.unlock_udomain(domain, &aor_key); + // if (ul_record) ul.release_urecord(ul_record); + /* Unlock the domain for this AOR: */ + //if (aor_key.len > 0) + // ul.unlock_udomain(domain, &aor_key); - registrations = registrations->next; - } + registrations = registrations->next; + } error: - /* Free the XML-Document */ - if(doc) xmlFreeDoc(doc); - return final_result; + /* Free the XML-Document */ + if (doc) xmlFreeDoc(doc); + return final_result; } +int reginfo_handle_notify(struct sip_msg* msg, char* domain, char* s2) { + + LM_DBG("Handling notify\n"); + str body; + int result = 1; + /* If not done yet, parse the whole message now: */ + if (parse_headers(msg, HDR_EOH_F, 0) == -1) { + LM_ERR("Error parsing headers\n"); + return -1; + } + if (get_content_length(msg) == 0) { + LM_DBG("Content length = 0\n"); + /* No Body? Then there is no published information available, which is ok. */ + return 1; + } else { + body.s = get_body(msg); + if (body.s == NULL) { + LM_ERR("cannot extract body from msg\n"); + return -1; + } + body.len = get_content_length(msg); + } -int reginfo_handle_notify(struct sip_msg* msg, char* domain, char* s2) { - - LM_DBG("Handling notify\n"); - str body; - int result = 1; - - /* If not done yet, parse the whole message now: */ - if (parse_headers(msg, HDR_EOH_F, 0) == -1) { - LM_ERR("Error parsing headers\n"); - return -1; - } - if (get_content_length(msg) == 0) { - LM_DBG("Content length = 0\n"); - /* No Body? Then there is no published information available, which is ok. */ - return 1; - } else { - body.s=get_body(msg); - if (body.s== NULL) { - LM_ERR("cannot extract body from msg\n"); - return -1; - } - body.len = get_content_length(msg); - } - - LM_DBG("Body is %.*s\n", body.len, body.s); - - result = process_body(msg, body, (udomain_t*)domain); - - - return result; + LM_DBG("Body is %.*s\n", body.len, body.s); + + result = process_body(msg, body, (udomain_t*) domain); + + + return result; } diff --git a/modules/ims_registrar_pcscf/reg_mod.c b/modules/ims_registrar_pcscf/reg_mod.c index 1a0035e4604..530ded1a43b 100644 --- a/modules/ims_registrar_pcscf/reg_mod.c +++ b/modules/ims_registrar_pcscf/reg_mod.c @@ -69,7 +69,6 @@ #include "reg_mod.h" #include "save.h" #include "service_routes.h" -#include "lookup.h" MODULE_VERSION usrloc_api_t ul; /**!< Structure containing pointers to usrloc functions*/ @@ -114,19 +113,14 @@ static int w_follows_service_routes(struct sip_msg* _m, char* _d, char* _foo); static int w_force_service_routes(struct sip_msg* _m, char* _d, char* _foo); static int w_is_registered(struct sip_msg* _m, char* _d, char* _foo); static int w_reginfo_handle_notify(struct sip_msg* _m, char* _d, char* _foo); -static int w_unregister(struct sip_msg* _m, char* _d, char* _aor, char* _received_host, char* _received_port); static int w_assert_identity(struct sip_msg* _m, char* _d, char* _preferred_uri); static int w_assert_called_identity(struct sip_msg* _m, char* _d, char* _foo); -static int w_lookup_transport(struct sip_msg* _m, char* _d, char* _uri); - /*! \brief Fixup functions */ static int domain_fixup(void** param, int param_no); -static int domain_uri_fixup(void** param, int param_no); static int save_fixup2(void** param, int param_no); static int assert_identity_fixup(void ** param, int param_no); -static int unregister_fixup(void ** param, int param_no); /* Pseudo-Variables */ static int pv_get_asserted_identity_f(struct sip_msg *, pv_param_t *, pv_value_t *); @@ -152,10 +146,6 @@ static cmd_export_t cmds[] = { {"pcscf_assert_identity", (cmd_function)w_assert_identity, 2, assert_identity_fixup, 0,REQUEST_ROUTE }, {"pcscf_assert_called_identity",(cmd_function)w_assert_called_identity, 1, assert_identity_fixup, 0,ONREPLY_ROUTE }, {"reginfo_handle_notify", (cmd_function)w_reginfo_handle_notify, 1, domain_fixup, 0,REQUEST_ROUTE}, - {"lookup_transport", (cmd_function)w_lookup_transport, 1, domain_fixup, 0,REQUEST_ROUTE|FAILURE_ROUTE}, - {"lookup_transport", (cmd_function)w_lookup_transport, 2, domain_uri_fixup, 0,REQUEST_ROUTE|FAILURE_ROUTE}, - {"pcscf_unregister", (cmd_function)w_unregister, 4, unregister_fixup, 0,ANY_ROUTE}, - {0, 0, 0, 0, 0, 0} }; @@ -365,23 +355,6 @@ static int domain_fixup(void** param, int param_no) return 0; } -static int domain_uri_fixup(void** param, int param_no) -{ - udomain_t* d; - - if (param_no == 1) { - if (ul.register_udomain((char*)*param, &d) < 0) { - LM_ERR("failed to register domain\n"); - return E_UNSPEC; - } - *param = (void*)d; - } else { - fixup_var_pve_str_12(param, param_no); - } - - return 0; -} - /*! \brief * Fixup for "save" function - both domain and flags */ @@ -431,21 +404,6 @@ static int w_save(struct sip_msg* _m, char* _d, char* _cflags) return save(_m, (udomain_t*)_d, ((int)(unsigned long)_cflags)); } -/*! \brief - * Wrapper to lookup_transport(location) - */ -static int w_lookup_transport(struct sip_msg* _m, char* _d, char* _uri) -{ - str uri = {0}; - if(_uri!=NULL && (fixup_get_svalue(_m, (gparam_p)_uri, &uri)!=0 || uri.len<=0)) - { - LM_ERR("invalid uri parameter\n"); - return -1; - } - - return lookup_transport(_m, (udomain_t*)_d, (uri.len>0)?&uri:NULL); -} - static int w_save_pending(struct sip_msg* _m, char* _d, char* _cflags) { return save_pending(_m, (udomain_t*)_d); @@ -489,76 +447,6 @@ static int w_assert_identity(struct sip_msg* _m, char* _d, char* _preferred_uri) return assert_identity( _m, (udomain_t*)_d, identity); } -/*! \brief - * Fixup for "assert_identity" function - both domain and URI to be asserted - */ -static int unregister_fixup(void ** param, int param_no) { - if (param_no == 1) { - return domain_fixup(param,param_no); - } else { - pv_elem_t *model=NULL; - str s; - - /* convert to str */ - s.s = (char*)*param; - s.len = strlen(s.s); - - model = NULL; - if(s.len==0) { - LM_ERR("no param!\n"); - return E_CFG; - } - if(pv_parse_format(&s, &model)<0 || model==NULL) { - LM_ERR("wrong format [%s]!\n", s.s); - return E_CFG; - } - *param = (void*)model; - return 0; - } - return E_CFG; -} - - -static int w_unregister(struct sip_msg* _m, char* _d, char* _aor, char* _received_host, char* _received_port) { - pv_elem_t *model; - str aor; - str received_host; - str received_port; - int port = 0; - - if ((_aor == NULL) || (_received_host == NULL) || (_received_port == NULL)) { - LM_ERR("error - bad parameters\n"); - return -1; - } - - model = (pv_elem_t*)_aor; - if (pv_printf_s(_m, model, &aor)<0) { - LM_ERR("error - cannot print the format\n"); - return -1; - } - LM_DBG("URI: %.*s\n", aor.len, aor.s); - - model = (pv_elem_t*)_received_host; - if (pv_printf_s(_m, model, &received_host)<0) { - LM_ERR("error - cannot print the format\n"); - return -1; - } - LM_DBG("Received-Host: %.*s\n", received_host.len, received_host.s); - - model = (pv_elem_t*)_received_port; - if (pv_printf_s(_m, model, &received_port)<0) { - LM_ERR("error - cannot print the format\n"); - return -1; - } - LM_DBG("Received-Port: %.*s\n", received_port.len, received_port.s); - if (str2sint(&received_port, &port) != 0) { - LM_ERR("error - cannot convert %.*s to an int!\n", received_port.len, received_port.s); - return -1; - } - - return unregister((udomain_t*)_d, &aor, &received_host, port); -} - static int w_assert_called_identity(struct sip_msg* _m, char* _d, char* _foo) { return assert_called_identity( _m, (udomain_t*)_d); } diff --git a/modules/ims_registrar_pcscf/save.c b/modules/ims_registrar_pcscf/save.c index 6ee868ebd36..21e679d9d78 100644 --- a/modules/ims_registrar_pcscf/save.c +++ b/modules/ims_registrar_pcscf/save.c @@ -123,6 +123,7 @@ static inline int update_contacts(struct sip_msg *req,struct sip_msg *rpl, udoma pcontact_t* pcontact; char srcip[50]; int_str val; + unsigned short port; pcscf_act_time(); local_time_now = time_now; @@ -155,6 +156,7 @@ static inline int update_contacts(struct sip_msg *req,struct sip_msg *rpl, udoma ci.received_host.s = 0; ci.received_port = 0; ci.received_proto = 0; + ci.searchflag = 1<uri, &ci.received_host, ci.received_port); - if (ul.get_pcontact(_d, &c->uri, &ci.received_host, ci.received_port, &pcontact) != 0) { //need to insert new contact + port = puri.port_no?puri.port_no:5060; + ci.via_host = puri.host; + ci.via_port = port; + ci.via_prot = puri.proto; + ul.lock_udomain(_d, &puri.host, port, puri.proto); + if (ul.get_pcontact(_d, &ci, &pcontact) != 0) { //need to insert new contact if ((expires-local_time_now)<=0) { //remove contact - de-register LM_DBG("This is a de-registration for contact <%.*s> but contact is not in usrloc - ignore\n", c->uri.len, c->uri.s); goto next_contact; @@ -201,9 +206,10 @@ static inline int update_contacts(struct sip_msg *req,struct sip_msg *rpl, udoma LM_DBG("contact already exists and is in state (%d) : [%s]\n",pcontact->reg_state, reg_state_to_string(pcontact->reg_state)); if ((expires-local_time_now)<=0) { //remove contact - de-register LM_DBG("This is a de-registration for contact <%.*s>\n", c->uri.len, c->uri.s); - if (ul.delete_pcontact(_d, &c->uri, &ci.received_host, ci.received_port, pcontact) != 0) { - LM_ERR("failed to delete pcscf contact <%.*s>\n", c->uri.len, c->uri.s); - } +// if (ul.delete_pcontact(_d, &c->uri, &ci.received_host, ci.received_port, pcontact) != 0) { +// LM_ERR("failed to delete pcscf contact <%.*s>\n", c->uri.len, c->uri.s); +// } + //TODO_LATEST replace above } else { //update contact LM_DBG("Updating contact: <%.*s>, old expires: %li, new expires: %i which is in %i seconds\n", c->uri.len, c->uri.s, pcontact->expires-local_time_now, @@ -216,7 +222,7 @@ static inline int update_contacts(struct sip_msg *req,struct sip_msg *rpl, udoma } } next_contact: - ul.unlock_udomain(_d, &c->uri, &ci.received_host, ci.received_port); + ul.unlock_udomain(_d, &puri.host, port, puri.proto); } } return 1; @@ -226,7 +232,7 @@ static inline int update_contacts(struct sip_msg *req,struct sip_msg *rpl, udoma /** * Save contact based on REGISTER request. this will be a pending save, until we receive response - * from SCSCF. If no response after pending_timeout seconds, the contacts is removed + * from SCSCF. If no response after pending_timeout seconds, the contacts is removed. Can only be used from REQUEST ROUTE */ int save_pending(struct sip_msg* _m, udomain_t* _d) { contact_body_t* cb = 0; @@ -234,22 +240,25 @@ int save_pending(struct sip_msg* _m, udomain_t* _d) { pcontact_t* pcontact; contact_t* c; struct pcontact_info ci; + struct via_body* vb; + unsigned short port, proto; int_str val; struct sip_uri parsed_received; char srcip[50]; memset(&ci, 0, sizeof(struct pcontact_info)); + + vb = cscf_get_ue_via(_m); + port = vb->port?vb->port:5060; + proto = vb->proto; cb = cscf_parse_contacts(_m); if (!cb || (!cb->contacts)) { LM_ERR("No contact headers\n"); goto error; } - c = cb->contacts; - if (!c) { - LM_ERR("no valid contact to register\n"); - goto error; - } + + c = cb->contacts; //TODO: need support for multiple contacts - currently assume one contact //make sure this is not a de-registration int expires_hdr = cscf_get_expires_hdr(_m, 0); @@ -263,6 +272,7 @@ int save_pending(struct sip_msg* _m, udomain_t* _d) { return 1; } } + pcscf_act_time(); int local_time_now = time_now; int expires = calc_contact_expires(c, expires_hdr, local_time_now); @@ -270,9 +280,14 @@ int save_pending(struct sip_msg* _m, udomain_t* _d) { LM_DBG("not doing pending reg on de-registration\n"); return 1; } + LM_DBG("Save Pending"); LM_DBG("contact requesting to expire in %d seconds\n", expires-local_time_now); /*populate CI with bare minimum*/ + ci.via_host = vb->host; + ci.via_port = port; + ci.via_prot = proto; + ci.aor = c->uri; ci.num_public_ids=0; ci.num_service_routes=0; ci.expires=local_time_now + pending_reg_expires; @@ -300,13 +315,14 @@ int save_pending(struct sip_msg* _m, udomain_t* _d) { ci.received_host.s = srcip; ci.received_port = _m->rcv.src_port; ci.received_proto = _m->rcv.proto; + } // Set to default, if not set: if (ci.received_port == 0) ci.received_port = 5060; - ul.lock_udomain(_d, &c->uri, &ci.received_host, ci.received_port); - if (ul.get_pcontact(_d, &c->uri, &ci.received_host, ci.received_port, &pcontact) != 0) { //need to insert new contact + ul.lock_udomain(_d, &ci.via_host, ci.via_port, ci.via_prot); + if (ul.get_pcontact(_d, &ci, &pcontact) != 0) { //need to insert new contact LM_DBG("Adding pending pcontact: <%.*s>\n", c->uri.len, c->uri.s); if (ul.insert_pcontact(_d, &c->uri, &ci, &pcontact) != 0) { LM_ERR("Failed inserting new pcontact\n"); @@ -317,7 +333,7 @@ int save_pending(struct sip_msg* _m, udomain_t* _d) { } else { //contact already exists - update LM_DBG("Contact already exists - not doing anything for now\n"); } - ul.unlock_udomain(_d, &c->uri, &ci.received_host, ci.received_port); + ul.unlock_udomain(_d, &ci.via_host, ci.via_port, ci.via_prot); return 1; diff --git a/modules/ims_registrar_pcscf/save.h b/modules/ims_registrar_pcscf/save.h index 6a4f81f299c..71aa4acf3cb 100644 --- a/modules/ims_registrar_pcscf/save.h +++ b/modules/ims_registrar_pcscf/save.h @@ -55,7 +55,7 @@ */ int save(struct sip_msg* _m, udomain_t* _d, int _cflags); int save_pending(struct sip_msg* _m, udomain_t* _d); -// int unregister(struct sip_msg* _m, char* _d, char* _uri); +int unregister(struct sip_msg* _m, char* _d, char* _uri); struct sip_msg* get_request_from_reply(struct sip_msg* reply); diff --git a/modules/ims_registrar_pcscf/service_routes.c b/modules/ims_registrar_pcscf/service_routes.c index a358bbf7ca8..cd75a74829f 100644 --- a/modules/ims_registrar_pcscf/service_routes.c +++ b/modules/ims_registrar_pcscf/service_routes.c @@ -23,6 +23,8 @@ #include "service_routes.h" #include "reg_mod.h" #include "save.h" +#include "../../parser/parse_via.h" +#include "../../parser/msg_parser.h" #include "../../data_lump.h" #include "../../lib/ims/ims_getters.h" @@ -164,111 +166,115 @@ int checkcontact(struct sip_msg* _m, pcontact_t * c) { * (search only once per Request) */ pcontact_t * getContactP(struct sip_msg* _m, udomain_t* _d) { - ppublic_t * p; - contact_body_t *b = 0; - contact_t *ct; - str received_host = {0, 0}; - char srcip[50]; - - received_host.len = ip_addr2sbuf(&_m->rcv.src_ip, srcip, sizeof(srcip)); - received_host.s = srcip; - - if (_m->id != current_msg_id) { - current_msg_id = _m->id; - c = NULL; - - if (is_registered_fallback2ip == 2) { - LM_DBG("Searching in usrloc for %.*s:%i (Proto %i)\n", - received_host.len, received_host.s, - _m->rcv.src_port, _m->rcv.proto); - - if (ul.get_pcontact_by_src(_d, &received_host, _m->rcv.src_port, _m->rcv.proto, &c) == 1) { - LM_DBG("No entry in usrloc for %.*s:%i (Proto %i) found!\n", received_host.len, received_host.s, _m->rcv.src_port, _m->rcv.proto); - } else { - if (checkcontact(_m, c) != 0) { - c = NULL; - } - } - } - - if (c == NULL) { - b = cscf_parse_contacts(_m); - - if (b && b->contacts) { - for (ct = b->contacts; ct; ct = ct->next) { - if (ul.get_pcontact(_d, &ct->uri, &received_host, _m->rcv.src_port, &c) == 0) { - if (checkcontact(_m, c) != 0) { - c = NULL; - } else { - break; - } - } - } - } else { - LM_WARN("No contact-header found?!?\n"); - } - } - - if ((c == NULL) && (is_registered_fallback2ip == 1)) { - LM_INFO("Contact not found based on Contact-header, trying IP/Port/Proto\n"); - received_host.len = ip_addr2sbuf(&_m->rcv.src_ip, srcip, sizeof(srcip)); - received_host.s = srcip; - if (ul.get_pcontact_by_src(_d, &received_host, _m->rcv.src_port, _m->rcv.proto, &c) == 1) { - LM_DBG("No entry in usrloc for %.*s:%i (Proto %i) found!\n", received_host.len, received_host.s, _m->rcv.src_port, _m->rcv.proto); - } else { - if (checkcontact(_m, c) != 0) { - c = NULL; - } - } - } - } - asserted_identity = NULL; - registration_contact = NULL; - if (c) { - registration_contact = &c->contact_user; - p = c->head; - while (p) { - if (p->is_default == 1) - asserted_identity = &p->public_identity; - p = p->next; - } - } - - return c; -} - -pcontact_t * getContactP_from_via(struct sip_msg* _m, udomain_t* _d) { - ppublic_t * p; - struct via_body *vb; - - vb = cscf_get_ue_via(_m); - if (!vb) { - LM_WARN("no via header.....strange!\n"); - return NULL; - } - - if (vb->port == 0) - vb->port = 5060; - - if (_m->id != current_msg_id) { - current_msg_id = _m->id; - c = NULL; - LM_DBG("Looking for <%d://%.*s:%d>\n", vb->proto, vb->host.len, vb->host.s, vb->port); - if (ul.get_pcontact_by_src(_d, &vb->host, vb->port, vb->proto, &c) == 1) - LM_WARN("No entry in usrloc for %.*s:%i (Proto %i) found!\n", vb->host.len, vb->host.s, vb->port, vb->proto); - } - - asserted_identity = NULL; - if (c) { - p = c->head; - while (p) { - if (p->is_default == 1) - asserted_identity = &p->public_identity; - p = p->next; - } - } - - return c; + ppublic_t * p; + contact_body_t *b = 0; + contact_t *ct; + pcontact_info_t search_ci; + str received_host = {0, 0}; + char srcip[50]; + struct via_body *vb; + unsigned short port, proto; + str host; + sip_uri_t contact_uri; + + b = cscf_parse_contacts(_m); + + if (_m->first_line.type == SIP_REPLY && _m->contact && _m->contact->parsed && b->contacts) { + LM_DBG("This is a reply - to look for contact we favour the contact header above the via (b2bua)... if no contact we will use last via\n"); + ct = b->contacts; + host = ct->uri; + if (parse_uri(ct->uri.s, ct->uri.len, &contact_uri) != 0) { + LM_WARN("Failed to parse contact [%.*s]\n", ct->uri.len, ct->uri.s); + return NULL; + } + host = contact_uri.host; + port = contact_uri.port_no ? contact_uri.port_no : 5060; + proto = contact_uri.proto; + if (proto == 0) { + LM_DBG("Contact protocol not specified - using received\n"); + proto = _m->rcv.proto; + } + } else { + if (_m->first_line.type == SIP_REPLY) + LM_DBG("This is a reply but we are forced to use the via header\n"); + else + LM_DBG("This is a request - using first via to find contact\n"); + + vb = cscf_get_ue_via(_m); + host = vb->host; + port = vb->port ? vb->port : 5060; + proto = vb->proto; + } + + LM_DBG("searching for contact with host:port:proto contact [%d://%.*s:%d]\n", proto, host.len, host.s, port); + + received_host.len = ip_addr2sbuf(&_m->rcv.src_ip, srcip, sizeof (srcip)); + received_host.s = srcip; + +// if (_m->id != current_msg_id) { + current_msg_id = _m->id; + c = NULL; + search_ci.received_host.s = 0; + search_ci.received_host.len = 0; + search_ci.received_port = 0; + search_ci.received_proto = 0; + search_ci.searchflag = SEARCH_NORMAL; + search_ci.via_host = host; + search_ci.via_port = port; + search_ci.via_prot = proto; + search_ci.aor.s = 0; + search_ci.aor.len = 0; + + if (c == NULL) { + b = cscf_parse_contacts(_m); + + if (b && b->contacts) { + for (ct = b->contacts; ct; ct = ct->next) { + search_ci.aor = ct->uri; + if (ul.get_pcontact(_d, &search_ci, &c) == 0) { + if (checkcontact(_m, c) != 0) { + c = NULL; + } else { + break; + } + } + } + } else { + LM_WARN("No contact-header found?!?\n"); + } + } + + if ((c == NULL) && (is_registered_fallback2ip == 1)) { + LM_INFO("Contact not found based on Contact-header, trying IP/Port/Proto\n"); + // received_host.len = ip_addr2sbuf(&_m->rcv.src_ip, srcip, sizeof(srcip)); + // received_host.s = srcip; + if (ul.get_pcontact(_d, &search_ci, &c) == 1) { + LM_DBG("No entry in usrloc for %.*s:%i (Proto %i) found!\n", received_host.len, received_host.s, _m->rcv.src_port, _m->rcv.proto); + } else { + if (checkcontact(_m, c) != 0) { + c = NULL; + } + } + } +// } + asserted_identity = NULL; + registration_contact = NULL; + if (c) { + registration_contact = &c->contact_user; + p = c->head; + while (p) { + if (p->is_default == 1) + asserted_identity = &p->public_identity; + p = p->next; + } + } + +// LM_DBG("pcontact flag is [%d]\n", c->flags); +// if (c && (c->flags & (1< not following service-routes */ if (c == NULL) return -1; @@ -290,11 +299,16 @@ int check_service_routes(struct sip_msg* _m, udomain_t* _d) { LM_DBG("Got %i Route-Headers.\n", c->num_service_routes); - received_host.len = ip_addr2sbuf(&_m->rcv.src_ip, srcip, sizeof(srcip)); - received_host.s = srcip; +// received_host.len = ip_addr2sbuf(&_m->rcv.src_ip, srcip, sizeof(srcip)); +// received_host.s = srcip; + vb = cscf_get_ue_via(_m); + port = vb->port?vb->port:5060; + proto = vb->proto; /* Lock this record while working with the data: */ - ul.lock_udomain(_d, &c->aor, &received_host, _m->rcv.src_port); + ul.lock_udomain(_d, &vb->host, port, proto); + + pcontact_t * c = getContactP(_m, _d); /* Check the route-set: */ if (_m->route) { @@ -366,12 +380,13 @@ int check_service_routes(struct sip_msg* _m, udomain_t* _d) { if (c->num_service_routes > 0) goto error; } /* Unlock domain */ - ul.unlock_udomain(_d, &c->aor, &received_host, _m->rcv.src_port); + ul.unlock_udomain(_d, &vb->host, port, proto); return 1; error: /* Unlock domain */ - ul.unlock_udomain(_d, &c->aor, &received_host, _m->rcv.src_port); + ul.unlock_udomain(_d, &vb->host, port, proto); return -1; + return 1; } static str route_start={"Route: <",8}; @@ -388,8 +403,11 @@ int force_service_routes(struct sip_msg* _m, udomain_t* _d) { struct lump* lmp = NULL; char * buf; pcontact_t * c = getContactP(_m, _d); - char srcip[20]; - str received_host; +// char srcip[20]; +// str received_host; + struct via_body* vb; + unsigned short port; + unsigned short proto; // Contact not found => not following service-routes if (c == NULL) return -1; @@ -397,6 +415,10 @@ int force_service_routes(struct sip_msg* _m, udomain_t* _d) { /* we need to be sure we have seen all HFs */ parse_headers(_m, HDR_EOH_F, 0); + vb = cscf_get_ue_via(_m); + port = vb->port?vb->port:5060; + proto = vb->proto; + /* Save current buffer */ buf = _m->buf; @@ -419,11 +441,11 @@ int force_service_routes(struct sip_msg* _m, udomain_t* _d) { _m->dst_uri.len = 0; } - received_host.len = ip_addr2sbuf(&_m->rcv.src_ip, srcip, sizeof(srcip)); - received_host.s = srcip; +// received_host.len = ip_addr2sbuf(&_m->rcv.src_ip, srcip, sizeof(srcip)); +// received_host.s = srcip; /* Lock this record while working with the data: */ - ul.lock_udomain(_d, &c->aor, &received_host, _m->rcv.src_port); + ul.lock_udomain(_d, &vb->host, port, proto); if (c->num_service_routes > 0) { /* Create anchor for new Route-Header: */ @@ -471,13 +493,14 @@ int force_service_routes(struct sip_msg* _m, udomain_t* _d) { } } /* Unlock domain */ - ul.unlock_udomain(_d, &c->aor, &received_host, _m->rcv.src_port); + ul.unlock_udomain(_d, &vb->host, port, proto); return 1; error: /* Unlock domain */ - ul.unlock_udomain(_d, &c->aor, &received_host, _m->rcv.src_port); + ul.unlock_udomain(_d, &vb->host, port, proto); return -1; + return 1; } /** @@ -488,30 +511,6 @@ int is_registered(struct sip_msg* _m, udomain_t* _d) { return -1; } -int unregister(udomain_t* _d, str * uri, str * received_host, int received_port) { - int result = -1; - struct pcontact * pcontact; - struct pcontact_info ci; - memset(&ci, 0, sizeof (struct pcontact_info)); - - if (ul.get_pcontact(_d, uri, received_host, received_port, &pcontact) == 0) { - /* Lock this record while working with the data: */ - ul.lock_udomain(_d, &pcontact->aor, received_host, received_port); - - LM_DBG("Updating contact [%.*s]: setting state to PCONTACT_DEREG_PENDING_PUBLISH\n", pcontact->aor.len, pcontact->aor.s); - - ci.reg_state = PCONTACT_DEREG_PENDING_PUBLISH; - ci.num_service_routes = 0; - if (ul.update_pcontact(_d, &ci, pcontact) == 0) result = 1; - - // if (ul.delete_pcontact(_d, &pc->aor, received_host, received_port, pcontact) == 0) result = 1; - - /* Unlock domain */ - ul.unlock_udomain(_d, &pcontact->aor, received_host, received_port); - } - return result; -} - /** * Get the current asserted identity for the user */ diff --git a/modules/ims_registrar_pcscf/service_routes.h b/modules/ims_registrar_pcscf/service_routes.h index 97cb6a3a65e..c7887f52c0f 100644 --- a/modules/ims_registrar_pcscf/service_routes.h +++ b/modules/ims_registrar_pcscf/service_routes.h @@ -61,9 +61,4 @@ int assert_identity(struct sip_msg* _m, udomain_t* _d, str identity); */ int assert_called_identity(struct sip_msg* _m, udomain_t* _d); -/** - * Unregister a contact - */ -int unregister(udomain_t* _d, str * uri, str * received_host, int received_port); - #endif /* SERVICE_ROUTES_H */ diff --git a/modules/ims_registrar_scscf/cxdx_avp.c b/modules/ims_registrar_scscf/cxdx_avp.c index d81207da21f..ab9490a0ecb 100644 --- a/modules/ims_registrar_scscf/cxdx_avp.c +++ b/modules/ims_registrar_scscf/cxdx_avp.c @@ -148,7 +148,7 @@ inline int cxdx_add_call_id(AAAMessage *msg, str data) cxdx_add_avp(msg,data.s,data.len, AVP_Call_Id, AAA_AVP_FLAG_VENDOR_SPECIFIC, - 0, + 50, AVP_DUPLICATE_DATA, __FUNCTION__); } diff --git a/modules/ims_registrar_scscf/cxdx_callbacks.c b/modules/ims_registrar_scscf/cxdx_callbacks.c index 7cfcbfa1de5..6278d8cb56c 100644 --- a/modules/ims_registrar_scscf/cxdx_callbacks.c +++ b/modules/ims_registrar_scscf/cxdx_callbacks.c @@ -25,6 +25,7 @@ #include "../../dprint.h" #include "../ims_usrloc_scscf/usrloc.h" #include "cxdx_avp.h" +#include "registrar_notify.h" extern struct cdp_binds cdpb; @@ -86,8 +87,15 @@ AAAMessage* cxdx_process_rtr(AAAMessage *rtr) { } for(i = 0; i < r->num_contacts; i++) { - LM_DBG("Expiring contact with AOR [%.*s]\n", r->newcontacts[i]->aor.len, r->newcontacts[i]->aor.s); - ul.expire_ucontact(r, r->newcontacts[i]); + LM_DBG("Deleting contact with AOR [%.*s]\n", r->newcontacts[i]->aor.len, r->newcontacts[i]->aor.s); + ul.lock_contact_slot_i(r->newcontacts[i]->sl); + r->newcontacts[i]->state = CONTACT_DELETE_PENDING; + if (r->shead) { + //send NOTIFY to all subscribers of this IMPU. + notify_subscribers(r); + } + r->newcontacts[i]->state = CONTACT_DELETED; + ul.unlock_contact_slot_i(r->newcontacts[i]->sl); } ul.unlock_udomain(udomain, &public_id); @@ -105,8 +113,15 @@ AAAMessage* cxdx_process_rtr(AAAMessage *rtr) { } for(i = 0; i < r->num_contacts; i++) { - LM_DBG("Expiring contact with AOR [%.*s]\n", r->newcontacts[i]->aor.len, r->newcontacts[i]->aor.s); - ul.expire_ucontact(r, r->newcontacts[i]); + LM_DBG("Deleting contact with AOR [%.*s]\n", r->newcontacts[i]->aor.len, r->newcontacts[i]->aor.s); + ul.lock_contact_slot_i(r->newcontacts[i]->sl); + r->newcontacts[i]->state = CONTACT_DELETE_PENDING; + if (r->shead) { + //send NOTIFY to all subscribers of this IMPU. + notify_subscribers(r); + } + r->newcontacts[i]->state = CONTACT_DELETED; + ul.unlock_contact_slot_i(r->newcontacts[i]->sl); } ul.unlock_udomain(udomain, &public_id); diff --git a/modules/ims_registrar_scscf/cxdx_sar.c b/modules/ims_registrar_scscf/cxdx_sar.c index 34016117585..d6ded974b78 100644 --- a/modules/ims_registrar_scscf/cxdx_sar.c +++ b/modules/ims_registrar_scscf/cxdx_sar.c @@ -63,6 +63,7 @@ #include "regtime.h" #include "../../parser/hf.h" #include "../../lib/ims/ims_getters.h" +#include "registrar_notify.h" extern struct cdp_binds cdpb; diff --git a/modules/ims_registrar_scscf/lookup.c b/modules/ims_registrar_scscf/lookup.c index 0eac3796fbe..9beaba46ea5 100644 --- a/modules/ims_registrar_scscf/lookup.c +++ b/modules/ims_registrar_scscf/lookup.c @@ -203,7 +203,7 @@ int lookup_path_to_contact(struct sip_msg* _m, char* contact_uri) { } LM_DBG("Looking up contact [%.*s]\n", s_contact_uri.len, s_contact_uri.s); - if (ul.get_ucontact(NULL, &s_contact_uri, 0, 0, 0, &contact) == 0) { //get_contact returns with lock + if (ul.get_ucontact(&s_contact_uri, 0, 0, 0, &contact) == 0) { //get_contact returns with lock if (!VALID_CONTACT(contact, act_time)) { LM_DBG("Contact is not valid...ignoring\n"); diff --git a/modules/ims_registrar_scscf/reg_mod.c b/modules/ims_registrar_scscf/reg_mod.c index 2c724930599..7dea88c9b73 100644 --- a/modules/ims_registrar_scscf/reg_mod.c +++ b/modules/ims_registrar_scscf/reg_mod.c @@ -162,6 +162,7 @@ int subscription_default_expires = 3600; /**< the default value for expires if n int subscription_min_expires = 10; /**< minimum subscription expiration time */ int subscription_max_expires = 1000000; /**< maximum subscription expiration time */ int subscription_expires_range = 0; +int contact_expires_buffer_percentage = 10; /**< percentage we expiry for contact we will substrace from reg response to UE */ int notification_list_size_threshold = 0; /**Threshold for size of notification list after which a warning is logged */ @@ -258,6 +259,7 @@ static param_export_t params[] = { {"subscription_default_expires", INT_PARAM, &subscription_default_expires}, {"subscription_min_expires", INT_PARAM, &subscription_min_expires}, {"subscription_max_expires", INT_PARAM, &subscription_max_expires}, + {"expires_buffer_percent", INT_PARAM, &contact_expires_buffer_percentage}, {"ue_unsubscribe_on_dereg", INT_PARAM, &ue_unsubscribe_on_dereg}, {"subscription_expires_range", INT_PARAM, &subscription_expires_range}, {"user_data_always", INT_PARAM, &user_data_always}, @@ -320,6 +322,11 @@ static int mod_init(void) { LM_ERR("Unable to allocate memory for service route uri\n"); return -1; } + + if (contact_expires_buffer_percentage < 0 || contact_expires_buffer_percentage > 90) { + LM_ERR("contact expires percentage not valid, must be >0 and <=90"); + return -1; + } memcpy(scscf_serviceroute_uri_str.s, orig_prefix.s, orig_prefix.len); scscf_serviceroute_uri_str.len = orig_prefix.len; diff --git a/modules/ims_registrar_scscf/registrar_notify.c b/modules/ims_registrar_scscf/registrar_notify.c index be10576c135..c0e6aece961 100644 --- a/modules/ims_registrar_scscf/registrar_notify.c +++ b/modules/ims_registrar_scscf/registrar_notify.c @@ -436,7 +436,7 @@ int can_subscribe_to_reg(struct sip_msg *msg, char *_t, char *str2) { * called to deliver new event into notification process * return 0 on success. anything else failure */ -int event_reg(udomain_t* _d, impurecord_t* r_passed, ucontact_t* c_passed, int event_type, str *presentity_uri, str *watcher_contact) { +int event_reg(udomain_t* _d, impurecord_t* r_passed, int event_type, str *presentity_uri, str *watcher_contact) { str content = {0, 0}; impurecord_t* r; @@ -453,8 +453,8 @@ int event_reg(udomain_t* _d, impurecord_t* r_passed, ucontact_t* c_passed, int e case IMS_REGISTRAR_NONE: return 0; case IMS_REGISTRAR_SUBSCRIBE: - if (r_passed || c_passed || !presentity_uri || !watcher_contact || !_d) { - LM_ERR("this is a subscribe called from cfg file: r_passed and c_passed should both be zero and presentity_uri, watcher_contact and _d should be valid for a subscribe"); + if (r_passed || !presentity_uri || !watcher_contact || !_d) { + LM_ERR("this is a subscribe called from cfg file: r_passed be zero and presentity_uri, watcher_contact and _d should be valid for a subscribe"); return 0; } LM_DBG("Event type is IMS REGISTRAR SUBSCRIBE about to get reginfo_full"); @@ -491,7 +491,7 @@ int event_reg(udomain_t* _d, impurecord_t* r_passed, ucontact_t* c_passed, int e LM_DBG("About to ceate notification"); - create_notifications(_d, r_passed, c_passed, presentity_uri, watcher_contact, content, event_type); + create_notifications(_d, r_passed, presentity_uri, watcher_contact, content, event_type); if (content.s) pkg_free(content.s); // if (send_now) notification_timer(0, 0); return 0; @@ -503,7 +503,7 @@ int event_reg(udomain_t* _d, impurecord_t* r_passed, ucontact_t* c_passed, int e case IMS_REGISTRAR_CONTACT_REGISTERED: case IMS_REGISTRAR_CONTACT_REFRESHED: case IMS_REGISTRAR_CONTACT_EXPIRED: - if (!r_passed || !c_passed || presentity_uri || watcher_contact || _d) { + if (!r_passed || presentity_uri || watcher_contact || _d) { LM_ERR("this is a contact change passed from ul callback: r_passed and c_passed should both be valid and presentity_uri, watcher_contact and _d should be 0 for ul callback"); return 0; } @@ -536,7 +536,7 @@ int event_reg(udomain_t* _d, impurecord_t* r_passed, ucontact_t* c_passed, int e LM_DBG("About to ceate notification"); - create_notifications(_d, r_passed, c_passed, presentity_uri, watcher_contact, content, event_type); + create_notifications(_d, r_passed, presentity_uri, watcher_contact, content, event_type); if (content.s) pkg_free(content.s); // if (send_now) notification_timer(0, 0); return 1; @@ -548,7 +548,22 @@ int event_reg(udomain_t* _d, impurecord_t* r_passed, ucontact_t* c_passed, int e } } -int process_contact(impurecord_t* presentity_impurecord, udomain_t * _d, int expires, str contact_uri, int contact_state) { +int notify_subscribers(impurecord_t* impurecord) { + event_reg(0, impurecord, IMS_REGISTRAR_CONTACT_UNREGISTERED, 0, 0); + + return 0; +} + +/*! Called to process a contact in a received publish document. No locks held when calling this function + * + * @param subscription + * @param _d + * @param expires + * @param contact_uri + * @param contact_state + * @return + */ +int process_contact(ims_subscription* subscription, udomain_t * _d, int expires, str contact_uri, int contact_state) { int ret = CSCF_RETURN_TRUE; int i, j; @@ -556,8 +571,8 @@ int process_contact(impurecord_t* presentity_impurecord, udomain_t * _d, int exp struct ucontact* ucontact; str callid = {0, 0}; str path = {0, 0}; - ims_subscription* subscription = 0; impurecord_t* implicit_impurecord = 0; + get_act_time(); //first get the subscription //then go through each implicit public identity (exclude the explicit identity) @@ -565,48 +580,42 @@ int process_contact(impurecord_t* presentity_impurecord, udomain_t * _d, int exp //then get the contact for each implicit IMPU and delete if contact_state == STATE_TERMINATED //then get the contact for each explicit IMPU and delete if contact_state == STATE_TERMINATED - subscription = presentity_impurecord->s; - if (!subscription) { - LM_DBG("No subscriber info associated with <%.*s>, so no implicit IMPUs to process\n", presentity_impurecord->public_identity.len, presentity_impurecord->public_identity.s); - goto done; - } - - ul.lock_subscription(subscription); - subscription->ref_count++; - LM_DBG("subscription ref count after add is now %d\n", subscription->ref_count); - ul.unlock_subscription(subscription); //now update the implicit set for (i = 0; i < subscription->service_profiles_cnt; i++) { for (j = 0; j < subscription->service_profiles[i].public_identities_cnt; j++) { pi = &(subscription->service_profiles[i].public_identities[j]); - if (memcmp(presentity_impurecord->public_identity.s, pi->public_identity.s, presentity_impurecord->public_identity.len) == 0) { //we don't need to update the explicit IMPU - LM_DBG("Ignoring explicit identity <%.*s>, updating later.....\n", presentity_impurecord->public_identity.len, presentity_impurecord->public_identity.s); - goto next_implicit_impu; - } + // if ((presentity_aor->len == pi->public_identity.len) && memcmp(presentity_aor.s, pi->public_identity.s, presentity_aor->len) == 0) { //we don't need to update the explicit IMPU + // LM_DBG("Ignoring explicit identity <%.*s>, updating later..... end of fn\n", presentity_aor.len, presentity_aor->s); + // goto next_implicit_impu; + // } ul.lock_udomain(_d, &pi->public_identity); if (ul.get_impurecord(_d, &pi->public_identity, &implicit_impurecord) != 0) { LM_DBG("usrloc does not have impurecord for implicity IMPU, ignore\n"); goto next_implicit_impu; } - if (ul.get_ucontact(implicit_impurecord, &contact_uri, &callid, &path, 0/*cseq*/, &ucontact) != 0) { //contact does not exist + if (ul.get_ucontact(&contact_uri, &callid, &path, 0/*cseq*/, &ucontact) != 0) { //contact does not exist LM_DBG("This contact: <%.*s> is not in usrloc, ignore - NOTE: You need S-CSCF usrloc set to match_mode CONTACT_ONLY\n", contact_uri.len, contact_uri.s); goto next_implicit_impu; } else {//contact exists - get_act_time(); - if (VALID_CONTACT(ucontact, act_time)) { + if (ucontact->state != CONTACT_DELETED) { if (contact_state == STATE_TERMINATED) { //delete contact LM_DBG("This contact <%.*s> is in state terminated and is in usrloc so removing it from usrloc\n", contact_uri.len, contact_uri.s); ul.lock_contact_slot(&contact_uri); - if (ul.unlink_contact_from_impu(implicit_impurecord, ucontact, 1, 0 /*implicit dereg of contact from IMPU*/) != 0) { - LM_ERR("Failed to delete ucontact <%.*s> from implicit IMPU\n", contact_uri.len, contact_uri.s); - ul.unlock_contact_slot(&contact_uri); - ul.release_ucontact(ucontact); - goto next_implicit_impu; //TODO: don't need to use goto here... + ucontact->state = CONTACT_DELETE_PENDING; + if (implicit_impurecord->shead) { + //send NOTIFY to all subscribers of this IMPU. + notify_subscribers(implicit_impurecord); } ul.unlock_contact_slot(&contact_uri); + // if (ul.unlink_contact_from_impu(implicit_impurecord, ucontact, 1, 0 /*implicit dereg of contact from IMPU*/) != 0) { + // LM_ERR("Failed to delete ucontact <%.*s> from implicit IMPU\n", contact_uri.len, contact_uri.s); + // ul.unlock_contact_slot(&contact_uri); + // ul.release_ucontact(ucontact); + // goto next_implicit_impu; //TODO: don't need to use goto here... + // } } else {//state is active LM_DBG("This contact: <%.*s> is not in state terminated and is in usrloc, ignore\n", contact_uri.len, contact_uri.s); ul.release_ucontact(ucontact); @@ -620,42 +629,59 @@ int process_contact(impurecord_t* presentity_impurecord, udomain_t * _d, int exp } } - ul.lock_subscription(subscription); - subscription->ref_count--; - LM_DBG("subscription ref count after sub is now %d\n", subscription->ref_count); - ul.unlock_subscription(subscription); - - // ul.lock_udomain(_d, &presentity_impurecord->public_identity); - - if (ul.get_ucontact(presentity_impurecord, &contact_uri, &callid, &path, 0/*cseq*/, &ucontact) != 0) { //contact does not exist - LM_DBG("This contact: <%.*s> is not in usrloc, ignore - NOTE: You need S-CSCF usrloc set to match_mode CONTACT_ONLY\n", contact_uri.len, contact_uri.s); - return ret; - } else {//contact exists - get_act_time(); - if (VALID_CONTACT(ucontact, act_time)) { - if (contact_state == STATE_TERMINATED) { - //delete contact - LM_DBG("This contact <%.*s> is in state terminated and is in usrloc so removing it from usrloc\n", contact_uri.len, contact_uri.s); - ul.lock_contact_slot(&contact_uri); - if (ul.unlink_contact_from_impu(presentity_impurecord, ucontact, 1, 0 /*implicit dereg of contact from IMPU */) != 0) { - LM_ERR("Failed to delete ucontact <%.*s>\n", contact_uri.len, contact_uri.s); - ret = CSCF_RETURN_FALSE; - ul.unlock_contact_slot(&contact_uri); - ul.release_ucontact(ucontact); - goto done; - } - ul.unlock_contact_slot(&contact_uri); - } else {//state is active - LM_DBG("This contact: <%.*s> is not in state terminated and is in usrloc, ignore\n", contact_uri.len, contact_uri.s); - ul.release_ucontact(ucontact); - goto done; - } + if (contact_state == STATE_TERMINATED) { + //at this point we can assume that all notifies were sent out to the subscribers of the IMPUs related to the explicit and implicit impus. + //we must now change the state of the contact to deleted so they can be removed from the IMPU in the timer process... + if (ul.get_ucontact(&contact_uri, &callid, &path, 0/*cseq*/, &ucontact) != 0) { //contact does not exist + LM_DBG("This contact: <%.*s> is not in usrloc, ignore - NOTE: You need S-CSCF usrloc set to match_mode CONTACT_ONLY\n", contact_uri.len, contact_uri.s); + } else { + ul.lock_contact_slot(&contact_uri); + ucontact->state = CONTACT_DELETED; + ul.unlock_contact_slot(&contact_uri); + ul.release_ucontact(ucontact); } - ul.release_ucontact(ucontact); } -done: - ul.unlock_udomain(_d, &presentity_impurecord->public_identity); + // ul.lock_subscription(subscription); + // subscription->ref_count--; + // LM_DBG("subscription ref count after sub is now %d\n", subscription->ref_count); + // ul.unlock_subscription(subscription); + + // ul.lock_udomain(_d, &presentity_impurecord->public_identity); + + // if (ul.get_ucontact(&contact_uri, &callid, &path, 0/*cseq*/, &ucontact) != 0) { //contact does not exist + // LM_DBG("This contact: <%.*s> is not in usrloc, ignore - NOTE: You need S-CSCF usrloc set to match_mode CONTACT_ONLY\n", contact_uri.len, contact_uri.s); + // return ret; + // } else {//contact exists + // if (VALID_CONTACT(ucontact, act_time)) { + // if (contact_state == STATE_TERMINATED) { + // //delete contact + // LM_DBG("This contact <%.*s> is in state terminated and is in usrloc so removing it from usrloc\n", contact_uri.len, contact_uri.s); + //// ul.lock_contact_slot(&contact_uri); + //// if (ul.unlink_contact_from_impu(presentity_impurecord, ucontact, 1, 0 /*implicit dereg of contact from IMPU */) != 0) { + //// LM_ERR("Failed to delete ucontact <%.*s>\n", contact_uri.len, contact_uri.s); + //// ret = CSCF_RETURN_FALSE; + //// ul.unlock_contact_slot(&contact_uri); + //// ul.release_ucontact(ucontact); + //// goto done; + //// } + //// ul.unlock_contact_slot(&contact_uri); + // ucontact->state = CONTACT_DELETE_PENDING; + // if (implicit_impurecord->shead) { + // //send NOTIFY to all subscribers of this IMPU. + // notify_subscribers(implicit_impurecord); + // } + // } else {//state is active + // LM_DBG("This contact: <%.*s> is not in state terminated and is in usrloc, ignore\n", contact_uri.len, contact_uri.s); + // ul.release_ucontact(ucontact); + // goto done; + // } + // } + // ul.release_ucontact(ucontact); + // } + + //done: + // ul.unlock_udomain(_d, &presentity_impurecord->public_identity); return ret; } @@ -742,6 +768,8 @@ int process_publish_body(struct sip_msg* msg, str publish_body, udomain_t * doma char * expires_char, * cseq_char; int cseq = 0; impurecord_t* presentity_impurecord; + ims_subscription* subscription = 0; + int subscription_locked; doc = xmlParseMemory(publish_body.s, publish_body.len); if (doc == NULL) { @@ -756,6 +784,7 @@ int process_publish_body(struct sip_msg* msg, str publish_body, udomain_t * doma } registrations = doc_root->children; while (registrations) { + subscription_locked = 0; /* Only process registration sub-items */ if (xmlStrcasecmp(registrations->name, BAD_CAST "registration") != 0) goto next_registration; @@ -771,13 +800,26 @@ int process_publish_body(struct sip_msg* msg, str publish_body, udomain_t * doma //TOD get IMPU record here ul.lock_udomain(domain, &aor); if (ul.get_impurecord(domain, &aor, &presentity_impurecord) != 0) { - LM_DBG("usrloc does not have imprecord for presentity being published too, ignore\n"); + LM_DBG("usrloc does not have impurecord for presentity being published too, ignore\n"); ul.unlock_udomain(domain, &aor); goto next_registration; } + //get the subscription which we will use later + + subscription = presentity_impurecord->s; + if (!subscription) { + LM_DBG("No subscriber info associated with <%.*s>, so no implicit IMPUs to process\n", presentity_impurecord->public_identity.len, presentity_impurecord->public_identity.s); + goto next_registration; + } + ul.lock_subscription(subscription); + subscription->ref_count++; + LM_DBG("subscription ref count after add is now %d\n", subscription->ref_count); + ul.unlock_subscription(subscription); + subscription_locked = 1; + ul.unlock_udomain(domain, &aor); - LM_DBG("Received impurecord for presentity being published on [%.*s]\n", presentity_impurecord->public_identity.len, presentity_impurecord->public_identity.s); + LM_DBG("Received impurecord for presentity being published on [%.*s]\n", aor.len, aor.s); if (reg_state == STATE_TERMINATED) { LM_DBG("This impurecord is in STATE_TERMINATED - TODO we should should delete all contacts"); @@ -871,7 +913,7 @@ int process_publish_body(struct sip_msg* msg, str publish_body, udomain_t * doma contact_uri.len, contact_uri.s); /* Add to Usrloc: */ - result = process_contact(presentity_impurecord, domain, expires, contact_uri, contact_state); + result = process_contact(subscription, domain, expires, contact_uri, contact_state); /* Process the result */ if (final_result != CSCF_RETURN_TRUE) final_result = result; @@ -884,6 +926,12 @@ int process_publish_body(struct sip_msg* msg, str publish_body, udomain_t * doma } next_registration: registrations = registrations->next; + if (subscription_locked) { + ul.lock_subscription(subscription); + subscription->ref_count--; + LM_DBG("subscription ref count after sub is now %d\n", subscription->ref_count); + ul.unlock_subscription(subscription); + } } error: /* Free the XML-Document */ @@ -1184,7 +1232,7 @@ int subscribe_to_reg(struct sip_msg *msg, char *_t, char *str2) { subscribe_reply(msg, 200, MSG_REG_SUBSCRIBE_OK, &expires, &scscf_name_str); //do reg event every time you get a subscribe - if (event_reg(domain, 0, 0, event_type, &presentity_uri, &watcher_contact) != 0) { + if (event_reg(domain, 0, event_type, &presentity_uri, &watcher_contact) != 0) { LM_ERR("failed adding notification for reg events\n"); ret = CSCF_RETURN_ERROR; goto doneorerror; @@ -1222,8 +1270,8 @@ int subscribe_to_reg(struct sip_msg *msg, char *_t, char *str2) { LM_DBG("Sending 200 OK to subscribing user"); subscribe_reply(msg, 200, MSG_REG_UNSUBSCRIBE_OK, &expires, &scscf_name_str); } - -doneorerror: + +doneorerror: //free memory if (presentity_uri.s) shm_free(presentity_uri.s); // shm_malloc in cscf_get_public_identity_from_requri or get_presentity_from_subscriber_dialog if (record_route.s) pkg_free(record_route.s); @@ -1343,7 +1391,7 @@ static str subs_active = {"active;expires=", 15}; * @param content - the body content * @param expires - the remaining subcription expiration time in seconds */ -void create_notifications(udomain_t* _t, impurecord_t* r_passed, ucontact_t* c_passed, str *presentity_uri, str *watcher_contact, str content, int event_type) { +void create_notifications(udomain_t* _t, impurecord_t* r_passed, str *presentity_uri, str *watcher_contact, str content, int event_type) { reg_notification *n; reg_subscriber *s; @@ -1362,7 +1410,7 @@ void create_notifications(udomain_t* _t, impurecord_t* r_passed, ucontact_t* c_p LM_DBG("Creating notification"); - if (r_passed && c_passed && !presentity_uri && !watcher_contact) { + if (r_passed && !presentity_uri && !watcher_contact) { LM_DBG("r_passed and c_passed are valid and presentity uri and watcher_contact is 0 - this must be a ul callback no need to lock domain"); r = r_passed; @@ -1424,13 +1472,14 @@ void create_notifications(udomain_t* _t, impurecord_t* r_passed, ucontact_t* c_p } } else { - if (event_type == IMS_REGISTRAR_CONTACT_UNREGISTERED && !ue_unsubscribe_on_dereg && - (contact_port_ip_match(&c_passed->c, &s->watcher_contact) && - (r_passed->public_identity.len == s->presentity_uri.len) && (memcmp(s->presentity_uri.s, r_passed->public_identity.s, r_passed->public_identity.len) == 0))) { - //if this is UNREGISTER and the UEs do not unsubscribe to dereg and this is a UE subscribing to its own reg event - //then we do not send notifications - LM_DBG("This is a UNREGISTER event for a UE that subscribed to its own state that does not unsubscribe to dereg - therefore no notification"); - } else { + //TODO: we must make this optimisation to not send NOTIFYs back to UE's *(they may have disappeared) +// if (event_type == IMS_REGISTRAR_CONTACT_UNREGISTERED && !ue_unsubscribe_on_dereg /*&& +// (contact_port_ip_match(&c_passed->c, &s->watcher_contact) */ +// && (r_passed->public_identity.len == s->presentity_uri.len) && (memcmp(s->presentity_uri.s, r_passed->public_identity.s, r_passed->public_identity.len) == 0)) { +// //if this is UNREGISTER and the UEs do not unsubscribe to dereg and this is a UE subscribing to its own reg event +// //then we do not send notifications +// LM_DBG("This is a UNREGISTER event for a UE that subscribed to its own state that does not unsubscribe to dereg - therefore no notification"); +// } else { LM_DBG("about to make new notification!"); LM_DBG("we always increment the local cseq and version before we send a new notification\n"); @@ -1447,7 +1496,7 @@ void create_notifications(udomain_t* _t, impurecord_t* r_passed, ucontact_t* c_p } else { LM_DBG("Notification does not exist"); } - } +// } } s = s->next; @@ -1527,6 +1576,77 @@ static str contact_e = {"\t\t\n", 13}; static str uri_s = {"\t\t\t", 8}; static str uri_e = {"\n", 7}; +static void process_xml_for_contact(str* buf, str* pad, ucontact_t* ptr) { + int expires; + param_t *param; + + if (ptr->state == CONTACT_DELETED) + return; + + expires = ptr->expires - act_time; + if (expires < 0 || (ptr->state == CONTACT_DELETE_PENDING) || (ptr->state == CONTACT_EXPIRE_PENDING_NOTIFY)) { + LM_WARN("Contact expires is negative - setting to 0\n"); + expires = 0; + } + if (expires == 0) { + if (ptr->q != -1) { + float q = (float) ptr->q / 1000; + sprintf(pad->s, contact_s_q.s, ptr, r_terminated.len, r_terminated.s, + r_expired.len, r_expired.s, expires, q); + } else { + sprintf(pad->s, contact_s_q.s, ptr, r_terminated.len, r_terminated.s, + r_expired.len, r_expired.s, expires); + } + } else { + if (ptr->q != -1) { + float q = (float) ptr->q / 1000; + sprintf(pad->s, contact_s_q.s, ptr, r_active.len, r_active.s, + r_registered.len, r_registered.s, expires, + q); + } else { + sprintf(pad->s, contact_s_q.s, ptr, r_active.len, r_active.s, + r_registered.len, r_registered.s, expires); + } + } + + pad->len = strlen(pad->s); + STR_APPEND(*buf, *pad); + STR_APPEND(*buf, uri_s); + + LM_DBG("Appending contact address: <%.*s>", ptr->c.len, ptr->c.s); + + STR_APPEND(*buf, (ptr->c)); + STR_APPEND(*buf, uri_e); + + param = ptr->params; + while (param) { + if (supported_param(¶m->name) != 0) { + param = param->next; + continue; + } + + if (param->body.len > 0) { + LM_DBG("This contact has params name: [%.*s] body [%.*s]\n", param->name.len, param->name.s, param->body.len, param->body.s); + if (param->body.s[0] == '<' && param->body.s[param->body.len - 1] == '>') { + LM_DBG("This param body starts with '<' and ends with '>' we will clean these for the NOTIFY XML with < and >\n"); + sprintf(pad->s, contact_s_params_with_body_fix.s, param->name.len, param->name.s, param->body.len - 2, param->body.s + 1); + } else { + sprintf(pad->s, contact_s_params_with_body.s, param->name.len, param->name.s, param->body.len, param->body.s); + } + + pad->len = strlen(pad->s); + STR_APPEND(*buf, *pad); + } else { + LM_DBG("This contact has params name: [%.*s] \n", param->name.len, param->name.s); + sprintf(pad->s, contact_s_params_no_body.s, param->name.len, param->name.s); + pad->len = strlen(pad->s); + STR_APPEND(*buf, *pad); + } + param = param->next; + } + STR_APPEND(*buf, contact_e); +} + /** * Creates the full reginfo XML. * @param pv - the r_public to create for @@ -1542,18 +1662,15 @@ str generate_reginfo_full(udomain_t* _t, str* impu_list, int num_impus, str *pri impurecord_t *r; int i, j, res; ucontact_t* ptr; - param_t *param; buf.s = bufc; buf.len = 0; pad.s = padc; pad.len = 0; - - int domain_locked = 1; + get_act_time(); +// int domain_locked = 1; int terminate_impu = 1; - int expires; - LM_DBG("Getting reginfo_full"); STR_APPEND(buf, xml_start); @@ -1563,31 +1680,33 @@ str generate_reginfo_full(udomain_t* _t, str* impu_list, int num_impus, str *pri for (i = 0; i < num_impus; i++) { LM_DBG("Scrolling through public identities, current one <%.*s>", impu_list[i].len, impu_list[i].s); - if (primary_locked && strncasecmp(impu_list[i].s, primary_impu->s, impu_list[i].len) == 0) { - LM_DBG("Don't need to lock this impu [%.*s] as its a ul callback so already locked\n", impu_list[i].len, impu_list[i].s); - domain_locked = 0; - } else { - LM_DBG("Need to lock this impu\n"); - ul.lock_udomain(_t, &impu_list[i]); - domain_locked = 1; - } - +// if (primary_locked && strncasecmp(impu_list[i].s, primary_impu->s, impu_list[i].len) == 0) { +// LM_DBG("Don't need to lock this impu [%.*s] as its a ul callback so already locked\n", impu_list[i].len, impu_list[i].s); +// domain_locked = 0; +// } else { +// LM_DBG("Need to lock this impu\n"); +// ul.lock_udomain(_t, &impu_list[i]); +// domain_locked = 1; +// } + + ul.lock_udomain(_t, &impu_list[i]); + res = ul.get_impurecord(_t, &(impu_list[i]), &r); if (res != 0) { LM_WARN("impu disappeared, ignoring it\n"); - if (domain_locked) { +// if (domain_locked) { ul.unlock_udomain(_t, &impu_list[i]); - } +// } continue; } - domain_locked = 1; + // domain_locked = 1; LM_DBG("Retrieved IMPU record"); j = 0; terminate_impu = 1; while (j < MAX_CONTACTS_PER_IMPU && (ptr = r->newcontacts[j])) { - if (((ptr->expires - act_time) > 0)) { + if (VALID_CONTACT(ptr, act_time)) { LM_DBG("IMPU <%.*s> has another active contact <%.*s> so will set its state to active\n", r->public_identity.len, r->public_identity.s, ptr->c.len, ptr->c.s); terminate_impu = 0; @@ -1611,84 +1730,20 @@ str generate_reginfo_full(udomain_t* _t, str* impu_list, int num_impus, str *pri j = 0; LM_DBG("Scrolling through contact for this IMPU"); +// if (contact && !domain_locked /* we're dealing with the primary impu most likely related to de-reg */) { +// LM_DBG("We're dealing with the primary IMPU here AND a contact was passed in - must have been an explicit dereg\n"); +// process_xml_for_contact(&buf, &pad, contact); //we do this because in the unlink_contact_from_impu the contact has already gone so we pass it in as a param... +// } while (j < MAX_CONTACTS_PER_IMPU && (ptr = r->newcontacts[j])) { - if (ptr->q != -1) { - LM_DBG("q value not equal to -1"); - float q = (float) ptr->q / 1000; - expires = ptr->expires - act_time; - if (expires < 0) { - LM_WARN("Contact expires is negative - setting to 0\n"); - expires = 0; - } - if (expires == 0) { - sprintf(pad.s, contact_s_q.s, ptr, r_terminated.len, r_terminated.s, - r_expired.len, r_expired.s, expires, - q); - } else { - sprintf(pad.s, contact_s_q.s, ptr, r_active.len, r_active.s, - r_registered.len, r_registered.s, expires, - q); - } - - } else { - LM_DBG("q value equal to -1"); - expires = ptr->expires - act_time; - if (expires < 0) { - LM_WARN("Contact expires is negative - setting to 0\n"); - expires = 0; - } - if (expires == 0) { - sprintf(pad.s, contact_s_q.s, ptr, r_terminated.len, r_terminated.s, - r_expired.len, r_expired.s, expires); - } else { - sprintf(pad.s, contact_s_q.s, ptr, r_active.len, r_active.s, - r_registered.len, r_registered.s, expires); - } - } - pad.len = strlen(pad.s); - STR_APPEND(buf, pad); - STR_APPEND(buf, uri_s); - - LM_DBG("Appending contact address: <%.*s>", ptr->c.len, ptr->c.s); - - STR_APPEND(buf, (ptr->c)); - STR_APPEND(buf, uri_e); - - param = ptr->params; - while (param) { - if (supported_param(¶m->name) != 0) { - param = param->next; - continue; - } - - if (param->body.len > 0) { - LM_DBG("This contact has params name: [%.*s] body [%.*s]\n", param->name.len, param->name.s, param->body.len, param->body.s); - if (param->body.s[0] == '<' && param->body.s[param->body.len - 1] == '>') { - LM_DBG("This param body starts with '<' and ends with '>' we will clean these for the NOTIFY XML with < and >\n"); - sprintf(pad.s, contact_s_params_with_body_fix.s, param->name.len, param->name.s, param->body.len - 2, param->body.s + 1); - } else { - sprintf(pad.s, contact_s_params_with_body.s, param->name.len, param->name.s, param->body.len, param->body.s); - } - - pad.len = strlen(pad.s); - STR_APPEND(buf, pad); - } else { - LM_DBG("This contact has params name: [%.*s] \n", param->name.len, param->name.s); - sprintf(pad.s, contact_s_params_no_body.s, param->name.len, param->name.s); - pad.len = strlen(pad.s); - STR_APPEND(buf, pad); - } - param = param->next; - } - STR_APPEND(buf, contact_e); + process_xml_for_contact(&buf, &pad, ptr); j++; } STR_APPEND(buf, registration_e); - if (domain_locked) { +// if (domain_locked) { ul.unlock_udomain(_t, &impu_list[i]); - } +// } } STR_APPEND(buf, r_reginfo_e); diff --git a/modules/ims_registrar_scscf/registrar_notify.h b/modules/ims_registrar_scscf/registrar_notify.h index c48c5feb2b7..ccb9f6c0721 100644 --- a/modules/ims_registrar_scscf/registrar_notify.h +++ b/modules/ims_registrar_scscf/registrar_notify.h @@ -123,14 +123,14 @@ int publish_reg(struct sip_msg *msg, char *str1, char *str2); int subscribe_reply(struct sip_msg *msg, int code, char *text, int *expires, str *contact); -int event_reg(udomain_t* _d, impurecord_t* r_passed, ucontact_t* c_passed, int event_type, str *presentity_uri, str *watcher_contact); +int event_reg(udomain_t* _d, impurecord_t* r_passed, int event_type, str *presentity_uri, str *watcher_contact); str generate_reginfo_full(udomain_t* _t, str* impu_list, int new_subscription, str *primary_impu, int primary_locked); str get_reginfo_partial(impurecord_t *r, ucontact_t *c, int event_type); -void create_notifications(udomain_t* _t, impurecord_t* r_passed, ucontact_t* c_passed, str *presentity_uri, str *watcher_contact, str content, int event_type); +void create_notifications(udomain_t* _t, impurecord_t* r_passed, str *presentity_uri, str *watcher_contact, str content, int event_type); void notification_event_process(); @@ -152,4 +152,6 @@ void notify_destroy(); int aor_to_contact(str* aor, str* contact); int contact_port_ip_match(str *c1, str *c2); +int notify_subscribers(impurecord_t* impurecord); + #endif //S_CSCF_REGISTRAR_NOTIFY_H_ diff --git a/modules/ims_registrar_scscf/reply.c b/modules/ims_registrar_scscf/reply.c index 5847e7a71ad..c52334faedc 100644 --- a/modules/ims_registrar_scscf/reply.c +++ b/modules/ims_registrar_scscf/reply.c @@ -65,6 +65,7 @@ #define CONTACT_SEP_LEN (sizeof(CONTACT_SEP) - 1) extern str scscf_serviceroute_uri_str; +extern int contact_expires_buffer_percentage; extern struct tm_binds tmb; @@ -421,7 +422,7 @@ int build_expired_contact(contact_t* chi, contact_for_header_t** contact_header) int build_contact(impurecord_t* impurec, contact_for_header_t** contact_header) { char *p, *cp; - int fl, len; + int fl, len, expires, expires_orig; ucontact_t* c; param_t* tmp; *contact_header = 0; @@ -469,7 +470,15 @@ int build_contact(impurecord_t* impurec, contact_for_header_t** contact_header) memcpy(p, EXPIRES_PARAM, EXPIRES_PARAM_LEN); p += EXPIRES_PARAM_LEN; - cp = int2str((int) (c->expires - act_time), &len); + + /* the expires we put in the contact header is decremented to give the UE some grace before we expires them */ + expires = expires_orig = (int)(c->expires - act_time); + expires = expires - (contact_expires_buffer_percentage*expires/100); + if (expires <= 0) { + LM_WARN("expires after buffer change was <= 0, not adding buffer space\n"); + expires = expires_orig; + } + cp = int2str(expires, &len); memcpy(p, cp, len); p += len; diff --git a/modules/ims_registrar_scscf/save.c b/modules/ims_registrar_scscf/save.c index 502b99936e9..ff75a0de7fa 100644 --- a/modules/ims_registrar_scscf/save.c +++ b/modules/ims_registrar_scscf/save.c @@ -137,9 +137,9 @@ static inline int calc_contact_expires(contact_t *c, unsigned int expires_hdr, i r = default_registrar_cfg.em_min_expires; end: - + r = randomize_expires(r, default_registrar_cfg.default_expires_range); - + LM_DBG("Calculated expires for contact is %d\n", r); return time(NULL) + r; } @@ -533,7 +533,7 @@ static inline int update_contacts_helper(struct sip_msg* msg, impurecord_t* impu qvalue_t qvalue; int sos = 0, expires; struct ucontact* ucontact; - int result; + int result, sl; LM_DBG("updating the contacts for IMPU <%.*s>\n", impu_rec->public_identity.len, impu_rec->public_identity.s); @@ -575,9 +575,8 @@ static inline int update_contacts_helper(struct sip_msg* msg, impurecord_t* impu LM_DBG("adding/updating contact based on prior existence\n"); //stick the contacts into usrloc //ul.lock_contact_slot(&chi->uri); - result = ul.get_ucontact(impu_rec, &chi->uri, ci->callid, + result = ul.get_ucontact(&chi->uri, ci->callid, ci->path, ci->cseq, &ucontact); - if (result != 0) { //get_contact returns with lock LM_DBG("inserting new contact\n"); if (ul.insert_ucontact(impu_rec, &chi->uri, ci, @@ -587,17 +586,23 @@ static inline int update_contacts_helper(struct sip_msg* msg, impurecord_t* impu goto error; } } else { - LM_DBG("Contact already exists - updating\n"); + LM_DBG("Contact already exists - updating - it's currently in state [%d]\n", ucontact->state); + sl = ucontact->sl; + ul.lock_contact_slot_i(sl); + if (ucontact->state != CONTACT_VALID) { + LM_WARN("contact is not in state valid - this is a race between dereg and reg/re-reg"); + ucontact->state = CONTACT_VALID; //TODO this should prob move into the contact info structure - ie pass state into update + } if (ul.update_ucontact(impu_rec, ucontact, ci) != 0) { LM_ERR("Error updating contact <%.*s>\n", chi->uri.len, chi->uri.s); + ul.unlock_contact_slot_i(sl); ul.release_ucontact(ucontact); // ul.unlock_contact_slot(&chi->uri); goto error; } + ul.unlock_contact_slot_i(sl); ul.release_ucontact(ucontact); } - - // ul.unlock_contact_slot(&chi->uri); } } @@ -612,68 +617,66 @@ static inline int update_contacts_helper(struct sip_msg* msg, impurecord_t* impu } /*NB remember to lock udomain prior to calling this*/ -static inline int unregister_contact(udomain_t* _d, str* public_identity, impurecord_t* _impu_rec, contact_t* chi) { - impurecord_t* impu_rec; +static inline int unregister_contact(contact_t* chi, contact_state_t state) { struct ucontact* ucontact; str callid = {0, 0}; str path = {0, 0}; - reg_subscriber *s; - - if (_impu_rec) { - LM_DBG("already have impurecord....\n"); - impu_rec = _impu_rec; - } else { - if (ul.get_impurecord(_d, public_identity, &impu_rec) != 0) { - LM_ERR("Error, no public identity exists for <%.*s>\n", public_identity->len, public_identity->s); - goto error; - } - } - + // if (_impu_rec) { + // LM_DBG("already have impurecord....\n"); + // impu_rec = _impu_rec; + // } else { + // if (ul.get_impurecord(_d, public_identity, &impu_rec) != 0) { + // LM_ERR("Error, no public identity exists for <%.*s>\n", public_identity->len, public_identity->s); + // goto error; + // } + // } - if (ul.get_ucontact(impu_rec, &chi->uri, &callid, &path, 0/*cseq*/, &ucontact) != 0) { + if (ul.get_ucontact(&chi->uri, &callid, &path, 0/*cseq*/, &ucontact) != 0) { LM_ERR("Can't unregister contact that does not exist <%.*s>\n", chi->uri.len, chi->uri.s); // ul.unlock_udomain(_d, public_identity); goto error; } get_act_time(); - if (!VALID_CONTACT(ucontact, act_time)) { - LM_DBG("Contact is not valid (expired).... ignoring\n"); + if (ucontact->state == CONTACT_DELETED) { + LM_DBG("Contact is not valid (expired/deleted).... ignoring\n"); ul.release_ucontact(ucontact); - // ul.unlock_udomain(_d, public_identity); - goto error; + return 0; } //Richard added this - fix to remove subscribes that have presentity and watcher uri same as a contact aor that is being removed //When UEs explicitly dereg - they don't unsubscribe, so we remove subscriptions for them //only do this if ue_unsubscribe_on_dereg is set to 0 - if (!ue_unsubscribe_on_dereg) { - s = impu_rec->shead; - LM_DBG("Checking if there is a subscription to this IMPU that has same watcher contact as this contact"); - while (s) { - - LM_DBG("Subscription for this impurecord: watcher uri [%.*s] presentity uri [%.*s] watcher contact [%.*s] ", s->watcher_uri.len, s->watcher_uri.s, - s->presentity_uri.len, s->presentity_uri.s, s->watcher_contact.len, s->watcher_contact.s); - LM_DBG("Contact to be removed [%.*s] ", ucontact->c.len, ucontact->c.s); - if (contact_port_ip_match(&s->watcher_contact, &ucontact->c)) { - //if ((s->watcher_contact.len == ucontact->c.len) && (strncasecmp(s->watcher_contact.s, ucontact->c.s, ucontact->c.len) == 0)) { - LM_DBG("This contact has a subscription to its own status - so going to delete the subscription"); - ul.external_delete_subscriber(s, _d, 0 /*domain is locked*/); - } - s = s->next; - } - } + // if (!ue_unsubscribe_on_dereg) { + // s = impu_rec->shead; + // LM_DBG("Checking if there is a subscription to this IMPU that has same watcher contact as this contact"); + // while (s) { + // + // LM_DBG("Subscription for this impurecord: watcher uri [%.*s] presentity uri [%.*s] watcher contact [%.*s] ", s->watcher_uri.len, s->watcher_uri.s, + // s->presentity_uri.len, s->presentity_uri.s, s->watcher_contact.len, s->watcher_contact.s); + // LM_DBG("Contact to be removed [%.*s] ", ucontact->c.len, ucontact->c.s); + // if (contact_port_ip_match(&s->watcher_contact, &ucontact->c)) { + // //if ((s->watcher_contact.len == ucontact->c.len) && (strncasecmp(s->watcher_contact.s, ucontact->c.s, ucontact->c.len) == 0)) { + // LM_DBG("This contact has a subscription to its own status - so going to delete the subscription"); + // ul.external_delete_subscriber(s, _d, 0 /*domain is locked*/); + // } + // s = s->next; + // } + // } // if (ul.delete_ucontact(impu_rec, ucontact) != 0) { ul.lock_contact_slot_i(ucontact->sl); - if (ul.unlink_contact_from_impu(impu_rec, ucontact, 1, 1/*explicit dereg of contact*/) != 0) { - LM_ERR("Failed to delete ucontact <%.*s>\n", chi->uri.len, chi->uri.s); - } + ucontact->state = state; + // notify_subscribers(impu_rec); + // ucontact->state = CONTACT_DELETED; + // if (ul.unlink_contact_from_impu(impu_rec, ucontact, 1, 1/*explicit dereg of contact*/) != 0) { + // LM_ERR("Failed to delete ucontact <%.*s>\n", chi->uri.len, chi->uri.s); + // } ul.unlock_contact_slot_i(ucontact->sl); ul.release_ucontact(ucontact); - LM_DBG("Contact unlinked successfully <%.*s>\n", chi->uri.len, chi->uri.s); + // LM_DBG("Contact unlinked successfully <%.*s>\n", chi->uri.len, chi->uri.s); // ul.unlock_udomain(_d, public_identity); return 0; @@ -681,6 +684,24 @@ static inline int unregister_contact(udomain_t* _d, str* public_identity, impure return -1; } +/** + * Get the number of valid contacts for an impu. Ie contacts not expired and not in deleted or delete_pending state + * @param impu + * @return + */ +int get_number_of_valid_contacts(impurecord_t* impu) { + int i; + int ret = 0; + get_act_time(); + for (i = 0; i < impu->num_contacts; i++) { + if (VALID_CONTACT(impu->newcontacts[i], act_time)) { + ret++; + } + } + + return ret; +} + /** * * @param msg @@ -713,7 +734,6 @@ int update_contacts(struct sip_msg* msg, udomain_t* _d, int first_unbarred_impu = 1; //this is used to flag the IMPU as anchor for implicit set int is_primary_impu = 0; int ret = 1; - if (msg) { expires_hdr = cscf_get_expires_hdr(msg, 0); //get the expires from the main body of the sip message (global) } @@ -762,6 +782,7 @@ int update_contacts(struct sip_msg* msg, udomain_t* _d, } //now build the contact buffer to be include in the reply message and unlock build_contact(impu_rec, contact_header); + notify_subscribers(impu_rec); ul.unlock_udomain(_d, public_identity); break; case AVP_IMS_SAR_RE_REGISTRATION: @@ -838,17 +859,11 @@ int update_contacts(struct sip_msg* msg, udomain_t* _d, if (ul.update_impurecord(_d, public_identity, 0, reg_state, -1 /*do not change send sar on delete */, 0 /*this is explicit so barring must be 0*/, 0, s, ccf1, ccf2, ecf1, ecf2, &impu_rec) != 0) { LM_ERR("Unable to update explicit impurecord for <%.*s>\n", public_identity->len, public_identity->s); } + notify_subscribers(impu_rec); break; case AVP_IMS_SAR_USER_DEREGISTRATION: /*TODO: if its not a star lets find all the contact records and remove them*/ - ul.lock_udomain(_d, public_identity); - - if (ul.get_impurecord(_d, public_identity, &impu_rec) != 0) { - LM_ERR("Error retrieving impu record\n"); - ul.unlock_udomain(_d, public_identity); - goto error; - } - + //first we update the state of the contact/s for (h = msg->contact; h; h = h->next) { if (h->type == HDR_CONTACT_T && h->parsed) { for (chi = ((contact_body_t*) h->parsed)->contacts; chi; chi = chi->next) { @@ -862,7 +877,7 @@ int update_contacts(struct sip_msg* msg, udomain_t* _d, goto error; } calc_contact_expires(chi, expires_hdr, sos); - if (unregister_contact(_d, public_identity, impu_rec, chi) != 0) { + if (unregister_contact(chi, CONTACT_DELETE_PENDING) != 0) { LM_ERR("Unable to remove contact <%.*s\n", chi->uri.len, chi->uri.s); } @@ -871,8 +886,16 @@ int update_contacts(struct sip_msg* msg, udomain_t* _d, } } } - - if (impu_rec->num_contacts >= 0 && impu_rec->newcontacts[0]) { + + //now, we get the subscription + ul.lock_udomain(_d, public_identity); + if (ul.get_impurecord(_d, public_identity, &impu_rec) != 0) { + LM_ERR("Error retrieving impu record on explicit de-reg nothing we can do from here on... aborting..\n"); + ul.unlock_udomain(_d, public_identity); + goto error; + } + int num_contacts = get_number_of_valid_contacts(impu_rec); + if (num_contacts > 0) { LM_DBG("contacts still available\n"); //TODO: add all other remaining contacts to reply message (contacts still registered for this IMPU) ret = 1; @@ -880,8 +903,6 @@ int update_contacts(struct sip_msg* msg, udomain_t* _d, LM_DBG("no more contacts available\n"); ret = 2; } - - //before we release IMPU record - lets save the subscription for implicit dereg ims_subscription* subscription = impu_rec->s; if (!subscription) { @@ -890,17 +911,18 @@ int update_contacts(struct sip_msg* msg, udomain_t* _d, ul.lock_subscription(subscription); subscription->ref_count++; //this is so we can de-reg the implicit set just now without holding the lock on the current IMPU ul.unlock_subscription(subscription); + } - ul.unlock_udomain(_d, public_identity); - //TODO: this needs a clean...... - //lock(subscription->lock); + ul.unlock_udomain(_d, public_identity); + + if (subscription) { for (i = 0; i < subscription->service_profiles_cnt; i++) { for (j = 0; j < subscription->service_profiles[i].public_identities_cnt; j++) { pi = &(subscription->service_profiles[i].public_identities[j]); - if (memcmp(public_identity->s, pi->public_identity.s, public_identity->len) == 0) { //we don't need to update the explicit IMPU - LM_DBG("Ignoring explicit identity <%.*s>, already de-reg/updated\n", public_identity->len, public_identity->s); - continue; - } + // if (memcmp(public_identity->s, pi->public_identity.s, public_identity->len) == 0) { //we don't need to update the explicit IMPU + // LM_DBG("Ignoring explicit identity <%.*s>, already de-reg/updated\n", public_identity->len, public_identity->s); + // continue; + // } ul.lock_udomain(_d, &pi->public_identity); if (ul.get_impurecord(_d, &pi->public_identity, &tmp_impu_rec) != 0) { LM_ERR("Can't find IMPU for implicit de-registration update....continuing\n"); @@ -908,6 +930,24 @@ int update_contacts(struct sip_msg* msg, udomain_t* _d, continue; } LM_DBG("Implicit deregistration of IMPU <%.*s>\n", pi->public_identity.len, pi->public_identity.s); + //TODO_LATEST: need to add back the following functionality + // if (!ue_unsubscribe_on_dereg) { + // subscriber = tmp_impu_rec->shead; + // LM_DBG("Checking if there is a subscription to this IMPU that has same watcher contact as this contact"); + // while (s) { + // + // LM_DBG("Subscription for this impurecord: watcher uri [%.*s] presentity uri [%.*s] watcher contact [%.*s] ", subscriber->watcher_uri.len, subscriber->watcher_uri.s, + // subscriber->presentity_uri.len, subscriber->presentity_uri.s, subscriber->watcher_contact.len, subscriber->watcher_contact.s); + // LM_DBG("Contact to be removed [%.*s] ", ucontact->c.len, ucontact->c.s); + // if (contact_port_ip_match(&subscriber->watcher_contact, &ucontact->c)) { + // //if ((s->watcher_contact.len == ucontact->c.len) && (strncasecmp(s->watcher_contact.s, ucontact->c.s, ucontact->c.len) == 0)) { + // LM_DBG("This contact has a subscription to its own status - so going to delete the subscription"); + // ul.external_delete_subscriber(subscriber, _d, 0 /*domain is locked*/); + // } + // s = s->next; + // } + // } + notify_subscribers(tmp_impu_rec); for (h = msg->contact; h; h = h->next) { if (h->type == HDR_CONTACT_T && h->parsed) { for (chi = ((contact_body_t*) h->parsed)->contacts; chi; chi = chi->next) { @@ -924,16 +964,16 @@ int update_contacts(struct sip_msg* msg, udomain_t* _d, goto error; } calc_contact_expires(chi, expires_hdr, sos); - if (unregister_contact(_d, 0, tmp_impu_rec, chi) != 0) { - LM_ERR("Unable to remove contact <%.*s\n", chi->uri.len, chi->uri.s); + if (unregister_contact(chi, CONTACT_DELETED) != 0) { + LM_ERR("Unable to remove contact <%.*s>\n", chi->uri.len, chi->uri.s); } } } } /*now lets see if we still have any contacts left to decide on return value*/ - - if (tmp_impu_rec->num_contacts && tmp_impu_rec->newcontacts[0]) + int num_valid_contacts = get_number_of_valid_contacts(tmp_impu_rec); + if (num_valid_contacts) LM_DBG("contacts still available after implicit dereg for IMPU: <%.*s>\n", pi->public_identity.len, pi->public_identity.s); else { LM_DBG("no contacts left after implicit dereg for IMPU: <%.*s>\n", pi->public_identity.len, pi->public_identity.s); @@ -1007,12 +1047,12 @@ int assign_server_unreg(struct sip_msg* _m, char* str1, str* direction, char* ro cfg_action_t* cfg_action; udomain_t* _d = (udomain_t*) str1; - + if (fixup_get_svalue(_m, (gparam_t*) route, &route_name) != 0) { LM_ERR("no async route block for assign_server_unreg\n"); return -1; } - + LM_DBG("Looking for route block [%.*s]\n", route_name.len, route_name.s); int ri = route_get(&main_rt, route_name.s); if (ri < 0) { @@ -1024,7 +1064,7 @@ int assign_server_unreg(struct sip_msg* _m, char* str1, str* direction, char* ro LM_ERR("empty action lists in route block [%.*s]\n", route_name.len, route_name.s); return -1; } - + LM_DBG("Assigning unregistered user for direction [%.*s]\n", direction->len, direction->s); enum cscf_dialog_direction dir = cscf_get_dialog_direction(direction->s); @@ -1137,7 +1177,7 @@ int save(struct sip_msg* msg, char* str1, char *route) { str public_identity, private_identity, realm; int sar_assignment_type = AVP_IMS_SAR_NO_ASSIGNMENT; str route_name; - + udomain_t* _d = (udomain_t*) str1; rerrno = R_FINE; @@ -1154,7 +1194,7 @@ int save(struct sip_msg* msg, char* str1, char *route) { LM_ERR("no async route block for assign_server_unreg\n"); return -1; } - + LM_DBG("Looking for route block [%.*s]\n", route_name.len, route_name.s); int ri = route_get(&main_rt, route_name.s); if (ri < 0) { @@ -1245,7 +1285,7 @@ int save(struct sip_msg* msg, char* str1, char *route) { goto error; } else if (res == 2) { //send sar - LM_DBG("no contacts left after de-registration, doing SAR\n"); + LM_DBG("no contacts left after explicit de-registration, doing SAR\n"); } else { //res=1 //still contacts left so return success LM_DBG("contacts still available after deregister.... not doing SAR\n"); @@ -1256,16 +1296,16 @@ int save(struct sip_msg* msg, char* str1, char *route) { } } } - + if (!user_data_always) { - if (require_user_data) - data_available = AVP_IMS_SAR_USER_DATA_NOT_AVAILABLE; - else - data_available = AVP_IMS_SAR_USER_DATA_ALREADY_AVAILABLE; + if (require_user_data) + data_available = AVP_IMS_SAR_USER_DATA_NOT_AVAILABLE; + else + data_available = AVP_IMS_SAR_USER_DATA_ALREADY_AVAILABLE; } else { - data_available = AVP_IMS_SAR_USER_DATA_NOT_AVAILABLE; + data_available = AVP_IMS_SAR_USER_DATA_NOT_AVAILABLE; } - + //before we send lets suspend the transaction t = tmb.t_gett(); if (t == NULL || t == T_UNDEFINED) { diff --git a/modules/ims_registrar_scscf/usrloc_cb.c b/modules/ims_registrar_scscf/usrloc_cb.c index c2397d6ea57..4205b0d9fd3 100644 --- a/modules/ims_registrar_scscf/usrloc_cb.c +++ b/modules/ims_registrar_scscf/usrloc_cb.c @@ -91,23 +91,9 @@ void ul_contact_changed(impurecord_t* r, ucontact_t* c, int type, void* param) { LM_DBG("There are no subscriptions for this IMPU therefore breaking out now as nothing to do"); return; } - +// if (type == UL_IMPU_DELETE_CONTACT) { LM_DBG("Received notification of UL CONTACT DELETE"); - event_reg(0, r, c, IMS_REGISTRAR_CONTACT_UNREGISTERED, 0, 0); - } else if (type == UL_IMPU_DELETE_CONTACT_IMPLICIT) { - LM_DBG("Received notification of UL CONTACT_DELETE_IMPLICIT"); - event_reg(0, r, c, IMS_REGISTRAR_CONTACT_UNREGISTERED_IMPLICIT, 0, 0); - } else if (type == UL_IMPU_EXPIRE_CONTACT) { - LM_DBG("Received notification of UL CONTACT EXPIRE"); - event_reg(0, r, c, IMS_REGISTRAR_CONTACT_EXPIRED, 0, 0); - } else if (type == UL_IMPU_UPDATE_CONTACT) { - LM_DBG("Received notification of UL CONTACT UPDATE"); - event_reg(0, r, c, IMS_REGISTRAR_CONTACT_REFRESHED, 0, 0); - } else if (type == UL_IMPU_NEW_CONTACT) { - LM_DBG("Received notification of UL IMPU CONTACT INSERT"); - event_reg(0, r, c, IMS_REGISTRAR_CONTACT_REGISTERED, 0, 0); - } else { - LM_DBG("This type of callback not supported here"); + event_reg(0, r, IMS_REGISTRAR_CONTACT_UNREGISTERED, 0, 0); } } diff --git a/modules/ims_usrloc_pcscf/pcontact.c b/modules/ims_usrloc_pcscf/pcontact.c index eb83806531f..1bb0af2b1ca 100644 --- a/modules/ims_usrloc_pcscf/pcontact.c +++ b/modules/ims_usrloc_pcscf/pcontact.c @@ -61,6 +61,8 @@ extern int db_mode; extern int hashing_type; +extern int expires_grace; + /*! retransmission detection interval in seconds */ int cseq_delay = 20; @@ -113,13 +115,14 @@ void free_ppublic(ppublic_t* _p) int new_pcontact(struct udomain* _d, str* _contact, struct pcontact_info* _ci, struct pcontact** _c) { - int i; + int i, has_rinstance=0; ppublic_t* ppublic_ptr; - int is_default = 1; + int is_default = 1, params_len; struct sip_uri sip_uri; - - - *_c = (pcontact_t*)shm_malloc(sizeof(pcontact_t)); + char* p, *params, *sep; + str rinstance = {0,0}; + + *_c = (pcontact_t*)shm_malloc(sizeof(pcontact_t) + _contact->len + _ci->received_host.len + _ci->via_host.len); if (*_c == 0) { LM_ERR("no more shared memory\n"); return -1; @@ -130,15 +133,13 @@ int new_pcontact(struct udomain* _d, str* _contact, struct pcontact_info* _ci, s _contact->len, _contact->s, _ci->num_public_ids, reg_state_to_string(_ci->reg_state)); + + - (*_c)->aor.s = (char*) shm_malloc(_contact->len); - if ((*_c)->aor.s == 0) { - LM_ERR("no more shared memory\n"); - shm_free(*_c); - *_c = 0; - return -2; - } - memcpy((*_c)->aor.s, _contact->s, _contact->len); + p = (char*)((struct pcontact*)(*_c) + 1); + (*_c)->aor.s = p; + memcpy(p, _contact->s, _contact->len); + p += _contact->len; (*_c)->aor.len = _contact->len; (*_c)->domain = (str*)_d; @@ -149,6 +150,36 @@ int new_pcontact(struct udomain* _d, str* _contact, struct pcontact_info* _ci, s *_c = 0; return -2; } + + /* is there an rinstance param */ + LM_DBG("checking for rinstance"); + /*check for alias - NAT */ + params = sip_uri.sip_params.s; + params_len = sip_uri.sip_params.len; + while (params_len >= RINSTANCE_LEN) { + if (strncmp(params, RINSTANCE, RINSTANCE_LEN) == 0) { + has_rinstance = 1; + break; + } + sep = memchr(params, 59 /* ; */, params_len); + if (sep == NULL) { + LM_DBG("no rinstance param\n"); + break; + } else { + params_len = params_len - (sep - params + 1); + params = sep + 1; + } + } + if (has_rinstance) { + rinstance.s = params + RINSTANCE_LEN; + rinstance.len = params_len - RINSTANCE_LEN; + sep = (char*)memchr(rinstance.s, 59 /* ; */, rinstance.len); + if (sep != NULL){ + rinstance.len = (sep-rinstance.s); + } + } + (*_c)->rinstance.s = rinstance.s; + (*_c)->rinstance.len = rinstance.len; (*_c)->contact_host.s = sip_uri.host.s; (*_c)->contact_host.len = sip_uri.host.len; (*_c)->contact_port = sip_uri.port_no; @@ -160,36 +191,24 @@ int new_pcontact(struct udomain* _d, str* _contact, struct pcontact_info* _ci, s // Add received Info: if (_ci->received_host.len > 0 && _ci->received_host.s) { - (*_c)->received_host.s = (char*) shm_malloc(_ci->received_host.len); - if ((*_c)->received_host.s == 0) { - LM_ERR("no more share memory\n"); - shm_free((*_c)->aor.s); - shm_free(*_c); - *_c = 0; - return -2; - } - memcpy((*_c)->received_host.s, _ci->received_host.s, _ci->received_host.len); + (*_c)->received_host.s = p; + memcpy(p, _ci->received_host.s, _ci->received_host.len); + p += _ci->received_host.len; (*_c)->received_host.len = _ci->received_host.len; (*_c)->received_port = _ci->received_port; (*_c)->received_proto = _ci->received_proto; } - - if (hashing_type==0) { - (*_c)->aorhash = core_hash(_contact, 0, 0); - } else if (hashing_type==1) { - if ((*_c)->received_host.len > 0 && memcmp((*_c)->contact_host.s, (*_c)->received_host.s, (*_c)->contact_host.len) != 0) { - LM_DBG("Looks like this contact is natted - contact URI: [%.*s] but came from [%.*s]\n", (*_c)->contact_host.len, - (*_c)->contact_host.s, (*_c)->received_host.len, (*_c)->received_host.s); - (*_c)->aorhash = core_hash(&(*_c)->received_host, 0, 0); - } else - (*_c)->aorhash = core_hash(&(*_c)->contact_host, 0, 0); - } else { - if ((*_c)->received_host.len > 0) { - (*_c)->aorhash = core_hash(&(*_c)->received_host, 0, 0); - } else { - (*_c)->aorhash = core_hash(&(*_c)->contact_host, 0, 0); - } - } + + if (_ci->via_host.len > 0 && _ci->via_host.s) { + (*_c)->via_host.s = p; + memcpy(p, _ci->via_host.s, _ci->via_host.len); + p += _ci->via_host.len; + (*_c)->via_host.len = _ci->via_host.len; + (*_c)->via_port = _ci->via_port; + (*_c)->via_proto = _ci->via_prot; + } + + (*_c)->aorhash = get_aor_hash(_d, &_ci->via_host, _ci->via_port, _ci->via_prot); //setup public ids for (i=0; i<_ci->num_public_ids; i++) { @@ -213,6 +232,12 @@ int new_pcontact(struct udomain* _d, str* _contact, struct pcontact_info* _ci, s (*_c)->num_service_routes = _ci->num_service_routes; } } + + LM_DBG("New contact host:port [%.*s:%d]\n", (*_c)->contact_host.len, (*_c)->contact_host.s, (*_c)->contact_port); + LM_DBG("New contact via host:port:proto: [%.*s:%d:%d]\n", (*_c)->via_host.len, (*_c)->via_host.s, (*_c)->via_port, (*_c)->via_proto); + LM_DBG("New contact received host:port:proto: [%.*s:%d:%d]\n", (*_c)->received_host.len, (*_c)->received_host.s, (*_c)->received_port, (*_c)->received_proto); + LM_DBG("New contact aorhash [%u]\n", (*_c)->aorhash); + return 0; out_of_memory: @@ -250,15 +275,6 @@ void free_pcontact(pcontact_t* _c) { _c->num_service_routes = 0; } } - //free URI - if (_c->aor.s) { - shm_free(_c->aor.s); - } - - //free received host - if (_c->received_host.s) { - shm_free(_c->received_host.s); - } if (_c->rx_session_id.len > 0 && _c->rx_session_id.s) shm_free(_c->rx_session_id.s); @@ -272,17 +288,26 @@ static inline void nodb_timer(pcontact_t* _c) "Expires: %d, " "Expires in: %d seconds, " "Received: %.*s:%d, " - "Proto: %d\n", + "Path: %.*s, " + "Proto: %d, " + "Hash: %u, " + "Slot: %u\n", _c->aor.len, _c->aor.s, reg_state_to_string(_c->reg_state), (int)_c->expires, (int)(_c->expires - time(NULL)), _c->received_host.len, _c->received_host.s, _c->received_port, - _c->received_proto); + _c->path.len, _c->path.s, + _c->received_proto, + _c->aorhash, + _c->sl); get_act_time(); - if ((_c->expires - act_time) <= -10) {//we've allowed some grace time TODO: add as parameter + + + if ((_c->expires - act_time) + expires_grace <= 0) {//we've allowed some grace time TODO: add as parameter + //if ((_c->expires - act_time) <= -10) {//we've allowed some grace time TODO: add as parameter LM_DBG("pcscf contact <%.*s> has expired and will be removed\n", _c->aor.len, _c->aor.s); if (exists_ulcb_type(PCSCF_CONTACT_EXPIRE)) { run_ul_callbacks(PCSCF_CONTACT_EXPIRE, _c); diff --git a/modules/ims_usrloc_pcscf/pcontact.h b/modules/ims_usrloc_pcscf/pcontact.h index 9df0004296b..cb75e109334 100644 --- a/modules/ims_usrloc_pcscf/pcontact.h +++ b/modules/ims_usrloc_pcscf/pcontact.h @@ -70,8 +70,8 @@ void timer_pcontact(pcontact_t* _r); int delete_ppublic(pcontact_t* _r/*, struct ucontact* _c*/); int get_ppublic(pcontact_t* _r); int aor_to_contact(str* aor, str* contact); -unsigned int get_hash_slot(udomain_t* _d, str* _aor, str* received_host, int received_port); -unsigned int get_aor_hash(udomain_t* _d, str* _aor, str* received_host, int received_port); +unsigned int get_hash_slot(udomain_t* _d, str* via_host, unsigned short via_port, unsigned short via_proto); +unsigned int get_aor_hash(udomain_t* _d, str* via_host, unsigned short via_port, unsigned short via_proto); #endif diff --git a/modules/ims_usrloc_pcscf/udomain.c b/modules/ims_usrloc_pcscf/udomain.c index 94a72db3933..04946997097 100644 --- a/modules/ims_usrloc_pcscf/udomain.c +++ b/modules/ims_usrloc_pcscf/udomain.c @@ -60,8 +60,10 @@ #include "../../parser/parse_uri.h" #include "../../lib/ims/useful_defs.h" +#include "../../modules/presence/presence.h" extern int db_mode; +extern int db_mode_ext; extern unsigned int hashing_type; extern int lookup_check_received; extern int match_contact_host_port; @@ -230,6 +232,7 @@ int mem_insert_pcontact(struct udomain* _d, str* _contact, struct pcontact_info* sl = ((*_c)->aorhash) & (_d->size - 1); (*_c)->sl = sl; + LM_DBG("Putting contact into slot [%d]\n", sl); slot_add(&_d->table[sl], *_c); update_stat(_d->contacts, 1); return 0; @@ -263,11 +266,11 @@ void mem_timer_udomain(udomain_t* _d) } } -void lock_udomain(udomain_t* _d, str* _aor, str* _received_host, unsigned short received_port) +void lock_udomain(udomain_t* _d, str* via_host, unsigned short via_port, unsigned short via_proto) { unsigned int sl; - sl = get_hash_slot(_d, _aor, _received_host, received_port); + sl = get_hash_slot(_d, via_host, via_port, via_proto); #ifdef GEN_LOCK_T_PREFERED lock_get(_d->table[sl].lock); @@ -276,10 +279,10 @@ void lock_udomain(udomain_t* _d, str* _aor, str* _received_host, unsigned short #endif } -void unlock_udomain(udomain_t* _d, str* _aor, str* _received_host, unsigned short received_port) +void unlock_udomain(udomain_t* _d, str* via_host, unsigned short via_port, unsigned short via_proto) { unsigned int sl; - sl = get_hash_slot(_d, _aor, _received_host, received_port); + sl = get_hash_slot(_d, via_host, via_port, via_proto); #ifdef GEN_LOCK_T_PREFERED lock_release(_d->table[sl].lock); #else @@ -326,6 +329,13 @@ int update_rx_regsession(struct udomain* _d, str* session_id, struct pcontact* _ return 0; } +/** + * assume locked before calling this - lock the contact slot... + * @param _d + * @param _ci + * @param _c + * @return + */ int update_pcontact(struct udomain* _d, struct pcontact_info* _ci, struct pcontact* _c) //TODO: should prob move this to pcontact { int is_default = 1; @@ -446,177 +456,102 @@ int insert_pcontact(struct udomain* _d, str* _contact, struct pcontact_info* _ci * @struct pontact** _c - contact to return to if found (null if not found) * @return 0 if found <>0 if not */ -int get_pcontact(udomain_t* _d, str* _contact, str* _received_host, int received_port, struct pcontact** _c) { - unsigned int sl, i, aorhash; +int get_pcontact(udomain_t* _d, pcontact_info_t* contact_info, struct pcontact** _c) { + unsigned int sl, i, aorhash, params_len, has_rinstance=0; struct pcontact* c; - ppublic_t* impu; - struct sip_uri needle_uri, impu_uri; - int port_match = 0; - str alias_host = {0, 0}; - struct sip_uri contact_uri; - - if (parse_uri(_contact->s, _contact->len, &needle_uri) != 0 ) { - LM_ERR("failed to parse search URI [%.*s]\n", _contact->len, _contact->s); - return 1; - } - - LM_DBG("Searching for contact in P-CSCF usrloc [%.*s]\n", - _contact->len, - _contact->s); + struct sip_uri needle_uri; + char *params, *sep; + str rinstance = {0, 0}; + + LM_DBG("Searching for contact with AOR [%.*s] in P-CSCF usrloc based on VIA [%d://%.*s:%d] Received [%d://%.*s:%d], Search flag is %d\n", + contact_info->aor.len, contact_info->aor.s, contact_info->via_prot, contact_info->via_host.len, contact_info->via_host.s, contact_info->via_port, + contact_info->received_proto, contact_info->received_host.len, contact_info->received_host.s, contact_info->received_port, contact_info->searchflag); + /* parse the uri in the NOTIFY */ + if (contact_info->aor.len>0 && contact_info->aor.s){ + LM_DBG("Have an AOR to search for\n"); + if (parse_uri(contact_info->aor.s, contact_info->aor.len, &needle_uri) != 0) { + LM_ERR("Unable to parse contact aor in get_pcontact [%.*s]\n", contact_info->aor.len, contact_info->aor.s); + return 0; + } + LM_DBG("checking for rinstance"); + /*check for alias - NAT */ + params = needle_uri.sip_params.s; + params_len = needle_uri.sip_params.len; + + while (params_len >= RINSTANCE_LEN) { + if (strncmp(params, RINSTANCE, RINSTANCE_LEN) == 0) { + has_rinstance = 1; + break; + } + sep = memchr(params, 59 /* ; */, params_len); + if (sep == NULL) { + LM_DBG("no rinstance param\n"); + break; + } else { + params_len = params_len - (sep - params + 1); + params = sep + 1; + } + } + if (has_rinstance) { + rinstance.s = params + RINSTANCE_LEN; + rinstance.len = params_len - RINSTANCE_LEN; + sep = (char*)memchr(rinstance.s, 59 /* ; */, rinstance.len); + if (sep != NULL){ + rinstance.len = (sep-rinstance.s); + } + LM_DBG("rinstance found [%.*s]\n", rinstance.len, rinstance.s); + } + } + + /* search in cache */ - aorhash = get_aor_hash(_d, _contact, _received_host, received_port); + aorhash = get_aor_hash(_d, &contact_info->via_host, contact_info->via_port, contact_info->via_prot); sl = aorhash & (_d->size - 1); + + LM_DBG("get_pcontact slot is [%d]\n", sl); c = _d->table[sl].first; for (i = 0; i < _d->table[sl].n; i++) { - - if ((c->aorhash == aorhash) && (c->aor.len == _contact->len) - && !memcmp(c->aor.s, _contact->s, _contact->len)) { - *_c = c; - return 0; + LM_DBG("comparing contact with aorhash [%u], aor [%.*s]\n", c->aorhash, c->aor.len, c->aor.s); + if ((c->aorhash == aorhash) && (c->contact_host.len == contact_info->via_host.len) && (memcmp(contact_info->via_host.s, c->contact_host.s, c->contact_host.len)==0) && (c->contact_port == contact_info->via_port) + && (!(contact_info->searchflag&SEARCH_RECEIVED) || ((contact_info->searchflag&SEARCH_RECEIVED) + && (c->received_host.len == contact_info->received_host.len && (memcmp(c->received_host.s, contact_info->received_host.s, contact_info->received_host.len)==0)) + && (c->received_port == contact_info->received_port)))) { + LM_DBG("found contact with URI [%.*s]\n", c->aor.len, c->aor.s); + if (has_rinstance) { + LM_DBG("confirming rinstance is the same - search has [%.*s] and proposed found contact has [%.*s]", + rinstance.len, rinstance.s, + c->rinstance.len, c->rinstance.s); + if ((rinstance.len == c->rinstance.len) && memcmp(rinstance.s, c->rinstance.s, rinstance.len) != 0) { + LM_DBG("rinstance does not match - not match here...\n"); + c = c->next; + continue; + } + } + *_c = c; + return 0; } - if(match_contact_host_port) { - LM_DBG("Comparing needle user@host:port [%.*s@%.*s:%d] and contact_user@contact_host:port [%.*s@%.*s:%d]\n", needle_uri.user.len, needle_uri.user.s, - needle_uri.host.len, needle_uri.host.s, needle_uri.port_no, - c->contact_user.len, c->contact_user.s, c->contact_host.len, c->contact_host.s, c->contact_port); - - if((needle_uri.user.len == c->contact_user.len && (memcmp(needle_uri.user.s, c->contact_user.s, needle_uri.user.len) ==0)) && - (needle_uri.host.len == c->contact_host.len && (memcmp(needle_uri.host.s, c->contact_host.s, needle_uri.host.len) ==0)) && - (needle_uri.port_no == c->contact_port)) { - LM_DBG("Match!!\n"); - *_c = c; - return 0; - } - } - - LM_DBG("Searching for [%.*s] and comparing to [%.*s]\n", _contact->len, _contact->s, c->aor.len, c->aor.s); - - /* hosts HAVE to match */ - if (lookup_check_received && ((needle_uri.host.len != c->received_host.len) || (memcmp(needle_uri.host.s, c->received_host.s, needle_uri.host.len)!=0))) { - //can't possibly match - LM_DBG("Lookup failed for [%.*s <=> %.*s]\n", needle_uri.host.len, needle_uri.host.s, c->received_host.len, c->received_host.s); - c = c->next; - continue; - } - - /* one of the ports must match, either the initial registered port, the received port, or one if the security ports (server) */ - if ((needle_uri.port_no != c->contact_port) && (needle_uri.port_no != c->received_port)) { - //check security ports - if (c->security) { - switch (c->security->type) { - case SECURITY_IPSEC: { - ipsec_t* ipsec = c->security->data.ipsec; - if (ipsec) { - LM_DBG("security server port is %d\n", ipsec->port_us); - LM_DBG("security client port is %d\n", ipsec->port_uc); - if (ipsec->port_us == needle_uri.port_no) { - LM_DBG("security port mathes contact\n"); - port_match = 1; - } - } - break; - } - case SECURITY_TLS: - case SECURITY_NONE: - LM_WARN("not implemented\n"); - break; - } - } - if (!port_match && c->security_temp) { - switch (c->security_temp->type) { - case SECURITY_IPSEC: { - ipsec_t* ipsec = c->security_temp->data.ipsec; - if (ipsec) { - LM_DBG("temp security server port is %d\n", ipsec->port_us); - LM_DBG("temp security client port is %d\n", ipsec->port_uc); - if (ipsec->port_us == needle_uri.port_no) { - LM_DBG("temp security port mathes contact\n"); - port_match = 1; - } - } - break; - } - case SECURITY_TLS: - case SECURITY_NONE: - LM_WARN("not implemented\n"); - break; - } - } - } else { - port_match = 1; - } - - if (!port_match){ - LM_DBG("Port don't match: %d (contact) %d (received) != %d!\n", - c->contact_port, c->received_port, needle_uri.port_no); - c = c->next; - continue; - } - - /* user parts must match (if not wildcarded) with either primary contact OR with any userpart in the implicit set (associated URIs).. */ - if((needle_uri.user.len == c->contact_user.len) && (memcmp(needle_uri.user.s, c->contact_user.s,needle_uri.user.len) == 0)) { - LM_DBG("Needle user part matches contact user part therefore this is a match\n"); - *_c = c; - return 0; - } else if ((needle_uri.user.len == 1) && (memcmp(needle_uri.user.s, "*", 1) == 0)) { /*wild card*/ - LM_DBG("This a wild card user part - we must check if hosts match or needle host matches alias\n"); - if(memcmp(needle_uri.host.s, c->contact_host.s, needle_uri.host.len) == 0) { - LM_DBG("Needle host matches contact host therefore this is a match\n"); - *_c = c; - return 0; - } else if ((parse_uri(c->aor.s, c->aor.len, &contact_uri) == 0) && ((get_alias_host_from_contact(&contact_uri.params, &alias_host)) == 0) && - (memcmp(needle_uri.host.s, alias_host.s, alias_host.len) == 0)) { - LM_DBG("Needle host matches contact alias therefore this is a match\n"); - *_c = c; - return 0; - - } - } - - /* check impus user parts */ - impu = c->head; - while (impu) { - if (parse_uri(impu->public_identity.s, impu->public_identity.len, &impu_uri) != 0) { - LM_ERR("failed to parse IMPU URI [%.*s]...continuing\n", impu->public_identity.len, impu->public_identity.s); - impu = impu->next; - continue; - } - LM_DBG("comparing first %d chars of impu [%.*s] for contact userpart [%.*s]\n", - needle_uri.user.len, - impu->public_identity.len, impu->public_identity.s, - needle_uri.user.len, needle_uri.user.s); - if (needle_uri.user.len == impu_uri.user.len && (memcmp(needle_uri.user.s, impu_uri.user.s, impu_uri.user.len)==0)) { - *_c = c; - return 0; - } - impu = impu->next; - } - c = c->next; } + + LM_DBG("contact not found in memory\n"); + return 1; /* Nothing found */ } int get_pcontact_by_src(udomain_t* _d, str * _host, unsigned short _port, unsigned short _proto, struct pcontact** _c) { - char c_contact[256], *p; str s_contact; - int ret; - - memset(c_contact, 0, 256); - strncpy(c_contact, "sip:*@", 6); //prepend *@ to host to wildcard on user search - p = c_contact + 6; - memcpy(p, _host->s, _host->len); - p = p + _host->len; - *p = ':'; - p++; - sprintf(p, "%d", _port); - s_contact.s = c_contact; - s_contact.len = strlen(c_contact); + pcontact_info_t contact_info; + int ret; LM_DBG("Trying to find contact by src with URI: [%.*s]\n", s_contact.len, s_contact.s); - ret = get_pcontact(_d, &s_contact, _host, _port, _c); + contact_info.via_host = *_host; + contact_info.via_port = _port; + contact_info.via_prot = _proto; + + ret = get_pcontact(_d, &contact_info, _c); return ret; } @@ -685,12 +620,10 @@ int assert_identity(udomain_t* _d, str * _host, unsigned short _port, unsigned s return 0; /* Nothing found */ } -int delete_pcontact(udomain_t* _d, str* _aor, str* _received_host, int _received_port, struct pcontact* _c) +int delete_pcontact(udomain_t* _d, /*str* _aor, str* _received_host, int _received_port,*/ struct pcontact* _c) { if (_c==0) { - if (get_pcontact(_d, _aor, _received_host, _received_port, &_c) > 0) { - return 0; - } + return 0; } if (exists_ulcb_type(PCSCF_CONTACT_DELETE)) { @@ -711,7 +644,7 @@ int delete_pcontact(udomain_t* _d, str* _aor, str* _received_host, int _received * \brief Convert database values into pcontact_info * * Convert database values into pcontact_info, - * expects 12 rows (aor, contact, received, received_port, received_proto, rx_session_id_col + * expects 15 rows (aor, host, port, protocol, received, received_port, received_proto, rx_session_id_col * reg_state, expires, socket, service_routes_col, public_ids, path * \param vals database values * \param contact contact @@ -720,44 +653,55 @@ int delete_pcontact(udomain_t* _d, str* _aor, str* _received_host, int _received static inline pcontact_info_t* dbrow2info( db_val_t *vals, str *contact) { static pcontact_info_t ci; - static str received, path, rx_session_id, implicit_impus, tmpstr, service_routes; + static str host, received, path, rx_session_id, implicit_impus, tmpstr, service_routes; static str *impu_list, *service_route_list; int flag=0, n; char *p, *q=0; memset( &ci, 0, sizeof(pcontact_info_t)); - received.s = (char*) VAL_STRING(vals + 2); - if (VAL_NULL(vals+2) || !received.s || !received.s[0]) { - received.len = 0; - received.s = 0; + host.s = (char*) VAL_STRING(vals + 1); + if (VAL_NULL(vals+1) || !host.s || !host.s[0]) { + host.len = 0; + host.s = 0; + } else { + host.len = strlen(host.s); + } + ci.via_host = host; + ci.via_port = VAL_INT(vals + 2); + ci.via_prot = VAL_INT(vals + 3); + received.s = (char*) VAL_STRING(vals + 4); + if (VAL_NULL(vals+4) || !received.s || !received.s[0]) { + LM_ERR("Empty received for contact [%.*s].... ignoring\n", contact->len, contact->s); + return 0; } else { received.len = strlen(received.s); } ci.received_host = received; - ci.received_port = VAL_INT(vals + 3); - ci.received_proto = VAL_INT(vals + 4); + ci.received_port = VAL_INT(vals + 5); + ci.received_proto = VAL_INT(vals + 6); - rx_session_id.s = (char*) VAL_STRING(vals + 5); - if (VAL_NULL(vals+5) || !rx_session_id.s || !rx_session_id.s[0]) { + rx_session_id.s = (char*) VAL_STRING(vals + 7); + if (VAL_NULL(vals+7) || !rx_session_id.s || !rx_session_id.s[0]) { rx_session_id.len = 0; rx_session_id.s = 0; } else { rx_session_id.len = strlen(rx_session_id.s); } ci.rx_regsession_id = &rx_session_id; - if (VAL_NULL(vals + 6)) { + if (VAL_NULL(vals + 8)) { LM_ERR("empty registration state in DB\n"); return 0; } - ci.reg_state = VAL_INT(vals + 6); - if (VAL_NULL(vals + 7)) { + ci.reg_state = VAL_INT(vals + 8); + + if (VAL_NULL(vals + 9)) { LM_ERR("empty expire\n"); return 0; } - ci.expires = VAL_TIME(vals + 7); - path.s = (char*)VAL_STRING(vals+11); - if (VAL_NULL(vals+11) || !path.s || !path.s[0]) { + ci.expires = VAL_TIME(vals + 9); + path.s = (char*)VAL_STRING(vals+13); + if (VAL_NULL(vals+13) || !path.s || !path.s[0]) { path.len = 0; path.s = 0; } else { @@ -766,8 +710,8 @@ static inline pcontact_info_t* dbrow2info( db_val_t *vals, str *contact) ci.path = &path; //public IDs - implicit set - implicit_impus.s = (char*) VAL_STRING(vals + 10); - if (!VAL_NULL(vals + 10) && implicit_impus.s && implicit_impus.s[0]) { + implicit_impus.s = (char*) VAL_STRING(vals + 12); + if (!VAL_NULL(vals + 12) && implicit_impus.s && implicit_impus.s[0]) { //how many n=0; p = implicit_impus.s; @@ -801,8 +745,8 @@ static inline pcontact_info_t* dbrow2info( db_val_t *vals, str *contact) } //service routes - service_routes.s = (char*) VAL_STRING(vals + 9); - if (!VAL_NULL(vals + 9) && service_routes.s && service_routes.s[0]) { + service_routes.s = (char*) VAL_STRING(vals + 11); + if (!VAL_NULL(vals + 11) && service_routes.s && service_routes.s[0]) { //how many n = 0; p = service_routes.s; @@ -851,9 +795,9 @@ int preload_udomain(db1_con_t* _c, udomain_t* _d) { pcontact_info_t *ci; db_row_t *row; - db_key_t columns[18]; + db_key_t columns[15]; db1_res_t* res = NULL; - str aor, contact; + str aor; int i, n; pcontact_t* c; @@ -862,17 +806,19 @@ int preload_udomain(db1_con_t* _c, udomain_t* _d) columns[0] = &domain_col; columns[1] = &aor_col; - columns[2] = &contact_col; - columns[3] = &received_col; - columns[4] = &received_port_col; - columns[5] = &received_proto_col; - columns[6] = &rx_session_id_col; - columns[7] = ®_state_col; - columns[8] = &expires_col; - columns[9] = &socket_col; - columns[10] = &service_routes_col; - columns[11] = &public_ids_col; - columns[12] = &path_col; + columns[2] = &host_col; + columns[3] = &port_col; + columns[4] = &protocol_col; + columns[5] = &received_col; + columns[6] = &received_port_col; + columns[7] = &received_proto_col; + columns[8] = &rx_session_id_col; + columns[9] = ®_state_col; + columns[10] = &expires_col; + columns[11] = &socket_col; + columns[12] = &service_routes_col; + columns[13] = &public_ids_col; + columns[14] = &path_col; if (ul_dbf.use_table(_c, _d->name) < 0) { LM_ERR("sql use_table failed\n"); @@ -884,7 +830,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, 13, 0, 0) < 0) { + if (ul_dbf.query(_c, 0, 0, 0, columns, 0, 15, 0, 0) < 0) { LM_ERR("db_query (1) failed\n"); return -1; } @@ -893,7 +839,7 @@ int preload_udomain(db1_con_t* _c, udomain_t* _d) return -1; } } else { - if (ul_dbf.query(_c, 0, 0, 0, columns, 0, 13, 0, &res) < 0) { + if (ul_dbf.query(_c, 0, 0, 0, columns, 0, 15, 0, &res) < 0) { LM_ERR("db_query failed\n"); return -1; } @@ -919,21 +865,21 @@ int preload_udomain(db1_con_t* _c, udomain_t* _d) continue; } aor.len = strlen(aor.s); - - ci = dbrow2info( ROW_VALUES(row)+1, &contact); - if (ci==0) { - LM_ERR("usrloc record for %.*s in table %s\n", - aor.len, aor.s, _d->name->s); - continue; - } - lock_udomain(_d, &aor, &ci->received_host, ci->received_port); + ci = dbrow2info(ROW_VALUES(row) + 1, &aor); + if (!ci) { + LM_WARN("Failed to get contact info from DB.... continuing...\n"); + continue; + } + lock_udomain(_d, &ci->via_host, ci->via_port, ci->via_prot); if ( (mem_insert_pcontact(_d, &aor, ci, &c)) != 0) { LM_ERR("inserting contact failed\n"); - unlock_udomain(_d, &aor, &ci->received_host, ci->received_port); + unlock_udomain(_d, &ci->via_host, ci->via_port, ci->via_prot); goto error1; } - unlock_udomain(_d, &aor, &ci->received_host, ci->received_port); + //c->flags = c->flags|(1<via_host, ci->via_port, ci->via_prot); } if (DB_CAPABILITY(ul_dbf, DB_CAP_FETCH)) { @@ -961,4 +907,93 @@ int preload_udomain(db1_con_t* _c, udomain_t* _d) return -1; } +pcontact_t* db_load_pcontact(db1_con_t* _c, udomain_t* _d, str *_aor) +{ + pcontact_info_t *ci; + db_key_t columns[15]; + db_key_t keys[1]; + db_val_t vals[1]; + db1_res_t* res = NULL; + db_row_t *row; + int i; + str aor; + + pcontact_t* c; + + keys[0] = &aor_col; + vals[0].type = DB1_STR; + vals[0].nul = 0; + vals[0].val.str_val = *_aor; + + columns[0] = &domain_col; + columns[1] = &aor_col; + columns[2] = &host_col; + columns[3] = &port_col; + columns[4] = &protocol_col; + columns[5] = &received_col; + columns[6] = &received_port_col; + columns[7] = &received_proto_col; + columns[8] = &rx_session_id_col; + columns[9] = ®_state_col; + columns[10] = &expires_col; + columns[11] = &socket_col; + columns[12] = &service_routes_col; + columns[13] = &public_ids_col; + columns[14] = &path_col; + + LM_DBG("Querying database for P-CSCF contact [%.*s]\n", _aor->len, _aor->s); + + 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, 0, &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",_aor->len, _aor->s, _d->name->len, _d->name->s); + ul_dbf.free_result(_c, res); + return 0; + } + + for(i = 0; i < RES_ROW_N(res); i++) { + row = RES_ROWS(res) + i; + + aor.s = (char*) VAL_STRING(ROW_VALUES(row) + 1); + if (VAL_NULL(ROW_VALUES(row) + 1) || aor.s == 0 || aor.s[0] == 0) { + LM_CRIT("empty aor record in table %s...skipping\n", _d->name->s); + continue; + } + aor.len = strlen(aor.s); + ci = dbrow2info(ROW_VALUES(row) + 1, &aor); + if (!ci) { + LM_WARN("Failed to get contact info from DB.... continuing...\n"); + continue; + } + lock_udomain(_d, &ci->via_host, ci->via_port, ci->via_prot); + + if ( (mem_insert_pcontact(_d, &aor, ci, &c)) != 0) { + LM_ERR("inserting contact failed\n"); + unlock_udomain(_d, &ci->via_host, ci->via_port, ci->via_prot); + goto error; + } + //c->flags = c->flags|(1<via_host, ci->via_port, ci->via_prot); + } + + ul_dbf.free_result(_c, res); + + return c; + +error: + free_pcontact(c); + + ul_dbf.free_result(_c, res); + return 0; +} + diff --git a/modules/ims_usrloc_pcscf/udomain.h b/modules/ims_usrloc_pcscf/udomain.h index 2446a9c6764..26bf1c78012 100644 --- a/modules/ims_usrloc_pcscf/udomain.h +++ b/modules/ims_usrloc_pcscf/udomain.h @@ -67,8 +67,8 @@ void mem_timer_udomain(udomain_t* _d); int mem_insert_pcontact(struct udomain* _d, str* _contact, struct pcontact_info* _ci, struct pcontact** _c); void mem_delete_pcontact(udomain_t* _d, struct pcontact* _r); -void lock_udomain(udomain_t* _d, str *_aor, str* _received_host, unsigned short int received_port); -void unlock_udomain(udomain_t* _d, str *_aor, str* _received_host, unsigned short int received_port); +void lock_udomain(udomain_t* _d, str* _via_host, unsigned short via_port, unsigned short via_protot); +void unlock_udomain(udomain_t* _d, str* _via_host, unsigned short via_port, unsigned short via_proto); void lock_ulslot(udomain_t* _d, int i); void unlock_ulslot(udomain_t* _d, int i); @@ -76,10 +76,10 @@ void unlock_ulslot(udomain_t* _d, int i); int update_rx_regsession(struct udomain* _d, str* session_id, struct pcontact* _c); int update_pcontact(struct udomain* _d, struct pcontact_info* _ci, struct pcontact* _c); int insert_pcontact(struct udomain* _d, str* _contact, struct pcontact_info* _ci, struct pcontact** _r); -int get_pcontact(udomain_t* _d, str* _aor, str* _received_host, int received_port, struct pcontact** _r); +int get_pcontact(udomain_t* _d, pcontact_info_t* contact_info, struct pcontact** _r); int get_pcontact_by_src(udomain_t* _d, str * _host, unsigned short _port, unsigned short _proto, struct pcontact** _c); int assert_identity(udomain_t* _d, str * _host, unsigned short _port, unsigned short _proto, str * _identity); -int delete_pcontact(udomain_t* _d, str* _aor, str* _received_host, int _received_port, struct pcontact* _r); +int delete_pcontact(udomain_t* _d, struct pcontact* _r); int update_security(udomain_t* _d, security_type _t, security_t* _s, struct pcontact* _c); int update_temp_security(udomain_t* _d, security_type _t, security_t* _s, struct pcontact* _c); diff --git a/modules/ims_usrloc_pcscf/ul_mod.c b/modules/ims_usrloc_pcscf/ul_mod.c index 11079da0b07..a3060cf6c12 100644 --- a/modules/ims_usrloc_pcscf/ul_mod.c +++ b/modules/ims_usrloc_pcscf/ul_mod.c @@ -72,6 +72,9 @@ static int child_init(int rank); /*!< Per-child init function extern int bind_usrloc(usrloc_api_t* api); extern int ul_locks_no; + +int expires_grace = 3600; //default is a grace period of 1 hour - after this contact is removed from P + /* * Module parameters and their default values */ @@ -114,6 +117,7 @@ static param_export_t params[] = { {"hashing_type", INT_PARAM, &hashing_type }, {"lookup_check_received", INT_PARAM, &lookup_check_received }, {"match_contact_host_port", INT_PARAM, &match_contact_host_port }, + {"expires_grace", INT_PARAM, &expires_grace }, {0, 0, 0} }; diff --git a/modules/ims_usrloc_pcscf/usrloc.c b/modules/ims_usrloc_pcscf/usrloc.c index 9de69b04764..0df5d0b4c6a 100644 --- a/modules/ims_usrloc_pcscf/usrloc.c +++ b/modules/ims_usrloc_pcscf/usrloc.c @@ -72,7 +72,6 @@ int bind_usrloc(usrloc_api_t* api) { api->insert_pcontact = insert_pcontact; api->delete_pcontact = delete_pcontact; api->get_pcontact = get_pcontact; - api->get_pcontact_by_src = get_pcontact_by_src; api->assert_identity = assert_identity; api->update_pcontact = update_pcontact; api->update_rx_regsession = update_rx_regsession; @@ -132,58 +131,21 @@ int get_alias_host_from_contact(str *contact_uri_params, str *alias_host) { /* return the slot id for inserting contacts in the hash */ -unsigned int get_hash_slot(udomain_t* _d, str* _aor, str* received_host, int received_port) { - struct sip_uri contact_uri; +unsigned int get_hash_slot(udomain_t* _d, str* via_host, unsigned short via_port, unsigned short via_proto) { unsigned int sl; - str alias_host = {0, 0}; - if ((hashing_type == 0) /*use full AOR for hash*/ || (parse_uri(_aor->s, _aor->len, &contact_uri) != 0)) { - if (hashing_type != 0) { - LM_DBG("Unable to get contact host:port from contact header [%.*s]... falling back to full AOR\n", _aor->len, _aor->s); - } - sl = core_hash(_aor, 0, _d->size); - } else { - if ( received_host && (memcmp(contact_uri.host.s, received_host->s, received_host->len) != 0)) { - LM_DBG("Looks like this contact is natted - contact URI: [%.*s] but came from received_host: [%.*s] so will use received_host for hash\n", contact_uri.host.len, contact_uri.host.s, - received_host->len, received_host->s); - sl = core_hash(received_host, 0, _d->size); - } else if (((get_alias_host_from_contact(&contact_uri.params, &alias_host)) == 0 && (memcmp(contact_uri.host.s, alias_host.s, alias_host.len) != 0))) { - LM_DBG("Looks like this contact is natted - as it has alias [%.*s] different from contact URI [%.*s] so will use alias for hash\n", - alias_host.len, alias_host.s, contact_uri.host.len, contact_uri.host.s); - sl = core_hash(&alias_host, 0, _d->size); - }else { - LM_DBG("using host for hash [%.*s]\n", contact_uri.host.len, contact_uri.host.s); - sl = core_hash(&contact_uri.host, 0, _d->size); - } - } + sl = get_aor_hash(_d, via_host, via_port, via_proto); + sl = sl & (_d->size - 1) ; LM_DBG("Returning hash slot: [%d]\n", sl); + return sl; } -unsigned int get_aor_hash(udomain_t* _d, str* _aor, str* received_host, int received_port) { - struct sip_uri contact_uri; +unsigned int get_aor_hash(udomain_t* _d, str* via_host, unsigned short via_port, unsigned short via_proto) { unsigned int aorhash; - str alias_host = {0, 0}; + + aorhash = core_hash(via_host, 0, 0); + LM_DBG("Returning hash: [%u]\n", aorhash); - if ((hashing_type == 0) /*use full AOR for hash*/ || (parse_uri(_aor->s, _aor->len, &contact_uri) != 0)) { - if (hashing_type != 0) { - LM_DBG("Unable to get contact host:port from contact header [%.*s]... falling back to full AOR\n", _aor->len, _aor->s); - } - aorhash = core_hash(_aor, 0, 0); - } else { - if ( received_host && (memcmp(contact_uri.host.s, received_host->s, received_host->len) != 0)) { - LM_DBG("Looks like this contact is natted - contact URI: [%.*s] but came from received_host: [%.*s] so will use received_host for hash\n", contact_uri.host.len, contact_uri.host.s, - received_host->len, received_host->s); - aorhash = core_hash(received_host, 0, 0); - } else if (((get_alias_host_from_contact(&contact_uri.params, &alias_host)) == 0 && (memcmp(contact_uri.host.s, alias_host.s, alias_host.len) != 0))) { - LM_DBG("Looks like this contact is natted - as it has alias [%.*s] different from contact URI [%.*s] so will use alias for hash\n", - alias_host.len, alias_host.s, contact_uri.host.len, contact_uri.host.s); - aorhash = core_hash(&alias_host, 0, 0); - }else { - LM_DBG("using host for hash [%.*s]\n", contact_uri.host.len, contact_uri.host.s); - aorhash = core_hash(&contact_uri.host, 0, 0); - } - } - LM_DBG("Returning hash slot: [%d]\n", aorhash); return aorhash; } diff --git a/modules/ims_usrloc_pcscf/usrloc.h b/modules/ims_usrloc_pcscf/usrloc.h index 11bcc9d2b61..cf7b01d7d8f 100644 --- a/modules/ims_usrloc_pcscf/usrloc.h +++ b/modules/ims_usrloc_pcscf/usrloc.h @@ -59,172 +59,190 @@ #define WRITE_BACK 2 //not implemented yet #define DB_ONLY 3 //not implemented yet +#define SEARCH_NORMAL 0 +#define SEARCH_RECEIVED 1 + +#define ALIAS "alias=" +#define ALIAS_LEN (sizeof(ALIAS) - 1) + +#define RINSTANCE "rinstance=" +#define RINSTANCE_LEN (sizeof(RINSTANCE) - 1) + #define VALID_CONTACT(c, t) ((c->expires>t) || (c->expires==0)) -struct hslot; /*!< Hash table slot */ +struct hslot; /*!< Hash table slot */ struct socket_info; int get_alias_host_from_contact(str *contact_uri_params, str *alias_host); struct udomain { - str* name; /*!< Domain name (NULL terminated) */ - int size; /*!< Hash table size */ - struct hslot* table; /*!< Hash table - array of collision slots */ - /* statistics */ - stat_var *contacts; /*!< no of registered contacts */ - stat_var *expired; /*!< no of expires */ + str* name; /*!< Domain name (NULL terminated) */ + int size; /*!< Hash table size */ + struct hslot* table; /*!< Hash table - array of collision slots */ + /* statistics */ + stat_var *contacts; /*!< no of registered contacts */ + stat_var *expired; /*!< no of expires */ }; typedef struct udomain udomain_t; /** Public Identity Structure */ typedef struct { - char barring; /**< Barring state */ - str public_identity; /**< Public Identity string */ - str wildcarded_psi; /** if exists is the wildcarded psi */ + char barring; /**< Barring state */ + str public_identity; /**< Public Identity string */ + str wildcarded_psi; /** if exists is the wildcarded psi */ } ims_public_identity; /** TLS SA Information */ typedef struct tls { - unsigned short port_tls; /**< Port UE TLS */ - unsigned long session_hash; + unsigned short port_tls; /**< Port UE TLS */ + unsigned long session_hash; } tls_t; /** IPSec SA Information */ typedef struct ipsec { - unsigned int spi_uc; /**< SPI Client to use */ - unsigned int spi_us; /**< SPI Server to use */ - unsigned int spi_pc; /**< SPI Client to use */ - unsigned int spi_ps; /**< SPI Server to use */ - unsigned short port_uc; /**< Port UE Client */ - unsigned short port_us; /**< Port UE Server */ - - str ealg; /**< Cypher Algorithm - ESP */ - str r_ealg; /**< received Cypher Algorithm - ESP */ - str ck; /**< Cypher Key */ - str alg; /**< Integrity Algorithm - AH */ - str r_alg; /**domain)); SET_STR_VALUE(GET_FIELD_IDX(values, LP_AOR_IDX), _c->aor); //TODO: need to clean AOR - SET_STR_VALUE(GET_FIELD_IDX(values, LP_CONTACT_IDX), _c->aor); SET_STR_VALUE(GET_FIELD_IDX(values, LP_RECEIVED_IDX), _c->received_host); + SET_STR_VALUE(GET_FIELD_IDX(values, LP_HOST_IDX), _c->via_host); SET_PROPER_NULL_FLAG((*_c->domain), values, LP_DOMAIN_IDX); SET_PROPER_NULL_FLAG(_c->aor, values, LP_AOR_IDX); - SET_PROPER_NULL_FLAG(_c->aor, values, LP_CONTACT_IDX); SET_PROPER_NULL_FLAG(_c->received_host, values, LP_RECEIVED_IDX); + SET_PROPER_NULL_FLAG(_c->via_host, values, LP_HOST_IDX); VAL_INT(GET_FIELD_IDX(values, LP_RECEIVED_PORT_IDX)) = _c->received_port; VAL_INT(GET_FIELD_IDX(values, LP_RECEIVED_PROTO_IDX)) = _c->received_proto; VAL_NULL(GET_FIELD_IDX(values, LP_RECEIVED_PORT_IDX)) = 0; VAL_NULL(GET_FIELD_IDX(values, LP_RECEIVED_PROTO_IDX)) = 0; + VAL_INT(GET_FIELD_IDX(values, LP_PORT_IDX)) = _c->via_port; + VAL_INT(GET_FIELD_IDX(values, LP_PROTOCOL_IDX)) = _c->via_proto; + VAL_NULL(GET_FIELD_IDX(values, LP_PORT_IDX)) = 0; + VAL_NULL(GET_FIELD_IDX(values, LP_PROTOCOL_IDX)) = 0; SET_STR_VALUE(GET_FIELD_IDX(values, LP_PATH_IDX), _c->path); + SET_STR_VALUE(GET_FIELD_IDX(values, LP_RINSTANCE_IDX), _c->rinstance); SET_STR_VALUE(GET_FIELD_IDX(values, LP_RX_SESSION_ID_IDX), _c->rx_session_id); - SET_PROPER_NULL_FLAG(_c->path, values, LP_PATH_IDX); + SET_PROPER_NULL_FLAG(_c->rinstance, values, LP_RINSTANCE_IDX); SET_PROPER_NULL_FLAG(_c->rx_session_id, values, LP_RX_SESSION_ID_IDX); VAL_DOUBLE(GET_FIELD_IDX(values, LP_REG_STATE_IDX)) = _c->reg_state; @@ -314,7 +325,7 @@ int db_insert_pcontact(struct pcontact* _c) return -1; } - if (ul_dbf.insert(ul_dbh, keys, values, 13) < 0) { + if (ul_dbf.insert(ul_dbh, keys, values, 15) < 0) { LM_ERR("inserting contact in db failed\n"); return -1; } diff --git a/modules/ims_usrloc_pcscf/usrloc_db.h b/modules/ims_usrloc_pcscf/usrloc_db.h index 85b9eca2050..1a5e2d2e03b 100644 --- a/modules/ims_usrloc_pcscf/usrloc_db.h +++ b/modules/ims_usrloc_pcscf/usrloc_db.h @@ -12,17 +12,20 @@ typedef enum location_pcscf_fields_idx { // LP_ID_IDX = 0, LP_DOMAIN_IDX = 0, LP_AOR_IDX, - LP_CONTACT_IDX, LP_RECEIVED_IDX, LP_RECEIVED_PORT_IDX, LP_RECEIVED_PROTO_IDX, LP_PATH_IDX, + LP_RINSTANCE_IDX, LP_RX_SESSION_ID_IDX, LP_REG_STATE_IDX, LP_EXPIRES_IDX, LP_SERVICE_ROUTES_IDX, LP_SOCKET_IDX, LP_PUBLIC_IPS_IDX, + LP_HOST_IDX, + LP_PORT_IDX, + LP_PROTOCOL_IDX } location_pcscf_fields_idx_t; @@ -52,12 +55,15 @@ typedef enum location_pcscf_fields_idx { #define ID_COL "id" #define DOMAIN_COL "domain" +#define HOST_COL "host" +#define PORT_COL "port" +#define PROTOCOL_COL "protocol" #define AOR_COL "aor" -#define CONTACT_COL "contact" #define RECEIVED_COL "received" #define RECEIVED_PORT_COL "received_port" #define RECEIVED_PROTO_COL "received_proto" #define PATH_COL "path" +#define RINSTANCE_COL "rinstance" #define RX_SESSION_ID_COL "rx_session_id" #define REG_STATE_COL "reg_state" #define EXPIRES_COL "expires" @@ -65,7 +71,7 @@ typedef enum location_pcscf_fields_idx { #define SOCKET_COL "socket" #define PUBLIC_IDS_COL "public_ids" #define SECURITY_TYPE_COL "security_type" -#define PROTOCOL_COL "protocol" + #define MODE_COL "mode" #define CK_COL "ck" #define IK_COL "ik" @@ -97,7 +103,9 @@ extern db_func_t ul_dbf; extern str id_col; extern str domain_col; extern str aor_col; -extern str contact_col; +extern str host_col; +extern str port_col; +extern str protocol_col; extern str received_col; extern str received_port_col; extern str received_proto_col; diff --git a/modules/ims_usrloc_scscf/contact_dlg_handlers.c b/modules/ims_usrloc_scscf/contact_dlg_handlers.c new file mode 100644 index 00000000000..7a68850601f --- /dev/null +++ b/modules/ims_usrloc_scscf/contact_dlg_handlers.c @@ -0,0 +1,184 @@ +/*-------------------------------------------------------------------------------------------------------------------\ +|Version Date Author Description | +|--------------------------------------------------------------------------------------------------------------------| +|Initial Build 20150825 T.Ntamane Contact dialog handler for expired|terminated|destroyed| etc | +\-----------------------------------------------------------------------------------------------------------------TN*/ + + +#include "impurecord.h" +#include "usrloc.h" +#include "ucontact.h" +#include "dlist.h" +#include "../../modules/dialog_ng/dlg_load.h" +#include "../../modules/dialog_ng/dlg_hash.h" + +extern struct dlg_binds dlgb; + +/*------------------------------------------------------------------------------------\ +| Our call back handler function that handles |confirmed|expired|terminated|destroy. | +\------------------------------------------------------------------------------* V1.1*/ + +static void contact_dlg_handler(struct dlg_cell* dlg, int cb_types, struct dlg_cb_params *dlg_params); + +/*-----------------------------------------------------------------------------------------------------------\ +| Our setup call back handler function primarily for create, that further register for other callback .| +\------------------------------------------------------------------------------------------------------V1.1-*/ +void contact_dlg_create_handler(struct dlg_cell* dlg, int cb_types, struct dlg_cb_params *dlg_params) { + if (cb_types != DLGCB_CREATED) { + LM_ERR("Unknown event type %d", cb_types); + return; + } + if ((dlgb.register_dlgcb(dlg, DLGCB_CONFIRMED | DLGCB_EXPIRED | DLGCB_TERMINATED | DLGCB_DESTROY | DLGCB_FAILED, contact_dlg_handler, 0, 0)) + + ) { + LM_ERR("Error registering dialog for contact caller id [%.*s] ", dlg->callid.len, dlg->callid.s); + return; + } + LM_DBG("Successfully registered contact dialog handler\n"); +} + +/** + * Search for a contact related to an IMPU based on an original contact string (uri) + * @param impu impurecord we will search through + * @param search_aor the raw uri we are using as the source of the search + * @return 0 on success, anything else on failure + */ +static inline int find_contact_from_impu(impurecord_t* impu, str* search_aor, ucontact_t** scontact) { + + int iCount = 0; + short i_searchlen; + char *s_term; + + if (!search_aor) + return 1; + + LM_DBG("Looking for contact [%.*s] for IMPU [%.*s]\n", search_aor->len, search_aor->s, impu->public_identity.len, impu->public_identity.s); + + s_term = memchr(search_aor->s,'@',search_aor->len); + if (!s_term) + { + LM_DBG("Malformed contact...bailing search\n"); + return 1; + } + i_searchlen = s_term - search_aor->s; + for (;iCount < impu->num_contacts ; ++iCount) + { + if( impu->newcontacts[iCount] && impu->newcontacts[iCount]->aor.s[i_searchlen] == '@' + && (memcmp(impu->newcontacts[iCount]->aor.s, search_aor->s, i_searchlen) == 0)) + { + *scontact = impu->newcontacts[iCount]; + return 0; + } + if (impu->newcontacts[iCount]) + LM_DBG("Skipping %.*s\n",impu->newcontacts[iCount]->aor.len,impu->newcontacts[iCount]->aor.s); + } + return 1; +} + +static void contact_dlg_handler(struct dlg_cell* dlg, int cb_types, struct dlg_cb_params *dlg_params) { + struct ucontact *ucontact_caller = 0x00, + *ucontact_callee = 0x00; + udomain_t *_d; + impurecord_t* from_impu, *to_impu; + str from_uri_clean, to_uri_clean; + char *p; + short iFoundCaller = 0, + iFoundCallee = 0; + static unsigned int i_confirmed_count = 0, + i_terminated_count =0; + + if ((cb_types == DLGCB_CONFIRMED) || + (cb_types == DLGCB_EXPIRED) || + (cb_types == DLGCB_TERMINATED) || + (cb_types == DLGCB_DESTROY) || + (cb_types == DLGCB_FAILED)) { + + //for now we will abort if there is no dlg_out.... TODO maybe we can only do the caller side.... + if (dlg->dlg_entry_out.first == 0x00) { + LM_ERR("no dlg out... ignoring!!! for type [%d]\n",cb_types); + return; + } + register_udomain("location", &_d); + + from_uri_clean.s = dlg->from_uri.s; + from_uri_clean.len = dlg->from_uri.len; + p = memchr(dlg->from_uri.s, ';', dlg->from_uri.len); + if (p) + from_uri_clean.len = p - from_uri_clean.s; + + lock_udomain(_d, &from_uri_clean); + if (get_impurecord(_d, &from_uri_clean, &from_impu) != 0) { + LM_DBG("Could not find caller impu for [%.*s]\n", from_uri_clean.len, from_uri_clean.s); + unlock_udomain(_d, &from_uri_clean); + return; + } + + if (find_contact_from_impu(from_impu, &dlg->caller_contact, &ucontact_caller) !=0) { + LM_DBG("Unable to find caller contact from dialog.... continuing\n"); + //unlock_udomain(_d, &from_uri_clean); + //return; + } + else { + iFoundCaller = 1; + } + unlock_udomain(_d, &from_uri_clean); + + to_uri_clean.s = dlg->dlg_entry_out.first->to_uri.s; + to_uri_clean.len = dlg->dlg_entry_out.first->to_uri.len; + p = memchr(dlg->dlg_entry_out.first->to_uri.s, ';', dlg->dlg_entry_out.first->to_uri.len); + if (p) + to_uri_clean.len = p - to_uri_clean.s; + + lock_udomain(_d, &to_uri_clean); + if (get_impurecord(_d, &to_uri_clean, &to_impu) != 0) { + LM_DBG("Could not find callee impu for [%.*s]\n", to_uri_clean.len, to_uri_clean.s); + unlock_udomain(_d, &to_uri_clean); + return; + } + if (find_contact_from_impu(to_impu, &dlg->dlg_entry_out.first->callee_contact, &ucontact_callee) !=0) { + LM_DBG("Unable to find callee contact from dialog.... continuing\n"); + //unlock_udomain(_d, &to_uri_clean); + //return; + } + else{ + iFoundCallee = 1; + } + unlock_udomain(_d, &to_uri_clean); + + } else { + LM_ERR("Unknown event type [%d] for callid [%.*s] ", cb_types, dlg->callid.len, dlg->callid.s); + return; + } + if(!iFoundCaller && !iFoundCallee) + { + LM_ERR("No Contacts found for both caller && callee ... bailing\n"); + return; + } + switch (cb_types) { + case DLGCB_CONFIRMED: + LM_DBG("Confirmed contact of type [%d] ,caller_id [%.*s] from handler ", cb_types, dlg->callid.len, dlg->callid.s); + if (iFoundCaller) + add_dialog_data_to_contact(ucontact_caller, dlg->h_entry, dlg->h_id); + if(iFoundCallee) + add_dialog_data_to_contact(ucontact_callee, dlg->h_entry, dlg->h_id);//dlg->dlg_entry_out.first->h_entry, dlg->dlg_entry_out.first->h_id); + i_confirmed_count++; + break; + case DLGCB_FAILED: + case DLGCB_DESTROY: + case DLGCB_EXPIRED: + case DLGCB_TERMINATED: + LM_DBG("Terminated contact of type [%d] , caller_id [%.*s] from handler ", cb_types, dlg->callid.len, dlg->callid.s); + if(iFoundCaller) + remove_dialog_data_from_contact(ucontact_caller, dlg->h_entry, dlg->h_id); + if(iFoundCallee) + //if (dlg->dlg_entry_out.first) { + remove_dialog_data_from_contact(ucontact_callee, dlg->h_entry, dlg->h_id);//dlg->dlg_entry_out.first->h_entry, dlg->dlg_entry_out.first->h_id); + //} + i_terminated_count++; + break; + } + if (i_confirmed_count > 100 && !(i_confirmed_count % 100)) + { + LM_ERR("Curent confirmed count %d terminated count %d \n",i_confirmed_count, i_terminated_count); + } +} diff --git a/modules/ims_usrloc_scscf/dlist.c b/modules/ims_usrloc_scscf/dlist.c index 919b5fceafc..bfaf50d9012 100644 --- a/modules/ims_usrloc_scscf/dlist.c +++ b/modules/ims_usrloc_scscf/dlist.c @@ -234,7 +234,7 @@ static inline int get_all_mem_ucontacts(void *buf, int len, unsigned int flags, * \param part_max maximal part * \return 0 on success, positive if buffer size was not sufficient, negative on failure */ -int get_all_ucontacts(void *buf, int len, unsigned int flags, +int get_all_scontacts(void *buf, int len, unsigned int flags, unsigned int part_idx, unsigned int part_max) { return get_all_mem_ucontacts( buf, len, flags, part_idx, part_max); diff --git a/modules/ims_usrloc_scscf/dlist.h b/modules/ims_usrloc_scscf/dlist.h index 3863efdcc20..e1007006291 100644 --- a/modules/ims_usrloc_scscf/dlist.h +++ b/modules/ims_usrloc_scscf/dlist.h @@ -130,7 +130,7 @@ int synchronize_all_udomains(void); * \param part_max maximal part * \return 0 on success, positive if buffer size was not sufficient, negative on failure */ -int get_all_ucontacts(void *buf, int len, unsigned int flags, +int get_all_scontacts(void *buf, int len, unsigned int flags, unsigned int part_idx, unsigned int part_max); diff --git a/modules/ims_usrloc_scscf/impurecord.c b/modules/ims_usrloc_scscf/impurecord.c index d783c065236..6d2869e3104 100644 --- a/modules/ims_usrloc_scscf/impurecord.c +++ b/modules/ims_usrloc_scscf/impurecord.c @@ -219,7 +219,7 @@ void free_impurecord(impurecord_t* _r) { */ void print_impurecord(FILE* _f, impurecord_t* _r) { ucontact_t* ptr; - int i; + int i = 0; fprintf(_f, "...Record(%p)...\n", _r); fprintf(_f, "domain : '%.*s'\n", _r->domain->len, ZSW(_r->domain->s)); @@ -268,7 +268,7 @@ void print_impurecord(FILE* _f, impurecord_t* _r) { * \param _ci contact information * \return pointer to new created contact on success, 0 on failure */ -ucontact_t* mem_insert_ucontact(impurecord_t* _r, str* _c, ucontact_info_t* _ci) { +ucontact_t* mem_insert_scontact(impurecord_t* _r, str* _c, ucontact_info_t* _ci) { ucontact_t* c; int sl; @@ -308,7 +308,14 @@ void mem_delete_ucontact(ucontact_t* _c) { struct contact_dialog_data *dialog_data; //tear down dialogs in dialog data list + LM_DBG("Checking if dialog_data is there and needs to be torn down\n"); + if(_c->first_dialog_data == 0) { + LM_DBG("first dialog is 0!\n"); + } else { + LM_DBG("first dialog is not 0\n"); + } for (dialog_data = _c->first_dialog_data; dialog_data;) { + LM_DBG("Going to tear down dialog with info h_entry [%d] h_id [%d]\n", dialog_data->h_entry, dialog_data->h_id); dlgb.lookup_terminate_dlg(dialog_data->h_entry, dialog_data->h_id, NULL); dialog_data = dialog_data->next; } @@ -316,7 +323,6 @@ void mem_delete_ucontact(ucontact_t* _c) { mem_remove_ucontact(_c); free_ucontact(_c); } - /*! * \brief Expires timer for NO_DB db_mode * @@ -374,11 +380,30 @@ static inline void process_impurecord(impurecord_t* _r) { if ((ptr = _r->newcontacts[k])) { flag = 1; if (!VALID_CONTACT(ptr, act_time)) { - LM_DBG("IMPU:<%.*s> - contact:<%.*s> has expired... unlinking contact from IMPU\n", _r->public_identity.len, _r->public_identity.s, ptr->c.len, ptr->c.s); - contacts_to_expire[num_contacts_to_expire] = ptr; - num_contacts_to_expire++; + if (ptr->state == CONTACT_DELETED) { + LM_DBG("Contact: <%.*s> has been deleted - unlinking from IMPU\n", ptr->c.len, ptr->c.s); + contacts_to_expire[num_contacts_to_expire] = ptr; + num_contacts_to_expire++; + } else if (ptr->state == CONTACT_EXPIRE_PENDING_NOTIFY) { + LM_DBG("Contact: <%.*s> is in state CONTACT_EXPIRE_PENDING_NOTIFY....running callback\n", ptr->c.len, ptr->c.s); + if (exists_ulcb_type(_r->cbs, UL_IMPU_DELETE_CONTACT)) { + LM_DBG("Running callback UL_IMPU_DELETE_CONTACT for contact [%.*s] and impu [%.*s]\n", ptr->c.len, ptr->c.s, _r->public_identity.len, _r->public_identity.s); + run_ul_callbacks(_r->cbs, UL_IMPU_DELETE_CONTACT, _r, ptr); + } + hascontacts = 1; // we do this because the impu must only be deleted if in state deleted.... + mustdeleteimpu = 0; + } else if (ptr->state == CONTACT_VALID) { + LM_DBG("Contact: <%.*s> is in state valid but it has expired.... ignoring as the contact check will set the appropriate action/state\n", ptr->c.len, ptr->c.s); + mustdeleteimpu = 0; + hascontacts = 1; + } else { + LM_WARN("Bogus state for contact [%.*s] - state: %d... ignoring", ptr->c.len, ptr->c.s, ptr->state); + mustdeleteimpu = 0; + hascontacts = 1; + } } else { - LM_DBG("\t\tContact #%i - %.*s, Ref [%d] (expires in %ld seconds)\n", k, ptr->c.len, ptr->c.s, ptr->ref_count, ptr->expires - act_time); + LM_DBG("\t\tContact #%i - %.*s, Ref [%d] (expires in %ld seconds) (State: %d)\n", + k, ptr->c.len, ptr->c.s, ptr->ref_count, ptr->expires - act_time, ptr->state); mustdeleteimpu = 0; hascontacts = 1; } @@ -389,7 +414,7 @@ static inline void process_impurecord(impurecord_t* _r) { } if (num_contacts_to_expire > 0) { - LM_DBG("\tThere are %d contacts to expire\n", num_contacts_to_expire); + LM_DBG("\tThere are %d contacts to expire/unlink\n", num_contacts_to_expire); for (n = 0; n < num_contacts_to_expire; n++) { ptr = contacts_to_expire[n]; LM_DBG("\t\texpiring contact %i: [%.*s] in slot [%d]\n", n, contacts_to_expire[n]->c.len, contacts_to_expire[n]->c.s, contacts_to_expire[n]->sl); @@ -441,7 +466,7 @@ int get_contacts_count(impurecord_t* _r) { * \param _c new created contact * \return 0 on success, -1 on failure */ -int insert_ucontact(impurecord_t* _r, str* _contact, ucontact_info_t* _ci, ucontact_t** _c) { +int insert_scontact(impurecord_t* _r, str* _contact, ucontact_info_t* _ci, ucontact_t** _c) { //First check our constraints if (maxcontact > 0 && maxcontact_behaviour > 0) { int numcontacts = get_contacts_count(_r); @@ -453,7 +478,7 @@ int insert_ucontact(impurecord_t* _r, str* _contact, ucontact_info_t* _ci, ucont case 2://overwrite oldest LM_DBG("Too many contacts already registered, overwriting oldest for IMPU <%.*s>\n", _r->public_identity.len, _r->public_identity.s); //we can just remove the first one seeing the contacts are ordered on insertion with newest last and oldest first - delete_ucontact(_r->newcontacts[0]); + //TODO:mem_delete_ucontact(_r, _r->contacts); break; default://unknown LM_ERR("unknown maxcontact behaviour..... ignoring\n"); @@ -464,7 +489,7 @@ int insert_ucontact(impurecord_t* _r, str* _contact, ucontact_info_t* _ci, ucont //at this stage we are safe to insert the new contact LM_DBG("INSERTing ucontact in usrloc module\n"); - if (((*_c) = mem_insert_ucontact(_r, _contact, _ci)) == 0) { + if (((*_c) = mem_insert_scontact(_r, _contact, _ci)) == 0) { LM_ERR("failed to insert contact\n"); return -1; } @@ -477,8 +502,8 @@ int insert_ucontact(impurecord_t* _r, str* _contact, ucontact_info_t* _ci, ucont //make sure IMPU is linked to this contact link_contact_to_impu(_r, *_c, 1); - - release_ucontact(*_c); + + release_scontact(*_c); if (exists_ulcb_type(NULL, UL_CONTACT_INSERT)) { run_ul_callbacks(NULL, UL_CONTACT_INSERT, _r, *_c); @@ -489,13 +514,14 @@ int insert_ucontact(impurecord_t* _r, str* _contact, ucontact_info_t* _ci, ucont return 0; } + /*! * \brief Delete ucontact from impurecord * \param _r record where the contact belongs to * \param _c deleted contact * \return 0 on success, -1 on failure */ -int delete_ucontact(struct ucontact* _c) { +int delete_scontact(struct ucontact* _c) { int ret = 0; LM_DBG("Deleting contact: [%.*s]\n", _c->c.len, _c->c.s); @@ -586,7 +612,7 @@ static inline struct ucontact* contact_port_ip_match(unsigned int slot, str* _c) static inline struct ucontact* contact_callid_match(unsigned int slot, str* _c, str *_callid) { ucontact_t* ptr = contact_list->slot[slot].first; - + while (ptr) { if ((_c->len == ptr->c.len) && (_callid->len == ptr->callid.len) && !memcmp(_c->s, ptr->c.s, _c->len) @@ -636,7 +662,7 @@ static inline struct ucontact* contact_path_match(unsigned int slot, str* _c, st * \return 0 - found, 1 - not found, -1 - invalid found, * -2 - found, but to be skipped (same cseq) - don't forget to release_ucontact so dec. the ref counter */ -int get_ucontact(impurecord_t* _r, str* _c, str* _callid, str* _path, int _cseq, struct ucontact** _co) { +int get_scontact(str* _c, str* _callid, str* _path, int _cseq, struct ucontact** _co) { unsigned int sl; ucontact_t* ptr; int with_callid = 0; @@ -677,9 +703,9 @@ int get_ucontact(impurecord_t* _r, str* _c, str* _callid, str* _path, int _cseq, if (_cseq < ptr->cseq) { LM_DBG("cseq less than expected\n"); } - + } - LM_DBG("contact found p=[%p], aor:[%.*s] and contact:[%.*s]\n", ptr, ptr->aor.len, ptr->aor.s, ptr->c.len, ptr->c.s); + LM_DBG("contact found p=[%p], aor:[%.*s] and contact:[%.*s], state [%d]\n", ptr, ptr->aor.len, ptr->aor.s, ptr->c.len, ptr->c.s, ptr->state); ref_contact_unsafe(ptr); *_co = ptr; unlock_contact_slot_i(sl); /*TODO: we probably need to ref count here..... */ @@ -781,21 +807,21 @@ int compare_subscription(ims_subscription* new, ims_subscription* orig) { for (k = 0; k < new->service_profiles_cnt; k++) { for (l = 0; l < new->service_profiles[k].public_identities_cnt; l++) { LM_DBG("new %.*s (%i) vs. orig %.*s (%i)\n", - new->service_profiles[k].public_identities[l].public_identity.len, - new->service_profiles[k].public_identities[l].public_identity.s, - new->service_profiles[k].public_identities[l].public_identity.len, - orig->service_profiles[i].public_identities[j].public_identity.len, - orig->service_profiles[i].public_identities[j].public_identity.s, - orig->service_profiles[i].public_identities[j].public_identity.len); - - if (orig->service_profiles[i].public_identities[j].public_identity.len == - new->service_profiles[k].public_identities[l].public_identity.len) { + new->service_profiles[k].public_identities[l].public_identity.len, + new->service_profiles[k].public_identities[l].public_identity.s, + new->service_profiles[k].public_identities[l].public_identity.len, + orig->service_profiles[i].public_identities[j].public_identity.len, + orig->service_profiles[i].public_identities[j].public_identity.s, + orig->service_profiles[i].public_identities[j].public_identity.len); + + if (orig->service_profiles[i].public_identities[j].public_identity.len == + new->service_profiles[k].public_identities[l].public_identity.len) { if (memcmp(orig->service_profiles[i].public_identities[j].public_identity.s, - new->service_profiles[k].public_identities[l].public_identity.s, - new->service_profiles[k].public_identities[l].public_identity.len) == 0) + new->service_profiles[k].public_identities[l].public_identity.s, + new->service_profiles[k].public_identities[l].public_identity.len) == 0) return 1; } - + } } } @@ -853,7 +879,7 @@ int update_impurecord(struct udomain* _d, str* public_identity, impurecord_t* im ref_subscription_unsafe(subs_ptr); //we reference coz we are using it - will be unreferenced later. add_subscription_unsafe(subs_ptr); unlock_subscription_slot(subs_ptr->sl); - } + } } lock_subscription(subs_ptr); subscription_locked = 1; @@ -1050,15 +1076,6 @@ int unlink_contact_from_impu(impurecord_t* impu, ucontact_t* contact, int write_ LM_DBG("zero'ing last pointer to contact in the list\n"); impu->newcontacts[i - 1] = 0; } - LM_DBG("unlinking contact [%.*s] from impu [%.*s]\n", found_contact->c.len, found_contact->c.s, impu->public_identity.len, impu->public_identity.s); - if (is_explicit && exists_ulcb_type(impu->cbs, UL_IMPU_DELETE_CONTACT)) { - LM_DBG("Running callback UL_IMPU_DELETE_CONTACT for contact [%.*s] and impu [%.*s]\n", found_contact->c.len, found_contact->c.s, impu->public_identity.len, impu->public_identity.s); - run_ul_callbacks(impu->cbs, UL_IMPU_DELETE_CONTACT, impu, found_contact); - } - if (!is_explicit && exists_ulcb_type(impu->cbs, UL_IMPU_DELETE_CONTACT_IMPLICIT)) { - LM_DBG("Running callback UL_IMPU_DELETE_CONTACT_IMPLICIT for contact [%.*s] and impu [%.*s]\n", found_contact->c.len, found_contact->c.s, impu->public_identity.len, impu->public_identity.s); - run_ul_callbacks(impu->cbs, UL_IMPU_DELETE_CONTACT_IMPLICIT, impu, found_contact); - } if (write_to_db && db_mode == WRITE_THROUGH && db_unlink_contact_from_impu(impu, found_contact) != 0) { LM_ERR("Failed to un-link DB contact [%.*s] from IMPU [%.*s]...continuing but db will be out of sync!\n", found_contact->c.len, found_contact->c.s, impu->public_identity.len, impu->public_identity.s); @@ -1085,12 +1102,15 @@ void unref_subscription_unsafe(ims_subscription* s) { LM_DBG("un-reffing subscription [%.*s] - was [%d]\n", s->private_identity.len, s->private_identity.s, s->ref_count); s->ref_count--; - if (s->ref_count == 0 && (s->sl >= 0)) { //-1 as sl means the subscription was never added to the list - sl = s->sl; - subs_slot_rem(&ims_subscription_list->slot[sl], s); + if (s->ref_count == 0) { + if (s->sl >= 0) { //-1 as sl means the subscription was never added to the list + sl = s->sl; + subs_slot_rem(&ims_subscription_list->slot[sl], s); + } delete_subscription(s); s = 0; } + } void ref_subscription(ims_subscription* s) { diff --git a/modules/ims_usrloc_scscf/impurecord.h b/modules/ims_usrloc_scscf/impurecord.h index 33b0fcbd570..11ab22417ab 100644 --- a/modules/ims_usrloc_scscf/impurecord.h +++ b/modules/ims_usrloc_scscf/impurecord.h @@ -97,7 +97,7 @@ void print_impurecord(FILE* _f, impurecord_t* _r); * \param _ci contact information * \return pointer to new created contact on success, 0 on failure */ -ucontact_t* mem_insert_ucontact(impurecord_t* _r, str* _c, ucontact_info_t* _ci); +ucontact_t* mem_insert_scontact(impurecord_t* _r, str* _c, ucontact_info_t* _ci); /*! @@ -142,7 +142,7 @@ void free_ims_subscription_data(ims_subscription *s); * \param _c new created contact * \return 0 on success, -1 on failure */ -int insert_ucontact(impurecord_t* _r, str* _contact, +int insert_scontact(impurecord_t* _r, str* _contact, ucontact_info_t* _ci, ucontact_t** _c); @@ -152,7 +152,7 @@ int insert_ucontact(impurecord_t* _r, str* _contact, * \param _c deleted contact * \return 0 on success, -1 on failure */ -int delete_ucontact(struct ucontact* _c); +int delete_scontact(struct ucontact* _c); /*! @@ -166,7 +166,7 @@ int delete_ucontact(struct ucontact* _c); * \return 0 - found, 1 - not found, -1 - invalid found, * -2 - found, but to be skipped (same cseq) */ -int get_ucontact(impurecord_t* _r, str* _c, str* _callid, str* _path, +int get_scontact(str* _c, str* _callid, str* _path, int _cseq, struct ucontact** _co); diff --git a/modules/ims_usrloc_scscf/ucontact.c b/modules/ims_usrloc_scscf/ucontact.c index 39d7de8ced5..de0477b35a6 100644 --- a/modules/ims_usrloc_scscf/ucontact.c +++ b/modules/ims_usrloc_scscf/ucontact.c @@ -396,7 +396,7 @@ static inline void update_contact_pos(struct impurecord* _r, ucontact_t* _c) { * \param _c updated contact * \return 0 on success, -1 on failure */ -int expire_ucontact(struct impurecord* _r, ucontact_t* _c) { +int expire_scontact(struct impurecord* _r, ucontact_t* _c) { /* we have to update memory in any case, but database directly * only in db_mode 1 */ LM_DBG("Expiring contact aor: [%.*s] and contact uri: [%.*s]\n", _c->aor.len, _c->aor.s, _c->c.len, _c->c.s); @@ -434,7 +434,7 @@ int expire_ucontact(struct impurecord* _r, ucontact_t* _c) { * \param _ci new contact informations * \return 0 on success, -1 on failure */ -int update_ucontact(struct impurecord* _r, ucontact_t* _c, ucontact_info_t* _ci) { +int update_scontact(struct impurecord* _r, ucontact_t* _c, ucontact_info_t* _ci) { /* we have to update memory in any case, but database directly * only in db_mode 1 */ LM_DBG("Updating contact aor: [%.*s] and contact uri: [%.*s]\n", _c->aor.len, _c->aor.s, _c->c.len, _c->c.s); @@ -503,7 +503,7 @@ int add_dialog_data_to_contact(ucontact_t* _c, unsigned int h_entry, unsigned in * used when this contact is part of a confirmed dialog so we can tear down the dialog if the contact is removed */ int remove_dialog_data_from_contact(ucontact_t* _c, unsigned int h_entry, unsigned int h_id) { - struct contact_dialog_data *dialog_data, *tmp_dialog_data; + struct contact_dialog_data *dialog_data, *tmp_dialog_data; LM_DBG("Removing dialog data from contact <%.*s> with h_entry <%d> and h_id <%d>", _c->c.len, _c->c.s, h_entry, h_id); for (dialog_data = _c->first_dialog_data; dialog_data;) { @@ -521,16 +521,17 @@ int remove_dialog_data_from_contact(ucontact_t* _c, unsigned int h_entry, unsign }else{ _c->last_dialog_data = tmp_dialog_data->prev; } - shm_free(tmp_dialog_data); - return 0; + shm_free(tmp_dialog_data); + return 0; } } - LM_DBG("Did not find dialog data to remove from contact"); - return 0; + + LM_DBG("Did not find dialog data to remove from contact"); + return 1; } -void release_ucontact(struct ucontact* _c) { +void release_scontact(struct ucontact* _c) { lock_contact_slot_i(_c->sl); unref_contact_unsafe(_c); unlock_contact_slot_i(_c->sl); diff --git a/modules/ims_usrloc_scscf/ucontact.h b/modules/ims_usrloc_scscf/ucontact.h index de45db7452d..7506b491159 100644 --- a/modules/ims_usrloc_scscf/ucontact.h +++ b/modules/ims_usrloc_scscf/ucontact.h @@ -105,7 +105,7 @@ int mem_update_ucontact(ucontact_t* _c, ucontact_info_t *_ci); * \param _ci new contact informations * \return 0 on success, -1 on failure */ -int update_ucontact(struct impurecord* _r, ucontact_t* _c, ucontact_info_t* _ci); +int update_scontact(struct impurecord* _r, ucontact_t* _c, ucontact_info_t* _ci); /*! * \brief Setting contact expires to now in memory @@ -120,12 +120,12 @@ int mem_expire_ucontact(ucontact_t* _c); * \param _c updated contact * \return 0 on success, -1 on failure */ -int expire_ucontact(struct impurecord* _r, ucontact_t* _c); +int expire_scontact(struct impurecord* _r, ucontact_t* _c); int remove_dialog_data_from_contact(ucontact_t* _c, unsigned int h_entry, unsigned int h_id); int add_dialog_data_to_contact(ucontact_t* _c, unsigned int h_entry, unsigned int h_id); -void release_ucontact(struct ucontact* _c); +void release_scontact(struct ucontact* _c); #endif diff --git a/modules/ims_usrloc_scscf/udomain.c b/modules/ims_usrloc_scscf/udomain.c index 18d83d0ebf0..8fa89f7436d 100644 --- a/modules/ims_usrloc_scscf/udomain.c +++ b/modules/ims_usrloc_scscf/udomain.c @@ -249,6 +249,10 @@ void mem_delete_impurecord(udomain_t* _d, struct impurecord* _r) { counter_add(ul_scscf_cnts_h.active_impus, -1); } +static unsigned int expired_contacts_size = 0; +ucontact_t** expired_contacts = 0; + + /*! * \brief Run timer handler for given domain * \param _d domain @@ -256,8 +260,25 @@ void mem_delete_impurecord(udomain_t* _d, struct impurecord* _r) { void mem_timer_udomain(udomain_t* _d) { struct impurecord* ptr, *t; struct ucontact* contact_ptr; + unsigned int num_expired_contacts = 0; int i, n, temp; - + time_t now; + + now = time(0); + int numcontacts = contact_list->max_collisions?(contact_list->max_collisions * contact_list->size):(contact_list->size); + if (expired_contacts_size < numcontacts) { + LM_DBG("Changing expired_contacts list size from %d to %d\n", expired_contacts_size, numcontacts); + if (expired_contacts){ + pkg_free(expired_contacts); + } + expired_contacts = (ucontact_t**)pkg_malloc(numcontacts*sizeof(ucontact_t**)); + if (!expired_contacts) { + LM_ERR("no more pkg mem\n"); + return; + } + expired_contacts_size = numcontacts; + } + //go through contacts first n = contact_list->max_collisions; LM_DBG("*** mem_timer_udomain - checking contacts - START ***\n"); @@ -265,8 +286,18 @@ void mem_timer_udomain(udomain_t* _d) { lock_contact_slot_i(i); contact_ptr = contact_list->slot[i].first; while (contact_ptr) { - LM_DBG("We have a contact in the new contact list in slot %d = [%.*s] (%.*s) which expires in %lf seconds and has a ref count of %d\n", i, contact_ptr->aor.len, contact_ptr->aor.s, contact_ptr->c.len, contact_ptr->c.s, (double) contact_ptr->expires - time(NULL), contact_ptr->ref_count); + LM_DBG("We have a contact in the new contact list in slot %d = [%.*s] (%.*s) which expires in %lf seconds and has a ref count of %d (state: %d)\n", + i, contact_ptr->aor.len, contact_ptr->aor.s, contact_ptr->c.len, contact_ptr->c.s, + (double) contact_ptr->expires - now, contact_ptr->ref_count, + contact_ptr->state); //contacts are now deleted during impurecord processing + if ((contact_ptr->expires-now) <=0 && (contact_ptr->state != CONTACT_DELETED)) { + LM_DBG("expiring contact [%.*s].... setting to CONTACT_EXPIRE_PENDING_NOTIFY\n", contact_ptr->aor.len, contact_ptr->aor.s); + contact_ptr->state = CONTACT_EXPIRE_PENDING_NOTIFY; + ref_contact_unsafe(contact_ptr); + expired_contacts[num_expired_contacts] = contact_ptr; + num_expired_contacts++; + } contact_ptr = contact_ptr->next; } if (contact_list->slot[i].n > n) { @@ -317,6 +348,15 @@ void mem_timer_udomain(udomain_t* _d) { } ims_subscription_list->max_collisions = n; + /* now we delete the expired contacts. (mark them for deletion */ + for (i=0; isl); + LM_DBG("Setting contact state to CONTACT_DELETED for contact [%.*s]\n", expired_contacts[i]->aor.len, expired_contacts[i]->aor.s); + expired_contacts[i]->state = CONTACT_DELETED; + unref_contact_unsafe(expired_contacts[i]); + unlock_contact_slot_i(expired_contacts[i]->sl); + } + } @@ -756,6 +796,6 @@ void unref_contact_unsafe(ucontact_t* c) { if (c->ref_count < 0) { LM_WARN("reference dropped below zero... this should not happen\n"); } - delete_ucontact(c); + delete_scontact(c); } } diff --git a/modules/ims_usrloc_scscf/ul_mod.c b/modules/ims_usrloc_scscf/ul_mod.c index 629cd86e110..0e0f7322871 100644 --- a/modules/ims_usrloc_scscf/ul_mod.c +++ b/modules/ims_usrloc_scscf/ul_mod.c @@ -79,6 +79,7 @@ static void timer(unsigned int ticks, void* param); /*!< Timer handler */ static int child_init(int rank); /*!< Per-child init function */ extern int bind_usrloc(usrloc_api_t* api); +extern void contact_dlg_create_handler(struct dlg_cell* dlg, int cb_types, struct dlg_cb_params *dlg_params);/*V1.1*/ extern int ul_locks_no; extern int subs_locks_no; extern int contacts_locks_no; @@ -374,7 +375,20 @@ static int mod_init(void) { } init_flag = 1; - + + /* From contact_dlg_handlers.c + * + * V1.1*/ + + if (dlgb.register_dlgcb(0x00, DLGCB_CREATED, contact_dlg_create_handler, 0x00, 0x00) ) + { + LM_ERR("Unable to setup DLGCB_CREATED"); + return -1; + } + else + { + LM_DBG(" DLGCB_CREATED created successfully"); + } return 0; } diff --git a/modules/ims_usrloc_scscf/usrloc.c b/modules/ims_usrloc_scscf/usrloc.c index 4901b28f940..fd3b536bc45 100644 --- a/modules/ims_usrloc_scscf/usrloc.c +++ b/modules/ims_usrloc_scscf/usrloc.c @@ -94,13 +94,13 @@ int bind_usrloc(usrloc_api_t* api) { api->ref_subscription = ref_subscription; api->unref_subscription = unref_subscription; - api->get_all_ucontacts = get_all_ucontacts; - api->insert_ucontact = insert_ucontact; - api->delete_ucontact = delete_ucontact; - api->get_ucontact = get_ucontact; - api->release_ucontact = release_ucontact; - api->update_ucontact = update_ucontact; - api->expire_ucontact = expire_ucontact; + api->get_all_ucontacts = get_all_scontacts; + api->insert_ucontact = insert_scontact; + api->delete_ucontact = delete_scontact; + api->get_ucontact = get_scontact; + api->release_ucontact = release_scontact; + api->update_ucontact = update_scontact; + api->expire_ucontact = expire_scontact; api->add_dialog_data_to_contact = add_dialog_data_to_contact; api->remove_dialog_data_from_contact = remove_dialog_data_from_contact; diff --git a/modules/ims_usrloc_scscf/usrloc.h b/modules/ims_usrloc_scscf/usrloc.h index 15e44c77b9e..a9dfef47a2d 100644 --- a/modules/ims_usrloc_scscf/usrloc.h +++ b/modules/ims_usrloc_scscf/usrloc.h @@ -143,12 +143,13 @@ typedef enum cstate { typedef enum contact_state { CONTACT_VALID, - CONTACT_EXPIRED, + CONTACT_DELETE_PENDING, + CONTACT_EXPIRE_PENDING_NOTIFY, CONTACT_DELETED } contact_state_t; /*! \brief Valid contact is a contact that either didn't expire yet or is permanent */ -#define VALID_CONTACT(c, t) ((c->expires>t) || (c->expires==0)) +#define VALID_CONTACT(c, t) (((c->expires>t) || (c->expires==0)) && c->state!=CONTACT_DELETED && c->state!=CONTACT_DELETE_PENDING && c->state!=CONTACT_EXPIRE_PENDING_NOTIFY) struct hslot; /*!< Hash table slot */ @@ -467,7 +468,7 @@ typedef int (*insert_ucontact_t)(struct impurecord* _r, str* _contact, struct uc typedef int (*delete_ucontact_t)(struct ucontact* _c); -typedef int (*get_ucontact_t)(struct impurecord* _r, str* _c, str* _callid, str* _path, int _cseq, struct ucontact** _co); +typedef int (*get_ucontact_t)(str* _c, str* _callid, str* _path, int _cseq, struct ucontact** _co); typedef void (*release_ucontact_t)(struct ucontact* _c); diff --git a/modules/ims_usrloc_scscf/usrloc_db.c b/modules/ims_usrloc_scscf/usrloc_db.c index 9cd49d20ce7..6e4890d6c7d 100644 --- a/modules/ims_usrloc_scscf/usrloc_db.c +++ b/modules/ims_usrloc_scscf/usrloc_db.c @@ -790,6 +790,8 @@ int preload_udomain(db1_con_t* _c, udomain_t* _d) { != 0) { LM_ERR("Unable to insert IMPU into memory [%.*s]\n", impu.len, impu.s); } + /* run the INSERTion callback so REGISTRAR can get into sync - ie subscribe for callbacks on the IMPU.... for NOTIFYs for example*/ + run_ul_callbacks(NULL, UL_IMPU_INSERT, impurecord, NULL); } /* add contacts */ @@ -844,9 +846,9 @@ int preload_udomain(db1_con_t* _c, udomain_t* _d) { continue; } - if (get_ucontact(impurecord, &contact, contact_data.callid, contact_data.path, contact_data.cseq, &c) != 0) { + if (get_scontact(&contact, contact_data.callid, contact_data.path, contact_data.cseq, &c) != 0) { LM_DBG("Contact doesn't exist yet, creating new one [%.*s]\n", contact.len, contact.s); - if ((c = mem_insert_ucontact(impurecord, &contact, &contact_data)) == 0) { + if ((c = mem_insert_scontact(impurecord, &contact, &contact_data)) == 0) { LM_ERR("Unable to insert contact [%.*s] for IMPU [%.*s] into memory... continuing...\n", contact.len, contact.s, impu.len, impu.s); @@ -854,7 +856,7 @@ int preload_udomain(db1_con_t* _c, udomain_t* _d) { } } link_contact_to_impu(impurecord, c, 0); - release_ucontact(c); + release_scontact(c); } if (DB_CAPABILITY(ul_dbf, DB_CAP_FETCH)) { if (ul_dbf.fetch_result(_c, &contact_rs, ul_fetch_rows) < 0) {