/* When done, this function also has the job to unref the dialog as removed * from timer list. This must be done in all cases!! */ void dlg_ontimeout( struct dlg_tl *tl) { struct dlg_cell *dlg; int new_state; int old_state; int unref; dlg = get_dlg_tl_payload(tl); LM_DBG("byeontimeout ? %d , state = %d\n",dlg->flags,dlg->state); if ( (dlg->flags&DLG_FLAG_BYEONTIMEOUT) && (dlg->state==DLG_STATE_CONFIRMED_NA || dlg->state==DLG_STATE_CONFIRMED)) { init_dlg_term_reason(dlg,"Lifetime Timeout",sizeof("Lifetime Timeout")-1); /* we just send the BYEs in both directions */ dlg_end_dlg( dlg, NULL); /* dialog is no longer refed by timer; from now one it is refed by the send_bye functions */ unref_dlg( dlg, 1); /* is not 100% sure, but do it */ if_update_stat( dlg_enable_stats, expired_dlgs, 1); return ; } next_state_dlg( dlg, DLG_EVENT_REQBYE, &old_state, &new_state, &unref); if (new_state==DLG_STATE_DELETED && old_state!=DLG_STATE_DELETED) { LM_DBG("timeout for dlg with CallID '%.*s' and tags '%.*s' '%.*s'\n", dlg->callid.len, dlg->callid.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[callee_idx(dlg)].tag.len, ZSW(dlg->legs[callee_idx(dlg)].tag.s)); /*destroy linkers */ destroy_linkers(dlg->profile_links); dlg->profile_links = NULL; /* dialog timeout */ run_dlg_callbacks( DLGCB_EXPIRED, dlg, 0, DLG_DIR_NONE, 0); /* delete the dialog from DB */ if (should_remove_dlg_db()) remove_dialog_from_db(dlg); unref_dlg(dlg, unref + 1 /*timer list*/); if_update_stat( dlg_enable_stats, expired_dlgs, 1); if_update_stat( dlg_enable_stats, active_dlgs, -1); } else { unref_dlg(dlg, 1 /*just timer list*/); } return; }
void dlg_ping_routine(unsigned int ticks , void * attr) { struct dlg_ping_list *expired,*it,*curr; struct dlg_cell *dlg; expired = get_timeout_dlgs(); it = expired; while (it) { dlg = it->dlg; LM_DBG("dialog %p has expired\n",dlg); curr = it->next; shm_free(it); dlg->pl = 0; it = curr; /* no longer reffed in list */ unref_dlg(dlg,1); /* dlg is still reffed in TM callback. deletion from memory * will happen only on 408 timeout */ /* FIXME - maybe better not to send BYE both ways as we know for sure one * end in down . */ dlg_end_dlg(dlg,0); } /* ping_timer->first now contains all active dialogs */ it = ping_timer->first; while (it) { dlg = it->dlg; /* do not ping ended dialogs */ if (dlg->state != DLG_STATE_DELETED) { if (dlg->flags & DLG_FLAG_PING_CALLER) { ref_dlg(dlg,1); if (send_leg_msg(dlg,&options_str,callee_idx(dlg), DLG_CALLER_LEG,0,0,reply_from_caller,dlg,unref_dlg_cb) < 0) { LM_ERR("failed to ping caller\n"); unref_dlg(dlg,1); } } if (dlg->flags & DLG_FLAG_PING_CALLEE) { ref_dlg(dlg,1); if (send_leg_msg(dlg,&options_str,DLG_CALLER_LEG, callee_idx(dlg),0,0,reply_from_callee,dlg,unref_dlg_cb) < 0) { LM_ERR("failed to ping callee\n"); unref_dlg(dlg,1); } } } it = it->next; } }
/* Wrapper for terminating dialog from API - from other modules */ static void rpc_end_dlg_entry_id(rpc_t *rpc, void *c) { unsigned int h_entry, h_id; struct dlg_cell * dlg = NULL; str rpc_extra_hdrs = {NULL,0}; int n; n = rpc->scan(c, "dd", &h_entry, &h_id); if (n < 2) { LM_ERR("unable to read the parameters (%d)\n", n); rpc->fault(c, 500, "Invalid parameters"); return; } if(rpc->scan(c, "*S", &rpc_extra_hdrs)<1) { rpc_extra_hdrs.s = NULL; rpc_extra_hdrs.len = 0; } dlg = lookup_dlg(h_entry, h_id);//increments ref count! if(dlg==NULL) { rpc->fault(c, 404, "Dialog not found"); return; } unref_dlg(dlg, 1); dlg_terminate(dlg, NULL, NULL/*reason*/, 2, NULL); }
/** * replicates the remote deletion of a dialog locally * by reading the relevant information using the Binary Packet Interface */ int dlg_replicated_delete(void) { str call_id, from_tag, to_tag; unsigned int dir, dst_leg; struct dlg_cell *dlg; int old_state, new_state, unref, ret; bin_pop_str(&call_id); bin_pop_str(&from_tag); bin_pop_str(&to_tag); LM_DBG("Deleting dialog with callid: %.*s\n", call_id.len, call_id.s); dlg = get_dlg(&call_id, &from_tag, &to_tag, &dir, &dst_leg); if (!dlg) { LM_ERR("dialog not found (callid: |%.*s| ftag: |%.*s|\n", call_id.len, call_id.s, from_tag.len, from_tag.s); return -1; } destroy_linkers(dlg->profile_links); dlg->profile_links = NULL; next_state_dlg(dlg, DLG_EVENT_REQBYE, &old_state, &new_state, &unref); if (old_state == new_state) { LM_ERR("duplicate dialog delete request (callid: |%.*s|" "ftag: |%.*s|\n", call_id.len, call_id.s, from_tag.len, from_tag.s); return -1; } ret = remove_dlg_timer(&dlg->tl); if (ret < 0) { LM_CRIT("unable to unlink the timer on dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[callee_idx(dlg)].tag.len, ZSW(dlg->legs[callee_idx(dlg)].tag.s)); } else if (ret > 0) { LM_DBG("dlg expired (not in timer list) on dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[callee_idx(dlg)].tag.len, ZSW(dlg->legs[callee_idx(dlg)].tag.s)); } else { /* dialog sucessfully removed from timer -> unref */ unref++; } unref_dlg(dlg, 1 + unref); if_update_stat(dlg_enable_stats, active_dlgs, -1); return 0; }
void unreference_dialog(void *dialog) { /* if the dialog table is gone, it means the system is shutting down.*/ if (!d_table) return; unref_dlg((struct dlg_cell*)dialog, 1); }
/*! * \brief Timer function that removes expired dialogs, run timeout route * \param tl dialog timer list */ void dlg_ontimeout(struct dlg_tl *tl) { struct dlg_cell *dlg; int new_state, old_state, unref; struct sip_msg *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); } } if ((dlg->dflags & DLG_FLAG_TOBYE) && (dlg->state == DLG_STATE_CONFIRMED)) { //TODO: dlg_bye_all(dlg, NULL); unref_dlg(dlg, 1); return; } next_state_dlg(dlg, DLG_EVENT_REQBYE, &old_state, &new_state, &unref, 0); 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); } else { unref_dlg(dlg, 1); } return; }
int dialog_cleanup( struct sip_msg *msg, void *param ) { if (current_processing_ctx && ctx_dialog_get()) { unref_dlg( ctx_dialog_get(), 1); ctx_dialog_set(NULL); } return SCB_RUN_ALL; }
/* cell- pointer to a struct dlg_cell * leg - a dialog leg to be BYE'ed : * = 0: caller leg * > 0: callee legs */ static inline int send_leg_bye(struct dlg_cell *cell, int dst_leg, int src_leg, str *extra_hdrs) { context_p old_ctx; context_p *new_ctx; dlg_t* dialog_info; str met = {"BYE", 3}; int result; if ((dialog_info = build_dlg_t(cell, dst_leg, src_leg)) == 0){ LM_ERR("failed to create dlg_t\n"); goto err; } LM_DBG("sending BYE to %s (%d)\n", (dst_leg==DLG_CALLER_LEG)?"caller":"callee", dst_leg); /* set new processing context */ if (push_new_processing_context( cell, &old_ctx, &new_ctx, NULL)!=0) goto err; ctx_lastdstleg_set(dst_leg); ref_dlg(cell, 1); result = d_tmb.t_request_within (&met, /* method*/ extra_hdrs, /* extra headers*/ NULL, /* body*/ dialog_info, /* dialog structure*/ bye_reply_cb, /* callback function*/ (void*)cell, /* callback parameter*/ NULL); /* release function*/ /* reset the processing contect */ if (current_processing_ctx == NULL) *new_ctx = NULL; else context_destroy(CONTEXT_GLOBAL, *new_ctx); current_processing_ctx = old_ctx; if(result < 0){ LM_ERR("failed to send the BYE request\n"); goto err1; } free_tm_dlg(dialog_info); LM_DBG("BYE sent to %s\n", (dst_leg==DLG_CALLER_LEG)?"caller":"callee"); return 0; err1: unref_dlg(cell, 1); err: return -1; }
static void unreference_dialog_cseq(void *cseq_wrap) { /* if the dialog table is gone, it means the system is shutting down.*/ if (!d_table) return; dlg_cseq_wrapper *wrap = (dlg_cseq_wrapper *)cseq_wrap; unref_dlg(wrap->dlg, 1); shm_free(wrap); }
static int w_dlg_get(struct sip_msg *msg, char *ci, char *ft, char *tt) { struct dlg_cell *dlg = NULL; str sc = {0,0}; str sf = {0,0}; str st = {0,0}; unsigned int dir = 0; if(ci==0 || ft==0 || tt==0) { LM_ERR("invalid parameters\n"); return -1; } if(fixup_get_svalue(msg, (gparam_p)ci, &sc)!=0) { LM_ERR("unable to get Call-ID\n"); return -1; } if(sc.s==NULL || sc.len == 0) { LM_ERR("invalid Call-ID parameter\n"); return -1; } if(fixup_get_svalue(msg, (gparam_p)ft, &sf)!=0) { LM_ERR("unable to get From tag\n"); return -1; } if(sf.s==NULL || sf.len == 0) { LM_ERR("invalid From tag parameter\n"); return -1; } if(fixup_get_svalue(msg, (gparam_p)tt, &st)!=0) { LM_ERR("unable to get To Tag\n"); return -1; } if(st.s==NULL || st.len == 0) { LM_ERR("invalid To tag parameter\n"); return -1; } dlg = get_dlg(&sc, &sf, &st, &dir); if(dlg==NULL) return -1; unref_dlg(dlg, 1); set_current_dialog(msg, dlg); _dlg_ctx.dlg = dlg; _dlg_ctx.dir = dir; return 1; }
int dialog_cleanup( struct sip_msg *msg, void *param ) { if (current_dlg_pointer) { unref_dlg( current_dlg_pointer, 1); current_dlg_pointer = NULL; } last_dst_leg = -1; /* need to return non-zero - 0 will break the exec of the request */ return 1; }
int dlg_th_onroute(struct dlg_cell *dlg, struct sip_msg *req, int dir) { struct hdr_field *it; char* buf = req->buf; /* delete vias */ if(dlg_del_vias(req) < 0) { LM_ERR("Failed to remove via headers\n"); return -1; } /* delete record route */ for (it=req->record_route;it;it=it->sibling) { if (del_lump(req, it->name.s - buf, it->len,HDR_RECORDROUTE_T) == 0) { LM_ERR("del_lump failed\n"); return -1; } LM_DBG("Delete record route: [%.*s]\n", it->len, it->name.s); } /* add route headers */ fix_route_dialog(req, dlg); /* replace contact*/ if(dlg_replace_contact(req, dlg) < 0) { LM_ERR("Failed to replace contact\n"); return -1; } /* register tm callback for response in */ ref_dlg( dlg , 1); if ( d_tmb.register_tmcb( req, 0, TMCB_RESPONSE_FWDED, (dir==DLG_DIR_UPSTREAM)?dlg_th_down_onreply:dlg_th_up_onreply, (void*)dlg, unreference_dialog)<0 ) { LM_ERR("failed to register TMCB\n"); unref_dlg( dlg , 1); } if (dir == DLG_DIR_UPSTREAM) { /* destination leg is the caller - force the send socket * as the one the caller was inited from */ req->force_send_socket = dlg->legs[DLG_CALLER_LEG].bind_addr; LM_DBG("forcing send socket for req going to caller\n"); } else { /* destination leg is the callee - force the send socket * as the one the callee was inited from */ req->force_send_socket = dlg->legs[callee_idx(dlg)].bind_addr; LM_DBG("forcing send socket for req going to callee\n"); } return 0; }
/*! * \brief Cleanup a profile * \param msg SIP message * \param flags unused * \param unused * \return 1 */ int profile_cleanup( struct sip_msg *msg, unsigned int flags, void *param ) { 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; }
/* Wrapper for terminating dialog from API - from other modules */ int w_api_lookup_terminate_dlg(unsigned int h_entry, unsigned int h_id, str *hdrs) { struct dlg_cell *dlg; dlg = lookup_dlg(h_entry, h_id); //increments ref count! if (!dlg) { LM_ERR("Asked to tear down non existent dialog\n"); return -1; } unref_dlg(dlg, 1); return dlg_terminate(dlg, NULL, NULL/*reason*/, 2, hdrs); }
str * api_get_dlg_variable(str *callid, str *ftag, str *ttag, str *key) { struct dlg_cell *dlg; unsigned int dir = DLG_DIR_NONE; dlg = get_dlg(callid, ftag, ttag, &dir); //increments ref count! if (!dlg) { LM_ERR("Asked to tear down non existent dialog\n"); return NULL; } unref_dlg(dlg, 1); return get_dlg_variable(dlg, key); }
/* Wrapper for terminating dialog from API - from other modules */ int w_api_terminate_dlg(str *callid, str *ftag, str *ttag, str *hdrs, str* reason) { struct dlg_cell *dlg; unsigned int dir = DLG_DIR_NONE; dlg = get_dlg(callid, ftag, ttag, &dir); //increments ref count! if (!dlg) { LM_ERR("Asked to tear down non existent dialog\n"); return -1; } unref_dlg(dlg, 1); return dlg_terminate(dlg, NULL, NULL/*reason*/, 2, hdrs); }
/* cell- pointer to a struct dlg_cell * leg - a dialog leg to be BYE'ed : * = 0: caller leg * > 0: callee legs */ static inline int send_leg_bye(struct dlg_cell *cell, int dst_leg, int src_leg, str *extra_hdrs) { dlg_t* dialog_info; struct dlg_cell *old_cell; str met = {"BYE", 3}; int result; if ((dialog_info = build_dlg_t(cell, dst_leg, src_leg)) == 0){ LM_ERR("failed to create dlg_t\n"); goto err; } LM_DBG("sending BYE to %s (%d)\n", (dst_leg==DLG_CALLER_LEG)?"caller":"callee", dst_leg); ref_dlg(cell, 1); old_cell = current_dlg_pointer; current_dlg_pointer = cell; result = d_tmb.t_request_within (&met, /* method*/ extra_hdrs, /* extra headers*/ NULL, /* body*/ dialog_info, /* dialog structure*/ bye_reply_cb, /* callback function*/ (void*)cell, /* callback parameter*/ NULL); /* release function*/ current_dlg_pointer = old_cell; if(result < 0){ LM_ERR("failed to send the BYE request\n"); goto err1; } free_tm_dlg(dialog_info); LM_DBG("BYE sent to %s\n", (dst_leg==DLG_CALLER_LEG)?"caller":"callee"); return 0; err1: unref_dlg(cell, 1); err: return -1; }
int dlg_th_onroute(struct dlg_cell *dlg, struct sip_msg *req, int dir) { struct hdr_field *it; char* buf = req->buf; int leg_id; if(dir == DLG_DIR_UPSTREAM) leg_id = callee_idx(dlg); else leg_id = DLG_CALLER_LEG; /* delete vias */ if(dlg_save_del_vias(req, &dlg->legs[leg_id]) < 0) { LM_ERR("Failed to save and remove via headers\n"); return -1; } /* delete record route */ for (it=req->record_route;it;it=it->sibling) { if (del_lump(req, it->name.s - buf, it->len,HDR_RECORDROUTE_T) == 0) { LM_ERR("del_lump failed \n"); return -1; } LM_DBG("Delete record route: [%.*s]\n", it->len, it->name.s); } /* add route headers */ fix_route_dialog(req, dlg); /* replace contact*/ if(dlg_replace_contact(req, dlg) < 0) { LM_ERR("Failed to replace contact\n"); return -1; } /* register tm callback for response in */ ref_dlg( dlg , 1); if ( d_tmb.register_tmcb( req, 0, TMCB_RESPONSE_FWDED, (dir==DLG_DIR_UPSTREAM)?dlg_th_down_onreply:dlg_th_up_onreply, (void*)dlg, unreference_dialog)<0 ) { LM_ERR("failed to register TMCB\n"); unref_dlg( dlg , 1); } return 0; }
/* cell- pointer to a struct dlg_cell * dir- direction: the request will be sent to: * DLG_CALLER_LEG (0): caller * DLG_CALLEE_LEG (1): callee */ static inline int send_bye(struct dlg_cell * cell, int dir, str *hdrs) { uac_req_t uac_r; dlg_t* dialog_info; str met = {"BYE", 3}; int result; /* do not send BYE request for non-confirmed dialogs (not supported) */ if (cell->state != DLG_STATE_CONFIRMED) { LM_ERR("terminating only 1 side of non-confirmed dialogs not supported by this function\n"); return -1; } /*verify direction*/ if ((dialog_info = build_dlg_t(cell, dir)) == 0) { LM_ERR("failed to create dlg_t\n"); goto err; } LM_DBG("sending BYE to %s\n", (dir == DLG_CALLER_LEG) ? "caller" : "callee"); ref_dlg(cell, 1); memset(&uac_r, '\0', sizeof (uac_req_t)); set_uac_req(&uac_r, &met, hdrs, NULL, dialog_info, TMCB_LOCAL_COMPLETED, bye_reply_cb, (void*) cell); result = d_tmb.t_request_within(&uac_r); if (result < 0) { LM_ERR("failed to send the BYE request\n"); goto err1; } free_tm_dlg(dialog_info); LM_DBG("BYE sent to %s\n", (dir == 0) ? "caller" : "callee"); return 0; err1: unref_dlg(cell, 1); err: if (dialog_info) free_tm_dlg(dialog_info); return -1; }
int register_dlgcb_nodlg(str *callid, str *ftag, str *ttag, int types, dialog_cb f, void *param, param_free_cb ff ) { struct dlg_cell *dlg; unsigned int dir = DLG_DIR_NONE; dlg = get_dlg(callid, ftag, ttag, &dir); //increments ref count! if (!dlg) { LM_ERR("Can't find dialog to register callback\n"); return -1; } int ret = register_dlgcb(dlg, types, f, param, ff); unref_dlg(dlg, 1); return ret; }
int terminate_dlg(unsigned int h_entry, unsigned int h_id,str *reason) { struct dlg_cell * dlg = NULL; int ret = 0; dlg = lookup_dlg(h_entry, h_id); if(!dlg) return 0; init_dlg_term_reason(dlg,reason->s,reason->len); if ( dlg_end_dlg( dlg, 0) ) { LM_ERR("Failed to end dialog"); ret = -1; } unref_dlg(dlg, 1); return ret; }
static int w_get_dlg_info(struct sip_msg *msg, char *attr, char *attr_val, char *key, char *key_val) { struct dlg_cell *dlg; pv_elem_t *pve = (pv_elem_t *)key_val; pv_spec_t *dst = (pv_spec_t *)attr_val; pv_value_t val; str val_s; int n; if ( pve==NULL || pv_printf_s(msg, pve, &val_s)!=0 || val_s.len == 0 || val_s.s == NULL) { LM_WARN("cannot get string for value\n"); return -1; } dlg = get_dlg_by_val( (str*)key, &val_s); if (dlg==NULL) { /* nothing found */ LM_DBG("no dialog found\n"); return -1; } /* dlg found - NOTE you have a ref! */ LM_DBG("dialog found, fetching variable\n"); if (fetch_dlg_value( dlg, (str*)attr, &val.rs, 0) ) { LM_DBG("failed to fetch dialog value <%.*s>\n", ((str*)attr)->len, ((str*)attr)->s); n = -1 ; } else { val.flags = PV_VAL_STR; n = (dst->setf( msg, &dst->pvp, 0, &val )==0)?1:-1; } unref_dlg(dlg, 1); return n; }
/*! * \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("starting dlg_new_dialog and method is [%.*s]\n", req->first_line.u.request.method.len, req->first_line.u.request.method.s); if (current_dlg_pointer != NULL) return -1; if (req->first_line.u.request.method_value == METHOD_CANCEL) return -1; if (pre_match_parse(req, &callid, &ftag, &ttag, 0) < 0) { LM_WARN("pre-matching failed\n"); return -1; } if (ttag.s != 0 && ttag.len != 0) return -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); if (detect_spirals) { if (spiral_detected == 1) return 0; dir = DLG_DIR_NONE; 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 (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(dlg, req, dlg->h_entry, dlg->h_id) < 0) { LM_ERR("failed to add RR param\n"); } } // get_dlg has incremented the ref count by 1 unref_dlg(dlg, 1); goto finish; } } spiral_detected = 0; 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 (dlg == 0) { LM_ERR("failed to create new dialog\n"); return -1; } /* 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; } dlg->transaction = t; /* Populate initial varlist: */ dlg->vars = get_local_varlist_pointer(req, 1); link_dlg(dlg, 0); if (run_initial_cbs) run_create_callbacks(dlg, req); //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; } 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; } // 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 (_dlg_ctx.to_bye != 0) dlg->dflags |= DLG_FLAG_TOBYE; 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; } } LM_DBG("Setting current dialog\n"); set_current_dialog(req, dlg); _dlg_ctx.dlg = dlg; ref_dlg(dlg, 1); return 0; error: LM_DBG("Error in build_new_dlg"); if (!spiral_detected) unref_dlg(dlg, 1); // undo ref regarding linking return -1; }
/*! * \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; 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) 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); dlg = (struct dlg_cell *) (*param->param); if (shutdown_done || dlg == 0) return; if (t) { dlg->transaction = t; } LM_DBG("DLG dialogid is entry:id [%i:%i]\n", dlg->h_entry, dlg->h_id); 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; } if (type == TMCB_RESPONSE_OUT) { LM_DBG("TMCB_RESPONSE_OUT\n"); return; } if (type == TMCB_RESPONSE_READY) { if (rpl == FAKED_REPLY) { LM_DBG("Faked reply\n"); 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; 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; } } LM_DBG("Got to-tag from response: %.*s \n", to_tag.len, to_tag.s); } 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; 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 (type == TMCB_RESPONSE_READY) { LM_DBG("Checking if there is an existing dialog_out entry with same to-tag"); dlg_entry_out = &dlg->dlg_entry_out; lock_get(dlg->dlg_out_entries_lock); dlg_out = dlg_entry_out->first; 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); /* 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"); } if (!dlg_out) { LM_ERR("failed to create new dialog out structure\n"); //TODO do something on this error! } } else { //This dlg_out already exists, update cseq and contact if present 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)); } } } if (new_state == DLG_STATE_EARLY) { run_dlg_callbacks(DLGCB_EARLY, dlg, req, rpl, DLG_DIR_UPSTREAM, 0); return; } LM_DBG("new state is %i and old state is %i\n", new_state, old_state); 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). //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); } } else { LM_ERR("There is no dlg_out structure - this is bad\n"); //TODO: add error handling here } /* set start time */ dlg->start_ts = (unsigned int) (time(0)); /* save the settings to the database, * if realtime saving mode configured- save dialog now * else: the next time the timer will fire the update*/ dlg->dflags |= DLG_FLAG_NEW; 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); } run_dlg_callbacks(DLGCB_CONFIRMED, dlg, req, rpl, DLG_DIR_UPSTREAM, 0); if (unref) unref_dlg(dlg, unref); 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); //assign new did to the created or updated dialog_out entry. update_dlg_out_did(dlg_out, &new_did); //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 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*/); //assign new did to dlg_in update_dlg_did(new_dlg, &new_did); if (new_dlg == 0) { LM_ERR("failed to create new dialog\n"); return; } //link the new_dlg with dlg_out object link_dlg_out(new_dlg, dlg_out, 0); } 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); return; } if (unref) unref_dlg(dlg, unref); return; }
/*! * \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) { struct dlg_cell *dlg; str val, callid, ftag, ttag; int h_entry, h_id, new_state, old_state, unref, event, timeout; unsigned int dir; int ret = 0; if (current_dlg_pointer != NULL) return; /* 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; dlg = 0; dir = DLG_DIR_NONE; 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 (parse_dlg_rr_param(val.s, val.s + val.len, &h_entry, &h_id) < 0) return; 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); // 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; } } /* set current dialog - re-use ref increment from dlg_get() above */ set_current_dialog(req, dlg); _dlg_ctx.dlg = dlg; 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; default: event = DLG_EVENT_REQ; } 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; /* 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); } 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); return; } if ((event == DLG_EVENT_REQ || event == DLG_EVENT_REQACK) && new_state == DLG_STATE_CONFIRMED) { 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; } if (old_state != DLG_STATE_CONFIRMED) { LM_DBG("confirming ACK successfully processed\n"); /* confirming ACK request */ run_dlg_callbacks(DLGCB_CONFIRMED, dlg, req, NULL, dir, 0); } else { 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) { /* 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); } } } } if (new_state == DLG_STATE_CONFIRMED && old_state != DLG_STATE_CONFIRMED) { dlg->dflags |= DLG_FLAG_CHANGED; } return; }
/** * replicates a confirmed dialog from another OpenSIPS instance * by reading the relevant information using the Binary Packet Interface */ int dlg_replicated_create(struct dlg_cell *cell, str *ftag, str *ttag, int safe) { int next_id, h_entry; unsigned int dir, dst_leg; str callid, from_uri, to_uri, from_tag, to_tag; str cseq1,cseq2,contact1,contact2,rroute1,rroute2,mangled_fu,mangled_tu; str sock, vars, profiles; struct dlg_cell *dlg = NULL; struct socket_info *caller_sock, *callee_sock; struct dlg_entry *d_entry; if_update_stat(dlg_enable_stats, processed_dlgs, 1); LM_DBG("Received replicated dialog!\n"); if (!cell) { bin_pop_str(&callid); bin_pop_str(&from_tag); bin_pop_str(&to_tag); bin_pop_str(&from_uri); bin_pop_str(&to_uri); dlg = get_dlg(&callid, &from_tag, &to_tag, &dir, &dst_leg); h_entry = dlg_hash(&callid); d_entry = &d_table->entries[h_entry]; if (safe) dlg_lock(d_table, d_entry); if (dlg) { LM_DBG("Dialog with ci '%.*s' is already created\n", callid.len, callid.s); unref_dlg_unsafe(dlg, 1, d_entry); dlg_unlock(d_table, d_entry); return 0; } dlg = build_new_dlg(&callid, &from_uri, &to_uri, &from_tag); if (!dlg) { LM_ERR("Failed to create replicated dialog!\n"); goto pre_linking_error; } } else { h_entry = dlg_hash(&cell->callid); d_entry = &d_table->entries[h_entry]; if (safe) dlg_lock(d_table, d_entry); from_tag = *ftag; to_tag = *ttag; dlg = cell; } bin_pop_int(&dlg->h_id); bin_pop_int(&dlg->start_ts); bin_pop_int(&dlg->state); next_id = d_table->entries[dlg->h_entry].next_id; d_table->entries[dlg->h_entry].next_id = (next_id <= dlg->h_id) ? (dlg->h_id + 1) : next_id; if (bin_pop_str(&sock)) goto pre_linking_error; caller_sock = fetch_socket_info(&sock); if (bin_pop_str(&sock)) goto pre_linking_error; callee_sock = fetch_socket_info(&sock); if (!caller_sock || !callee_sock) { LM_ERR("Dialog in DB doesn't match any listening sockets\n"); goto pre_linking_error; } bin_pop_str(&cseq1); bin_pop_str(&cseq2); bin_pop_str(&rroute1); bin_pop_str(&rroute2); bin_pop_str(&contact1); bin_pop_str(&contact2); bin_pop_str(&mangled_fu); bin_pop_str(&mangled_tu); /* add the 2 legs */ if (dlg_add_leg_info(dlg, &from_tag, &rroute1, &contact1, &cseq1, caller_sock, 0, 0) != 0 || dlg_add_leg_info(dlg, &to_tag, &rroute2, &contact2, &cseq2, callee_sock, &mangled_fu, &mangled_tu) != 0) { LM_ERR("dlg_set_leg_info failed\n"); goto pre_linking_error; } dlg->legs_no[DLG_LEG_200OK] = DLG_FIRST_CALLEE_LEG; /* link the dialog into the hash */ dlg->h_id = d_entry->next_id++; if (!d_entry->first) d_entry->first = d_entry->last = dlg; else { d_entry->last->next = dlg; dlg->prev = d_entry->last; d_entry->last = dlg; } dlg->ref++; d_entry->cnt++; bin_pop_str(&vars); bin_pop_str(&profiles); bin_pop_int(&dlg->user_flags); bin_pop_int(&dlg->flags); bin_pop_int((void *)&dlg->tl.timeout); bin_pop_int(&dlg->legs[DLG_CALLER_LEG].last_gen_cseq); bin_pop_int(&dlg->legs[callee_idx(dlg)].last_gen_cseq); if (dlg->tl.timeout <= (unsigned int)time(0)) dlg->tl.timeout = 0; else dlg->tl.timeout -= (unsigned int)time(0); /* restore the timer values */ if (insert_dlg_timer(&dlg->tl, (int)dlg->tl.timeout) != 0) { LM_CRIT("Unable to insert dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[callee_idx(dlg)].tag.len, ZSW(dlg->legs[callee_idx(dlg)].tag.s)); goto error; } if (dlg->state == DLG_STATE_CONFIRMED_NA || dlg->state == DLG_STATE_CONFIRMED) active_dlgs_cnt++; /* reference the dialog as kept in the timer list */ ref_dlg_unsafe(dlg, 1); LM_DBG("Received initial timeout of %d for dialog %.*s, safe = %d\n",dlg->tl.timeout,callid.len,callid.s,safe); dlg->lifetime = 0; /* Do not replicate the pinging - we might terminate dialogs badly when running as backup if (dlg->flags & DLG_FLAG_PING_CALLER || dlg->flags & DLG_FLAG_PING_CALLEE) { if (insert_ping_timer(dlg) != 0) LM_CRIT("Unable to insert dlg %p into ping timer\n",dlg); else { ref_dlg_unsafe(dlg, 1); } } */ if (dlg_db_mode == DB_MODE_DELAYED) { /* to be later removed by timer */ ref_dlg_unsafe(dlg, 1); } if (vars.s && vars.len != 0) read_dialog_vars(vars.s, vars.len, dlg); dlg_unlock(d_table, d_entry); if (profiles.s && profiles.len != 0) read_dialog_profiles(profiles.s, profiles.len, dlg, 0, 1); if_update_stat(dlg_enable_stats, active_dlgs, 1); run_load_callback_per_dlg(dlg); return 0; pre_linking_error: dlg_unlock(d_table, d_entry); if (dlg) destroy_dlg(dlg); return -1; error: dlg_unlock(d_table, d_entry); if (dlg) unref_dlg(dlg, 1); return -1; }
void unref_dlg_cb(void *dlg) { if (!d_table) return; unref_dlg((struct dlg_cell*)dlg,1); }
/*parameters from MI: h_entry, h_id of the requested dialog*/ struct mi_root * mi_terminate_dlg(struct mi_root *cmd_tree, void *param ){ struct mi_node* node; unsigned int h_entry, h_id; struct dlg_cell * dlg = NULL; str *mi_extra_hdrs = NULL; int status, msg_len; char *msg; if( d_table ==NULL) goto end; node = cmd_tree->node.kids; h_entry = h_id = 0; if (node==NULL || node->next==NULL) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); if (!node->value.s|| !node->value.len|| strno2int(&node->value,&h_entry)<0) goto error; node = node->next; if ( !node->value.s || !node->value.len || strno2int(&node->value,&h_id)<0) goto error; if (node->next) { node = node->next; if (node->value.len && node->value.s) mi_extra_hdrs = &node->value; } LM_DBG("h_entry %u h_id %u\n", h_entry, h_id); dlg = lookup_dlg(h_entry, h_id); /* lookup_dlg has incremented the reference count !! */ if(dlg){ init_dlg_term_reason(dlg,"MI Termination",sizeof("MI Termination")-1); if ( dlg_end_dlg( dlg, mi_extra_hdrs) ) { status = 500; msg = MI_DLG_OPERATION_ERR; msg_len = MI_DLG_OPERATION_ERR_LEN; } else { status = 200; msg = MI_OK_S; msg_len = MI_OK_LEN; } unref_dlg(dlg, 1); return init_mi_tree(status, msg, msg_len); } end: return init_mi_tree(404, MI_DIALOG_NOT_FOUND, MI_DIALOG_NOT_FOUND_LEN); error: return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); }
/* TODO: add reason parameter to mi interface */ struct mi_root * mi_terminate_dlg(struct mi_root *cmd_tree, void *param) { struct mi_node* node; struct dlg_cell * dlg = NULL; str mi_extra_hdrs = {NULL, 0}; int status, msg_len; char *msg; str callid = {NULL, 0}; str ftag = {NULL, 0}; str ttag = {NULL, 0}; if (d_table == NULL) goto end; node = cmd_tree->node.kids; if (node == NULL || node->next == NULL || node->next->next == NULL) return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); if (!node->value.s || !node->value.len) { goto error; } else { callid = node->value; } node = node->next; if (!node->value.s || !node->value.len) { goto error; } else { ftag = node->value; } node = node->next; if (!node->value.s || !node->value.len) { goto error; } else { ttag = node->value; } if (node->next) { node = node->next; if (node->value.len && node->value.s) mi_extra_hdrs = node->value; } unsigned int dir = DLG_DIR_NONE; LM_DBG("Looking for callid [%.*s]\n", callid.len, callid.s); dlg = get_dlg(&callid, &ftag, &ttag, &dir); //increments ref count! if (dlg) { LM_DBG("Found dialog to terminate and it is in state [%i]\n", dlg->state); if (dlg_terminate(dlg, 0, NULL/*reson*/, /* all sides of a dialog*/ 2, &mi_extra_hdrs) < 0) { status = 500; msg = MI_DLG_OPERATION_ERR; msg_len = MI_DLG_OPERATION_ERR_LEN; } else { status = 200; msg = MI_OK_S; msg_len = MI_OK_LEN; } unref_dlg(dlg, 1); return init_mi_tree(status, msg, msg_len); } end: return init_mi_tree(404, MI_DIALOG_NOT_FOUND, MI_DIALOG_NOT_FOUND_LEN); error: return init_mi_tree(400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); }
/*callback function to handle responses to the BYE request */ void bye_reply_cb(struct cell* t, int type, struct tmcb_params* ps) { struct dlg_cell* dlg; int event, old_state, new_state, unref, ret; struct dlg_cell_out *dlg_out = 0; if (ps->param == NULL || *ps->param == NULL) { LM_ERR("invalid parameter\n"); return; } if (ps->code < 200) { LM_DBG("receiving a provisional reply\n"); return; } LM_DBG("receiving a final reply %d\n", ps->code); dlg = (struct dlg_cell *) (*(ps->param)); event = DLG_EVENT_REQBYE; //get the corresponding dlg out structure for this REQ struct dlg_entry_out *dlg_entry_out = &dlg->dlg_entry_out; lock_get(dlg->dlg_out_entries_lock); dlg_out = dlg_entry_out->first; //TODO check for concurrent call if (!dlg_out) return; next_state_dlg(dlg, event, &old_state, &new_state, &unref, &dlg_out->to_tag); lock_release(dlg->dlg_out_entries_lock); if (new_state == DLG_STATE_DELETED && old_state != DLG_STATE_DELETED) { LM_DBG("removing dialog with h_entry %u and h_id %u\n", dlg->h_entry, dlg->h_id); /* 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'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s); } else if (ret > 0) { LM_WARN("inconsitent dlg timer data on dlg %p [%u:%u] " "with clid '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s); } else { unref++; } /* dialog terminated (BYE) */ run_dlg_callbacks(DLGCB_TERMINATED, dlg, ps->req, ps->rpl, DLG_DIR_NONE, 0); /* derefering the dialog */ unref_dlg(dlg, unref + 1); } if (new_state == DLG_STATE_DELETED && old_state == DLG_STATE_DELETED) { /* trash the dialog from DB and memory */ if (dlg_db_mode) remove_dialog_in_from_db(dlg); /* force delete from mem */ unref_dlg(dlg, 1); } }