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; }
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; }
/*! * \brief Unset a dialog profile * \param msg SIP message * \param value value * \param profile dialog profile table * \return 1 on success, -1 on failure */ int unset_dlg_profile(sip_msg_t *msg, str *value, dlg_profile_table_t *profile) { 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 = dlg_get_msg_dialog(msg); if (dlg==NULL) { LM_WARN("dialog is NULL for delete profile\n"); return -1; } /* check the dialog linkers */ d_entry = &d_table->entries[dlg->h_entry]; dlg_lock( d_table, d_entry); linker = dlg->profile_links; linker_prev = NULL; for( ; linker ; linker_prev=linker,linker=linker->next) { if (linker->profile==profile) { if (profile->has_value==0) { goto found; } else if (value && value->len==linker->hash_linker.value.len && memcmp(value->s,linker->hash_linker.value.s,value->len)==0){ goto found; } /* allow further search - maybe the dialog is inserted twice in * the same profile, but with different values -bogdan */ } } atomic_or_int((volatile int*)&dlg->dflags, DLG_FLAG_CHANGED_PROF); dlg_unlock( d_table, d_entry); dlg_release(dlg); return -1; found: /* table still locked */ /* remove the linker element from dialog */ if (linker_prev==NULL) { dlg->profile_links = linker->next; } else { linker_prev->next = linker->next; } linker->next = NULL; dlg_unlock( d_table, d_entry); /* remove linker from profile table and free it */ destroy_linkers(linker); dlg_release(dlg); return 1; }
int update_dlg_timeout(dlg_cell_t *dlg, int timeout) { if(update_dlg_timer(&dlg->tl, timeout) < 0) { LM_ERR("failed to update dialog lifetime\n"); dlg_release(dlg); return -1; } dlg->lifetime = timeout; dlg->dflags |= DLG_FLAG_CHANGED; dlg_release(dlg); return 0; }
/*! * \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_isflagset(struct sip_msg *msg, char *flag, str *s2) { dlg_ctx_t *dctx; dlg_cell_t *d; int val; int ret; if(fixup_get_ivalue(msg, (gparam_p)flag, &val)!=0) { LM_ERR("no flag value\n"); return -1; } if(val<0 || val>31) return -1; if ( (dctx=dlg_get_dlg_ctx())==NULL ) return -1; d = dlg_get_by_iuid(&dctx->iuid); if(d!=NULL) { ret = (d->sflags&(1<<val))?1:-1; dlg_release(d); return ret; } return (dctx->flags&(1<<val))?1:-1; }
/** * run keep-alive list * */ int dlg_ka_run(ticks_t ti) { dlg_ka_t *dka; dlg_cell_t *dlg; if(dlg_ka_interval<=0) return 0; while(1) { /* get head item */ lock_get(dlg_ka_list_lock); if(*dlg_ka_list_head==NULL) { lock_release(dlg_ka_list_lock); return 0; } dka = *dlg_ka_list_head; #if 0 LM_DBG("dlg ka timer at %lu for" " dlg[%u,%u] on %lu\n", (unsigned long)ti, dka->iuid.h_entry, dka->iuid.h_id, (unsigned long)dka->katime); #endif if(dka->katime>ti) { lock_release(dlg_ka_list_lock); return 0; } if(*dlg_ka_list_head == *dlg_ka_list_tail) { *dlg_ka_list_head = NULL; *dlg_ka_list_tail = NULL; } *dlg_ka_list_head = dka->next; lock_release(dlg_ka_list_lock); /* send keep-alive for dka */ dlg = dlg_get_by_iuid(&dka->iuid); if(dlg==NULL) { shm_free(dka); dka = NULL; } else { if(dka->iflags & DLG_IFLAG_KA_SRC) dlg_send_ka(dlg, DLG_CALLER_LEG, 0); if(dka->iflags & DLG_IFLAG_KA_DST) dlg_send_ka(dlg, DLG_CALLEE_LEG, 0); dlg_release(dlg); } /* append to tail */ if(dka!=NULL) { lock_get(dlg_ka_list_lock); if(*dlg_ka_list_tail!=NULL) (*dlg_ka_list_tail)->next = dka; if(*dlg_ka_list_head==NULL) *dlg_ka_list_head = dka; *dlg_ka_list_tail = dka; lock_release(dlg_ka_list_lock); } } return 0; }
/*! * \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 (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); return; }
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 Function that executes BYE reply callbacks * \param t transaction, unused * \param type type of the callback, should be TMCB_RESPONSE_FWDED * \param params saved dialog structure inside the callback */ static void dlg_terminated_confirmed(tm_cell_t *t, int type, struct tmcb_params* params) { dlg_cell_t *dlg = NULL; dlg_iuid_t *iuid = NULL; if(!params || !params->req || !params->param) { LM_ERR("invalid parameters!\n"); return; } iuid = (dlg_iuid_t*)*params->param; if(iuid==NULL) return; dlg = dlg_get_by_iuid(iuid); if(dlg==NULL) { 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); dlg_release(dlg); }
static void rpc_end_dlg_entry_id(rpc_t *rpc, void *c) { unsigned int h_entry, h_id; dlg_cell_t * 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 = dlg_lookup(h_entry, h_id); if(dlg==NULL) { rpc->fault(c, 404, "Dialog not found"); return; } dlg_bye_all(dlg, (rpc_extra_hdrs.len>0)?&rpc_extra_hdrs:NULL); dlg_release(dlg); }
static int w_dlg_set_property(struct sip_msg *msg, char *prop, char *s2) { dlg_ctx_t *dctx; dlg_cell_t *d; str val; if(fixup_get_svalue(msg, (gparam_t*)prop, &val)!=0) { LM_ERR("no property value\n"); return -1; } if(val.len<=0) { LM_ERR("empty property value\n"); return -1; } if ( (dctx=dlg_get_dlg_ctx())==NULL ) return -1; if(val.len==6 && strncmp(val.s, "ka-src", 6)==0) { dctx->iflags |= DLG_IFLAG_KA_SRC; d = dlg_get_by_iuid(&dctx->iuid); if(d!=NULL) { d->iflags |= DLG_IFLAG_KA_SRC; dlg_release(d); } } else if(val.len==6 && strncmp(val.s, "ka-dst", 6)==0) { dctx->iflags |= DLG_IFLAG_KA_DST; d = dlg_get_by_iuid(&dctx->iuid); if(d!=NULL) { d->iflags |= DLG_IFLAG_KA_DST; dlg_release(d); } } else if(val.len==15 && strncmp(val.s, "timeout-noreset", 15)==0) { dctx->iflags |= DLG_IFLAG_TIMER_NORESET; d = dlg_get_by_iuid(&dctx->iuid); if(d!=NULL) { d->iflags |= DLG_IFLAG_TIMER_NORESET; dlg_release(d); } } else { LM_ERR("unknown property value [%.*s]\n", val.len, val.s); return -1; } return 1; }
int pv_get_dlg_variable(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { 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) { LM_CRIT("BUG - bad parameters\n"); return -1; } /* Retrieve the dialog for current message */ dlg=dlg_get_msg_dialog( msg); if (dlg) { /* Lock the dialog */ dlg_lock(d_table, &(d_table->entries[dlg->h_entry])); } else { /* Verify the local list */ 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.len<value->len+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])); dlg_release(dlg); } if (spv.s) return pv_get_strval(msg, param, res, &spv); return pv_get_null(msg, param, res); }
static int w_dlg_get(struct sip_msg *msg, char *ci, char *ft, char *tt) { dlg_cell_t *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; /* set shorcut to dialog internal unique id */ _dlg_ctx.iuid.h_entry = dlg->h_entry; _dlg_ctx.iuid.h_id = dlg->h_id; _dlg_ctx.dir = dir; dlg_release(dlg); return 1; }
/* * Determine if message is in a dialog currently being tracked */ int is_known_dlg(struct sip_msg *msg) { dlg_cell_t *dlg; dlg = dlg_get_msg_dialog(msg); if(dlg == NULL) return -1; dlg_release(dlg); return 1; }
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; }
/*! * \brief Check if a dialog belongs to a profile * \param msg SIP message * \param profile dialog profile table * \param value value * \return 1 on success, -1 on failure */ int is_dlg_in_profile(struct sip_msg *msg, struct dlg_profile_table *profile, str *value) { 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 = dlg_get_msg_dialog(msg); if (dlg == NULL) { LM_DBG("Error: Current dlg is null"); return -1; } 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); for (linker = dlg->profile_links; linker; linker = linker->next) { LM_DBG("Running through linkers"); if (linker->profile == profile) { LM_DBG("Profile matches"); if (profile->has_value == 0) { LM_DBG("Profile has value is zero returning true"); dlg_unlock( d_table, d_entry); 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); ret = 1; goto done; } /* allow further search - maybe the dialog is inserted twice in * the same profile, but with different values -bogdan */ } } dlg_unlock( d_table, d_entry); done: dlg_release(dlg); return ret; }
/*! * */ 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; }
/*! * \brief Check if a dialog belongs to a profile * \param msg SIP message * \param profile dialog profile table * \param value value * \return 1 on success, -1 on failure */ int is_dlg_in_profile(struct sip_msg *msg, struct dlg_profile_table *profile, str *value) { struct dlg_cell *dlg; struct dlg_profile_link *linker; struct dlg_entry *d_entry; int ret; /* get current dialog */ dlg = dlg_get_msg_dialog(msg); if (dlg==NULL) return -1; ret = -1; /* check the dialog linkers */ d_entry = &d_table->entries[dlg->h_entry]; dlg_lock( d_table, d_entry); for( linker=dlg->profile_links ; linker ; linker=linker->next) { if (linker->profile==profile) { if (profile->has_value==0) { dlg_unlock( d_table, d_entry); 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){ dlg_unlock( d_table, d_entry); ret = 1; goto done; } /* allow further search - maybe the dialog is inserted twice in * the same profile, but with different values -bogdan */ } } dlg_unlock( d_table, d_entry); done: dlg_release(dlg); return ret; }
/*! * \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; }
int dlg_set_timeout_by_profile(struct dlg_profile_table *profile, str *value, int timeout) { unsigned int i = 0; dlg_cell_t *this_dlg = NULL; struct dlg_profile_hash *ph = NULL; /* Private structure necessary for manipulating dialog * timeouts outside of profile locks. Admittedly, an * ugly hack, but avoids some concurrency issues. */ struct dlg_map_list { unsigned int h_id; unsigned int h_entry; struct dlg_map_list *next; } *map_head, *map_scan, *map_scan_next; map_head = NULL; /* If the profile has no value, iterate through every * node and set its timeout. */ if(profile->has_value == 0 || value == NULL) { lock_get(&profile->lock); for(i = 0; i < profile->size; i ++) { ph = profile->entries[i].first; if(!ph) continue; do { struct dlg_map_list *d = malloc(sizeof(struct dlg_map_list)); if(!d) return -1; memset(d, 0, sizeof(struct dlg_map_list)); d->h_id = ph->dlg->h_id; d->h_entry = ph->dlg->h_entry; if(map_head == NULL) map_head = d; else { d->next = map_head; map_head = d; } ph = ph->next; } while(ph != profile->entries[i].first); } lock_release(&profile->lock); } else { i = calc_hash_profile(value, NULL, profile); lock_get(&profile->lock); ph = profile->entries[i].first; if(ph) { do { if(ph && value->len == ph->value.len && memcmp(value->s, ph->value.s, value->len) == 0) { struct dlg_map_list *d = malloc(sizeof(struct dlg_map_list)); if(!d) return -1; memset(d, 0, sizeof(struct dlg_map_list)); d->h_id = ph->dlg->h_id; d->h_entry = ph->dlg->h_entry; if(map_head == NULL) map_head = d; else { d->next = map_head; map_head = d; } } ph = ph->next; } while(ph && ph != profile->entries[i].first); } lock_release(&profile->lock); } /* Walk the list and bulk-set the timeout */ for(map_scan = map_head; map_scan != NULL; map_scan = map_scan_next) { map_scan_next = map_scan->next; this_dlg = dlg_lookup(map_scan->h_entry, map_scan->h_id); if(!this_dlg) { LM_CRIT("Unable to find dialog %d:%d\n", map_scan->h_entry, map_scan->h_id); } else if(this_dlg->state >= DLG_STATE_EARLY) { if(update_dlg_timeout(this_dlg, timeout) < 0) { LM_ERR("Unable to set timeout on %d:%d\n", map_scan->h_entry, map_scan->h_id); } dlg_release(this_dlg); } free(map_scan); } return 0; }
int dlg_cseq_msg_sent(void *data) { sip_msg_t msg; str *obuf; unsigned int direction; dlg_cell_t *dlg = NULL; str nval = STR_NULL; char tbuf[BUF_SIZE]; int tbuf_len = 0; struct via_body *via; hdr_field_t *hfk = NULL; obuf = (str*)data; memset(&msg, 0, sizeof(sip_msg_t)); msg.buf = obuf->s; msg.len = obuf->len; if(dlg_cseq_prepare_msg(&msg)!=0) { goto done; } if(msg.first_line.type==SIP_REPLY) { /* nothing to do for outgoing replies */ goto done; } LM_DBG("traking cseq updates\n"); via = (struct via_body*)msg.h_via1->parsed; if(via->branch==NULL || via->branch->value.len<=0) { LM_DBG("no branch parameter in top via\n"); goto done; } direction = DLG_DIR_NONE; dlg = dlg_lookup_msg_dialog(&msg, &direction); if(dlg == NULL) { LM_DBG("no dialog for this request\n"); goto done; } /* supported only for downstrem direction */ if(direction != DLG_DIR_DOWNSTREAM) { LM_DBG("request not going downstream (%u)\n", direction); goto done; } /* check if transaction is marked for a new increment */ if(get_cseq(&msg)->method_id!=METHOD_ACK) { parse_headers(&msg, HDR_EOH_F, 0); hfk = sr_hdr_get_z(&msg, "P-K-Auth-CSeq"); if(hfk!=NULL) { LM_DBG("uac auth request - cseq inc needed\n"); nval = hfk->body; trim(&nval); } else { LM_DBG("uac auth request - cseq inc not needed\n"); goto done; } } if(nval.len<=0) { goto done; } if(msg.len + 3 + 2*nval.len>=BUF_SIZE) { LM_ERR("new messages is too big\n"); goto done; } LM_DBG("updating cseq to: %.*s\n", nval.len, nval.s); /* new cseq value */ dlg->iflags |= DLG_IFLAG_CSEQ_DIFF; if(via->branch->value.s<get_cseq(&msg)->number.s) { /* Via is before CSeq */ /* copy first part till after via branch */ tbuf_len = via->branch->value.s + via->branch->value.len - msg.buf; memcpy(tbuf, msg.buf, tbuf_len); /* complete via branch */ tbuf[tbuf_len++] = '.'; tbuf[tbuf_len++] = 'c'; tbuf[tbuf_len++] = 's'; memcpy(tbuf+tbuf_len, get_cseq(&msg)->number.s, get_cseq(&msg)->number.len); tbuf_len += get_cseq(&msg)->number.len; /* copy till beginning of cseq number */ memcpy(tbuf+tbuf_len, via->branch->value.s + via->branch->value.len, get_cseq(&msg)->number.s - via->branch->value.s - via->branch->value.len); tbuf_len += get_cseq(&msg)->number.s - via->branch->value.s - via->branch->value.len; /* add new value */ memcpy(tbuf+tbuf_len, nval.s, nval.len); tbuf_len += nval.len; if(hfk && hfk->name.s > get_cseq(&msg)->number.s) { /* copy from after cseq number to the beginning of hfk */ memcpy(tbuf+tbuf_len, get_cseq(&msg)->number.s+get_cseq(&msg)->number.len, hfk->name.s - get_cseq(&msg)->number.s - get_cseq(&msg)->number.len); tbuf_len += hfk->name.s - get_cseq(&msg)->number.s - get_cseq(&msg)->number.len; /* copy from after hfk to the end of sip message */ memcpy(tbuf+tbuf_len, hfk->name.s + hfk->len, msg.buf + msg.len - hfk->name.s - hfk->len); tbuf_len += msg.buf + msg.len - hfk->name.s - hfk->len; } else { /* copy from after cseq number to the end of sip message */ memcpy(tbuf+tbuf_len, get_cseq(&msg)->number.s+get_cseq(&msg)->number.len, msg.buf + msg.len - get_cseq(&msg)->number.s - get_cseq(&msg)->number.len); tbuf_len += msg.buf+msg.len - get_cseq(&msg)->number.s - get_cseq(&msg)->number.len; } } else { /* CSeq is before Via */ /* copy till beginning of cseq number */ tbuf_len = get_cseq(&msg)->number.s - msg.buf; memcpy(tbuf, msg.buf, tbuf_len); /* add new value */ memcpy(tbuf+tbuf_len, nval.s, nval.len); tbuf_len += nval.len; /* copy from after cseq number to the after via branch */ memcpy(tbuf+tbuf_len, get_cseq(&msg)->number.s+get_cseq(&msg)->number.len, via->branch->value.s + via->branch->value.len - get_cseq(&msg)->number.s - get_cseq(&msg)->number.len); tbuf_len += via->branch->value.s + via->branch->value.len - get_cseq(&msg)->number.s - get_cseq(&msg)->number.len; /* complete via branch */ tbuf[tbuf_len++] = '.'; tbuf[tbuf_len++] = 'c'; tbuf[tbuf_len++] = 's'; memcpy(tbuf+tbuf_len, get_cseq(&msg)->number.s, get_cseq(&msg)->number.len); tbuf_len += get_cseq(&msg)->number.len; if(hfk && hfk->name.s > get_cseq(&msg)->number.s) { /* copy from after via to the beginning of hfk */ memcpy(tbuf+tbuf_len, via->branch->value.s + via->branch->value.len, hfk->name.s - via->branch->value.s - via->branch->value.len); tbuf_len += hfk->name.s - via->branch->value.s - via->branch->value.len; /* copy from after hfk to the end of sip message */ memcpy(tbuf+tbuf_len, hfk->name.s + hfk->len, msg.buf + msg.len - hfk->name.s - hfk->len); tbuf_len += msg.buf + msg.len - hfk->name.s - hfk->len; } else { /* copy from after via to the end of sip message */ memcpy(tbuf+tbuf_len, via->branch->value.s + via->branch->value.len, msg.buf + msg.len - via->branch->value.s - via->branch->value.len); tbuf_len += msg.buf+msg.len - via->branch->value.s - via->branch->value.len; } } /* replace old msg content */ obuf->s = pkg_malloc((tbuf_len+1)*sizeof(char)); if(obuf->s==NULL) { LM_ERR("not enough memory for new message\n"); goto done; } memcpy(obuf->s, tbuf, tbuf_len); obuf->s[tbuf_len] = 0; obuf->len = tbuf_len; done: if(dlg!=NULL) dlg_release(dlg); free_sip_msg(&msg); return 0; }
/*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,0}; 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 = dlg_lookup(h_entry, h_id); // lookup_dlg has incremented the reference count if(dlg){ if(dlg_bye_all(dlg,(mi_extra_hdrs.len>0)?&mi_extra_hdrs:NULL)<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; } dlg_release(dlg); 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); }
/*! * \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) { dlg_cell_t *dlg = NULL; dlg_iuid_t *iuid = NULL; int new_state, old_state, unref, event; str tag; sip_msg_t *req = param->req; sip_msg_t *rpl = param->rpl; if (shutdown_done) return; iuid = (dlg_iuid_t*)(*param->param); dlg = dlg_get_by_iuid(iuid); if(dlg==0) return; unref = 0; 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; } if (type==TMCB_RESPONSE_FWDED) { /* The state does not change, but the msg is mutable in this callback*/ run_dlg_callbacks(DLGCB_RESPONSE_FWDED, dlg, req, rpl, DLG_DIR_UPSTREAM, 0); goto done; } 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; next_state_dlg( dlg, event, &old_state, &new_state, &unref); dlg_run_event_route(dlg, (rpl==FAKED_REPLY)?NULL:rpl, old_state, new_state); if (new_state==DLG_STATE_EARLY) { run_dlg_callbacks(DLGCB_EARLY, dlg, req, rpl, DLG_DIR_UPSTREAM, 0); if (old_state!=DLG_STATE_EARLY) if_update_stat(dlg_enable_stats, early_dlgs, 1); goto done; } if (new_state==DLG_STATE_CONFIRMED_NA && old_state!=DLG_STATE_CONFIRMED_NA && old_state!=DLG_STATE_CONFIRMED ) { LM_DBG("dialog %p confirmed (ACK pending)\n",dlg); if (rpl != FAKED_REPLY) { /* get to tag*/ if ( !rpl->to && ((parse_headers(rpl, HDR_TO_F,0)<0) || !rpl->to) ) { LM_ERR("bad reply or missing TO hdr :-/\n"); tag.s = 0; tag.len = 0; } else { tag = get_to(rpl)->tag_value; if (tag.s==0 || tag.len==0) { LM_ERR("missing TAG param in TO hdr :-/\n"); tag.s = 0; tag.len = 0; } } /* save callee's tag, cseq, contact and record route*/ if (populate_leg_info( dlg, rpl, t, DLG_CALLEE_LEG, &tag) !=0) { LM_ERR("could not add further info to the dialog\n"); } } else { LM_ERR("Faked reply!\n"); } /* 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 ( 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' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, event, old_state, new_state, 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 { /* dialog pointer inserted in timer list */ dlg_ref(dlg, 1); } /* dialog confirmed (ACK pending) */ run_dlg_callbacks( DLGCB_CONFIRMED_NA, dlg, req, rpl, DLG_DIR_UPSTREAM, 0); if (old_state==DLG_STATE_EARLY) if_update_stat(dlg_enable_stats, early_dlgs, -1); if (unref) dlg_unref(dlg, unref); if_update_stat(dlg_enable_stats, active_dlgs, 1); goto done; } 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); if(dlg_wait_ack==1) dlg_set_tm_waitack(t, dlg); /* do unref */ if (unref) dlg_unref(dlg, unref); if (old_state==DLG_STATE_EARLY) if_update_stat(dlg_enable_stats, early_dlgs, -1); if_update_stat(dlg_enable_stats, failed_dlgs, 1); goto done; } if (unref) dlg_unref(dlg, unref); done: /* unref due to dlg_get_by_iuid() */ dlg_release(dlg); return; }
/*! * \brief Set a dialog profile * \param msg SIP message * \param value value * \param profile dialog profile table * \return 0 on success, -1 on failure */ int set_dlg_profile(struct sip_msg *msg, str *value, struct dlg_profile_table *profile) { dlg_cell_t *dlg = NULL; dlg_profile_link_t *linker; /* get current dialog */ 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", REQUEST_ROUTE); return -1; } /* build new linker */ linker = (struct dlg_profile_link*)shm_malloc( sizeof(struct dlg_profile_link) + (profile->has_value?value->len:0) ); if (linker==NULL) { LM_ERR("no more shm memory\n"); goto error; } memset(linker, 0, sizeof(struct dlg_profile_link)); /* set backpointers to profile and linker (itself) */ linker->profile = profile; linker->hash_linker.linker = linker; /* set the value */ if (profile->has_value) { linker->hash_linker.value.s = (char*)(linker+1); 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; }
/*! * \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; }
int dlg_cseq_update(sip_msg_t *msg) { dlg_cell_t *dlg = NULL; unsigned int direction; unsigned int ninc = 0; unsigned int vinc = 0; str nval; str *pval; if(dlg_cseq_prepare_msg(msg)!=0) { goto error; } if(msg->first_line.type==SIP_REPLY) { /* nothing to do for outgoing replies */ goto done; } LM_DBG("initiating cseq updates\n"); direction = DLG_DIR_NONE; dlg = dlg_lookup_msg_dialog(msg, &direction); if(dlg == NULL) { LM_DBG("no dialog for this request\n"); goto done; } /* supported only for downstrem direction */ if(direction != DLG_DIR_DOWNSTREAM) { LM_DBG("request not going downstream (%u)\n", direction); goto done; } ninc = 1; /* take the increment value from dialog */ if((dlg->iflags&DLG_IFLAG_CSEQ_DIFF)==DLG_IFLAG_CSEQ_DIFF) { /* get dialog variable holding cseq diff */ pval = get_dlg_variable(dlg, &_dlg_cseq_diff_var_name); if(pval==NULL || pval->s==NULL || pval->len<=0) { LM_DBG("dialog marked with cseq diff but no variable set yet\n"); goto done; } if(str2int(pval, &vinc)<0) { LM_ERR("invalid dlg cseq diff var value: %.*s\n", pval->len, pval->s); goto done; } } vinc += ninc; if(vinc==0) { LM_DBG("nothing to increment\n"); goto done; } nval.s = int2str(vinc, &nval.len); if(set_dlg_variable(dlg, &_dlg_cseq_diff_var_name, &nval) <0) { LM_ERR("failed to set the dlg cseq diff var\n"); goto done; } str2int(&get_cseq(msg)->number, &ninc); vinc += ninc; nval.s = int2str(vinc, &nval.len); trim(&nval); LM_DBG("adding auth cseq header value: %.*s\n", nval.len, nval.s); parse_headers(msg, HDR_EOH_F, 0); sr_hdr_add_zs(msg, "P-K-Auth-CSeq", &nval); done: if(dlg!=NULL) dlg_release(dlg); return 0; error: if(dlg!=NULL) dlg_release(dlg); return -1; }
int pv_set_dlg_variable(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val) { 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 dialog for current message */ dlg=dlg_get_msg_dialog( msg); if (dlg) { /* Lock the dialog */ dlg_lock(d_table, &(d_table->entries[dlg->h_entry])); } else { /* Verify the local list */ get_local_varlist_pointer(msg, 0); } if (val==NULL || val->flags&(PV_VAL_NONE|PV_VAL_NULL|PV_VAL_EMPTY)) { /* if NULL, remove the value */ 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])); dlg_release(dlg); } return ret; } } else { /* if value, must be string */ if ( !(val->flags&PV_VAL_STR)) { LM_ERR("non-string values are not supported\n"); /* unlock dialog */ if (dlg) dlg_unlock(d_table, &(d_table->entries[dlg->h_entry])); goto error; } 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])); goto error; } } /* unlock dialog */ if (dlg) { 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); } print_lists(dlg); dlg_release(dlg); return 0; error: dlg_release(dlg); return -1; }
int pv_get_dlg(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { dlg_cell_t *dlg = NULL; int res_type = 0; str sv = { 0 }; unsigned int ui = 0; if(param==NULL) return -1; if(_dlg_ctx.iuid.h_id==0) { /* Retrieve the dialog for current message */ dlg=dlg_get_msg_dialog(msg); } else { /* Retrieve the dialog for current context */ dlg=dlg_get_by_iuid(&_dlg_ctx.iuid); } if(dlg == NULL) return pv_get_null(msg, param, res); switch(param->pvn.u.isname.name.n) { case 1: res_type = 1; ui = (unsigned int)dlg->h_id; break; case 2: res_type = 1; ui = (unsigned int)dlg->state; break; case 3: if(dlg->route_set[DLG_CALLEE_LEG].s==NULL || dlg->route_set[DLG_CALLEE_LEG].len<=0) goto done; sv.s = pv_get_buffer(); sv.len = dlg->route_set[DLG_CALLEE_LEG].len; if(pv_get_buffer_size()<sv.len) goto done; res_type = 2; strncpy(sv.s, dlg->route_set[DLG_CALLEE_LEG].s, sv.len); sv.s[sv.len] = '\0'; break; case 4: res_type = 1; ui = (unsigned int)dlg->dflags; break; case 5: res_type = 1; ui = (unsigned int)dlg->sflags; break; case 6: if(dlg->callid.s==NULL || dlg->callid.len<=0) goto done; sv.s = pv_get_buffer(); sv.len = dlg->callid.len; if(pv_get_buffer_size()<sv.len) goto done; res_type = 2; strncpy(sv.s, dlg->callid.s, sv.len); sv.s[sv.len] = '\0'; break; case 7: if(dlg->to_uri.s==NULL || dlg->to_uri.len<=0) goto done; sv.s = pv_get_buffer(); sv.len = dlg->to_uri.len; if(pv_get_buffer_size()<sv.len) goto done; res_type = 2; strncpy(sv.s, dlg->to_uri.s, sv.len); sv.s[sv.len] = '\0'; break; case 8: if(dlg->tag[DLG_CALLEE_LEG].s==NULL || dlg->tag[DLG_CALLEE_LEG].len<=0) goto done; sv.s = pv_get_buffer(); sv.len = dlg->tag[DLG_CALLEE_LEG].len; if(pv_get_buffer_size()<sv.len) goto done; res_type = 2; strncpy(sv.s, dlg->tag[DLG_CALLEE_LEG].s, sv.len); sv.s[sv.len] = '\0'; break; case 9: res_type = 1; ui = (unsigned int)dlg->toroute; break; case 10: if(dlg->cseq[DLG_CALLEE_LEG].s==NULL || dlg->cseq[DLG_CALLEE_LEG].len<=0) goto done; sv.s = pv_get_buffer(); sv.len = dlg->cseq[DLG_CALLEE_LEG].len; if(pv_get_buffer_size()<sv.len) goto done; res_type = 2; strncpy(sv.s, dlg->cseq[DLG_CALLEE_LEG].s, sv.len); sv.s[sv.len] = '\0'; break; case 11: if(dlg->route_set[DLG_CALLER_LEG].s==NULL || dlg->route_set[DLG_CALLER_LEG].len<=0) goto done; sv.s = pv_get_buffer(); sv.len = dlg->route_set[DLG_CALLER_LEG].len; if(pv_get_buffer_size()<sv.len) goto done; res_type = 2; strncpy(sv.s, dlg->route_set[DLG_CALLER_LEG].s, sv.len); sv.s[sv.len] = '\0'; break; case 12: if(dlg->from_uri.s==NULL || dlg->from_uri.len<=0) goto done; sv.s = pv_get_buffer(); sv.len = dlg->from_uri.len; if(pv_get_buffer_size()<sv.len) goto done; res_type = 2; strncpy(sv.s, dlg->from_uri.s, sv.len); sv.s[sv.len] = '\0'; break; case 13: if(dlg->tag[DLG_CALLER_LEG].s==NULL || dlg->tag[DLG_CALLER_LEG].len<=0) goto done; sv.s = pv_get_buffer(); sv.len = dlg->tag[DLG_CALLER_LEG].len; if(pv_get_buffer_size()<sv.len) goto done; res_type = 2; strncpy(sv.s, dlg->tag[DLG_CALLER_LEG].s, sv.len); sv.s[sv.len] = '\0'; break; case 14: res_type = 1; ui = (unsigned int)dlg->lifetime; break; case 15: res_type = 1; ui = (unsigned int)dlg->start_ts; break; case 16: if(dlg->cseq[DLG_CALLER_LEG].s==NULL || dlg->cseq[DLG_CALLER_LEG].len<=0) goto done; sv.s = pv_get_buffer(); sv.len = dlg->cseq[DLG_CALLER_LEG].len; if(pv_get_buffer_size()<sv.len) goto done; res_type = 2; strncpy(sv.s, dlg->cseq[DLG_CALLER_LEG].s, sv.len); sv.s[sv.len] = '\0'; break; case 17: if(dlg->contact[DLG_CALLEE_LEG].s==NULL || dlg->contact[DLG_CALLEE_LEG].len<=0) goto done; sv.s = pv_get_buffer(); sv.len = dlg->contact[DLG_CALLEE_LEG].len; if(pv_get_buffer_size()<sv.len) goto done; res_type = 2; strncpy(sv.s, dlg->contact[DLG_CALLEE_LEG].s, sv.len); sv.s[sv.len] = '\0'; break; case 18: if(dlg->bind_addr[DLG_CALLEE_LEG]==NULL) goto done; sv.s = pv_get_buffer(); sv.len = dlg->bind_addr[DLG_CALLEE_LEG]->sock_str.len; if(pv_get_buffer_size()<sv.len) goto done; res_type = 2; strncpy(sv.s, dlg->bind_addr[DLG_CALLEE_LEG]->sock_str.s, sv.len); sv.s[sv.len] = '\0'; break; case 19: if(dlg->contact[DLG_CALLER_LEG].s==NULL || dlg->contact[DLG_CALLER_LEG].len<=0) goto done; sv.s = pv_get_buffer(); sv.len = dlg->contact[DLG_CALLER_LEG].len; if(pv_get_buffer_size()<sv.len) goto done; res_type = 2; strncpy(sv.s, dlg->contact[DLG_CALLER_LEG].s, sv.len); sv.s[sv.len] = '\0'; break; case 20: if(dlg->bind_addr[DLG_CALLER_LEG]==NULL) goto done; sv.s = pv_get_buffer(); sv.len = dlg->bind_addr[DLG_CALLER_LEG]->sock_str.len; if(pv_get_buffer_size()<sv.len) goto done; res_type = 2; strncpy(sv.s, dlg->bind_addr[DLG_CALLER_LEG]->sock_str.s, sv.len); sv.s[sv.len] = '\0'; break; case 21: res_type = 1; ui = (unsigned int)dlg->h_entry; break; default: res_type = 1; ui = (unsigned int)dlg->ref; } done: dlg_release(dlg); switch(res_type) { case 1: return pv_get_uintval(msg, param, res, ui); case 2: return pv_get_strval(msg, param, res, &sv); default: return pv_get_null(msg, param, res); } }