/*! * \brief Parse SIP message and populate leg informations * * Parse SIP message and populate leg informations. * \param dlg the dialog to add cseq, contact & record_route * \param msg sip message * \param t transaction * \param leg type of the call leg * \param tag SIP To tag * \return 0 on success, -1 on failure * \note for a request: get record route in normal order, for a reply get * in reverse order, skipping the ones from the request and the proxies' own */ int populate_leg_info(struct dlg_cell *dlg, struct sip_msg *msg, struct cell* t, unsigned int leg, str *tag) { unsigned int skip_recs; str cseq; str contact; str rr_set; struct socket_info* callee_bind_address = NULL; if (leg == DLG_CALLER_LEG) dlg->caller_bind_addr = msg->rcv.bind_address; else callee_bind_address = msg->rcv.bind_address; /* extract the cseq number as string from the request or response*/ //TO DO - can pair the cseqs here to make sure that the response and request are in sync if ((!msg->cseq && (parse_headers(msg, HDR_CSEQ_F, 0) < 0 || !msg->cseq)) || !msg->cseq->parsed) { LM_ERR("bad sip message or missing CSeq hdr :-/\n"); goto error0; } cseq = (get_cseq(msg))->number; /* extract the contact address */ if (!msg->contact && (parse_headers(msg, HDR_CONTACT_F, 0) < 0 || !msg->contact)) { LM_ERR("bad sip message or missing Contact hdr\n"); goto error0; } if (parse_contact(msg->contact) < 0 || ((contact_body_t *) msg->contact->parsed)->contacts == NULL || ((contact_body_t *) msg->contact->parsed)->contacts->next != NULL) { LM_ERR("bad Contact HDR\n"); goto error0; } contact = ((contact_body_t *) msg->contact->parsed)->contacts->uri; /* extract the RR parts */ if (!msg->record_route && (parse_headers(msg, HDR_EOH_F, 0) < 0)) { LM_ERR("failed to parse record route header\n"); goto error0; } if (leg == DLG_CALLER_LEG) { skip_recs = 0; } else { skip_recs = 0; /* was the 200 OK received or local generated */ /*skip_recs = dlg->from_rr_nb + ((t->relayed_reply_branch >= 0) ? ((t->uac[t->relayed_reply_branch].flags & TM_UAC_FLAG_R2) ? 2 : ((t->uac[t->relayed_reply_branch].flags & TM_UAC_FLAG_RR) ? 1 : 0)) : 0); * */ } if (msg->record_route) { if (print_rr_body(msg->record_route, &rr_set, leg, &skip_recs) != 0) { LM_ERR("failed to print route records \n"); goto error0; } } else { rr_set.s = 0; rr_set.len = 0; } if (leg == DLG_CALLER_LEG) dlg->from_rr_nb = skip_recs; LM_DBG("route_set %.*s, contact %.*s, cseq %.*s and bind_addr %.*s\n", rr_set.len, rr_set.s, contact.len, contact.s, cseq.len, cseq.s, msg->rcv.bind_address->sock_str.len, msg->rcv.bind_address->sock_str.s); if (dlg_set_leg_info(dlg, tag, &rr_set, &contact, &cseq, callee_bind_address, leg) != 0) { LM_ERR("dlg_set_leg_info failed\n"); if (rr_set.s) pkg_free(rr_set.s); goto error0; } if (rr_set.s) pkg_free(rr_set.s); return 0; error0: return -1; }
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; }
/** * @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; }
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; }