int set_dlg_variable(struct dlg_cell *dlg, str *key, str *val) { int ret = -1; if( !dlg || !key || key->len > strlen(key->s) || (val && val->len > strlen(val->s))) { LM_ERR("BUG - bad parameters\n"); return -1; } dlg_lock(d_table, &(d_table->entries[dlg->h_entry])); ret = set_dlg_variable_unsafe(dlg, key, val); if(ret!= 0) goto done; 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); return 0; done: dlg_unlock(d_table, &(d_table->entries[dlg->h_entry])); return ret; }
int set_dlg_variable(struct dlg_cell *dlg, str *key, str *val) { if( !dlg || !key || key->len > strlen(key->s) || (val && val->len > strlen(val->s))) { LM_ERR("BUG - bad parameters\n"); return -1; } dlg_lock(d_table, &(d_table->entries[dlg->h_entry])); if( !val) { if (set_dlg_variable_unsafe(dlg, key, NULL)!=0) { LM_ERR("failed to delete dialog variable <%.*s>\n", key->len,key->s); goto error; } } else { if (set_dlg_variable_unsafe(dlg, key, val)!=0) { LM_ERR("failed to store dialog values <%.*s>\n",key->len,key->s); goto error; } } 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); return 0; error: dlg_unlock(d_table, &(d_table->entries[dlg->h_entry])); 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; }
/*! * \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 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; }
/*! * \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) { if ((dlg->dflags & DLG_FLAG_INSERTED) == 0) { dlg->dflags |= DLG_FLAG_NEW; } else { dlg->dflags |= DLG_FLAG_CHANGED; } if (dlg_db_mode == DB_MODE_REALTIME) update_dialog_dbinfo(dlg); 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*/ if ((dlg->dflags & DLG_FLAG_INSERTED) == 0) { dlg->dflags |= DLG_FLAG_NEW; } else { dlg->dflags |= DLG_FLAG_CHANGED; } if (dlg_db_mode == DB_MODE_REALTIME) update_dialog_dbinfo(dlg); if (0 != insert_dlg_timer(&dlg->tl, dlg->lifetime)) { LM_CRIT("Unable to insert dlg %p [%u:%u] on event %d [%d->%d] " "with clid '%.*s' and tags '%.*s' \n", dlg, dlg->h_entry, dlg->h_id, event, old_state, new_state, dlg->callid.len, dlg->callid.s, dlg->from_tag.len, dlg->from_tag.s); } else { ref_dlg(dlg, 1); } 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(dlg_db_mode==DB_MODE_REALTIME && (dlg->dflags&DLG_FLAG_CHANGED)) { update_dialog_dbinfo(dlg); } 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; if(dlg_db_mode == DB_MODE_REALTIME) update_dialog_dbinfo(dlg); } return; }
void dlg_onroute(struct sip_msg* req, str *route_params, void *param) { struct dlg_cell *dlg; str val = {0,0}; str callid; str ftag; str ttag; int h_entry; int h_id; int new_state; int old_state; int unref; int event; int timeout; unsigned int update_val; unsigned int dir,dst_leg,src_leg; int ret = 0,ok = 1; struct dlg_entry *d_entry; str *msg_cseq; char *final_cseq; /* as this callback is triggered from loose_route, which can be accidentaly called more than once from script, we need to be sure we do this only once !*/ if (current_dlg_pointer) 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; dst_leg = -1; /* From RR callback, param will be NULL * From match_dialog, param might have a value, if we * are in the topology hiding case & we were able to extract the * DID from the R-URI */ if (param) val = *((str *)param); if ( seq_match_mode!=SEQ_MATCH_NO_ID ) { if( val.s == NULL && 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_DBG("unable to find dialog for %.*s " "with route param '%.*s'\n", req->first_line.u.request.method.len, req->first_line.u.request.method.s, val.len,val.s); } else { /* lookup_dlg has incremented the ref count by 1 */ if (pre_match_parse( req, &callid, &ftag, &ttag)<0) { unref_dlg(dlg, 1); return; } if (match_dialog(dlg,&callid,&ftag,&ttag,&dir, &dst_leg )==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->legs[DLG_CALLER_LEG].tag.len, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[callee_idx(dlg)].tag.len, ZSW(dlg->legs[callee_idx(dlg)].tag.s), dlg->legs[callee_idx(dlg)].tag.len); unref_dlg(dlg, 1); dlg = NULL; } } if (dlg==NULL && seq_match_mode==SEQ_MATCH_STRICT_ID ) return; } } if (dlg==0) { if (pre_match_parse( req, &callid, &ftag, &ttag)<0) return; /* TODO - try to use the RR dir detection to speed up here the * search -bogdan */ dlg = get_dlg(&callid, &ftag, &ttag, &dir, &dst_leg); if (!dlg){ LM_DBG("Callid '%.*s' not found\n", req->callid->body.len, req->callid->body.s); return; } } /* run state machine */ switch ( req->first_line.u.request.method_value ) { case METHOD_PRACK: event = DLG_EVENT_REQPRACK; break; case METHOD_ACK: event = DLG_EVENT_REQACK; break; case METHOD_BYE: event = DLG_EVENT_REQBYE; break; default: event = DLG_EVENT_REQ; } /* save last_dst_leg before running state machine * helpful for logging various bogus cases according to the RFC */ last_dst_leg = dst_leg; next_state_dlg( dlg, event, &old_state, &new_state, &unref); /* set current dialog - it will keep a ref! */ set_current_dialog(dlg); log_bogus_dst_leg(dlg); d_entry = &(d_table->entries[dlg->h_entry]); if(dlg->flags & DLG_FLAG_TOPHIDING) { dlg_th_onroute(dlg, req, dir); } /* run actions for the transition */ if (event==DLG_EVENT_REQBYE && new_state==DLG_STATE_DELETED && old_state!=DLG_STATE_DELETED) { /*destroy linkers */ destroy_linkers(dlg->profile_links); dlg->profile_links = NULL; if (!dlg->terminate_reason.s) { if (last_dst_leg == 0) init_dlg_term_reason(dlg,"Upstream BYE",sizeof("Upstream BYE")-1); else init_dlg_term_reason(dlg,"Downstream BYE",sizeof("Downstream BYE")-1); } LM_DBG("BYE successfully processed - dst_leg = %d\n",last_dst_leg); if (dlg->flags & DLG_FLAG_PING_CALLER || dlg->flags & DLG_FLAG_PING_CALLEE) { dlg_lock (d_table,d_entry); if (dlg->legs[dst_leg].last_gen_cseq) { update_val = ++(dlg->legs[dst_leg].last_gen_cseq); dlg_unlock (d_table,d_entry); if (update_msg_cseq(req,0,update_val) != 0) LM_ERR("failed to update BYE msg cseq\n"); msg_cseq = &((struct cseq_body *)req->cseq->parsed)->number; final_cseq = shm_malloc(msg_cseq->len + 1); if (final_cseq == 0) { LM_ERR("no more shm mem\n"); goto after_unlock5; } memcpy(final_cseq,msg_cseq->s,msg_cseq->len); final_cseq[msg_cseq->len] = 0; if ( d_tmb.register_tmcb( req, 0, TMCB_RESPONSE_FWDED, fix_final_cseq, (void*)final_cseq, 0)<0 ) { LM_ERR("failed to register TMCB (2)\n"); } } else dlg_unlock (d_table,d_entry); } after_unlock5: /* remove from timer */ 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++; } /* dialog terminated (BYE) */ run_dlg_callbacks( DLGCB_TERMINATED, dlg, req, dir, 0); /* delete the dialog from DB */ if (should_remove_dlg_db()) remove_dialog_from_db(dlg); /* destroy dialog */ unref_dlg(dlg, unref); if_update_stat( dlg_enable_stats, active_dlgs, -1); return; } if ( (event==DLG_EVENT_REQ || event==DLG_EVENT_REQACK) && new_state==DLG_STATE_CONFIRMED) { LM_DBG("sequential request successfully processed (dst_leg=%d)\n", dst_leg); /* within dialog request */ run_dlg_callbacks( DLGCB_REQ_WITHIN, dlg, req, dir, 0); timeout = get_dlg_timeout_update(req); /* update timer during sequential request? */ if (timeout != 0) { dlg->lifetime = timeout; if (update_dlg_timer( &dlg->tl, dlg->lifetime )==-1) LM_ERR("failed to update dialog lifetime\n"); } if ( event!=DLG_EVENT_REQACK ) { if (dst_leg==-1 || switch_cseqs(dlg, dst_leg) != 0 || update_cseqs(dlg,req,dst_leg,0)) { ok = 0; LM_ERR("cseqs update failed on leg=%d\n",dst_leg); } if (req->first_line.u.request.method_value == METHOD_INVITE) { if (dst_leg == DLG_CALLER_LEG) src_leg = callee_idx(dlg); else src_leg = DLG_CALLER_LEG; if (update_cseqs(dlg,req,src_leg,1) != 0) { ok=0; LM_ERR("failed to update inv cseq on leg %d\n",src_leg); } } if (dlg->flags & DLG_FLAG_PING_CALLER || dlg->flags & DLG_FLAG_PING_CALLEE) { dlg_lock (d_table, d_entry); if (dlg->legs[dst_leg].last_gen_cseq) { update_val = ++(dlg->legs[dst_leg].last_gen_cseq); dlg_unlock( d_table, d_entry ); if (update_msg_cseq(req,0,update_val) != 0) { LM_ERR("failed to update sequential request msg cseq\n"); ok = 0; } } else dlg_unlock( d_table, d_entry ); } if (ok) { dlg->flags |= DLG_FLAG_CHANGED; if ( dlg_db_mode==DB_MODE_REALTIME ) update_dialog_dbinfo(dlg); if (replication_dests) replicate_dialog_updated(dlg); } } else { if (dlg->flags & DLG_FLAG_PING_CALLER || dlg->flags & DLG_FLAG_PING_CALLEE) { dlg_lock (d_table, d_entry); if (dlg->legs[dst_leg].last_gen_cseq) { update_val = dlg->legs[dst_leg].last_gen_cseq; dlg_unlock( d_table, d_entry ); if (update_msg_cseq(req,0,update_val) != 0) { LM_ERR("failed to update ACK msg cseq\n"); } } else dlg_unlock( d_table, d_entry ); } } if ( event!=DLG_EVENT_REQACK) { /* register callback for the replies of this request */ if (dlg->flags & DLG_FLAG_PING_CALLER || dlg->flags & DLG_FLAG_PING_CALLEE) { dlg_lock( d_table, d_entry); if (dlg->legs[dst_leg].last_gen_cseq) { /* ref the dialog as registered into the transaction callback. * unref will be done when the callback will be destroyed */ ref_dlg_unsafe( dlg, 1); dlg_unlock( d_table,d_entry); if(parse_headers(req, HDR_CSEQ_F, 0) <0 ) { LM_ERR("failed to parse cseq header \n"); unref_dlg(dlg,1); goto early_check; } msg_cseq = &((struct cseq_body *)req->cseq->parsed)->number; dlg_cseq_wrapper *wrap = shm_malloc(sizeof(dlg_cseq_wrapper) + msg_cseq->len); if (wrap == 0){ LM_ERR("No more shm mem\n"); unref_dlg(dlg, 1); goto early_check; } wrap->dlg = dlg; wrap->cseq.s = (char *)(wrap + 1); wrap->cseq.len = msg_cseq->len; memcpy(wrap->cseq.s,msg_cseq->s,msg_cseq->len); if ( d_tmb.register_tmcb( req, 0, TMCB_RESPONSE_FWDED, (dir==DLG_DIR_UPSTREAM)?dlg_seq_down_onreply_mod_cseq:dlg_seq_up_onreply_mod_cseq, (void*)wrap, unreference_dialog_cseq)<0 ) { LM_ERR("failed to register TMCB (2)\n"); unref_dlg( dlg , 1); shm_free(wrap); } } else { /* dialog is in ping timer list * but no pings have been generated yet */ dlg_unlock ( d_table, d_entry ); goto regular_indlg_req; } } else { regular_indlg_req: if (dlg->cbs.types & DLGCB_RESPONSE_WITHIN) { ref_dlg( dlg , 1); 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); } } } } } early_check: if ( (event==DLG_EVENT_REQPRACK || event == DLG_EVENT_REQ) && new_state==DLG_STATE_EARLY) { /* within dialog request */ run_dlg_callbacks( DLGCB_REQ_WITHIN, dlg, req, dir, 0); LM_DBG("EARLY event %d successfully processed (dst_leg=%d)\n",event,dst_leg); if (dst_leg==-1 || switch_cseqs(dlg, dst_leg) != 0 || update_cseqs(dlg,req,dst_leg,0)) LM_ERR("cseqs update failed on leg=%d\n",dst_leg); } if(new_state==DLG_STATE_CONFIRMED && old_state==DLG_STATE_CONFIRMED_NA){ dlg->flags |= DLG_FLAG_CHANGED; if(dlg_db_mode == DB_MODE_REALTIME) update_dialog_dbinfo(dlg); if (replication_dests) replicate_dialog_updated(dlg); } return; }
static void dlg_onreply(struct cell* t, int type, struct tmcb_params *param) { struct sip_msg *rpl,*req; struct dlg_cell *dlg; int new_state; int old_state; int unref; int event; str mangled_from = {0,0}; str mangled_to = {0,0}; str *req_out_buff; dlg = (struct dlg_cell *)(*param->param); if (shutdown_done || dlg==0) return; rpl = param->rpl; req = param->req; if (type==TMCB_RESPONSE_FWDED) { /* this callback is under transaction lock (by TM), so it is save to operate at write level, but we need to take care on write-read conflicts -bogdan */ if (rpl!=FAKED_REPLY) { if (req->msg_flags & (FL_USE_UAC_FROM | FL_USE_UAC_TO ) ) { req_out_buff = &t->uac[d_tmb.get_branch_index()].request.buffer; if (extract_ftc_hdrs(req_out_buff->s,req_out_buff->len, (req->msg_flags & FL_USE_UAC_FROM )?&mangled_from:0, (req->msg_flags & FL_USE_UAC_TO )?&mangled_to:0,0,0) != 0) { LM_ERR("failed to extract mangled FROM and TO hdrs\n"); mangled_from.len = 0; mangled_from.s = NULL; mangled_to.len = 0; mangled_to.s = NULL; } else { if ((req->msg_flags & FL_USE_UAC_FROM) && (mangled_from.len == 0 || mangled_from.s == NULL)) LM_CRIT("extract_ftc_hdrs ok but no from extracted : [%.*s]\n",req_out_buff->len,req_out_buff->s); if ((req->msg_flags & FL_USE_UAC_TO) && (mangled_to.len == 0 || mangled_to.s == NULL)) LM_CRIT("extract_ftc_hdrs ok but no to extracted : [%.*s]\n",req_out_buff->len,req_out_buff->s); } } push_reply_in_dialog( rpl, t, dlg,&mangled_from,&mangled_to); if((dlg->flags & DLG_FLAG_TOPHIDING) && dlg_th_onreply(dlg, rpl,req, 1, DLG_DIR_UPSTREAM) < 0) LM_ERR("Failed to transform the reply for topology hiding\n"); } else { LM_DBG("dialog replied from script - cannot get callee info\n"); } /* The state does not change, but the msg is mutable in this callback*/ run_dlg_callbacks(DLGCB_RESPONSE_FWDED, dlg, rpl, DLG_DIR_UPSTREAM, 0); return; } if (type==TMCB_TRANS_CANCELLED) { /* only if we did force match the Cancel to the * dialog before ( from the script ) */ if (current_dlg_pointer == NULL) { /* reference and attached to script */ ref_dlg(dlg,1); current_dlg_pointer = t->dialog_ctx; } return; } if (type==TMCB_TRANS_DELETED) 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); if (new_state==DLG_STATE_EARLY && old_state!=DLG_STATE_EARLY) { run_dlg_callbacks(DLGCB_EARLY, dlg, rpl, DLG_DIR_UPSTREAM, 0); if_update_stat(dlg_enable_stats, early_dlgs, 1); return; } if (new_state==DLG_STATE_CONFIRMED_NA && old_state!=DLG_STATE_CONFIRMED_NA && old_state!=DLG_STATE_CONFIRMED ) { LM_DBG("dialog %p confirmed\n",dlg); /* set start time */ dlg->start_ts = (unsigned int)(time(0)); 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->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 { /* reference dialog as kept in timer list */ ref_dlg(dlg,1); } if (dlg->flags & DLG_FLAG_PING_CALLER || dlg->flags & DLG_FLAG_PING_CALLEE) { if (0 != insert_ping_timer( dlg)) { LM_CRIT("Unable to insert ping 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->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 { /* reference dialog as kept in ping timer list */ ref_dlg(dlg,1); } } /* save the settings to the database, * if realtime saving mode configured- save dialog now * else: the next time the timer will fire the update*/ dlg->flags |= DLG_FLAG_NEW; if ( dlg_db_mode==DB_MODE_REALTIME ) update_dialog_dbinfo(dlg); /* dialog confirmed */ run_dlg_callbacks( DLGCB_CONFIRMED, dlg, rpl, DLG_DIR_UPSTREAM, 0); if (replication_dests) replicate_dialog_created(dlg); if (old_state==DLG_STATE_EARLY) if_update_stat(dlg_enable_stats, early_dlgs, -1); if_update_stat(dlg_enable_stats, active_dlgs, 1); return; } if ( old_state!=DLG_STATE_DELETED && new_state==DLG_STATE_DELETED ) { LM_DBG("dialog %p failed (negative reply)\n", dlg); /*destroy linkers */ destroy_linkers(dlg->profile_links); dlg->profile_links = NULL; /* dialog setup not completed (3456XX) */ run_dlg_callbacks( DLGCB_FAILED, dlg, rpl, DLG_DIR_UPSTREAM, 0); /* do unref */ if (unref) unref_dlg(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); return; } /* in any other case, check if the dialog state machine requests to unref the dialog */ if (unref) unref_dlg(dlg,unref); return; }