/*! * \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; if(req->first_line.u.request.method_value != METHOD_INVITE) return; dlg = dlg_get_ctx_dialog(); if (dlg!=NULL) { if (!initial_cbs_inscript) { if (spiral_detected == 1) run_dlg_callbacks( DLGCB_SPIRALED, dlg, req, NULL, DLG_DIR_DOWNSTREAM, 0); else if (spiral_detected == 0) run_create_callbacks(dlg, req); } } if (dlg==NULL) { if((req->flags&dlg_flag)!=dlg_flag) return; dlg_new_dialog(req, t, 1); dlg = dlg_get_ctx_dialog(); } if (dlg!=NULL) { dlg_set_tm_callbacks(t, req, dlg, spiral_detected); dlg_release(dlg); } }
static int w_dlg_bye(struct sip_msg *msg, char *side, char *s2) { dlg_cell_t *dlg = NULL; int n; dlg = dlg_get_ctx_dialog(); if(dlg==NULL) return -1; n = (int)(long)side; if(n==1) { if(dlg_bye(dlg, NULL, DLG_CALLER_LEG)!=0) goto error; goto done; } else if(n==2) { if(dlg_bye(dlg, NULL, DLG_CALLEE_LEG)!=0) goto error; goto done; } else { if(dlg_bye_all(dlg, NULL)!=0) goto error; goto done; } done: dlg_release(dlg); return 1; error: dlg_release(dlg); return -1; }
int dlg_cfg_cb(sip_msg_t *msg, unsigned int flags, void *cbp) { dlg_cell_t *dlg; if(flags&POST_SCRIPT_CB) { dlg = dlg_get_ctx_dialog(); if(dlg!=NULL) { if(_dlg_ctx.t==0 && (dlg->state==DLG_STATE_UNCONFIRMED || _dlg_ctx.expect_t==1)) { if(_dlg_ctx.cpid!=0 && _dlg_ctx.cpid==my_pid()) { /* release to destroy dialog if created by this process * and request was not forwarded */ if(dlg->state==DLG_STATE_UNCONFIRMED) { LM_DBG("new dialog with no transaction after config" " execution\n"); } else { LM_DBG("dialog with no expected transaction after" " config execution\n"); } dlg_release(dlg); } } /* get ctx dlg increased ref count - release now */ dlg_release(dlg); } } memset(&_dlg_ctx, 0, sizeof(dlg_ctx_t)); return 1; }
int dlg_manage(sip_msg_t *msg) { str tag; int backup_mode; dlg_cell_t *dlg = NULL; tm_cell_t *t = NULL; if( (msg->to==NULL && parse_headers(msg, HDR_TO_F,0)<0) || msg->to==NULL ) { LM_ERR("bad TO header\n"); return -1; } tag = get_to(msg)->tag_value; if(tag.s!=0 && tag.len!=0) { backup_mode = seq_match_mode; seq_match_mode = SEQ_MATCH_NO_ID; dlg_onroute(msg, NULL, NULL); seq_match_mode = backup_mode; } else { t = d_tmb.t_gett(); if(t==T_UNDEFINED) t = NULL; if(dlg_new_dialog(msg, t, initial_cbs_inscript)!=0) return -1; dlg = dlg_get_ctx_dialog(); if(dlg==NULL) return -1; if(t!=NULL) dlg_set_tm_callbacks(t, msg, dlg, spiral_detected); dlg_release(dlg); } return 1; }
/*! * \brief Cleanup a profile * \param msg SIP message * \param flags unused * \param param unused * \return 1 */ int profile_cleanup( struct sip_msg *msg, unsigned int flags, void *param ) { dlg_cell_t *dlg; if(get_route_type()==LOCAL_ROUTE) { return 1; } current_dlg_msg_id = 0; current_dlg_msg_pid = 0; dlg = dlg_get_ctx_dialog(); if (dlg!=NULL) { if(dlg->dflags & DLG_FLAG_TM) { dlg_unref(dlg, 1); } else { /* dialog didn't make it to tm */ dlg_unref(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; }
/*! * */ dlg_cell_t *dlg_lookup_msg_dialog(sip_msg_t *msg, unsigned int *dir) { dlg_cell_t *dlg = NULL; str callid; str ftag; str ttag; unsigned int vdir; /* Retrieve the current dialog */ dlg = dlg_get_ctx_dialog(); if(dlg!=NULL) { if(dir) { if (pre_match_parse(msg, &callid, &ftag, &ttag, 0)<0) { dlg_release(dlg); return NULL; } if (dlg->tag[DLG_CALLER_LEG].len == ftag.len && strncmp(dlg->tag[DLG_CALLER_LEG].s, ftag.s, ftag.len)==0 && strncmp(dlg->callid.s, callid.s, callid.len)==0) { *dir = DLG_DIR_DOWNSTREAM; } else { if (ttag.len>0 && dlg->tag[DLG_CALLER_LEG].len == ttag.len && strncmp(dlg->tag[DLG_CALLER_LEG].s, ttag.s, ttag.len)==0 && strncmp(dlg->callid.s, callid.s, callid.len)==0) { *dir = DLG_DIR_UPSTREAM; } } } return dlg; } if (pre_match_parse(msg, &callid, &ftag, &ttag, 0)<0) return NULL; vdir = DLG_DIR_NONE; dlg = get_dlg(&callid, &ftag, &ttag, &vdir); if (dlg==NULL){ LM_DBG("dlg with callid '%.*s' not found\n", msg->callid->body.len, msg->callid->body.s); return NULL; } if(dir) *dir = vdir; return dlg; }
static int w_dlg_refer(struct sip_msg *msg, char *side, char *to) { dlg_cell_t *dlg; int n; str st = {0,0}; dlg = dlg_get_ctx_dialog(); if(dlg==NULL) return -1; n = (int)(long)side; if(fixup_get_svalue(msg, (gparam_p)to, &st)!=0) { LM_ERR("unable to get To\n"); goto error; } if(st.s==NULL || st.len == 0) { LM_ERR("invalid To parameter\n"); goto error; } if(n==1) { if(dlg_transfer(dlg, &st, DLG_CALLER_LEG)!=0) goto error; } else { if(dlg_transfer(dlg, &st, DLG_CALLEE_LEG)!=0) goto error; } dlg_release(dlg); return 1; error: dlg_release(dlg); return -1; }
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; }
/*! * \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 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 */ int dlg_new_dialog(sip_msg_t *req, struct cell *t, const int run_initial_cbs) { dlg_cell_t *dlg; str s; str callid; str ftag; str ttag; str req_uri; unsigned int dir; dlg = dlg_get_ctx_dialog(); if(dlg != NULL) { dlg_release(dlg); return -1; } if(req->first_line.u.request.method_value != METHOD_INVITE) 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); dir = DLG_DIR_NONE; /* search dialog by SIP attributes * - hash table slot is left locked */ dlg = dlg_search(&callid, &ftag, &ttag, &dir); if(dlg) { if (detect_spirals) { if (spiral_detected == 1) { dlg_hash_release(&callid); return 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); /* set ctx dlg id shortcuts */ _dlg_ctx.iuid.h_entry = dlg->h_entry; _dlg_ctx.iuid.h_id = dlg->h_id; /* search_dlg() has incremented the ref count by 1 */ dlg_release(dlg); dlg_hash_release(&callid); return 0; } dlg_release(dlg); } } spiral_detected = 0; dlg = build_new_dlg (&callid /*callid*/, &(get_from(req)->uri) /*from uri*/, &(get_to(req)->uri) /*to uri*/, &ftag/*from_tag*/, &req_uri /*r-uri*/ ); if (dlg==0) { dlg_hash_release(&callid); 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) { dlg_hash_release(&callid); LM_ERR("could not add further info to the dialog\n"); shm_free(dlg); return -1; } /* Populate initial varlist: */ dlg->vars = get_local_varlist_pointer(req, 1); /* after dlg_search() slot was kept locked */ link_dlg(dlg, 0, 1); /* unlock after dlg_search() */ dlg_hash_release(&callid); 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; if (dlg_send_bye!=0 || _dlg_ctx.to_bye!=0) dlg->iflags |= DLG_IFLAG_TIMEOUTBYE; 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_update_stat( dlg_enable_stats, processed_dlgs, 1); _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); return 0; error: if (!spiral_detected) dlg_unref(dlg, 1); // undo ref regarding linking return -1; }
/*! * \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, reset; unsigned int dir; int ret = 0; dlg = dlg_get_ctx_dialog(); if (dlg!=NULL) { dlg_release(dlg); 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 = dlg_lookup(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 dlg_release(dlg); 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, callee tag='%.*s'/%d\n", dlg->callid.len, dlg->callid.s, dlg->callid.len, dlg->tag[DLG_CALLER_LEG].len, dlg->tag[DLG_CALLER_LEG].s, dlg->tag[DLG_CALLER_LEG].len, dlg->tag[DLG_CALLEE_LEG].len, dlg->tag[DLG_CALLEE_LEG].s, dlg->tag[DLG_CALLEE_LEG].len); // lookup_dlg has incremented the ref count by 1 dlg_release(dlg); // 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==0){ 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.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; } } /* 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); 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); /* 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 { dlg_ref(dlg, 1); } } } if (new_state==DLG_STATE_CONFIRMED && old_state!=DLG_STATE_CONFIRMED) dlg_ka_add(dlg); /* 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' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg->tag[DLG_CALLER_LEG].len, dlg->tag[DLG_CALLER_LEG].s, dlg->tag[DLG_CALLEE_LEG].len, dlg->tag[DLG_CALLEE_LEG].s); } else if (ret > 0) { LM_WARN("inconsitent dlg timer data 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->tag[DLG_CALLER_LEG].len, dlg->tag[DLG_CALLER_LEG].s, dlg->tag[DLG_CALLEE_LEG].len, dlg->tag[DLG_CALLEE_LEG].s); } else { /* one extra unref due to removal from timer list */ unref++; } /* dialog terminated (BYE) */ dlg_terminated( req, dlg, dir); dlg_unref(dlg, unref); _dlg_ctx.cpid = my_pid(); _dlg_ctx.expect_t = 1; dlg_set_ctx_iuid(dlg); if_update_stat( dlg_enable_stats, active_dlgs, -1); goto done; } if ( (event==DLG_EVENT_REQ || event==DLG_EVENT_REQACK) && (new_state==DLG_STATE_CONFIRMED || new_state==DLG_STATE_EARLY)) { timeout = get_dlg_timeout(req); if (timeout!=default_timeout) { dlg->lifetime = timeout; } reset = !((dlg->iflags & DLG_IFLAG_TIMER_NORESET) || dlg_timeout_noreset); 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(dlg_refresh_contacts(dlg, req, dir)!=0) { LM_ERR("contacts update failed\n"); } else { dlg->dflags |= DLG_FLAG_CHANGED; } if(update_cseqs(dlg, req, dir)!=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); } if (old_state==DLG_STATE_CONFIRMED_NA) { 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 ) { 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); } } } } } 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); } done: dlg_release(dlg); return; }