/*! * \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 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 ht dmq callback */ int dlg_dmq_handle_msg(struct sip_msg* msg, peer_reponse_t* resp, dmq_node_t* node) { int content_length; str body; dlg_cell_t *dlg; int unref = 0; int ret; srjson_doc_t jdoc, prof_jdoc; srjson_t *it = NULL; dlg_dmq_action_t action = DLG_DMQ_NONE; dlg_iuid_t iuid; str profiles = {0, 0}, callid = {0, 0}, tag1 = {0,0}, tag2 = {0,0}, contact1 = {0,0}, contact2 = {0,0}, k={0,0}, v={0,0}; str cseq1 = {0,0}, cseq2 = {0,0}, route_set1 = {0,0}, route_set2 = {0,0}, from_uri = {0,0}, to_uri = {0,0}, req_uri = {0,0}; unsigned int init_ts = 0, start_ts = 0, lifetime = 0; unsigned int state = 1; srjson_t *vj; /* received dmq message */ LM_DBG("dmq message received\n"); if(!msg->content_length) { LM_ERR("no content length header found\n"); goto invalid2; } content_length = get_content_length(msg); if(!content_length) { LM_DBG("content length is 0\n"); goto invalid2; } body.s = get_body(msg); body.len = content_length; if (!body.s) { LM_ERR("unable to get body\n"); goto error; } /* parse body */ LM_DBG("body: %.*s\n", body.len, body.s); srjson_InitDoc(&jdoc, NULL); jdoc.buf = body; if(jdoc.root == NULL) { jdoc.root = srjson_Parse(&jdoc, jdoc.buf.s); if(jdoc.root == NULL) { LM_ERR("invalid json doc [[%s]]\n", jdoc.buf.s); goto invalid; } } for(it=jdoc.root->child; it; it = it->next) { if ((it->string == NULL) || (strcmp(it->string, "vars")==0)) continue; LM_DBG("found field: %s\n", it->string); if (strcmp(it->string, "action")==0) { action = SRJSON_GET_UINT(it); } else if (strcmp(it->string, "h_entry")==0) { iuid.h_entry = SRJSON_GET_UINT(it); } else if (strcmp(it->string, "h_id")==0) { iuid.h_id = SRJSON_GET_UINT(it); } else if (strcmp(it->string, "init_ts")==0) { init_ts = SRJSON_GET_UINT(it); } else if (strcmp(it->string, "start_ts")==0) { start_ts = SRJSON_GET_UINT(it); } else if (strcmp(it->string, "state")==0) { state = SRJSON_GET_UINT(it); } else if (strcmp(it->string, "lifetime")==0) { lifetime = SRJSON_GET_UINT(it); } else if (strcmp(it->string, "callid")==0) { callid.s = it->valuestring; callid.len = strlen(callid.s); } else if (strcmp(it->string, "profiles")==0) { profiles.s = it->valuestring; profiles.len = strlen(profiles.s); } else if (strcmp(it->string, "tag1")==0) { tag1.s = it->valuestring; tag1.len = strlen(tag1.s); } else if (strcmp(it->string, "tag2")==0) { tag2.s = it->valuestring; tag2.len = strlen(tag2.s); } else if (strcmp(it->string, "cseq1")==0) { cseq1.s = it->valuestring; cseq1.len = strlen(cseq1.s); } else if (strcmp(it->string, "cseq2")==0) { cseq2.s = it->valuestring; cseq2.len = strlen(cseq2.s); } else if (strcmp(it->string, "route_set1")==0) { route_set1.s = it->valuestring; route_set1.len = strlen(route_set1.s); } else if (strcmp(it->string, "route_set2")==0) { route_set2.s = it->valuestring; route_set2.len = strlen(route_set2.s); } else if (strcmp(it->string, "contact1")==0) { contact1.s = it->valuestring; contact1.len = strlen(contact1.s); } else if (strcmp(it->string, "contact2")==0) { contact2.s = it->valuestring; contact2.len = strlen(contact2.s); } else if (strcmp(it->string, "from_uri")==0) { from_uri.s = it->valuestring; from_uri.len = strlen(from_uri.s); } else if (strcmp(it->string, "to_uri")==0) { to_uri.s = it->valuestring; to_uri.len = strlen(to_uri.s); } else if (strcmp(it->string, "req_uri")==0) { req_uri.s = it->valuestring; req_uri.len = strlen(req_uri.s); } else { LM_ERR("unrecognized field in json object\n"); } } dlg = dlg_get_by_iuid(&iuid); if (dlg) { LM_DBG("found dialog [%u:%u] at %p\n", iuid.h_entry, iuid.h_id, dlg); unref++; } switch(action) { case DLG_DMQ_UPDATE: LM_DBG("Updating dlg [%u:%u] with callid [%.*s]\n", iuid.h_entry, iuid.h_id, callid.len, callid.s); if (!dlg) { dlg = build_new_dlg(&callid, &from_uri, &to_uri, &tag1, &req_uri); if (!dlg) { LM_ERR("failed to build new dialog\n"); goto error; } if(dlg->h_entry != iuid.h_entry){ LM_ERR("inconsistent hash data from peer: " "make sure all Kamailio's use the same hash size\n"); shm_free(dlg); goto error; } /* link the dialog */ link_dlg(dlg, 0, 0); dlg_set_leg_info(dlg, &tag1, &route_set1, &contact1, &cseq1, 0); /* override generated h_id */ dlg->h_id = iuid.h_id; /* prevent DB sync */ dlg->dflags &= ~(DLG_FLAG_NEW|DLG_FLAG_CHANGED); dlg->iflags |= DLG_IFLAG_DMQ_SYNC; } else { /* remove existing profiles */ if (dlg->profile_links!=NULL) { destroy_linkers(dlg->profile_links); dlg->profile_links = NULL; } } dlg->init_ts = init_ts; dlg->start_ts = start_ts; vj = srjson_GetObjectItem(&jdoc, jdoc.root, "vars"); if(vj!=NULL) { for(it=vj->child; it; it = it->next) { k.s = it->string; k.len = strlen(k.s); v.s = it->valuestring; v.len = strlen(v.s); set_dlg_variable(dlg, &k, &v); } } /* add profiles */ if(profiles.s!=NULL) { srjson_InitDoc(&prof_jdoc, NULL); prof_jdoc.buf = profiles; dlg_json_to_profiles(dlg, &prof_jdoc); srjson_DestroyDoc(&prof_jdoc); } if (state == dlg->state) { break; } /* intentional fallthrough */ case DLG_DMQ_STATE: if (!dlg) { LM_ERR("dialog [%u:%u] not found\n", iuid.h_entry, iuid.h_id); goto error; } if (state < dlg->state) { LM_NOTICE("Ignoring backwards state change on dlg [%u:%u]" " with callid [%.*s] from state [%u] to state [%u]\n", iuid.h_entry, iuid.h_id, dlg->callid.len, dlg->callid.s, dlg->state, state); break; } LM_DBG("State update dlg [%u:%u] with callid [%.*s] from state [%u]" " to state [%u]\n", iuid.h_entry, iuid.h_id, dlg->callid.len, dlg->callid.s, dlg->state, state); switch (state) { case DLG_STATE_EARLY: dlg->start_ts = start_ts; dlg->lifetime = lifetime; dlg_set_leg_info(dlg, &tag1, &route_set1, &contact1, &cseq1, 0); break; case DLG_STATE_CONFIRMED: dlg->start_ts = start_ts; dlg->lifetime = lifetime; dlg_set_leg_info(dlg, &tag1, &route_set1, &contact1, &cseq1, 0); dlg_set_leg_info(dlg, &tag2, &route_set2, &contact2, &cseq2, 1); if (insert_dlg_timer( &dlg->tl, dlg->lifetime ) != 0) { LM_CRIT("Unable to insert dlg timer %p [%u:%u]\n", dlg, dlg->h_entry, dlg->h_id); } else { /* dialog pointer inserted in timer list */ dlg_ref(dlg, 1); } break; case DLG_STATE_DELETED: if (dlg->state == DLG_STATE_CONFIRMED) { ret = remove_dialog_timer(&dlg->tl); if (ret == 0) { /* one extra unref due to removal from timer list */ unref++; } else if (ret < 0) { LM_CRIT("unable to unlink the timer on dlg %p [%u:%u]\n", dlg, dlg->h_entry, dlg->h_id); } } /* prevent DB sync */ dlg->dflags |= DLG_FLAG_NEW; /* keep dialog around for a bit, to prevent out-of-order * syncs to reestablish the dlg */ dlg->init_ts = time(NULL); break; default: LM_ERR("unhandled state update to state %u\n", state); dlg_unref(dlg, unref); goto error; } dlg->state = state; break; case DLG_DMQ_RM: if (!dlg) { LM_DBG("dialog [%u:%u] not found\n", iuid.h_entry, iuid.h_id); goto error; } LM_DBG("Removed dlg [%u:%u] with callid [%.*s] int state [%u]\n", iuid.h_entry, iuid.h_id, dlg->callid.len, dlg->callid.s, dlg->state); if (dlg->state==DLG_STATE_CONFIRMED || dlg->state==DLG_STATE_EARLY) { ret = remove_dialog_timer(&dlg->tl); if (ret == 0) { /* one extra unref due to removal from timer list */ unref++; } else if (ret < 0) { LM_CRIT("unable to unlink the timer on dlg %p [%u:%u]\n", dlg, dlg->h_entry, dlg->h_id); } } /* prevent DB sync */ dlg->dflags |= DLG_FLAG_NEW; unref++; break; case DLG_DMQ_SYNC: dmq_send_all_dlgs(0); break; case DLG_DMQ_NONE: break; } if (dlg && unref) dlg_unref(dlg, unref); srjson_DestroyDoc(&jdoc); resp->reason = dmq_200_rpl; resp->resp_code = 200; return 0; invalid: srjson_DestroyDoc(&jdoc); invalid2: resp->reason = dmq_400_rpl; resp->resp_code = 400; return 0; error: srjson_DestroyDoc(&jdoc); resp->reason = dmq_500_rpl; resp->resp_code = 500; return 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; }
void dlg_bridge_tm_callback(struct cell *t, int type, struct tmcb_params *ps) { struct sip_msg *msg = NULL; dlg_transfer_ctx_t *dtc = NULL; struct dlg_cell *dlg = NULL; str s; str cseq; str empty = {"", 0}; if(ps->param==NULL || *ps->param==0) { LM_DBG("message id not received\n"); return; } dtc = *((dlg_transfer_ctx_t**)ps->param); if(dtc==NULL) return; LM_DBG("completed with status %d\n", ps->code); if(ps->code>=300) goto error; /* 2xx - build dialog/send refer */ msg = ps->rpl; if((msg->cseq==NULL || parse_headers(msg,HDR_CSEQ_F,0)<0) || msg->cseq==NULL || msg->cseq->parsed==NULL) { LM_ERR("bad sip message or missing CSeq hdr :-/\n"); goto error; } cseq = (get_cseq(msg))->number; if((msg->to==NULL && parse_headers(msg, HDR_TO_F,0)<0) || msg->to==NULL) { LM_ERR("bad request or missing TO hdr\n"); goto error; } if(parse_from_header(msg)) { LM_ERR("bad request or missing FROM hdr\n"); goto error; } if((msg->callid==NULL && parse_headers(msg,HDR_CALLID_F,0)<0) || msg->callid==NULL){ LM_ERR("bad request or missing CALLID hdr\n"); goto error; } s = msg->callid->body; trim(&s); /* some sanity checks */ if (s.len==0 || get_from(msg)->tag_value.len==0) { LM_ERR("invalid request -> callid (%d) or from TAG (%d) empty\n", s.len, get_from(msg)->tag_value.len); goto error; } dlg = build_new_dlg(&s /*callid*/, &(get_from(msg)->uri) /*from uri*/, &(get_to(msg)->uri) /*to uri*/, &(get_from(msg)->tag_value)/*from_tag*/, &(get_to(msg)->uri) /*use to as r-uri*/ ); if (dlg==0) { LM_ERR("failed to create new dialog\n"); goto error; } dtc->dlg = dlg; if (dlg_set_leg_info(dlg, &(get_from(msg)->tag_value), &empty, &dlg_bridge_controller, &cseq, DLG_CALLER_LEG)!=0) { LM_ERR("dlg_set_leg_info failed\n"); goto error; } if (populate_leg_info(dlg, msg, t, DLG_CALLEE_LEG, &(get_to(msg)->tag_value)) !=0) { LM_ERR("could not add further info to the dialog\n"); shm_free(dlg); goto error; } if(dlg_refer_callee(dtc)!=0) goto error; return; error: dlg_transfer_ctx_free(dtc); return; }
int dlg_transfer(struct dlg_cell *dlg, str *to, int side) { dlg_transfer_ctx_t *dtc = NULL; struct dlg_cell *ndlg = NULL; str from; str empty = {"", 0}; dtc = (dlg_transfer_ctx_t*)shm_malloc(sizeof(dlg_transfer_ctx_t)); if(dtc==NULL) { LM_ERR("no shm\n"); return -1; } if(side==DLG_CALLEE_LEG) { from = dlg->from_uri; } else { from = dlg->to_uri; } memset(dtc, 0, sizeof(dlg_transfer_ctx_t)); dtc->from.s = (char*)shm_malloc((from.len+1)*sizeof(char)); if(dtc->from.s==NULL) { LM_ERR("no shm\n"); shm_free(dtc); return -1; } dtc->to.s = (char*)shm_malloc((to->len+1)*sizeof(char)); if(dtc->to.s==NULL) { LM_ERR("no shm\n"); shm_free(dtc->from.s); shm_free(dtc); return -1; } memcpy(dtc->from.s, from.s, from.len); dtc->from.len = from.len; dtc->from.s[dtc->from.len] = '\0'; memcpy(dtc->to.s, to->s, to->len); dtc->to.len = to->len; dtc->to.s[dtc->to.len] = '\0'; if(side==DLG_CALLER_LEG) ndlg = build_new_dlg(&dlg->callid /*callid*/, &dlg->to_uri /*from uri*/, &dlg->from_uri /*to uri*/, &dlg->tag[side]/*from_tag*/, &dlg->req_uri /*req uri */ ); else ndlg = build_new_dlg(&dlg->callid /*callid*/, &dlg->from_uri /*from uri*/, &dlg->to_uri /*to uri*/, &dlg->tag[side]/*from_tag*/, &dlg->req_uri /*req uri */ ); if (ndlg==0) { LM_ERR("failed to create new dialog\n"); goto error; } dtc->dlg = ndlg; if (dlg_set_leg_info(ndlg, &dlg->tag[side], &empty, &dlg->contact[side], &dlg->cseq[side], DLG_CALLER_LEG)!=0) { LM_ERR("dlg_set_leg_info failed for caller\n"); goto error; } if(side==DLG_CALLEE_LEG) side = DLG_CALLER_LEG; else side = DLG_CALLEE_LEG; if (dlg_set_leg_info(ndlg, &dlg->tag[side], &dlg->route_set[side], &dlg->contact[side], &dlg->cseq[side], DLG_CALLEE_LEG)!=0) { LM_ERR("dlg_set_leg_info failed for caller\n"); goto error; } if(dlg_refer_callee(dtc)!=0) goto error; return 0; error: dlg_transfer_ctx_free(dtc); return -1; }
/** * replicates the remote update of an ongoing dialog locally * by reading the relevant information using the Binary Packet Interface */ int dlg_replicated_update(void) { struct dlg_cell *dlg; str call_id, from_tag, to_tag, from_uri, to_uri, vars, profiles; unsigned int dir, dst_leg; int timeout, h_entry; str st; struct dlg_entry *d_entry; bin_pop_str(&call_id); bin_pop_str(&from_tag); bin_pop_str(&to_tag); bin_pop_str(&from_uri); bin_pop_str(&to_uri); LM_DBG("replicated update for ['%.*s' '%.*s' '%.*s' '%.*s' '%.*s']\n", call_id.len, call_id.s, from_tag.len, from_tag.s, to_tag.len, to_tag.s, from_uri.len, from_uri.s, to_uri.len, to_uri.s); dlg = get_dlg(&call_id, &from_tag, &to_tag, &dir, &dst_leg); h_entry = dlg_hash(&call_id); d_entry = &d_table->entries[h_entry]; dlg_lock(d_table, d_entry); if (!dlg) { /* TODO: change to LM_DBG */ LM_ERR("dialog not found, building new\n"); dlg = build_new_dlg(&call_id, &from_uri, &to_uri, &from_tag); if (!dlg) { LM_ERR("Failed to create replicated dialog!\n"); goto error; } return dlg_replicated_create(dlg, &from_tag, &to_tag, 0); } bin_skip_int(2); bin_pop_int(&dlg->state); bin_skip_str(2); bin_pop_str(&st); if (dlg_update_cseq(dlg, DLG_CALLER_LEG, &st, 0) != 0) { LM_ERR("failed to update caller cseq\n"); goto error; } bin_pop_str(&st); if (dlg_update_cseq(dlg, callee_idx(dlg), &st, 0) != 0) { LM_ERR("failed to update callee cseq\n"); goto error; } bin_skip_str(6); bin_pop_str(&vars); bin_pop_str(&profiles); bin_pop_int(&dlg->user_flags); bin_pop_int(&dlg->flags); bin_pop_int(&timeout); bin_skip_int(2); timeout -= time(0); LM_DBG("Received updated timeout of %d for dialog %.*s\n",timeout,call_id.len,call_id.s); if (dlg->lifetime != timeout) { dlg->lifetime = timeout; if (update_dlg_timer(&dlg->tl, dlg->lifetime) == -1) LM_ERR("failed to update dialog lifetime!\n"); } unref_dlg_unsafe(dlg, 1, d_entry); if (vars.s && vars.len != 0) read_dialog_vars(vars.s, vars.len, dlg); dlg_unlock(d_table, d_entry); if (profiles.s && profiles.len != 0) read_dialog_profiles(profiles.s, profiles.len, dlg, 1, 1); return 0; error: dlg_unlock(d_table, d_entry); return -1; }
/** * replicates a confirmed dialog from another OpenSIPS instance * by reading the relevant information using the Binary Packet Interface */ int dlg_replicated_create(struct dlg_cell *cell, str *ftag, str *ttag, int safe) { int next_id, h_entry; unsigned int dir, dst_leg; str callid, from_uri, to_uri, from_tag, to_tag; str cseq1,cseq2,contact1,contact2,rroute1,rroute2,mangled_fu,mangled_tu; str sock, vars, profiles; struct dlg_cell *dlg = NULL; struct socket_info *caller_sock, *callee_sock; struct dlg_entry *d_entry; if_update_stat(dlg_enable_stats, processed_dlgs, 1); LM_DBG("Received replicated dialog!\n"); if (!cell) { bin_pop_str(&callid); bin_pop_str(&from_tag); bin_pop_str(&to_tag); bin_pop_str(&from_uri); bin_pop_str(&to_uri); dlg = get_dlg(&callid, &from_tag, &to_tag, &dir, &dst_leg); h_entry = dlg_hash(&callid); d_entry = &d_table->entries[h_entry]; if (safe) dlg_lock(d_table, d_entry); if (dlg) { LM_DBG("Dialog with ci '%.*s' is already created\n", callid.len, callid.s); unref_dlg_unsafe(dlg, 1, d_entry); dlg_unlock(d_table, d_entry); return 0; } dlg = build_new_dlg(&callid, &from_uri, &to_uri, &from_tag); if (!dlg) { LM_ERR("Failed to create replicated dialog!\n"); goto pre_linking_error; } } else { h_entry = dlg_hash(&cell->callid); d_entry = &d_table->entries[h_entry]; if (safe) dlg_lock(d_table, d_entry); from_tag = *ftag; to_tag = *ttag; dlg = cell; } bin_pop_int(&dlg->h_id); bin_pop_int(&dlg->start_ts); bin_pop_int(&dlg->state); next_id = d_table->entries[dlg->h_entry].next_id; d_table->entries[dlg->h_entry].next_id = (next_id <= dlg->h_id) ? (dlg->h_id + 1) : next_id; if (bin_pop_str(&sock)) goto pre_linking_error; caller_sock = fetch_socket_info(&sock); if (bin_pop_str(&sock)) goto pre_linking_error; callee_sock = fetch_socket_info(&sock); if (!caller_sock || !callee_sock) { LM_ERR("Dialog in DB doesn't match any listening sockets\n"); goto pre_linking_error; } bin_pop_str(&cseq1); bin_pop_str(&cseq2); bin_pop_str(&rroute1); bin_pop_str(&rroute2); bin_pop_str(&contact1); bin_pop_str(&contact2); bin_pop_str(&mangled_fu); bin_pop_str(&mangled_tu); /* add the 2 legs */ if (dlg_add_leg_info(dlg, &from_tag, &rroute1, &contact1, &cseq1, caller_sock, 0, 0) != 0 || dlg_add_leg_info(dlg, &to_tag, &rroute2, &contact2, &cseq2, callee_sock, &mangled_fu, &mangled_tu) != 0) { LM_ERR("dlg_set_leg_info failed\n"); goto pre_linking_error; } dlg->legs_no[DLG_LEG_200OK] = DLG_FIRST_CALLEE_LEG; /* link the dialog into the hash */ dlg->h_id = d_entry->next_id++; if (!d_entry->first) d_entry->first = d_entry->last = dlg; else { d_entry->last->next = dlg; dlg->prev = d_entry->last; d_entry->last = dlg; } dlg->ref++; d_entry->cnt++; bin_pop_str(&vars); bin_pop_str(&profiles); bin_pop_int(&dlg->user_flags); bin_pop_int(&dlg->flags); bin_pop_int((void *)&dlg->tl.timeout); bin_pop_int(&dlg->legs[DLG_CALLER_LEG].last_gen_cseq); bin_pop_int(&dlg->legs[callee_idx(dlg)].last_gen_cseq); if (dlg->tl.timeout <= (unsigned int)time(0)) dlg->tl.timeout = 0; else dlg->tl.timeout -= (unsigned int)time(0); /* restore the timer values */ if (insert_dlg_timer(&dlg->tl, (int)dlg->tl.timeout) != 0) { LM_CRIT("Unable to insert dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[callee_idx(dlg)].tag.len, ZSW(dlg->legs[callee_idx(dlg)].tag.s)); goto error; } if (dlg->state == DLG_STATE_CONFIRMED_NA || dlg->state == DLG_STATE_CONFIRMED) active_dlgs_cnt++; /* reference the dialog as kept in the timer list */ ref_dlg_unsafe(dlg, 1); LM_DBG("Received initial timeout of %d for dialog %.*s, safe = %d\n",dlg->tl.timeout,callid.len,callid.s,safe); dlg->lifetime = 0; /* Do not replicate the pinging - we might terminate dialogs badly when running as backup if (dlg->flags & DLG_FLAG_PING_CALLER || dlg->flags & DLG_FLAG_PING_CALLEE) { if (insert_ping_timer(dlg) != 0) LM_CRIT("Unable to insert dlg %p into ping timer\n",dlg); else { ref_dlg_unsafe(dlg, 1); } } */ if (dlg_db_mode == DB_MODE_DELAYED) { /* to be later removed by timer */ ref_dlg_unsafe(dlg, 1); } if (vars.s && vars.len != 0) read_dialog_vars(vars.s, vars.len, dlg); dlg_unlock(d_table, d_entry); if (profiles.s && profiles.len != 0) read_dialog_profiles(profiles.s, profiles.len, dlg, 0, 1); if_update_stat(dlg_enable_stats, active_dlgs, 1); run_load_callback_per_dlg(dlg); return 0; pre_linking_error: dlg_unlock(d_table, d_entry); if (dlg) destroy_dlg(dlg); return -1; error: dlg_unlock(d_table, d_entry); if (dlg) unref_dlg(dlg, 1); return -1; }
/** * replicates a confirmed dialog from another OpenSIPS instance * by reading the relevant information using the Binary Packet Interface */ int dlg_replicated_create(bin_packet_t *packet, struct dlg_cell *cell, str *ftag, str *ttag, int safe) { int h_entry; unsigned int dir, dst_leg; str callid = { NULL, 0 }, from_uri, to_uri, from_tag, to_tag; str cseq1, cseq2, contact1, contact2, rroute1, rroute2, mangled_fu, mangled_tu; str sock, vars, profiles; struct dlg_cell *dlg = NULL; struct socket_info *caller_sock, *callee_sock; struct dlg_entry *d_entry; LM_DBG("Received replicated dialog!\n"); if (!cell) { DLG_BIN_POP(str, packet, callid, malformed); DLG_BIN_POP(str, packet, from_tag, malformed); DLG_BIN_POP(str, packet, to_tag, malformed); DLG_BIN_POP(str, packet, from_uri, malformed); DLG_BIN_POP(str, packet, to_uri, malformed); dlg = get_dlg(&callid, &from_tag, &to_tag, &dir, &dst_leg); h_entry = dlg_hash(&callid); d_entry = &d_table->entries[h_entry]; if (safe) dlg_lock(d_table, d_entry); if (dlg) { LM_DBG("Dialog with ci '%.*s' is already created\n", callid.len, callid.s); unref_dlg_unsafe(dlg, 1, d_entry); /* unmark dlg as loaded from DB (otherwise it would have been * dropped later when syncing from cluster is done) */ dlg->flags &= ~DLG_FLAG_FROM_DB; dlg_unlock(d_table, d_entry); return 0; } dlg = build_new_dlg(&callid, &from_uri, &to_uri, &from_tag); if (!dlg) { LM_ERR("Failed to create replicated dialog!\n"); goto pre_linking_error; } } else { h_entry = dlg_hash(&cell->callid); d_entry = &d_table->entries[h_entry]; if (safe) dlg_lock(d_table, d_entry); from_tag = *ftag; to_tag = *ttag; dlg = cell; } if_update_stat(dlg_enable_stats, processed_dlgs, 1); DLG_BIN_POP(int, packet, dlg->h_id, pre_linking_error); DLG_BIN_POP(int, packet, dlg->start_ts, pre_linking_error); DLG_BIN_POP(int, packet, dlg->state, pre_linking_error); /* next_id follows the max value of all replicated ids */ if (d_table->entries[dlg->h_entry].next_id <= dlg->h_id) d_table->entries[dlg->h_entry].next_id = dlg->h_id + 1; DLG_BIN_POP(str, packet, sock, pre_linking_error); caller_sock = fetch_socket_info(&sock); DLG_BIN_POP(str, packet, sock, pre_linking_error); callee_sock = fetch_socket_info(&sock); if (!caller_sock || !callee_sock) { LM_ERR("Replicated dialog doesn't match any listening sockets\n"); goto pre_linking_error; } DLG_BIN_POP(str, packet, cseq1, pre_linking_error); DLG_BIN_POP(str, packet, cseq2, pre_linking_error); DLG_BIN_POP(str, packet, rroute1, pre_linking_error); DLG_BIN_POP(str, packet, rroute2, pre_linking_error); DLG_BIN_POP(str, packet, contact1, pre_linking_error); DLG_BIN_POP(str, packet, contact2, pre_linking_error); DLG_BIN_POP(str, packet, mangled_fu, pre_linking_error); DLG_BIN_POP(str, packet, mangled_tu, pre_linking_error); /* add the 2 legs */ /* TODO - sdp here */ if (dlg_update_leg_info(0, dlg, &from_tag, &rroute1, &contact1, &cseq1, caller_sock, 0, 0,0) != 0 || dlg_update_leg_info(1, dlg, &to_tag, &rroute2, &contact2, &cseq2, callee_sock, &mangled_fu, &mangled_tu,0) != 0) { LM_ERR("dlg_set_leg_info failed\n"); goto pre_linking_error; } dlg->legs_no[DLG_LEG_200OK] = DLG_FIRST_CALLEE_LEG; /* link the dialog into the hash */ if (!d_entry->first) d_entry->first = d_entry->last = dlg; else { d_entry->last->next = dlg; dlg->prev = d_entry->last; d_entry->last = dlg; } dlg->ref++; d_entry->cnt++; DLG_BIN_POP(str, packet, vars, pre_linking_error); DLG_BIN_POP(str, packet, profiles, pre_linking_error); DLG_BIN_POP(int, packet, dlg->user_flags, pre_linking_error); DLG_BIN_POP(int, packet, dlg->mod_flags, pre_linking_error); DLG_BIN_POP(int, packet, dlg->flags, pre_linking_error); /* also save the dialog into the DB on this instance */ dlg->flags |= DLG_FLAG_NEW; DLG_BIN_POP(int, packet, dlg->tl.timeout, pre_linking_error); DLG_BIN_POP(int, packet, dlg->legs[DLG_CALLER_LEG].last_gen_cseq, pre_linking_error); DLG_BIN_POP(int, packet, dlg->legs[callee_idx(dlg)].last_gen_cseq, pre_linking_error); if (dlg->tl.timeout <= (unsigned int) time(0)) dlg->tl.timeout = 0; else dlg->tl.timeout -= (unsigned int) time(0); /* restore the timer values */ if (insert_dlg_timer(&dlg->tl, (int) dlg->tl.timeout) != 0) { LM_CRIT("Unable to insert dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[callee_idx(dlg)].tag.len, ZSW(dlg->legs[callee_idx(dlg)].tag.s)); goto error; } if (dlg->state == DLG_STATE_CONFIRMED_NA || dlg->state == DLG_STATE_CONFIRMED) active_dlgs_cnt++; /* reference the dialog as kept in the timer list */ ref_dlg_unsafe(dlg, 1); LM_DBG("Received initial timeout of %d for dialog %.*s, safe = %d\n", dlg->tl.timeout, callid.len, callid.s, safe); dlg->lifetime = 0; if (dlg->flags & DLG_FLAG_PING_CALLER || dlg->flags & DLG_FLAG_PING_CALLEE) { if (insert_ping_timer(dlg) != 0) LM_CRIT("Unable to insert dlg %p into ping timer\n",dlg); else { ref_dlg_unsafe(dlg, 1); } } if (dlg_db_mode == DB_MODE_DELAYED) { /* to be later removed by timer */ ref_dlg_unsafe(dlg, 1); } if (vars.s && vars.len != 0) read_dialog_vars(vars.s, vars.len, dlg); dlg_unlock(d_table, d_entry); if (profiles.s && profiles.len != 0) read_dialog_profiles(profiles.s, profiles.len, dlg, 0, 1); if_update_stat(dlg_enable_stats, active_dlgs, 1); run_load_callback_per_dlg(dlg); return 0; pre_linking_error: dlg_unlock(d_table, d_entry); if (dlg) destroy_dlg(dlg); return -1; error: dlg_unlock(d_table, d_entry); if (dlg) unref_dlg(dlg, 1); malformed: return -1; }
/** * replicates the remote update of an ongoing dialog locally * by reading the relevant information using the Binary Packet Interface */ int dlg_replicated_update(bin_packet_t *packet) { struct dlg_cell *dlg; str call_id, from_tag, to_tag, from_uri, to_uri, vars, profiles; unsigned int dir, dst_leg; int timeout, h_entry; str st; struct dlg_entry *d_entry; int rcv_flags, save_new_flag; bin_pop_str(packet, &call_id); bin_pop_str(packet, &from_tag); bin_pop_str(packet, &to_tag); bin_pop_str(packet, &from_uri); bin_pop_str(packet, &to_uri); LM_DBG("replicated update for ['%.*s' '%.*s' '%.*s' '%.*s' '%.*s']\n", call_id.len, call_id.s, from_tag.len, from_tag.s, to_tag.len, to_tag.s, from_uri.len, from_uri.s, to_uri.len, to_uri.s); dlg = get_dlg(&call_id, &from_tag, &to_tag, &dir, &dst_leg); h_entry = dlg_hash(&call_id); d_entry = &d_table->entries[h_entry]; dlg_lock(d_table, d_entry); if (!dlg) { LM_DBG("dialog not found, building new\n"); dlg = build_new_dlg(&call_id, &from_uri, &to_uri, &from_tag); if (!dlg) { LM_ERR("Failed to create replicated dialog!\n"); goto error; } return dlg_replicated_create(packet ,dlg, &from_tag, &to_tag, 0); } bin_skip_int(packet, 2); bin_pop_int(packet, &dlg->state); bin_skip_str(packet, 2); bin_pop_str(packet, &st); if (dlg_update_cseq(dlg, DLG_CALLER_LEG, &st, 0) != 0) { LM_ERR("failed to update caller cseq\n"); goto error; } bin_pop_str(packet, &st); if (dlg_update_cseq(dlg, callee_idx(dlg), &st, 0) != 0) { LM_ERR("failed to update callee cseq\n"); goto error; } bin_skip_str(packet, 6); bin_pop_str(packet, &vars); bin_pop_str(packet, &profiles); bin_pop_int(packet, &dlg->user_flags); bin_pop_int(packet, &dlg->mod_flags); bin_pop_int(packet, &rcv_flags); /* make sure an update received immediately after a create can't * incorrectly erase the DLG_FLAG_NEW before locally writing to DB */ save_new_flag = dlg->flags & DLG_FLAG_NEW; dlg->flags = rcv_flags; dlg->flags |= ((save_new_flag ? DLG_FLAG_NEW : 0) | DLG_FLAG_CHANGED); bin_pop_int(packet, &timeout); bin_skip_int(packet, 2); timeout -= time(0); LM_DBG("Received updated timeout of %d for dialog %.*s\n", timeout, call_id.len, call_id.s); if (dlg->lifetime != timeout) { dlg->lifetime = timeout; switch (update_dlg_timer(&dlg->tl, dlg->lifetime) ) { case -1: LM_ERR("failed to update dialog lifetime!\n"); /* continue */ case 0: /* timeout value was updated */ break; case 1: /* dlg inserted in timer list with new expire (reference it)*/ ref_dlg(dlg,1); } } unref_dlg_unsafe(dlg, 1, d_entry); if (vars.s && vars.len != 0) read_dialog_vars(vars.s, vars.len, dlg); dlg->flags |= DLG_FLAG_VP_CHANGED; dlg_unlock(d_table, d_entry); if (profiles.s && profiles.len != 0) read_dialog_profiles(profiles.s, profiles.len, dlg, 1, 1); return 0; error: dlg_unlock(d_table, d_entry); return -1; }
static int load_dialog_info_from_db(int dlg_hash_size) { db_res_t * res; db_val_t * values; db_row_t * rows; int i, nr_rows; struct dlg_cell *dlg; str callid, from_uri, to_uri, from_tag, to_tag; str cseq1,cseq2,contact1,contact2,rroute1,rroute2,mangled_fu,mangled_tu; unsigned int next_id; int no_rows = 10; struct socket_info *caller_sock,*callee_sock; res = 0; if((nr_rows = select_entire_dialog_table(&res,&no_rows)) < 0) goto end; nr_rows = RES_ROW_N(res); do { LM_DBG("loading information from database for %i dialogs\n", nr_rows); rows = RES_ROWS(res); /* for every row---dialog */ for(i=0; i<nr_rows; i++){ values = ROW_VALUES(rows + i); if (VAL_NULL(values) || VAL_NULL(values+1)) { LM_ERR("columns %.*s or/and %.*s cannot be null -> skipping\n", h_entry_column.len, h_entry_column.s, h_id_column.len, h_id_column.s); continue; } if (VAL_NULL(values+7) || VAL_NULL(values+8)) { LM_ERR("columns %.*s or/and %.*s cannot be null -> skipping\n", start_time_column.len, start_time_column.s, state_column.len, state_column.s); continue; } if ( VAL_INT(values+8) == DLG_STATE_DELETED ) { LM_DBG("dialog already terminated -> skipping\n"); continue; } caller_sock = create_socket_info(values, 16); callee_sock = create_socket_info(values, 17); if (caller_sock == NULL || callee_sock == NULL) { LM_ERR("Dialog in DB doesn't match any listening sockets"); continue; } /*restore the dialog info*/ GET_STR_VALUE(callid, values, 2, 1, 0); GET_STR_VALUE(from_uri, values, 3, 1, 0); GET_STR_VALUE(from_tag, values, 4, 1, 0); GET_STR_VALUE(to_uri, values, 5, 1, 0); if((dlg=build_new_dlg(&callid, &from_uri, &to_uri, &from_tag))==0){ LM_ERR("failed to build new dialog\n"); goto error; } if(dlg->h_entry != VAL_INT(values)){ LM_ERR("inconsistent hash data in the dialog database: " "you may have restarted opensips using a different " "hash_size: please erase %.*s database and restart\n", dialog_table_name.len, dialog_table_name.s); shm_free(dlg); goto error; } /*link the dialog*/ link_dlg(dlg, 0); dlg->h_id = VAL_INT(values+1); next_id = d_table->entries[dlg->h_entry].next_id; d_table->entries[dlg->h_entry].next_id = (next_id < dlg->h_id) ? (dlg->h_id+1) : next_id; GET_STR_VALUE(to_tag, values, 6, 1, 1); dlg->start_ts = VAL_INT(values+7); dlg->state = VAL_INT(values+8); if (dlg->state==DLG_STATE_CONFIRMED_NA || dlg->state==DLG_STATE_CONFIRMED) { active_dlgs_cnt++; } else if (dlg->state==DLG_STATE_EARLY) { early_dlgs_cnt++; } GET_STR_VALUE(cseq1, values, 10 , 1, 1); GET_STR_VALUE(cseq2, values, 11 , 1, 1); GET_STR_VALUE(rroute1, values, 12, 0, 0); GET_STR_VALUE(rroute2, values, 13, 0, 0); GET_STR_VALUE(contact1, values, 14, 0, 1); GET_STR_VALUE(contact2, values, 15, 0, 1); GET_STR_VALUE(mangled_fu, values, 24,0,1); GET_STR_VALUE(mangled_tu, values, 25,0,1); /* add the 2 legs */ if ( (dlg_add_leg_info( dlg, &from_tag, &rroute1, &contact1, &cseq1, caller_sock,0,0)!=0) || (dlg_add_leg_info( dlg, &to_tag, &rroute2, &contact2, &cseq2, callee_sock,&mangled_fu,&mangled_tu)!=0) ) { LM_ERR("dlg_set_leg_info failed\n"); /* destroy the dialog */ unref_dlg(dlg,1); continue; } dlg->legs_no[DLG_LEG_200OK] = DLG_FIRST_CALLEE_LEG; /* script variables */ if (!VAL_NULL(values+18)) read_dialog_vars( VAL_STR(values+18).s, VAL_STR(values+18).len, dlg); /* profiles */ if (!VAL_NULL(values+19)) read_dialog_profiles( VAL_STR(values+19).s, strlen(VAL_STR(values+19).s), dlg,0); /* script flags */ if (!VAL_NULL(values+20)) { dlg->user_flags = VAL_INT(values+20); } /* top hiding */ dlg->flags = VAL_INT(values+23); if (dlg_db_mode==DB_MODE_SHUTDOWN) dlg->flags |= DLG_FLAG_NEW; /* calculcate timeout */ dlg->tl.timeout = (unsigned int)(VAL_INT(values+9)) + get_ticks(); if (dlg->tl.timeout<=(unsigned int)time(0)) dlg->tl.timeout = 0; else dlg->tl.timeout -= (unsigned int)time(0); /* restore the timer values */ if (0 != insert_dlg_timer( &(dlg->tl), (int)dlg->tl.timeout )) { LM_CRIT("Unable to insert dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[callee_idx(dlg)].tag.len, ZSW(dlg->legs[callee_idx(dlg)].tag.s)); /* destroy the dialog */ unref_dlg(dlg,1); continue; } /* reference the dialog as kept in the timer list */ ref_dlg(dlg,1); LM_DBG("current dialog timeout is %u\n", dlg->tl.timeout); dlg->lifetime = 0; dlg->legs[DLG_CALLER_LEG].last_gen_cseq = (unsigned int)(VAL_INT(values+21)); dlg->legs[callee_idx(dlg)].last_gen_cseq = (unsigned int)(VAL_INT(values+22)); if (dlg->flags & DLG_FLAG_PING_CALLER || dlg->flags & DLG_FLAG_PING_CALLEE) { if (0 != insert_ping_timer(dlg)) LM_CRIT("Unable to insert dlg %p into ping timer\n",dlg); else { /* reference dialog as kept in ping timer list */ ref_dlg(dlg,1); } } next_dialog: ; } /* any more data to be fetched ?*/ if (DB_CAPABILITY(dialog_dbf, DB_CAP_FETCH)) { if (dialog_dbf.fetch_result( dialog_db_handle, &res,no_rows) < 0) { LM_ERR("fetching more rows failed\n"); goto error; } nr_rows = RES_ROW_N(res); } else { nr_rows = 0; } }while (nr_rows>0); end: dialog_dbf.free_result(dialog_db_handle, res); return 0; error: dialog_dbf.free_result(dialog_db_handle, res); return -1; }
static int sync_dlg_db_mem(void) { db_res_t * res; db_val_t * values; db_row_t * rows; struct dlg_entry *d_entry; struct dlg_cell *it,*known_dlg,*dlg=NULL; int i, nr_rows,callee_leg_idx,next_id,db_timeout; int no_rows = 10; unsigned int db_caller_cseq,db_callee_cseq,dlg_caller_cseq,dlg_callee_cseq; struct socket_info *caller_sock,*callee_sock; str callid, from_uri, to_uri, from_tag, to_tag; str cseq1,cseq2,contact1,contact2,rroute1,rroute2,mangled_fu,mangled_tu; res = 0; if((nr_rows = select_entire_dialog_table(&res,&no_rows)) < 0) goto error; nr_rows = RES_ROW_N(res); do { LM_DBG("loading information from database for %i dialogs\n", nr_rows); rows = RES_ROWS(res); /* for every row---dialog */ for(i=0; i<nr_rows; i++){ values = ROW_VALUES(rows + i); if (VAL_NULL(values) || VAL_NULL(values+1)) { LM_ERR("columns %.*s or/and %.*s cannot be null -> skipping\n", h_entry_column.len, h_entry_column.s, h_id_column.len, h_id_column.s); continue; } if (VAL_NULL(values+7) || VAL_NULL(values+8)) { LM_ERR("columns %.*s or/and %.*s cannot be null -> skipping\n", start_time_column.len, start_time_column.s, state_column.len, state_column.s); continue; } if ( VAL_INT(values+8) == DLG_STATE_DELETED ) { LM_DBG("dialog already terminated -> skipping\n"); continue; } /*restore the dialog info*/ GET_STR_VALUE(callid, values, 2, 1, 0); GET_STR_VALUE(from_tag, values, 4, 1, 0); GET_STR_VALUE(to_tag, values, 6, 1, 1); /* TODO - check about hash resize ? maybe hash was lowered & we overflow the hash */ known_dlg = 0; d_entry = &(d_table->entries[VAL_INT(values)]); for (it=d_entry->first;it;it=it->next) if (it->callid.len == callid.len && it->legs[DLG_CALLER_LEG].tag.len == from_tag.len && memcmp(it->callid.s,callid.s,callid.len)==0 && memcmp(it->legs[DLG_CALLER_LEG].tag.s,from_tag.s,from_tag.len)==0) { /* callid & ftag match */ callee_leg_idx = callee_idx(it); if (it->legs[callee_leg_idx].tag.len == to_tag.len && memcmp(it->legs[callee_leg_idx].tag.s,to_tag.s,to_tag.len)==0) { /* full dlg match */ known_dlg = it; break; } } if (known_dlg == 0) { LM_DBG("First seen dialog - load all stuff - callid = [%.*s]\n",callid.len,callid.s); GET_STR_VALUE(from_uri, values, 3, 1, 0); GET_STR_VALUE(to_uri, values, 5, 1, 0); caller_sock = create_socket_info(values, 16); callee_sock = create_socket_info(values, 17); if (caller_sock == NULL || callee_sock == NULL) { LM_ERR("Dialog in DB doesn't match any listening sockets"); continue; } /* first time we see this dialog - build it from scratch */ if((dlg=build_new_dlg(&callid, &from_uri, &to_uri, &from_tag))==0){ LM_ERR("failed to build new dialog\n"); goto error; } if(dlg->h_entry != VAL_INT(values)){ LM_ERR("inconsistent hash data in the dialog database: " "you may have restarted opensips using a different " "hash_size: please erase %.*s database and restart\n", dialog_table_name.len, dialog_table_name.s); shm_free(dlg); goto error; } /*link the dialog*/ link_dlg(dlg, 0); dlg->h_id = VAL_INT(values+1); next_id = d_table->entries[dlg->h_entry].next_id; d_table->entries[dlg->h_entry].next_id = (next_id < dlg->h_id) ? (dlg->h_id+1) : next_id; dlg->start_ts = VAL_INT(values+7); dlg->state = VAL_INT(values+8); if (dlg->state==DLG_STATE_CONFIRMED_NA || dlg->state==DLG_STATE_CONFIRMED) { if_update_stat(dlg_enable_stats, active_dlgs, 1); } else if (dlg->state==DLG_STATE_EARLY) { if_update_stat(dlg_enable_stats, early_dlgs, 1); } GET_STR_VALUE(cseq1, values, 10 , 1, 1); GET_STR_VALUE(cseq2, values, 11 , 1, 1); GET_STR_VALUE(rroute1, values, 12, 0, 0); GET_STR_VALUE(rroute2, values, 13, 0, 0); GET_STR_VALUE(contact1, values, 14, 0, 1); GET_STR_VALUE(contact2, values, 15, 0, 1); GET_STR_VALUE(mangled_fu, values, 24,0,1); GET_STR_VALUE(mangled_tu, values, 25,0,1); /* add the 2 legs */ if ( (dlg_add_leg_info( dlg, &from_tag, &rroute1, &contact1, &cseq1, caller_sock,0,0)!=0) || (dlg_add_leg_info( dlg, &to_tag, &rroute2, &contact2, &cseq2, callee_sock,&mangled_fu,&mangled_tu)!=0) ) { LM_ERR("dlg_set_leg_info failed\n"); /* destroy the dialog */ unref_dlg(dlg,1); continue; } dlg->legs_no[DLG_LEG_200OK] = DLG_FIRST_CALLEE_LEG; /* script variables */ if (!VAL_NULL(values+18)) read_dialog_vars( VAL_STR(values+18).s, VAL_STR(values+18).len, dlg); /* profiles */ if (!VAL_NULL(values+19)) read_dialog_profiles( VAL_STR(values+19).s, strlen(VAL_STR(values+19).s), dlg,0); /* script flags */ if (!VAL_NULL(values+20)) { dlg->user_flags = VAL_INT(values+20); } /* top hiding */ dlg->flags = VAL_INT(values+23); if (dlg_db_mode==DB_MODE_SHUTDOWN) dlg->flags |= DLG_FLAG_NEW; /* calculcate timeout */ dlg->tl.timeout = (unsigned int)(VAL_INT(values+9)) + get_ticks(); if (dlg->tl.timeout<=(unsigned int)time(0)) dlg->tl.timeout = 0; else dlg->tl.timeout -= (unsigned int)time(0); /* restore the timer values */ if (0 != insert_dlg_timer( &(dlg->tl), (int)dlg->tl.timeout )) { LM_CRIT("Unable to insert dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[callee_idx(dlg)].tag.len, ZSW(dlg->legs[callee_idx(dlg)].tag.s)); /* destroy the dialog */ unref_dlg(dlg,1); continue; } /* reference the dialog as kept in the timer list */ ref_dlg(dlg,1); LM_DBG("current dialog timeout is %u\n", dlg->tl.timeout); dlg->lifetime = 0; dlg->legs[DLG_CALLER_LEG].last_gen_cseq = (unsigned int)(VAL_INT(values+21)); dlg->legs[callee_idx(dlg)].last_gen_cseq = (unsigned int)(VAL_INT(values+22)); if (dlg->flags & DLG_FLAG_PING_CALLER || dlg->flags & DLG_FLAG_PING_CALLEE) { if (0 != insert_ping_timer(dlg)) LM_CRIT("Unable to insert dlg %p into ping timer\n",dlg); else { /* reference dialog as kept in ping timer list */ ref_dlg(dlg,1); } } } else { /* we already saw this dialog before * check which is the newer version */ if (known_dlg->state > VAL_INT(values+8)) { LM_DBG("mem has a newer state - ignore \n"); /* we know a newer version compared to the DB * ignore it */ goto next_dialog; } else if (known_dlg->state == VAL_INT(values+8)) { LM_DBG("mem has same state as DB \n"); /* same state :-( no way to tell which is newer */ /* play nice and store longest timeout, although not always correct*/ db_timeout = (unsigned int)(VAL_INT(values+9)) + get_ticks(); if (db_timeout<=(unsigned int)time(0)) db_timeout = 0; else db_timeout -= (unsigned int)time(0); if (known_dlg->tl.timeout < db_timeout) known_dlg->tl.timeout = db_timeout; /* check with is newer cseq for caller leg */ if (!VAL_NULL(values+10)) { cseq1.s = VAL_STR(values+10).s; cseq1.len = strlen(cseq1.s); str2int(&cseq1,&db_caller_cseq); str2int(&known_dlg->legs[DLG_CALLER_LEG].r_cseq,&dlg_caller_cseq); /* Is DB cseq newer ? */ if (db_caller_cseq > dlg_caller_cseq) { if (known_dlg->legs[DLG_CALLER_LEG].r_cseq.len < cseq1.len) { known_dlg->legs[DLG_CALLER_LEG].r_cseq.s = shm_realloc(known_dlg->legs[DLG_CALLER_LEG].r_cseq.s,cseq1.len); if (!known_dlg->legs[DLG_CALLER_LEG].r_cseq.s) { LM_ERR("no more shm\n"); goto next_dialog; } } memcpy(known_dlg->legs[DLG_CALLER_LEG].r_cseq.s,cseq1.s,cseq1.len); known_dlg->legs[DLG_CALLER_LEG].r_cseq.len = cseq1.len; } } else { /* DB has a null cseq - just keep * what we have so far */ ; } /* check with is newer cseq for caller leg */ if (!VAL_NULL(values+11)) { cseq2.s = VAL_STR(values+11).s; cseq2.len = strlen(cseq2.s); callee_leg_idx = callee_idx(known_dlg); str2int(&cseq2,&db_callee_cseq); str2int(&known_dlg->legs[callee_leg_idx].r_cseq,&dlg_callee_cseq); /* Is DB cseq newer ? */ if (db_callee_cseq > dlg_callee_cseq) { if (known_dlg->legs[callee_leg_idx].r_cseq.len < cseq2.len) { known_dlg->legs[callee_leg_idx].r_cseq.s = shm_realloc(known_dlg->legs[callee_leg_idx].r_cseq.s,cseq2.len); if (!known_dlg->legs[callee_leg_idx].r_cseq.s) { LM_ERR("no more shm\n"); goto next_dialog; } } memcpy(known_dlg->legs[callee_leg_idx].r_cseq.s,cseq2.s,cseq2.len); known_dlg->legs[callee_leg_idx].r_cseq.len = cseq2.len; } } else { /* DB has a null cseq - just keep * what we have so far */ ; } /* update ping cseqs, whichever is newer */ if (known_dlg->legs[DLG_CALLER_LEG].last_gen_cseq < (unsigned int)(VAL_INT(values+21))) known_dlg->legs[DLG_CALLER_LEG].last_gen_cseq = (unsigned int)(VAL_INT(values+21)); if (known_dlg->legs[callee_idx(known_dlg)].last_gen_cseq < (unsigned int)(VAL_INT(values+22))) known_dlg->legs[callee_idx(known_dlg)].last_gen_cseq = (unsigned int)(VAL_INT(values+22)); /* update script variables * if already found, delete the old ones * and replace with new one */ if (!VAL_NULL(values+18)) read_dialog_vars( VAL_STR(values+18).s, VAL_STR(values+18).len, known_dlg); /* skip flags - keep what we have - anyway can't tell which is new */ /* profiles - do not insert into a profile * is dlg is already in that profile*/ if (!VAL_NULL(values+19)) read_dialog_profiles( VAL_STR(values+19).s, strlen(VAL_STR(values+19).s), known_dlg,1); } else { /* DB has newer state, just update fields from DB */ LM_DBG("DB has newer state \n"); /* set new state */ known_dlg->state = VAL_INT(values+8); /* update timeout */ known_dlg->tl.timeout = (unsigned int)(VAL_INT(values+9)) + get_ticks(); if (known_dlg->tl.timeout<=(unsigned int)time(0)) known_dlg->tl.timeout = 0; else known_dlg->tl.timeout -= (unsigned int)time(0); /* update cseqs */ if (!VAL_NULL(values+10)) { cseq1.s = VAL_STR(values+10).s; cseq1.len = strlen(cseq1.s); if (known_dlg->legs[DLG_CALLER_LEG].r_cseq.len < cseq1.len) { known_dlg->legs[DLG_CALLER_LEG].r_cseq.s = shm_realloc(known_dlg->legs[DLG_CALLER_LEG].r_cseq.s,cseq1.len); if (!known_dlg->legs[DLG_CALLER_LEG].r_cseq.s) { LM_ERR("no more shm\n"); goto next_dialog; } } memcpy(known_dlg->legs[DLG_CALLER_LEG].r_cseq.s,cseq1.s,cseq1.len); known_dlg->legs[DLG_CALLER_LEG].r_cseq.len = cseq1.len; } if (!VAL_NULL(values+11)) { cseq2.s = VAL_STR(values+11).s; cseq2.len = strlen(cseq1.s); callee_leg_idx = callee_idx(known_dlg); if (known_dlg->legs[callee_leg_idx].r_cseq.len < cseq2.len) { known_dlg->legs[callee_leg_idx].r_cseq.s = shm_realloc(known_dlg->legs[callee_leg_idx].r_cseq.s,cseq2.len); if (!known_dlg->legs[callee_leg_idx].r_cseq.s) { LM_ERR("no more shm\n"); goto next_dialog; } } memcpy(known_dlg->legs[callee_leg_idx].r_cseq.s,cseq2.s,cseq2.len); known_dlg->legs[callee_leg_idx].r_cseq.len = cseq2.len; } /* update ping cseqs */ known_dlg->legs[DLG_CALLER_LEG].last_gen_cseq = (unsigned int)(VAL_INT(values+21)); known_dlg->legs[callee_idx(known_dlg)].last_gen_cseq = (unsigned int)(VAL_INT(values+22)); /* update flags */ known_dlg->flags = VAL_INT(values+23); if (dlg_db_mode==DB_MODE_SHUTDOWN) known_dlg->flags |= DLG_FLAG_NEW; /* update script variables * if already found, delete the old one * and replace with new one */ if (!VAL_NULL(values+18)) read_dialog_vars( VAL_STR(values+18).s, VAL_STR(values+18).len, known_dlg); /* profiles - do not insert into a profile * is dlg is already in that profile*/ if (!VAL_NULL(values+19)) read_dialog_profiles( VAL_STR(values+19).s, strlen(VAL_STR(values+19).s), known_dlg,1); } } next_dialog: ; } /* any more data to be fetched ?*/ if (DB_CAPABILITY(dialog_dbf, DB_CAP_FETCH)) { if (dialog_dbf.fetch_result( dialog_db_handle, &res,no_rows) < 0) { LM_ERR("fetching more rows failed\n"); goto error; } nr_rows = RES_ROW_N(res); } else { nr_rows = 0; } }while (nr_rows>0); return 0; error: return -1; }
int dlg_create_dialog(struct cell* t, struct sip_msg *req,unsigned int flags) { struct dlg_cell *dlg; str s; int extra_ref; /* module is stricly designed for dialog calls */ if (req->first_line.u.request.method_value!=METHOD_INVITE) return -1; if ( (!req->to && parse_headers(req, HDR_TO_F,0)<0) || !req->to ) { LM_ERR("bad request or missing TO hdr :-/\n"); return -1; } s = get_to(req)->tag_value; if (s.s!=0 && s.len!=0) return -1; if ( parse_from_header(req)) { LM_ERR("bad request or missing FROM hdr :-/\n"); return -1; } if ((!req->callid && parse_headers(req,HDR_CALLID_F,0)<0) || !req->callid){ LM_ERR("bad request or missing CALLID hdr :-/\n"); return -1; } s = req->callid->body; trim(&s); /* some sanity checks */ if (s.len==0 || get_from(req)->tag_value.len==0) { LM_ERR("invalid request -> callid (%d) or from TAG (%d) empty\n", s.len, get_from(req)->tag_value.len); return -1; } dlg = build_new_dlg( &s /*callid*/, &(get_from(req)->uri) /*from uri*/, &(get_to(req)->uri) /*to uri*/, &(get_from(req)->tag_value)/*from_tag*/ ); if (dlg==0) { LM_ERR("failed to create new dialog\n"); return -1; } dlg->flags |= flags; /* save caller's tag, cseq, contact and record route*/ if (init_leg_info(dlg, req, t, &(get_from(req)->tag_value),NULL,NULL ) !=0) { LM_ERR("could not add further info to the dialog\n"); shm_free(dlg); return -1; } /* set current dialog */ set_current_dialog(dlg); last_dst_leg = DLG_FIRST_CALLEE_LEG; extra_ref=2; /* extra ref for the callback and current dlg hook */ if (dlg_db_mode == DB_MODE_DELAYED) extra_ref++; /* extra ref for the timer to delete the dialog */ link_dlg( dlg , extra_ref); 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 ( d_tmb.register_tmcb( req, t, TMCB_RESPONSE_PRE_OUT|TMCB_RESPONSE_FWDED|TMCB_TRANS_CANCELLED, dlg_onreply, (void*)dlg, unreference_dialog_create)<0 ) { LM_ERR("failed to register TMCB\n"); goto error; } /* complete the dialog setup only if transaction aleady exists; if not, wait for the TMCB_REQUEST_IN callback to do this job */ if (t) { /* first INVITE seen (dialog created, unconfirmed) */ run_create_callbacks( dlg, req); LM_DBG("t hash_index = %u, t label = %u\n",t->hash_index,t->label); dlg->initial_t_hash_index = t->hash_index; dlg->initial_t_label = t->label; dlg->lifetime = get_dlg_timeout(req); t->dialog_ctx = (void*) dlg; dlg->flags |= DLG_FLAG_ISINIT; } if_update_stat( dlg_enable_stats, processed_dlgs, 1); return 0; error: unref_dlg(dlg,2); dialog_cleanup( req, NULL); if_update_stat(dlg_enable_stats, failed_dlgs, 1); return -1; }