/*! * \brief Execute callback for the BYE request and register callback for the BYE reply * \param req request message * \param dlg corresponding dialog * \param dir message direction */ static void dlg_terminated(struct sip_msg* req, struct dlg_cell* dlg, unsigned int dir) { if (!req) { LM_ERR("request is empty!"); return; } if (!dlg) { LM_ERR("dialog is empty!"); return; } /* dialog terminated (BYE) */ run_dlg_callbacks(DLGCB_TERMINATED, dlg, req, NULL, dir, 0); /* register callback for the coresponding reply */ LM_DBG("Registering tmcb1\n"); if (d_tmb.register_tmcb(req, 0, TMCB_RESPONSE_OUT, dlg_terminated_confirmed, (void*) dlg, 0) <= 0) { LM_ERR("cannot register response callback for BYE request\n"); return; } }
/*! * \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); }
/* this is only called from destroy_dlg, where the cell's entry * lock is acquired */ int remove_dialog_from_db(struct dlg_cell * cell) { static db_ps_t my_ps = NULL; db_val_t values[2]; db_key_t match_keys[2] = { &h_entry_column, &h_id_column}; /*if the dialog hasn 't been yet inserted in the database*/ LM_DBG("trying to remove a dialog, update_flag is %i\n", cell->flags); if (cell->flags & DLG_FLAG_NEW) return 0; if (use_dialog_table()!=0) return -1; VAL_TYPE(values) = VAL_TYPE(values+1) = DB_INT; VAL_NULL(values) = VAL_NULL(values+1) = 0; VAL_INT(values) = cell->h_entry; VAL_INT(values+1) = cell->h_id; CON_PS_REFERENCE(dialog_db_handle) = &my_ps; if(dialog_dbf.delete(dialog_db_handle, match_keys, 0, values, 2) < 0) { LM_ERR("failed to delete database information\n"); return -1; } LM_DBG("callid was %.*s\n", cell->callid.len, cell->callid.s ); /* dialog saved */ run_dlg_callbacks( DLGCB_SAVED, cell, 0, DLG_DIR_NONE, 0); return 0; }
/*! * \brief Execute callback for the BYE request and register callback for the BYE reply * \param req request message * \param dlg corresponding dialog * \param dir message direction */ static void dlg_terminated(sip_msg_t *req, dlg_cell_t *dlg, unsigned int dir) { dlg_iuid_t *iuid = NULL; if(!req) { LM_ERR("request is empty!"); return; } if(!dlg) { LM_ERR("dialog is empty!"); return; } /* dialog terminated (BYE) */ run_dlg_callbacks(DLGCB_TERMINATED, dlg, req, NULL, dir, 0); iuid = dlg_get_iuid_shm_clone(dlg); if(iuid==NULL) return; /* register callback for the coresponding reply */ if (d_tmb.register_tmcb(req, 0, TMCB_RESPONSE_OUT, dlg_terminated_confirmed, (void*)iuid, dlg_iuid_sfree) <= 0 ) { LM_ERR("cannot register response callback for BYE request\n"); return; } }
/*! * \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; }
void destroy_dlg(struct dlg_cell *dlg) { int ret = 0; LM_DBG("destroying dialog %p\n",dlg); 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_leg_print_info( dlg, DLG_CALLER_LEG, tag), dlg_leg_print_info( dlg, callee_idx(dlg), tag)); } else if (ret > 0) { LM_DBG("dlg expired or not in list - 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_leg_print_info( dlg, DLG_CALLER_LEG, tag), dlg_leg_print_info( dlg, callee_idx(dlg), tag)); } run_dlg_callbacks( DLGCB_DESTROY , dlg, 0, DLG_DIR_NONE, NULL, 0); free_dlg_dlg(dlg); }
/*! * \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); } }
/* When done, this function also has the job to unref the dialog as removed * from timer list. This must be done in all cases!! */ void dlg_ontimeout( struct dlg_tl *tl) { struct dlg_cell *dlg; int new_state; int old_state; int unref; dlg = get_dlg_tl_payload(tl); LM_DBG("byeontimeout ? %d , state = %d\n",dlg->flags,dlg->state); if ( (dlg->flags&DLG_FLAG_BYEONTIMEOUT) && (dlg->state==DLG_STATE_CONFIRMED_NA || dlg->state==DLG_STATE_CONFIRMED)) { init_dlg_term_reason(dlg,"Lifetime Timeout",sizeof("Lifetime Timeout")-1); /* we just send the BYEs in both directions */ dlg_end_dlg( dlg, NULL); /* dialog is no longer refed by timer; from now one it is refed by the send_bye functions */ unref_dlg( dlg, 1); /* is not 100% sure, but do it */ if_update_stat( dlg_enable_stats, expired_dlgs, 1); return ; } next_state_dlg( dlg, DLG_EVENT_REQBYE, &old_state, &new_state, &unref); if (new_state==DLG_STATE_DELETED && old_state!=DLG_STATE_DELETED) { LM_DBG("timeout for dlg with CallID '%.*s' and tags '%.*s' '%.*s'\n", dlg->callid.len, dlg->callid.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[callee_idx(dlg)].tag.len, ZSW(dlg->legs[callee_idx(dlg)].tag.s)); /*destroy linkers */ destroy_linkers(dlg->profile_links); dlg->profile_links = NULL; /* dialog timeout */ run_dlg_callbacks( DLGCB_EXPIRED, dlg, 0, DLG_DIR_NONE, 0); /* delete the dialog from DB */ if (should_remove_dlg_db()) remove_dialog_from_db(dlg); unref_dlg(dlg, unref + 1 /*timer list*/); if_update_stat( dlg_enable_stats, expired_dlgs, 1); if_update_stat( dlg_enable_stats, active_dlgs, -1); } else { unref_dlg(dlg, 1 /*just timer list*/); } return; }
/*! * \brief Helper method that outputs a dialog via the RPC interface * \see rpc_print_dlg * \param rpc RPC node that should be filled * \param c RPC void pointer * \param dlg printed dialog * \param with_context if 1 then the dialog context will be also printed * \return 0 on success, -1 on failure */ static inline void internal_rpc_print_dlg(rpc_t *rpc, void *c, dlg_cell_t *dlg, int with_context) { rpc_cb_ctx_t rpc_cb; time_t _start_ts, _stop_ts; struct tm *_start_t, *_stop_t; char _start_date_buf[RPC_DATE_BUF_LEN]="UNSPECIFIED"; char _stop_date_buf[RPC_DATE_BUF_LEN]="UNSPECIFIED"; _start_ts = (time_t)dlg->start_ts; if (_start_ts) { _start_t = localtime(&_start_ts); strftime(_start_date_buf, RPC_DATE_BUF_LEN - 1, "%Y-%m-%d %H:%M:%S", _start_t); if (dlg->tl.timeout) { _stop_ts = time(0) + dlg->tl.timeout - get_ticks(); _stop_t = localtime(&_stop_ts); strftime(_stop_date_buf, RPC_DATE_BUF_LEN - 1, "%Y-%m-%d %H:%M:%S", _stop_t); } } rpc->printf(c, "hash:%u:%u state:%u ref_count:%u " "timestart:%u timeout:%u lifetime:%u datestart:%s datestop:%s", dlg->h_entry, dlg->h_id, dlg->state, dlg->ref, _start_ts, dlg->tl.timeout, dlg->lifetime, _start_date_buf, _stop_date_buf); rpc->printf(c, "\tcallid:%.*s from_tag:%.*s to_tag:%.*s", 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); rpc->printf(c, "\tfrom_uri:%.*s to_uri:%.*s", dlg->from_uri.len, dlg->from_uri.s, dlg->to_uri.len, dlg->to_uri.s); rpc->printf(c, "\tcaller_contact:%.*s caller_cseq:%.*s", dlg->contact[DLG_CALLER_LEG].len, dlg->contact[DLG_CALLER_LEG].s, dlg->cseq[DLG_CALLER_LEG].len, dlg->cseq[DLG_CALLER_LEG].s); rpc->printf(c, "\tcaller_route_set: %.*s", dlg->route_set[DLG_CALLER_LEG].len, dlg->route_set[DLG_CALLER_LEG].s); rpc->printf(c, "\tcallee_contact:%.*s callee_cseq:%.*s", dlg->contact[DLG_CALLEE_LEG].len, dlg->contact[DLG_CALLEE_LEG].s, dlg->cseq[DLG_CALLEE_LEG].len, dlg->cseq[DLG_CALLEE_LEG].s); rpc->printf(c, "\tcallee_route_set: %.*s", dlg->route_set[DLG_CALLEE_LEG].len, dlg->route_set[DLG_CALLEE_LEG].s); if (dlg->bind_addr[DLG_CALLEE_LEG]) { rpc->printf(c, "\tcaller_bind_addr:%.*s callee_bind_addr:%.*s", dlg->bind_addr[DLG_CALLER_LEG]->sock_str.len, dlg->bind_addr[DLG_CALLER_LEG]->sock_str.s, dlg->bind_addr[DLG_CALLEE_LEG]->sock_str.len, dlg->bind_addr[DLG_CALLEE_LEG]->sock_str.s); } else { rpc->printf(c, "\tcaller_bind_addr:%.*s callee_bind_addr:", dlg->bind_addr[DLG_CALLER_LEG]->sock_str.len, dlg->bind_addr[DLG_CALLER_LEG]->sock_str.s); } if (with_context) { rpc_cb.rpc = rpc; rpc_cb.c = c; run_dlg_callbacks( DLGCB_RPC_CONTEXT, dlg, NULL, NULL, DLG_DIR_NONE, (void *)&rpc_cb); } }
void bin_push_dlg(bin_packet_t *packet, struct dlg_cell *dlg) { int callee_leg; str *vars, *profiles; callee_leg = callee_idx(dlg); bin_push_str(packet, &dlg->callid); bin_push_str(packet, &dlg->legs[DLG_CALLER_LEG].tag); bin_push_str(packet, &dlg->legs[callee_leg].tag); bin_push_str(packet, &dlg->from_uri); bin_push_str(packet, &dlg->to_uri); bin_push_int(packet, dlg->h_id); bin_push_int(packet, dlg->start_ts); bin_push_int(packet, dlg->state); bin_push_str(packet, &dlg->legs[DLG_CALLER_LEG].bind_addr->sock_str); if (dlg->legs[callee_leg].bind_addr) bin_push_str(packet, &dlg->legs[callee_leg].bind_addr->sock_str); else bin_push_str(packet, NULL); bin_push_str(packet, &dlg->legs[DLG_CALLER_LEG].r_cseq); bin_push_str(packet, &dlg->legs[callee_leg].r_cseq); bin_push_str(packet, &dlg->legs[DLG_CALLER_LEG].route_set); bin_push_str(packet, &dlg->legs[callee_leg].route_set); bin_push_str(packet, &dlg->legs[DLG_CALLER_LEG].contact); bin_push_str(packet, &dlg->legs[callee_leg].contact); bin_push_str(packet, &dlg->legs[callee_leg].from_uri); bin_push_str(packet, &dlg->legs[callee_leg].to_uri); /* give modules the chance to write values/profiles before replicating */ run_dlg_callbacks(DLGCB_WRITE_VP, dlg, NULL, DLG_DIR_NONE, NULL, 1); vars = write_dialog_vars(dlg->vals); profiles = write_dialog_profiles(dlg->profile_links); bin_push_str(packet, vars); bin_push_str(packet, profiles); bin_push_int(packet, dlg->user_flags); bin_push_int(packet, dlg->mod_flags); bin_push_int(packet, dlg->flags & ~(DLG_FLAG_NEW|DLG_FLAG_CHANGED|DLG_FLAG_VP_CHANGED|DLG_FLAG_FROM_DB)); bin_push_int(packet, (unsigned int)time(0) + dlg->tl.timeout - get_ticks()); bin_push_int(packet, dlg->legs[DLG_CALLER_LEG].last_gen_cseq); bin_push_int(packet, dlg->legs[callee_leg].last_gen_cseq); }
/*! * \brief Timer function that removes expired dialogs, run timeout route * \param tl dialog timer list */ void dlg_ontimeout(struct dlg_tl *tl) { struct dlg_cell *dlg; int new_state, old_state, unref; struct sip_msg *fmsg; /* get the dialog tl payload */ dlg = ((struct dlg_cell*) ((char *) (tl) - (unsigned long) (&((struct dlg_cell*) 0)->tl))); if (dlg->toroute > 0 && dlg->toroute < main_rt.entries && main_rt.rlist[dlg->toroute] != NULL) { fmsg = faked_msg_next(); if (exec_pre_script_cb(fmsg, REQUEST_CB_TYPE) > 0) { dlg_set_ctx_dialog(dlg); LM_DBG("executing route %d on timeout\n", dlg->toroute); set_route_type(REQUEST_ROUTE); run_top_route(main_rt.rlist[dlg->toroute], fmsg, 0); dlg_set_ctx_dialog(0); exec_post_script_cb(fmsg, REQUEST_CB_TYPE); } } if ((dlg->dflags & DLG_FLAG_TOBYE) && (dlg->state == DLG_STATE_CONFIRMED)) { //TODO: dlg_bye_all(dlg, NULL); unref_dlg(dlg, 1); return; } next_state_dlg(dlg, DLG_EVENT_REQBYE, &old_state, &new_state, &unref, 0); if (new_state == DLG_STATE_DELETED && old_state != DLG_STATE_DELETED) { LM_WARN("timeout for dlg with CallID '%.*s' and tags '%.*s'\n", dlg->callid.len, dlg->callid.s, dlg->from_tag.len, dlg->from_tag.s); /* dialog timeout */ run_dlg_callbacks(DLGCB_EXPIRED, dlg, NULL, NULL, DLG_DIR_NONE, 0); unref_dlg(dlg, unref + 1); } else { unref_dlg(dlg, 1); } return; }
static void dlg_seq_down_onreply(struct cell* t, int type, struct tmcb_params *param) { struct dlg_cell *dlg; dlg = (struct dlg_cell *)(*param->param); if (shutdown_done || dlg==0) return; if (type==TMCB_RESPONSE_FWDED && (dlg->cbs.types)&DLGCB_RESPONSE_WITHIN) { run_dlg_callbacks(DLGCB_RESPONSE_WITHIN, dlg, param->rpl, DLG_DIR_DOWNSTREAM, 0); return; } return; }
/*! * \brief Helper function that run dialog callbacks on forwarded requests * \see dlg_seq_up_onreply * \see dlg_seq_down_onreply * \param t transaction, unused * \param type type of the callback, should be TMCB_RESPONSE_FWDED * \param param saved dialog structure inside the callback * \param direction direction of the request */ static void dlg_seq_onreply_helper(struct cell* t, int type, struct tmcb_params *param, const int direction) { struct dlg_cell *dlg; dlg = (struct dlg_cell *) (*param->param); if (shutdown_done || dlg == 0) return; if (type == TMCB_RESPONSE_FWDED) { run_dlg_callbacks(DLGCB_RESPONSE_WITHIN, dlg, param->req, param->rpl, direction, 0); return; } return; }
static void dlg_seq_down_onreply_mod_cseq(struct cell* t, int type, struct tmcb_params *param) { struct dlg_cell *dlg; dlg = ((dlg_cseq_wrapper*)*param->param)->dlg; if (shutdown_done || dlg==0) return; if (update_msg_cseq((struct sip_msg *)param->rpl,&((dlg_cseq_wrapper *)*param->param)->cseq,0) != 0) LM_ERR("failed to update CSEQ in msg\n"); if (type==TMCB_RESPONSE_FWDED && (dlg->cbs.types)&DLGCB_RESPONSE_WITHIN) { run_dlg_callbacks(DLGCB_RESPONSE_WITHIN, dlg, param->rpl, DLG_DIR_DOWNSTREAM, 0); return; } return; }
/*! * \brief Function that executes BYE reply callbacks * \param t transaction, unused * \param type type of the callback, should be TMCB_RESPONSE_FWDED * \param param saved dialog structure inside the callback */ static void dlg_terminated_confirmed(struct cell* t, int type, struct tmcb_params* params) { if (!params || !params->req || !params->param) { LM_ERR("invalid parameters!\n"); return; } struct dlg_cell* dlg = (struct dlg_cell*) *params->param; if (!dlg) { LM_ERR("failed to get dialog from params!\n"); return; } /* dialog termination confirmed (BYE reply) */ run_dlg_callbacks(DLGCB_TERMINATED_CONFIRMED, dlg, params->req, params->rpl, DLG_DIR_UPSTREAM, 0); }
/*! * \brief Helper method that outputs a dialog via the RPC interface * \see rpc_print_dlg * \param rpc RPC node that should be filled * \param c RPC void pointer * \param dlg printed dialog * \param with_context if 1 then the dialog context will be also printed * \return 0 on success, -1 on failure */ static inline void internal_rpc_print_dlg(rpc_t *rpc, void *c, dlg_cell_t *dlg, int with_context) { rpc_cb_ctx_t rpc_cb; rpc->printf(c, "hash:%u:%u state:%u ref_count:%u timestart:%u timeout:%u", dlg->h_entry, dlg->h_id, dlg->state, dlg->ref, dlg->start_ts, dlg->tl.timeout); rpc->printf(c, "\tcallid:%.*s from_tag:%.*s to_tag:%.*s", 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); rpc->printf(c, "\tfrom_uri:%.*s to_uri:%.*s", dlg->from_uri.len, dlg->from_uri.s, dlg->to_uri.len, dlg->to_uri.s); rpc->printf(c, "\tcaller_contact:%.*s caller_cseq:%.*s", dlg->contact[DLG_CALLER_LEG].len, dlg->contact[DLG_CALLER_LEG].s, dlg->cseq[DLG_CALLER_LEG].len, dlg->cseq[DLG_CALLER_LEG].s); rpc->printf(c, "\tcaller_route_set: %.*s", dlg->route_set[DLG_CALLER_LEG].len, dlg->route_set[DLG_CALLER_LEG].s); rpc->printf(c, "\tcallee_contact:%.*s callee_cseq:%.*s", dlg->contact[DLG_CALLEE_LEG].len, dlg->contact[DLG_CALLEE_LEG].s, dlg->cseq[DLG_CALLEE_LEG].len, dlg->cseq[DLG_CALLEE_LEG].s); rpc->printf(c, "\tcallee_route_set: %.*s", dlg->route_set[DLG_CALLEE_LEG].len, dlg->route_set[DLG_CALLEE_LEG].s); if (dlg->bind_addr[DLG_CALLEE_LEG]) { rpc->printf(c, "\tcaller_bind_addr:%.*s callee_bind_addr:%.*s", dlg->bind_addr[DLG_CALLER_LEG]->sock_str.len, dlg->bind_addr[DLG_CALLER_LEG]->sock_str.s, dlg->bind_addr[DLG_CALLEE_LEG]->sock_str.len, dlg->bind_addr[DLG_CALLEE_LEG]->sock_str.s); } else { rpc->printf(c, "\tcaller_bind_addr:%.*s callee_bind_addr:", dlg->bind_addr[DLG_CALLER_LEG]->sock_str.len, dlg->bind_addr[DLG_CALLER_LEG]->sock_str.s); } if (with_context) { rpc_cb.rpc = rpc; rpc_cb.c = c; run_dlg_callbacks( DLGCB_RPC_CONTEXT, dlg, NULL, NULL, DLG_DIR_NONE, (void *)&rpc_cb); } }
/*callback function to handle responses to the BYE request */ void bye_reply_cb(struct cell* t, int type, struct tmcb_params* ps) { struct dlg_cell* dlg; int event, old_state, new_state, unref, ret; struct dlg_cell_out *dlg_out = 0; if (ps->param == NULL || *ps->param == NULL) { LM_ERR("invalid parameter\n"); return; } if (ps->code < 200) { LM_DBG("receiving a provisional reply\n"); return; } LM_DBG("receiving a final reply %d\n", ps->code); dlg = (struct dlg_cell *) (*(ps->param)); event = DLG_EVENT_REQBYE; //get the corresponding dlg out structure for this REQ struct dlg_entry_out *dlg_entry_out = &dlg->dlg_entry_out; lock_get(dlg->dlg_out_entries_lock); dlg_out = dlg_entry_out->first; //TODO check for concurrent call if (!dlg_out) return; next_state_dlg(dlg, event, &old_state, &new_state, &unref, &dlg_out->to_tag); lock_release(dlg->dlg_out_entries_lock); if (new_state == DLG_STATE_DELETED && old_state != DLG_STATE_DELETED) { LM_DBG("removing dialog with h_entry %u and h_id %u\n", dlg->h_entry, dlg->h_id); /* remove from timer */ ret = remove_dialog_timer(&dlg->tl); if (ret < 0) { LM_CRIT("unable to unlink the timer on dlg %p [%u:%u] " "with clid '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s); } else if (ret > 0) { LM_WARN("inconsitent dlg timer data on dlg %p [%u:%u] " "with clid '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s); } else { unref++; } /* dialog terminated (BYE) */ run_dlg_callbacks(DLGCB_TERMINATED, dlg, ps->req, ps->rpl, DLG_DIR_NONE, 0); /* derefering the dialog */ unref_dlg(dlg, unref + 1); } if (new_state == DLG_STATE_DELETED && old_state == DLG_STATE_DELETED) { /* trash the dialog from DB and memory */ if (dlg_db_mode) remove_dialog_in_from_db(dlg); /* force delete from mem */ unref_dlg(dlg, 1); } }
/*! * \brief Function that is registered as RR callback for dialog tracking * * Function that is registered as RR callback for dialog tracking. It * sets the appropriate events after the SIP method and run the state * machine to update the dialog state. It updates then the saved * dialogs and also the statistics. * \param req SIP request * \param route_params record-route parameter * \param param unused */ void dlg_onroute(struct sip_msg* req, str *route_params, void *param) { struct dlg_cell *dlg; str val, callid, ftag, ttag; int h_entry, h_id, new_state, old_state, unref, event, timeout; unsigned int dir; int ret = 0; if (current_dlg_pointer != NULL) return; /* skip initial requests - they may end up here because of the * preloaded route */ if ((!req->to && parse_headers(req, HDR_TO_F, 0) < 0) || !req->to) { LM_ERR("bad request or missing TO hdr :-/\n"); return; } if (get_to(req)->tag_value.len == 0) return; dlg = 0; dir = DLG_DIR_NONE; if (seq_match_mode != SEQ_MATCH_NO_ID) { if (d_rrb.get_route_param(req, &rr_param, &val) != 0) { LM_DBG("Route param '%.*s' not found\n", rr_param.len, rr_param.s); if (seq_match_mode == SEQ_MATCH_STRICT_ID) return; } else { LM_DBG("route param is '%.*s' (len=%d)\n", val.len, val.s, val.len); if (parse_dlg_rr_param(val.s, val.s + val.len, &h_entry, &h_id) < 0) return; dlg = lookup_dlg(h_entry, h_id); if (dlg == 0) { LM_WARN("unable to find dialog for %.*s " "with route param '%.*s' [%u:%u]\n", req->first_line.u.request.method.len, req->first_line.u.request.method.s, val.len, val.s, h_entry, h_id); if (seq_match_mode == SEQ_MATCH_STRICT_ID) return; } else { if (pre_match_parse(req, &callid, &ftag, &ttag, 1) < 0) { // lookup_dlg has incremented the ref count by 1 unref_dlg(dlg, 1); return; } if (match_dialog(dlg, &callid, &ftag, &ttag, &dir) == 0) { LM_WARN("tight matching failed for %.*s with callid='%.*s'/%d, " "ftag='%.*s'/%d, ttag='%.*s'/%d and direction=%d\n", req->first_line.u.request.method.len, req->first_line.u.request.method.s, callid.len, callid.s, callid.len, ftag.len, ftag.s, ftag.len, ttag.len, ttag.s, ttag.len, dir); LM_WARN("dialog identification elements are callid='%.*s'/%d, " "caller tag='%.*s'/%d\n", dlg->callid.len, dlg->callid.s, dlg->callid.len, dlg->from_tag.len, dlg->from_tag.s, dlg->from_tag.len); // lookup_dlg has incremented the ref count by 1 unref_dlg(dlg, 1); // Reset variables in order to do a lookup based on SIP-Elements. dlg = 0; dir = DLG_DIR_NONE; if (seq_match_mode == SEQ_MATCH_STRICT_ID) return; } } } } if (dlg == 0) { if (pre_match_parse(req, &callid, &ftag, &ttag, 1) < 0) return; /* TODO - try to use the RR dir detection to speed up here the * search -bogdan */ dlg = get_dlg(&callid, &ftag, &ttag, &dir); if (!dlg) { LM_DBG("Callid '%.*s' not found\n", req->callid->body.len, req->callid->body.s); return; } } /* set current dialog - re-use ref increment from dlg_get() above */ set_current_dialog(req, dlg); _dlg_ctx.dlg = dlg; if (d_tmb.register_tmcb(req, NULL, TMCB_REQUEST_FWDED, store_dlg_in_tm_cb, (void*) dlg, NULL) < 0) { LM_ERR("failed to store dialog in transaction during dialog creation for later reference\n"); } /* run state machine */ switch (req->first_line.u.request.method_value) { case METHOD_PRACK: event = DLG_EVENT_REQPRACK; break; case METHOD_ACK: event = DLG_EVENT_REQACK; break; case METHOD_BYE: event = DLG_EVENT_REQBYE; break; default: event = DLG_EVENT_REQ; } next_state_dlg(dlg, event, &old_state, &new_state, &unref, 0); LM_DBG("unref after next state is %i\n", unref); CURR_DLG_ID = req->id; CURR_DLG_LIFETIME = (unsigned int) (time(0)) - dlg->start_ts; CURR_DLG_STATUS = new_state; /* run actions for the transition */ if (event == DLG_EVENT_REQBYE && new_state == DLG_STATE_DELETED && old_state != DLG_STATE_DELETED) { LM_DBG("BYE successfully processed\n"); /* remove from timer */ ret = remove_dialog_timer(&dlg->tl); if (ret < 0) { LM_CRIT("unable to unlink the timer on dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg->from_tag.len, dlg->from_tag.s); } else if (ret > 0) { LM_WARN("inconsitent dlg timer data on dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s' \n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg->from_tag.len, dlg->from_tag.s); } else { unref++; } /* dialog terminated (BYE) */ dlg_terminated(req, dlg, dir); unref_dlg(dlg, unref); return; } if ((event == DLG_EVENT_REQ || event == DLG_EVENT_REQACK) && new_state == DLG_STATE_CONFIRMED) { timeout = get_dlg_timeout(req); if (timeout != default_timeout) { dlg->lifetime = timeout; } if (update_dlg_timer(&dlg->tl, dlg->lifetime) == -1) { LM_ERR("failed to update dialog lifetime\n"); } if (update_cseqs(dlg, req, dir, &ttag) != 0) { LM_ERR("cseqs update failed\n"); } else { dlg->dflags |= DLG_FLAG_CHANGED; } if (old_state != DLG_STATE_CONFIRMED) { LM_DBG("confirming ACK successfully processed\n"); /* confirming ACK request */ run_dlg_callbacks(DLGCB_CONFIRMED, dlg, req, NULL, dir, 0); } else { LM_DBG("sequential request successfully processed\n"); /* within dialog request */ run_dlg_callbacks(DLGCB_REQ_WITHIN, dlg, req, NULL, dir, 0); if ((event != DLG_EVENT_REQACK) && (dlg->cbs.types) & DLGCB_RESPONSE_WITHIN) { /* ref the dialog as registered into the transaction callback. * unref will be done when the callback will be destroyed */ ref_dlg(dlg, 1); /* register callback for the replies of this request */ if (d_tmb.register_tmcb(req, 0, TMCB_RESPONSE_FWDED, (dir == DLG_DIR_UPSTREAM) ? dlg_seq_down_onreply : dlg_seq_up_onreply, (void*) dlg, unreference_dialog) < 0) { LM_ERR("failed to register TMCB (2)\n"); unref_dlg(dlg, 1); } } } } if (new_state == DLG_STATE_CONFIRMED && old_state != DLG_STATE_CONFIRMED) { dlg->dflags |= DLG_FLAG_CHANGED; } return; }
void dialog_update_db(unsigned int ticks, void * param) { static db_ps_t my_ps_update = NULL; static db_ps_t my_ps_insert = NULL; static db_ps_t my_ps_update_vp = NULL; int index; db_val_t values[DIALOG_TABLE_TOTAL_COL_NO]; struct dlg_entry entry; struct dlg_cell * cell; unsigned char on_shutdown; int callee_leg,ins_done=0; static query_list_t *ins_list = NULL; db_key_t insert_keys[DIALOG_TABLE_TOTAL_COL_NO] = { &h_entry_column, &h_id_column, &call_id_column, &from_uri_column, &from_tag_column, &to_uri_column, &to_tag_column, &from_sock_column, &to_sock_column, &start_time_column, &from_route_column, &to_route_column, &from_contact_column, &to_contact_column, &mangled_fu_column, &mangled_tu_column, /*update chunk */ &state_column, &timeout_column, &from_cseq_column, &to_cseq_column, &from_ping_cseq_column, &to_ping_cseq_column, &vars_column, &profiles_column, &sflags_column, &flags_column}; if (dialog_db_handle==0 || use_dialog_table()!=0) return; on_shutdown = (ticks==0); /*save the current dialogs information*/ VAL_TYPE(values) = VAL_TYPE(values+1) = VAL_TYPE(values+9) = VAL_TYPE(values+16) = VAL_TYPE(values+17) = VAL_TYPE(values+20) = VAL_TYPE(values+21) = VAL_TYPE(values+24) = VAL_TYPE(values+25)= DB_INT; VAL_TYPE(values+2) = VAL_TYPE(values+3) = VAL_TYPE(values+4) = VAL_TYPE(values+5) = VAL_TYPE(values+6) = VAL_TYPE(values+7) = VAL_TYPE(values+8) = VAL_TYPE(values+10) = VAL_TYPE(values+11) = VAL_TYPE(values+12) = VAL_TYPE(values+13) = VAL_TYPE(values+14) = VAL_TYPE(values+15) = VAL_TYPE(values+18) = VAL_TYPE(values+19) = VAL_TYPE(values+22) = VAL_TYPE(values+23) = DB_STR; for(index = 0; index< d_table->size; index++){ /* lock the whole entry */ entry = (d_table->entries)[index]; dlg_lock( d_table, &entry); for(cell = entry.first; cell != NULL; cell = cell->next){ callee_leg = callee_idx(cell); if( (cell->flags & DLG_FLAG_NEW) != 0 ) { if ( cell->state == DLG_STATE_DELETED ) { /* don't need to insert dialogs already terminated */ continue; } LM_DBG("inserting new dialog %p\n",cell); SET_INT_VALUE(values, cell->h_entry); SET_INT_VALUE(values+1, cell->h_id); SET_STR_VALUE(values+2, cell->callid); SET_STR_VALUE(values+3, cell->from_uri); SET_STR_VALUE(values+4, cell->legs[DLG_CALLER_LEG].tag); SET_STR_VALUE(values+5, cell->to_uri); SET_STR_VALUE(values+6, cell->legs[callee_leg].tag); SET_STR_VALUE(values+7, cell->legs[DLG_CALLER_LEG].bind_addr->sock_str); if (cell->legs[callee_leg].bind_addr) { SET_STR_VALUE(values+8, cell->legs[callee_leg].bind_addr->sock_str); } else { VAL_NULL(values+8) = 1; } SET_INT_VALUE(values+9, cell->start_ts); SET_STR_VALUE(values+10, cell->legs[DLG_CALLER_LEG].route_set); SET_STR_VALUE(values+11, cell->legs[callee_leg].route_set); SET_STR_VALUE(values+12, cell->legs[DLG_CALLER_LEG].contact); SET_STR_VALUE(values+13, cell->legs[callee_leg].contact); SET_STR_VALUE(values+14,cell->legs[callee_leg].from_uri); SET_STR_VALUE(values+15,cell->legs[callee_leg].to_uri); SET_INT_VALUE(values+16, cell->state); SET_INT_VALUE(values+17, (unsigned int)((unsigned int)time(0) + cell->tl.timeout - get_ticks()) ); SET_STR_VALUE(values+18, cell->legs[DLG_CALLER_LEG].r_cseq); SET_STR_VALUE(values+19, cell->legs[callee_leg].r_cseq); SET_INT_VALUE(values+20, cell->legs[DLG_CALLER_LEG].last_gen_cseq); SET_INT_VALUE(values+21, cell->legs[callee_leg].last_gen_cseq); set_final_update_cols(values+22, cell, on_shutdown); SET_INT_VALUE(values+25, cell->flags & ~(DLG_FLAG_NEW|DLG_FLAG_CHANGED|DLG_FLAG_VP_CHANGED)); CON_PS_REFERENCE(dialog_db_handle) = &my_ps_insert; if (con_set_inslist(&dialog_dbf,dialog_db_handle, &ins_list,insert_keys,DIALOG_TABLE_TOTAL_COL_NO) < 0 ) CON_RESET_INSLIST(dialog_db_handle); if((dialog_dbf.insert(dialog_db_handle, insert_keys, values, DIALOG_TABLE_TOTAL_COL_NO)) !=0){ LM_ERR("could not add another dialog to db\n"); goto error; } if (ins_done==0) ins_done=1; /* dialog saved */ run_dlg_callbacks( DLGCB_SAVED, cell, 0, DLG_DIR_NONE, 0); cell->flags &= ~(DLG_FLAG_NEW |DLG_FLAG_CHANGED|DLG_FLAG_VP_CHANGED); } else if ( (cell->flags & DLG_FLAG_CHANGED)!=0 || on_shutdown ){ LM_DBG("updating existing dialog %p\n",cell); SET_INT_VALUE(values, cell->h_entry); SET_INT_VALUE(values+1, cell->h_id); SET_INT_VALUE(values+16, cell->state); SET_INT_VALUE(values+17, (unsigned int)((unsigned int)time(0) + cell->tl.timeout - get_ticks()) ); SET_STR_VALUE(values+18, cell->legs[DLG_CALLER_LEG].r_cseq); SET_STR_VALUE(values+19, cell->legs[callee_leg].r_cseq); SET_INT_VALUE(values+20, cell->legs[DLG_CALLER_LEG].last_gen_cseq); SET_INT_VALUE(values+21, cell->legs[callee_leg].last_gen_cseq); set_final_update_cols(values+22, cell, 1); SET_INT_VALUE(values+25, cell->flags); CON_PS_REFERENCE(dialog_db_handle) = &my_ps_update; if((dialog_dbf.update(dialog_db_handle, (insert_keys), 0, (values), (insert_keys+16), (values+16), 2, 10)) !=0) { LM_ERR("could not update database info\n"); goto error; } /* dialog saved */ run_dlg_callbacks( DLGCB_SAVED, cell, 0, DLG_DIR_NONE, 0); cell->flags &= ~(DLG_FLAG_CHANGED|DLG_FLAG_VP_CHANGED); } else if (cell->flags & DLG_FLAG_VP_CHANGED) { SET_INT_VALUE(values, cell->h_entry); SET_INT_VALUE(values+1, cell->h_id); set_final_update_cols(values+22, cell, 0); CON_PS_REFERENCE(dialog_db_handle) = &my_ps_update_vp; if((dialog_dbf.update(dialog_db_handle, (insert_keys), 0, (values), (insert_keys+22), (values+22), 2, 3)) !=0) { LM_ERR("could not update database info\n"); goto error; } run_dlg_callbacks( DLGCB_SAVED, cell, 0, DLG_DIR_NONE, 0); cell->flags &= ~DLG_FLAG_VP_CHANGED; } } dlg_unlock( d_table, &entry); } if (ins_done) { LM_DBG("dlg timer attempting to flush rows to DB\n"); /* flush everything to DB * so that next-time timer fires * we are sure that DB updates will be succesful */ if (ql_flush_rows(&dialog_dbf,dialog_db_handle,ins_list) < 0) LM_ERR("failed to flush rows to DB\n"); } return; error: dlg_unlock( d_table, &entry); }
/*! * \brief Helper method that output a dialog via the MI interface * \see mi_print_dlg * \param rpl MI node that should be filled * \param dlg printed dialog * \param with_context if 1 then the dialog context will be also printed * \return 0 on success, -1 on failure */ static inline int internal_mi_print_dlg(struct mi_node *rpl, struct dlg_cell *dlg, int with_context) { struct mi_node* node= NULL; struct mi_node* node1 = NULL; struct mi_attr* attr= NULL; int len; char* p; node = add_mi_node_child(rpl, 0, "dialog",6 , 0, 0 ); if (node==0) goto error; attr = addf_mi_attr( node, 0, "hash", 4, "%u:%u", dlg->h_entry, dlg->h_id ); if (attr==0) goto error; p= int2str((unsigned long)dlg->state, &len); node1 = add_mi_node_child( node, MI_DUP_VALUE, "state", 5, p, len); if (node1==0) goto error; p= int2str((unsigned long)dlg->ref, &len); node1 = add_mi_node_child( node, MI_DUP_VALUE, "ref_count", 9, p, len); if (node1==0) goto error; p= int2str((unsigned long)dlg->start_ts, &len); node1 = add_mi_node_child(node,MI_DUP_VALUE,"timestart",9, p, len); if (node1==0) goto error; p= int2str((unsigned long)dlg->tl.timeout, &len); node1 = add_mi_node_child(node,MI_DUP_VALUE, "timeout", 7, p, len); if (node1==0) goto error; node1 = add_mi_node_child(node, MI_DUP_VALUE, "callid", 6, dlg->callid.s, dlg->callid.len); if(node1 == 0) goto error; node1 = add_mi_node_child(node, MI_DUP_VALUE, "from_uri", 8, dlg->from_uri.s, dlg->from_uri.len); if(node1 == 0) goto error; node1 = add_mi_node_child(node, MI_DUP_VALUE, "from_tag", 8, dlg->tag[DLG_CALLER_LEG].s, dlg->tag[DLG_CALLER_LEG].len); if(node1 == 0) goto error; node1 = add_mi_node_child(node, MI_DUP_VALUE, "caller_contact", 14, dlg->contact[DLG_CALLER_LEG].s, dlg->contact[DLG_CALLER_LEG].len); if(node1 == 0) goto error; node1 = add_mi_node_child(node, MI_DUP_VALUE, "caller_cseq", 11, dlg->cseq[DLG_CALLER_LEG].s, dlg->cseq[DLG_CALLER_LEG].len); if(node1 == 0) goto error; node1 = add_mi_node_child(node, MI_DUP_VALUE,"caller_route_set",16, dlg->route_set[DLG_CALLER_LEG].s, dlg->route_set[DLG_CALLER_LEG].len); if(node1 == 0) goto error; if (dlg->bind_addr[DLG_CALLER_LEG]) { node1 = add_mi_node_child(node, 0, "caller_bind_addr",16, dlg->bind_addr[DLG_CALLER_LEG]->sock_str.s, dlg->bind_addr[DLG_CALLER_LEG]->sock_str.len); } else { node1 = add_mi_node_child(node, 0, "caller_bind_addr",16,0,0); } if (dlg->bind_addr[DLG_CALLEE_LEG]) { node1 = add_mi_node_child(node, 0, "callee_bind_addr",16, dlg->bind_addr[DLG_CALLEE_LEG]->sock_str.s, dlg->bind_addr[DLG_CALLEE_LEG]->sock_str.len); } else { node1 = add_mi_node_child(node, 0, "callee_bind_addr",16,0,0); } node1 = add_mi_node_child(node, MI_DUP_VALUE, "to_uri", 6, dlg->to_uri.s, dlg->to_uri.len); if(node1 == 0) goto error; node1 = add_mi_node_child(node, MI_DUP_VALUE, "to_tag", 6, dlg->tag[DLG_CALLEE_LEG].s, dlg->tag[DLG_CALLEE_LEG].len); if(node1 == 0) goto error; node1 = add_mi_node_child(node, MI_DUP_VALUE, "callee_contact", 14, dlg->contact[DLG_CALLEE_LEG].s, dlg->contact[DLG_CALLEE_LEG].len); if(node1 == 0) goto error; node1 = add_mi_node_child(node, MI_DUP_VALUE, "callee_cseq", 11, dlg->cseq[DLG_CALLEE_LEG].s, dlg->cseq[DLG_CALLEE_LEG].len); if(node1 == 0) goto error; node1 = add_mi_node_child(node, MI_DUP_VALUE,"callee_route_set",16, dlg->route_set[DLG_CALLEE_LEG].s, dlg->route_set[DLG_CALLEE_LEG].len); if(node1 == 0) goto error; if (with_context) { node1 = add_mi_node_child(node, 0, "context", 7, 0, 0); if(node1 == 0) goto error; run_dlg_callbacks( DLGCB_MI_CONTEXT, dlg, NULL, NULL, DLG_DIR_NONE, (void *)node1); } return 0; error: LM_ERR("failed to add node\n"); return -1; }
/*! * \brief Destroy a dialog, run callbacks and free memory * \param dlg destroyed dialog */ void destroy_dlg(struct dlg_cell *dlg) { int ret = 0; struct dlg_var *var; LM_DBG("destroying dialog %p (ref %d)\n", dlg, dlg->ref); 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_DBG("removed timer for 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); } run_dlg_callbacks( DLGCB_DESTROY , dlg, NULL, NULL, DLG_DIR_NONE, 0); /* delete the dialog from DB*/ if (dlg_db_mode) remove_dialog_from_db(dlg); if (dlg->cbs.first) destroy_dlg_callbacks_list(dlg->cbs.first); if (dlg->profile_links) destroy_linkers(dlg->profile_links); if (dlg->tag[DLG_CALLER_LEG].s) shm_free(dlg->tag[DLG_CALLER_LEG].s); if (dlg->tag[DLG_CALLEE_LEG].s) shm_free(dlg->tag[DLG_CALLEE_LEG].s); if (dlg->cseq[DLG_CALLER_LEG].s) shm_free(dlg->cseq[DLG_CALLER_LEG].s); if (dlg->cseq[DLG_CALLEE_LEG].s) shm_free(dlg->cseq[DLG_CALLEE_LEG].s); if (dlg->toroute_name.s) shm_free(dlg->toroute_name.s); while (dlg->vars) { var = dlg->vars; dlg->vars = dlg->vars->next; shm_free(var->key.s); shm_free(var->value.s); shm_free(var); } shm_free(dlg); dlg = 0; }
/*! * \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 update_dialog_dbinfo(struct dlg_cell * cell) { static db_ps_t my_ps_insert = NULL; static db_ps_t my_ps_update = NULL; static db_ps_t my_ps_update_vp = NULL; struct dlg_entry entry; db_val_t values[DIALOG_TABLE_TOTAL_COL_NO]; int callee_leg; db_key_t insert_keys[DIALOG_TABLE_TOTAL_COL_NO] = { &h_entry_column, &h_id_column, &call_id_column, &from_uri_column, &from_tag_column, &to_uri_column, &to_tag_column, &from_sock_column, &to_sock_column, &start_time_column, &mangled_fu_column, &mangled_tu_column, &state_column, &timeout_column, &from_cseq_column, &to_cseq_column, &from_ping_cseq_column, &to_ping_cseq_column,&flags_column, &vars_column, &profiles_column, &sflags_column, &from_route_column, &to_route_column, &from_contact_column,&to_contact_column}; if(use_dialog_table()!=0) return -1; callee_leg= callee_idx(cell); if((cell->flags & DLG_FLAG_NEW) != 0){ /* save all the current dialogs information*/ VAL_TYPE(values) = VAL_TYPE(values+1) = VAL_TYPE(values+9) = VAL_TYPE(values+12) = VAL_TYPE(values+13) = VAL_TYPE(values+16) = VAL_TYPE(values+17) = VAL_TYPE(values+18) = VAL_TYPE(values+21) = DB_INT; VAL_TYPE(values+2) = VAL_TYPE(values+3) = VAL_TYPE(values+4) = VAL_TYPE(values+5) = VAL_TYPE(values+6) = VAL_TYPE(values+7) = VAL_TYPE(values+8) = VAL_TYPE(values+10) = VAL_TYPE(values+11) = VAL_TYPE(values+14) = VAL_TYPE(values+15) = VAL_TYPE(values+19) = VAL_TYPE(values+20) = VAL_TYPE(values+22) = VAL_TYPE(values+23) = VAL_TYPE(values+24) = VAL_TYPE(values+25) = DB_STR; /* lock the entry */ entry = (d_table->entries)[cell->h_entry]; dlg_lock( d_table, &entry); SET_INT_VALUE(values, cell->h_entry); SET_INT_VALUE(values+1, cell->h_id); SET_STR_VALUE(values+2, cell->callid); SET_STR_VALUE(values+3, cell->from_uri); SET_STR_VALUE(values+4, cell->legs[DLG_CALLER_LEG].tag); SET_STR_VALUE(values+5, cell->to_uri); SET_STR_VALUE(values+6, cell->legs[callee_leg].tag); SET_STR_VALUE(values+7, cell->legs[DLG_CALLER_LEG].bind_addr->sock_str); if (cell->legs[callee_leg].bind_addr) { SET_STR_VALUE(values+8, cell->legs[callee_leg].bind_addr->sock_str); } else { VAL_NULL(values+8) = 1; } SET_INT_VALUE(values+9, cell->start_ts); SET_STR_VALUE(values+10,cell->legs[callee_leg].from_uri); SET_STR_VALUE(values+11,cell->legs[callee_leg].to_uri); SET_INT_VALUE(values+12, cell->state); SET_INT_VALUE(values+13, (unsigned int)( (unsigned int)time(0) + cell->tl.timeout - get_ticks()) ); SET_STR_VALUE(values+14, cell->legs[DLG_CALLER_LEG].r_cseq); SET_STR_VALUE(values+15, cell->legs[callee_leg].r_cseq); SET_INT_VALUE(values+16,cell->legs[DLG_CALLER_LEG].last_gen_cseq); SET_INT_VALUE(values+17,cell->legs[callee_leg].last_gen_cseq); SET_INT_VALUE(values+18, cell->flags & ~(DLG_FLAG_NEW|DLG_FLAG_CHANGED|DLG_FLAG_VP_CHANGED)); set_final_update_cols(values+19, cell, 0); SET_STR_VALUE(values+22, cell->legs[DLG_CALLER_LEG].route_set); SET_STR_VALUE(values+23, cell->legs[callee_leg].route_set); SET_STR_VALUE(values+24, cell->legs[DLG_CALLER_LEG].contact); SET_STR_VALUE(values+25, cell->legs[callee_leg].contact); CON_PS_REFERENCE(dialog_db_handle) = &my_ps_insert; if((dialog_dbf.insert(dialog_db_handle, insert_keys, values, DIALOG_TABLE_TOTAL_COL_NO)) !=0){ LM_ERR("could not add another dialog to db\n"); goto error; } /* dialog saved */ run_dlg_callbacks( DLGCB_SAVED, cell, 0, DLG_DIR_NONE, 0); cell->flags &= ~(DLG_FLAG_NEW|DLG_FLAG_CHANGED|DLG_FLAG_VP_CHANGED); } else if((cell->flags & DLG_FLAG_CHANGED) != 0) { /* save only dialog's state and timeout */ VAL_TYPE(values) = VAL_TYPE(values+1) = VAL_TYPE(values+12) = VAL_TYPE(values+13) = VAL_TYPE(values+16) = VAL_TYPE(values+17) = VAL_TYPE(values+18) = VAL_TYPE(values+21) =DB_INT; VAL_TYPE(values+14) = VAL_TYPE(values+15) = VAL_TYPE(values+19) = VAL_TYPE(values+20) = DB_STR; /* lock the entry */ entry = (d_table->entries)[cell->h_entry]; dlg_lock( d_table, &entry); SET_INT_VALUE(values, cell->h_entry); SET_INT_VALUE(values+1, cell->h_id); SET_INT_VALUE(values+12, cell->state); SET_INT_VALUE(values+13, (unsigned int)( (unsigned int)time(0) + cell->tl.timeout - get_ticks()) ); SET_STR_VALUE(values+14, cell->legs[DLG_CALLER_LEG].r_cseq); SET_STR_VALUE(values+15, cell->legs[callee_leg].r_cseq); SET_INT_VALUE(values+16,cell->legs[DLG_CALLER_LEG].last_gen_cseq); SET_INT_VALUE(values+17,cell->legs[callee_leg].last_gen_cseq); SET_INT_VALUE(values+18, cell->flags); set_final_update_cols(values+19, cell, 1); CON_PS_REFERENCE(dialog_db_handle) = &my_ps_update; if((dialog_dbf.update(dialog_db_handle, (insert_keys), 0, (values), (insert_keys+12), (values+12), 2, 10)) !=0){ LM_ERR("could not update database info\n"); goto error; } /* dialog saved */ run_dlg_callbacks( DLGCB_SAVED, cell, 0, DLG_DIR_NONE, 0); cell->flags &= ~(DLG_FLAG_CHANGED|DLG_FLAG_VP_CHANGED); } else if (cell->flags & DLG_FLAG_VP_CHANGED) { cell->flags |= DLG_FLAG_VP_CHANGED; VAL_TYPE(values) = VAL_TYPE(values+1) = VAL_TYPE(values+21) = DB_INT; VAL_TYPE(values+19) = VAL_TYPE(values+20) = DB_STR; /* lock the entry */ entry = (d_table->entries)[cell->h_entry]; dlg_lock( d_table, &entry); SET_INT_VALUE(values, cell->h_entry); SET_INT_VALUE(values+1, cell->h_id); set_final_update_cols(values+19, cell, 0); CON_PS_REFERENCE(dialog_db_handle) = &my_ps_update_vp; if((dialog_dbf.update(dialog_db_handle, (insert_keys), 0, (values), (insert_keys+19), (values+19), 2, 3)) !=0){ LM_ERR("could not update database info\n"); goto error; } run_dlg_callbacks( DLGCB_SAVED, cell, 0, DLG_DIR_NONE, 0); cell->flags &= ~DLG_FLAG_VP_CHANGED; } else { return 0; } dlg_unlock( d_table, &entry); return 0; error: dlg_unlock( d_table, &entry); 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; }
static void dual_bye_event(struct dlg_cell* dlg, struct sip_msg *req, int extra_unref) { int event, old_state, new_state, unref, ret; struct dlg_cell *curr; event = DLG_EVENT_REQBYE; last_dst_leg = dlg->legs_no[DLG_LEG_200OK]; next_state_dlg(dlg, event, DLG_DIR_DOWNSTREAM, &old_state, &new_state, &unref, 0); unref += extra_unref; if(new_state == DLG_STATE_DELETED && old_state != DLG_STATE_DELETED){ LM_DBG("removing dialog with h_entry %u and h_id %u\n", dlg->h_entry, dlg->h_id); /*destroy linkers */ dlg_lock_dlg(dlg); destroy_linkers(dlg->profile_links, 0); dlg->profile_links = NULL; dlg_unlock_dlg(dlg); /* 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_leg_print_info( dlg, DLG_CALLER_LEG, tag), dlg_leg_print_info( dlg, callee_idx(dlg), tag)); } else if (ret > 0) { LM_DBG("dlg already expired (not in timer list) %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_leg_print_info( dlg, DLG_CALLER_LEG, tag), dlg_leg_print_info( dlg, callee_idx(dlg), tag)); } else { /* successfully removed from timer list */ unref++; } curr = current_dlg_pointer; current_dlg_pointer = dlg; /* dialog terminated (BYE) */ run_dlg_callbacks( DLGCB_TERMINATED, dlg, req, DLG_DIR_NONE, 0); current_dlg_pointer = curr; LM_DBG("first final reply\n"); /* derefering the dialog */ unref_dlg(dlg, unref); if_update_stat( dlg_enable_stats, active_dlgs, -1); } if(new_state == DLG_STATE_DELETED && old_state == DLG_STATE_DELETED ) { /* trash the dialog from DB and memory */ LM_DBG("second final reply\n"); /* delete the dialog from DB */ if (should_remove_dlg_db()) remove_dialog_from_db(dlg); /* force delete from mem */ unref_dlg(dlg, unref); } }
/* callback function to handle responses to the BYE request */ void bye_reply_cb(struct cell* t, int type, struct tmcb_params* ps){ struct dlg_cell* dlg; int event, old_state, new_state, unref, ret; dlg_iuid_t *iuid = NULL; if(ps->param == NULL || *ps->param == NULL){ LM_ERR("invalid parameter\n"); return; } if(ps->code < 200){ LM_DBG("receiving a provisional reply\n"); return; } LM_DBG("receiving a final reply %d\n",ps->code); iuid = (dlg_iuid_t*)(*ps->param); dlg = dlg_get_by_iuid(iuid); if(dlg==0) return; event = DLG_EVENT_REQBYE; next_state_dlg(dlg, event, &old_state, &new_state, &unref); if(new_state == DLG_STATE_DELETED && old_state != DLG_STATE_DELETED){ LM_DBG("removing dialog with h_entry %u and h_id %u\n", dlg->h_entry, dlg->h_id); /* remove from timer */ ret = remove_dialog_timer(&dlg->tl); if (ret < 0) { LM_CRIT("unable to unlink the timer on dlg %p [%u:%u] " "with clid '%.*s' 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 { unref++; } /* dialog terminated (BYE) */ run_dlg_callbacks( DLGCB_TERMINATED, dlg, ps->req, ps->rpl, DLG_DIR_NONE, 0); LM_DBG("first final reply\n"); /* derefering the dialog */ dlg_unref(dlg, unref+1); if_update_stat( dlg_enable_stats, active_dlgs, -1); } if(new_state == DLG_STATE_DELETED && old_state == DLG_STATE_DELETED ) { /* trash the dialog from DB and memory */ LM_DBG("second final reply\n"); /* delete the dialog from DB */ if (dlg_db_mode) remove_dialog_from_db(dlg); /* force delete from mem */ dlg_unref(dlg, 1); } dlg_iuid_sfree(iuid); }
/*! * \brief Function that is registered as TM callback and called on replies * * Function that is registered as TM callback and called on replies. It * parses the reply and set the appropriate event. This is then used to * update the dialog state, run eventual dialog callbacks and save or * update the necessary informations about the dialog. * \see next_state_dlg * \param t transaction, unused * \param type type of the entered callback * \param param saved dialog structure in the callback */ static void dlg_onreply(struct cell* t, int type, struct tmcb_params *param) { struct dlg_cell *dlg; struct dlg_cell_out *dlg_out = 0; int new_state, old_state, unref, event; str to_tag, to_uri; struct sip_msg *req = param->req; struct sip_msg *rpl = param->rpl; struct dlg_entry_out* dlg_entry_out = 0; if (t && t->fwded_totags) LM_DBG("ONREPLY CALL_BACK from TM received and type is [%i] and TO is [%.*s]\n", type, t->fwded_totags->tag.len, t->fwded_totags->tag.s); else LM_DBG("ONREPLY CALL_BACK from TM received and type is [%i]\n", type); dlg = (struct dlg_cell *) (*param->param); if (shutdown_done || dlg == 0) return; if (t) { dlg->transaction = t; } LM_DBG("DLG dialogid is entry:id [%i:%i]\n", dlg->h_entry, dlg->h_id); if (type == TMCB_RESPONSE_FWDED) { // The state does not change, but the msg is mutable in this callback LM_DBG("TMCB_RESPONSE_FWDED from TM received"); run_dlg_callbacks(DLGCB_RESPONSE_FWDED, dlg, req, rpl, DLG_DIR_UPSTREAM, 0); return; } if (type == TMCB_RESPONSE_OUT) { LM_DBG("TMCB_RESPONSE_OUT\n"); return; } if (type == TMCB_RESPONSE_READY) { if (rpl == FAKED_REPLY) { LM_DBG("Faked reply\n"); return; } // get to tag LM_DBG("Extracting to-tag from reply"); if (!rpl->to && ((parse_headers(rpl, HDR_TO_F, 0) < 0) || !rpl->to)) { LM_ERR("bad reply or missing TO hdr :-/\n"); to_tag.s = 0; to_tag.len = 0; } else { //populate to uri for this branch. to_uri = get_to(rpl)->uri; to_tag = get_to(rpl)->tag_value; if (to_tag.s == 0 || to_tag.len == 0) { LM_ERR("missing TAG param in TO hdr :-/\n"); to_tag.s = 0; to_tag.len = 0; //Here we assume that the transaction module timer will remove any early dialogs return; } } LM_DBG("Got to-tag from response: %.*s \n", to_tag.len, to_tag.s); } if (type == TMCB_DESTROY) event = DLG_EVENT_TDEL; else if (param->code < 200) event = DLG_EVENT_RPL1xx; else if (param->code < 300) event = DLG_EVENT_RPL2xx; else event = DLG_EVENT_RPL3xx; LM_DBG("Calling next_state_dlg and event is %i\n", event); next_state_dlg(dlg, event, &old_state, &new_state, &unref, &to_tag); if (type == TMCB_RESPONSE_READY) { LM_DBG("Checking if there is an existing dialog_out entry with same to-tag"); dlg_entry_out = &dlg->dlg_entry_out; lock_get(dlg->dlg_out_entries_lock); dlg_out = dlg_entry_out->first; LM_DBG("Scanning dlg_entry_out list for dlg_out"); while (dlg_out) { //Check if there is an already dialog_out entry with same To-tag if (dlg_out->to_tag.len == to_tag.len && memcmp(dlg_out->to_tag.s, to_tag.s, dlg_out->to_tag.len) == 0) { //Found a dialog_out entry with same to_tag! LM_DBG("Found dlg_out for to-tag: %.*s\n", dlg_out->to_tag.len, dlg_out->to_tag.s); break; } dlg_out = dlg_out->next; } lock_release(dlg->dlg_out_entries_lock); if (!dlg_out) { LM_DBG("No dlg_out entry found - creating a new dialog_out entry on dialog [%p]\n", dlg); dlg_out = build_new_dlg_out(dlg, &to_uri, &to_tag); link_dlg_out(dlg, dlg_out, 0); /* save callee's cseq, caller cseq, callee contact and callee record route*/ if (populate_leg_info(dlg, rpl, t, DLG_CALLEE_LEG, &to_tag) != 0) { LM_ERR("could not add further info to the dlg out\n"); } if (!dlg_out) { LM_ERR("failed to create new dialog out structure\n"); //TODO do something on this error! } } else { //This dlg_out already exists, update cseq and contact if present LM_DBG("dlg_out entry found - updating cseq's for dialog out [%p] for to-tag [%.*s] \n", dlg_out, dlg_out->to_tag.len, dlg_out->to_tag.s); if ((!rpl->cseq && parse_headers(rpl, HDR_CSEQ_F, 0) < 0) || !rpl->cseq || !rpl->cseq->parsed) { LM_ERR("bad sip message or missing CSeq hdr :-/\n"); } dlg_update_cseq(dlg, DLG_CALLEE_LEG, &((get_cseq(rpl))->number), &(dlg_out->to_tag)); /* extract the contact address to update if present*/ if (!rpl->contact && (parse_headers(rpl, HDR_CONTACT_F, 0) < 0 || !rpl->contact)) { LM_ERR("Can not update callee contact: bad sip message or missing Contact hdr\n"); } else if (parse_contact(rpl->contact) < 0 || ((contact_body_t *) rpl->contact->parsed)->contacts == NULL || ((contact_body_t *) rpl->contact->parsed)->contacts->next != NULL) { LM_ERR("Can not update callee contact: bad Contact HDR\n"); } else { str contact; contact = ((contact_body_t *) rpl->contact->parsed)->contacts->uri; dlg_update_contact(dlg, DLG_CALLEE_LEG, &contact, &(dlg_out->to_tag)); } } } if (new_state == DLG_STATE_EARLY) { run_dlg_callbacks(DLGCB_EARLY, dlg, req, rpl, DLG_DIR_UPSTREAM, 0); return; } LM_DBG("new state is %i and old state is %i\n", new_state, old_state); if ((new_state == DLG_STATE_CONFIRMED) && (event == DLG_EVENT_RPL2xx)) { LM_DBG("dialog %p confirmed \n", dlg); //Remove all the other entries in dialog_out for the same dialog after TM expires the transaction //(not before in order to absorb late in-early-dialog requests). //remove all other dlg_out objects if (dlg_out) { if (d_tmb.register_tmcb(req, NULL, TMCB_DESTROY, unlink_dlgouts_from_cb, (void*) dlg, NULL) < 0) { LM_ERR("failed to register deletion delay function\n"); LM_DBG("Removing all other DLGs"); dlg_remove_dlg_out(dlg_out, dlg, 0); } else { //mark the outs for deletion dlg_remove_dlg_out(dlg_out, dlg, 1); } } else { LM_ERR("There is no dlg_out structure - this is bad\n"); //TODO: add error handling here } /* set start time */ dlg->start_ts = (unsigned int) (time(0)); /* save the settings to the database, * if realtime saving mode configured- save dialog now * else: the next time the timer will fire the update*/ dlg->dflags |= DLG_FLAG_NEW; if (0 != insert_dlg_timer(&dlg->tl, dlg->lifetime)) { LM_CRIT("Unable to insert dlg %p [%u:%u] on event %d [%d->%d] " "with clid '%.*s' and tags '%.*s' \n", dlg, dlg->h_entry, dlg->h_id, event, old_state, new_state, dlg->callid.len, dlg->callid.s, dlg->from_tag.len, dlg->from_tag.s); } else { ref_dlg(dlg, 1); } run_dlg_callbacks(DLGCB_CONFIRMED, dlg, req, rpl, DLG_DIR_UPSTREAM, 0); if (unref) unref_dlg(dlg, unref); return; } if (new_state == DLG_STATE_CONCURRENTLY_CONFIRMED && (old_state == DLG_STATE_CONFIRMED || old_state == DLG_STATE_CONCURRENTLY_CONFIRMED)) { //This is a concurrently confirmed call LM_DBG("This is a concurrently confirmed call."); //Create a new Dialog ID token “X” //Not sure how to do this so just going to use existing Did and add an X character to it str new_did; create_concurrent_did(dlg, &new_did); //assign new did to the created or updated dialog_out entry. update_dlg_out_did(dlg_out, &new_did); //Then, duplicate the dialog_in entry and set its Dialog ID value to new_did //for now rather just create new dlg structure with the correct params - this should be fixed if future use requires struct dlg_cell *new_dlg = 0; new_dlg = build_new_dlg(&(dlg->callid) /*callid*/, &(dlg->from_uri) /*from uri*/, &(dlg->from_tag)/*from_tag*/, &(dlg->req_uri) /*r-uri*/); //assign new did to dlg_in update_dlg_did(new_dlg, &new_did); if (new_dlg == 0) { LM_ERR("failed to create new dialog\n"); return; } //link the new_dlg with dlg_out object link_dlg_out(new_dlg, dlg_out, 0); } if (old_state != DLG_STATE_DELETED && new_state == DLG_STATE_DELETED) { LM_DBG("dialog %p failed (negative reply)\n", dlg); /* dialog setup not completed (3456XX) */ run_dlg_callbacks(DLGCB_FAILED, dlg, req, rpl, DLG_DIR_UPSTREAM, 0); /* do unref */ if (unref) unref_dlg(dlg, unref); return; } if (unref) unref_dlg(dlg, unref); return; }
/*! * \brief Function that is registered as RR callback for dialog tracking * * Function that is registered as RR callback for dialog tracking. It * sets the appropriate events after the SIP method and run the state * machine to update the dialog state. It updates then the saved * dialogs and also the statistics. * \param req SIP request * \param route_params record-route parameter * \param param unused */ void dlg_onroute(struct sip_msg* req, str *route_params, void *param) { 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 Create a new dialog from a sip message * * Create a new dialog from a SIP message, register a callback * to keep track of the dialog with help of the tm module. * This function is either called from the request callback, or * from the dlg_manage function in the configuration script. * \see dlg_onreq * \see w_dlg_manage * \param msg SIP message * \param t transaction * \return 0 on success, -1 on failure */ int dlg_new_dialog(struct sip_msg *req, struct cell *t, const int run_initial_cbs) { struct dlg_cell *dlg; str s; str callid; str ftag; str ttag; str req_uri; unsigned int dir; LM_DBG("starting dlg_new_dialog and method is [%.*s]\n", req->first_line.u.request.method.len, req->first_line.u.request.method.s); if (current_dlg_pointer != NULL) return -1; if (req->first_line.u.request.method_value == METHOD_CANCEL) return -1; if (pre_match_parse(req, &callid, &ftag, &ttag, 0) < 0) { LM_WARN("pre-matching failed\n"); return -1; } if (ttag.s != 0 && ttag.len != 0) return -1; if (pv_printf_s(req, ruri_param_model, &req_uri) < 0) { LM_ERR("error - cannot print the r-uri format\n"); return -1; } trim(&req_uri); if (detect_spirals) { if (spiral_detected == 1) return 0; dir = DLG_DIR_NONE; dlg = get_dlg(&callid, &ftag, &ttag, &dir); if (dlg) { LM_DBG("Callid '%.*s' found, must be a spiraled request\n", callid.len, callid.s); spiral_detected = 1; if (run_initial_cbs) run_dlg_callbacks(DLGCB_SPIRALED, dlg, req, NULL, DLG_DIR_DOWNSTREAM, 0); //Add did to rr header for all spiralled requested INVITEs if (req->first_line.u.request.method_value == METHOD_INVITE) { if (add_dlg_rr_param(dlg, req, dlg->h_entry, dlg->h_id) < 0) { LM_ERR("failed to add RR param\n"); } } // get_dlg has incremented the ref count by 1 unref_dlg(dlg, 1); goto finish; } } spiral_detected = 0; LM_DBG("Building new Dialog for call-id %.*s\n", callid.len, callid.s); LM_DBG("SIP Method: %.*s \n", req->first_line.u.request.method.len, req->first_line.u.request.method.s); dlg = build_new_dlg(&callid /*callid*/, &(get_from(req)->uri) /*from uri*/, &ftag/*from_tag*/, &req_uri /*r-uri*/); if (dlg == 0) { LM_ERR("failed to create new dialog\n"); return -1; } /* save caller's tag, cseq, contact and record route*/ if (populate_leg_info(dlg, req, t, DLG_CALLER_LEG, &(get_from(req)->tag_value)) != 0) { LM_ERR("could not add further info to the dialog\n"); lock_destroy(dlg->dlg_out_entries_lock); lock_dealloc(dlg->dlg_out_entries_lock); shm_free(dlg); return -1; } dlg->transaction = t; /* Populate initial varlist: */ dlg->vars = get_local_varlist_pointer(req, 1); link_dlg(dlg, 0); if (run_initial_cbs) run_create_callbacks(dlg, req); //Dialog will *always* use DID cookie so no need for match mode anymore if (add_dlg_rr_param(dlg, req, dlg->h_entry, dlg->h_id) < 0) { LM_ERR("failed to add RR param\n"); goto error; } if (d_tmb.register_tmcb(req, t, TMCB_RESPONSE_READY | TMCB_RESPONSE_FWDED | TMCB_RESPONSE_OUT, dlg_onreply, (void*) dlg, unref_new_dialog) < 0) { LM_ERR("failed to register TMCB\n"); goto error; } // increase reference counter because of registered callback ref_dlg(dlg, 1); dlg->lifetime = get_dlg_timeout(req); s.s = _dlg_ctx.to_route_name; s.len = strlen(s.s); dlg_set_toroute(dlg, &s); dlg->sflags |= _dlg_ctx.flags; if (_dlg_ctx.to_bye != 0) dlg->dflags |= DLG_FLAG_TOBYE; finish: if (t) { // transaction exists ==> keep ref counter large enough to // avoid premature cleanup and ensure proper dialog referencing if (store_dlg_in_tm(req, t, dlg) < 0) { LM_ERR("failed to store dialog in transaction\n"); goto error; } } else { // no transaction exists ==> postpone work until we see the // request being forwarded statefully if (d_tmb.register_tmcb(req, NULL, TMCB_REQUEST_FWDED, store_dlg_in_tm_cb, (void*) dlg, NULL) < 0) { LM_ERR("failed to register callback for storing dialog in transaction\n"); goto error; } } LM_DBG("Setting current dialog\n"); set_current_dialog(req, dlg); _dlg_ctx.dlg = dlg; ref_dlg(dlg, 1); return 0; error: LM_DBG("Error in build_new_dlg"); if (!spiral_detected) unref_dlg(dlg, 1); // undo ref regarding linking return -1; }
/*! * \brief Timer function that removes expired dialogs, run timeout route * \param tl dialog timer list */ void dlg_ontimeout(struct dlg_tl *tl) { dlg_cell_t *dlg; int new_state, old_state, unref; sip_msg_t *fmsg; void* timeout_cb = 0; /* get the dialog tl payload */ dlg = ((struct dlg_cell*)((char *)(tl) - (unsigned long)(&((struct dlg_cell*)0)->tl))); /* mark dialog as expired */ dlg->dflags |= DLG_FLAG_EXPIRED; if(dlg->state==DLG_STATE_CONFIRMED_NA || dlg->state==DLG_STATE_CONFIRMED) { if(dlg->toroute>0 && dlg->toroute<main_rt.entries && main_rt.rlist[dlg->toroute]!=NULL) { fmsg = faked_msg_next(); if (exec_pre_script_cb(fmsg, REQUEST_CB_TYPE)>0) { dlg_ref(dlg, 1); dlg_set_ctx_iuid(dlg); LM_DBG("executing route %d on timeout\n", dlg->toroute); set_route_type(REQUEST_ROUTE); run_top_route(main_rt.rlist[dlg->toroute], fmsg, 0); dlg_reset_ctx_iuid(); exec_post_script_cb(fmsg, REQUEST_CB_TYPE); dlg_unref(dlg, 1); } } if(dlg->iflags&DLG_IFLAG_TIMEOUTBYE) { /* set the dialog context so that it's available in * tm:local-request event route */ dlg_set_ctx_iuid(dlg); if(dlg_bye_all(dlg, NULL)<0) dlg_unref(dlg, 1); dlg_reset_ctx_iuid(); dlg_unref(dlg, 1); if_update_stat(dlg_enable_stats, expired_dlgs, 1); return; } } next_state_dlg( dlg, DLG_EVENT_REQBYE, &old_state, &new_state, &unref); /* used for computing duration for timed out acknowledged dialog */ if (DLG_STATE_CONFIRMED == old_state) { timeout_cb = (void *)CONFIRMED_DIALOG_STATE; } dlg_run_event_route(dlg, NULL, old_state, new_state); if (new_state==DLG_STATE_DELETED && old_state!=DLG_STATE_DELETED) { LM_WARN("timeout for dlg with CallID '%.*s' and tags '%.*s' '%.*s'\n", 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); /* dialog timeout */ run_dlg_callbacks( DLGCB_EXPIRED, dlg, NULL, NULL, DLG_DIR_NONE, timeout_cb); dlg_unref(dlg, unref+1); if_update_stat( dlg_enable_stats, expired_dlgs, 1); if_update_stat( dlg_enable_stats, active_dlgs, -1); } else { dlg_unref(dlg, 1); } return; }