mirror of https://github.com/sipwise/kamailio.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
874 lines
21 KiB
874 lines
21 KiB
/*
|
|
* Copyright (C) 2007 Voice System SRL
|
|
*
|
|
* This file is part of Kamailio, a free SIP server.
|
|
*
|
|
* Kamailio is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version
|
|
*
|
|
* Kamailio is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*/
|
|
|
|
|
|
/*!
|
|
* \file
|
|
* \brief Requests
|
|
* \ingroup dialog
|
|
* Module: \ref dialog
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "../../core/dprint.h"
|
|
#include "../../core/ut.h"
|
|
#include "../../lib/srdb1/db.h"
|
|
#include "../../core/config.h"
|
|
#include "../../core/socket_info.h"
|
|
#include "../../core/dset.h"
|
|
#include "../../modules/tm/dlg.h"
|
|
#include "../../modules/tm/tm_load.h"
|
|
#include "../../core/counters.h"
|
|
#include "../../core/parser/contact/parse_contact.h"
|
|
#include "dlg_timer.h"
|
|
#include "dlg_hash.h"
|
|
#include "dlg_handlers.h"
|
|
#include "dlg_req_within.h"
|
|
#include "dlg_db_handler.h"
|
|
|
|
|
|
#define MAX_FWD_HDR "Max-Forwards: " MAX_FWD CRLF
|
|
#define MAX_FWD_HDR_LEN (sizeof(MAX_FWD_HDR) - 1)
|
|
|
|
extern str dlg_extra_hdrs;
|
|
extern str dlg_lreq_callee_headers;
|
|
extern int dlg_ka_failed_limit;
|
|
extern int dlg_filter_mode;
|
|
|
|
extern int bye_early_code;
|
|
extern str bye_early_reason;
|
|
|
|
/**
|
|
*
|
|
*/
|
|
int free_tm_dlg(dlg_t *td)
|
|
{
|
|
if(td) {
|
|
if(td->route_set)
|
|
free_rr(&td->route_set);
|
|
pkg_free(td);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
dlg_t *build_dlg_t(struct dlg_cell *cell, int dir)
|
|
{
|
|
|
|
dlg_t *td = NULL;
|
|
str cseq;
|
|
unsigned int loc_seq;
|
|
char nbuf[MAX_URI_SIZE];
|
|
char dbuf[80];
|
|
str nuri = STR_NULL;
|
|
str duri = STR_NULL;
|
|
size_t sz;
|
|
char *p;
|
|
|
|
/*remote target--- Request URI*/
|
|
if(cell->contact[dir].s == 0 || cell->contact[dir].len == 0) {
|
|
LM_ERR("no contact available\n");
|
|
goto error;
|
|
}
|
|
if(cell->route_set[dir].s == NULL || cell->route_set[dir].len <= 0) {
|
|
/*try to restore alias parameter if no route set */
|
|
nuri.s = nbuf;
|
|
nuri.len = MAX_URI_SIZE;
|
|
duri.s = dbuf;
|
|
duri.len = 80;
|
|
if(uri_restore_rcv_alias(&cell->contact[dir], &nuri, &duri) < 0) {
|
|
nuri.len = 0;
|
|
duri.len = 0;
|
|
}
|
|
}
|
|
if(nuri.len > 0 && duri.len > 0) {
|
|
sz = sizeof(dlg_t) + (nuri.len + duri.len + 2) * sizeof(char);
|
|
} else {
|
|
sz = sizeof(dlg_t);
|
|
}
|
|
td = (dlg_t *)pkg_malloc(sz);
|
|
if(!td) {
|
|
LM_ERR("out of pkg memory\n");
|
|
return NULL;
|
|
}
|
|
memset(td, 0, sz);
|
|
|
|
/*local sequence number*/
|
|
cseq = (dir == DLG_CALLER_LEG) ? cell->cseq[DLG_CALLEE_LEG]
|
|
: cell->cseq[DLG_CALLER_LEG];
|
|
if(str2int(&cseq, &loc_seq) != 0) {
|
|
LM_ERR("invalid cseq\n");
|
|
goto error;
|
|
}
|
|
/*we don not increase here the cseq as this will be done by TM*/
|
|
td->loc_seq.value = loc_seq;
|
|
td->loc_seq.is_set = 1;
|
|
|
|
/*route set*/
|
|
if(cell->route_set[dir].s && cell->route_set[dir].len) {
|
|
if(parse_rr_body(cell->route_set[dir].s, cell->route_set[dir].len,
|
|
&td->route_set)
|
|
!= 0) {
|
|
LM_ERR("failed to parse route set\n");
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
if(nuri.len > 0 && duri.len > 0) {
|
|
/* req uri */
|
|
p = (char *)td + sizeof(dlg_t);
|
|
strncpy(p, nuri.s, nuri.len);
|
|
p[nuri.len] = '\0';
|
|
td->rem_target.s = p;
|
|
td->rem_target.len = nuri.len;
|
|
/* dst uri */
|
|
p += nuri.len + 1;
|
|
strncpy(p, duri.s, duri.len);
|
|
p[duri.len] = '\0';
|
|
td->dst_uri.s = p;
|
|
td->dst_uri.len = duri.len;
|
|
} else {
|
|
td->rem_target = cell->contact[dir];
|
|
}
|
|
|
|
td->rem_uri = (dir == DLG_CALLER_LEG) ? cell->from_uri : cell->to_uri;
|
|
td->loc_uri = (dir == DLG_CALLER_LEG) ? cell->to_uri : cell->from_uri;
|
|
td->id.call_id = cell->callid;
|
|
td->id.rem_tag = cell->tag[dir];
|
|
td->id.loc_tag = (dir == DLG_CALLER_LEG) ? cell->tag[DLG_CALLEE_LEG]
|
|
: cell->tag[DLG_CALLER_LEG];
|
|
|
|
td->state = DLG_CONFIRMED;
|
|
td->send_sock = cell->bind_addr[dir];
|
|
|
|
return td;
|
|
|
|
error:
|
|
free_tm_dlg(td);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* 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("inconsistent 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_CONFIRMED, 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);
|
|
}
|
|
|
|
|
|
/* callback function to handle responses to the keep-alive request */
|
|
void dlg_ka_cb_all(struct cell *t, int type, struct tmcb_params *ps, int dir)
|
|
{
|
|
int tend;
|
|
dlg_cell_t *dlg;
|
|
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) {
|
|
dlg_iuid_sfree(iuid);
|
|
return;
|
|
}
|
|
|
|
if(ps->code == 408 || ps->code == 481) {
|
|
if(dlg->state != DLG_STATE_CONFIRMED) {
|
|
LM_DBG("skip updating non-confirmed dialogs\n");
|
|
goto done;
|
|
}
|
|
if(ps->code == 408
|
|
&& (dlg->cseq[dir].len == 0
|
|
|| (dlg->cseq[dir].len == 1
|
|
&& dlg->cseq[dir].s[0] == '\0'))) {
|
|
LM_DBG("ignore 408 for %s cseq 0\n",
|
|
((dir == DLG_CALLER_LEG) ? "caller" : "callee"));
|
|
goto done;
|
|
}
|
|
tend = 0;
|
|
if(dir == DLG_CALLER_LEG) {
|
|
dlg->ka_src_counter++;
|
|
if(dlg->ka_src_counter >= dlg_ka_failed_limit) {
|
|
tend = 1;
|
|
}
|
|
} else {
|
|
dlg->ka_dst_counter++;
|
|
if(dlg->ka_dst_counter >= dlg_ka_failed_limit) {
|
|
tend = 1;
|
|
}
|
|
}
|
|
if(tend) {
|
|
if(update_dlg_timer(&dlg->tl, 10) < 0) {
|
|
LM_ERR("failed to update dialog lifetime\n");
|
|
goto done;
|
|
}
|
|
dlg->lifetime = 10;
|
|
dlg->dflags |= DLG_FLAG_CHANGED;
|
|
}
|
|
} else {
|
|
if(dlg->state == DLG_STATE_CONFIRMED) {
|
|
if(dir == DLG_CALLER_LEG) {
|
|
dlg->ka_src_counter = 0;
|
|
} else {
|
|
dlg->ka_dst_counter = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
done:
|
|
dlg_unref(dlg, 1);
|
|
dlg_iuid_sfree(iuid);
|
|
}
|
|
|
|
/* callback function to handle responses to the keep-alive request to src */
|
|
void dlg_ka_cb_src(struct cell *t, int type, struct tmcb_params *ps)
|
|
{
|
|
dlg_ka_cb_all(t, type, ps, DLG_CALLER_LEG);
|
|
}
|
|
|
|
/* callback function to handle responses to the keep-alive request to dst */
|
|
void dlg_ka_cb_dst(struct cell *t, int type, struct tmcb_params *ps)
|
|
{
|
|
dlg_ka_cb_all(t, type, ps, DLG_CALLEE_LEG);
|
|
}
|
|
|
|
static inline int build_extra_hdr(
|
|
struct dlg_cell *cell, str *extra_hdrs, str *str_hdr)
|
|
{
|
|
char *p;
|
|
int blen;
|
|
|
|
str_hdr->len = MAX_FWD_HDR_LEN + dlg_extra_hdrs.len;
|
|
if(extra_hdrs && extra_hdrs->len > 0)
|
|
str_hdr->len += extra_hdrs->len;
|
|
|
|
blen = str_hdr->len + 3 /* '\r\n\0' */;
|
|
|
|
/* reserve space for callee headers in local requests */
|
|
if(dlg_lreq_callee_headers.len > 0)
|
|
blen += dlg_lreq_callee_headers.len + 2 /* '\r\n' */;
|
|
|
|
str_hdr->s = (char *)pkg_malloc(blen * sizeof(char));
|
|
if(!str_hdr->s) {
|
|
LM_ERR("out of pkg memory\n");
|
|
goto error;
|
|
}
|
|
|
|
memcpy(str_hdr->s, MAX_FWD_HDR, MAX_FWD_HDR_LEN);
|
|
p = str_hdr->s + MAX_FWD_HDR_LEN;
|
|
if(dlg_extra_hdrs.len) {
|
|
memcpy(p, dlg_extra_hdrs.s, dlg_extra_hdrs.len);
|
|
p += dlg_extra_hdrs.len;
|
|
}
|
|
if(extra_hdrs && extra_hdrs->len > 0)
|
|
memcpy(p, extra_hdrs->s, extra_hdrs->len);
|
|
|
|
return 0;
|
|
|
|
error:
|
|
return -1;
|
|
}
|
|
|
|
|
|
/* cell- pointer to a struct dlg_cell
|
|
* dir- direction: the request will be sent to:
|
|
* DLG_CALLER_LEG (0): caller
|
|
* DLG_CALLEE_LEG (1): callee
|
|
*/
|
|
static inline int send_bye(struct dlg_cell *cell, int dir, str *hdrs)
|
|
{
|
|
uac_req_t uac_r;
|
|
dlg_t *dialog_info;
|
|
str met = {"BYE", 3};
|
|
int result;
|
|
dlg_iuid_t *iuid = NULL;
|
|
str lhdrs;
|
|
|
|
/* dialog is already in deleted state, nothing to do */
|
|
if(cell->state == DLG_STATE_DELETED) {
|
|
LM_WARN("dlg [%u:%u] with callid %.*s already in deleted state, BYE "
|
|
"not sent.\n",
|
|
cell->h_entry, cell->h_id, cell->callid.len, cell->callid.s);
|
|
return 0;
|
|
}
|
|
|
|
/* Send Cancel or final response for non-confirmed dialogs */
|
|
if(cell->state != DLG_STATE_CONFIRMED_NA
|
|
&& cell->state != DLG_STATE_CONFIRMED) {
|
|
if(cell->t) {
|
|
if(dir == DLG_CALLER_LEG) {
|
|
if(d_tmb.t_reply(cell->t->uas.request, bye_early_code,
|
|
bye_early_reason.s)
|
|
< 0) {
|
|
LM_ERR("Failed to send reply to caller\n");
|
|
return -1;
|
|
}
|
|
LM_DBG("\"%d %.*s\" sent to caller\n", bye_early_code,
|
|
bye_early_reason.len, bye_early_reason.s);
|
|
} else {
|
|
d_tmb.cancel_all_uacs(cell->t, 0);
|
|
LM_DBG("CANCEL sent to callee(s)\n");
|
|
}
|
|
return 0;
|
|
} else {
|
|
LM_ERR("terminating non-confirmed dialog not possible, transaction "
|
|
"not longer available.\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/*verify direction*/
|
|
|
|
if((dialog_info = build_dlg_t(cell, dir)) == 0) {
|
|
LM_ERR("failed to create dlg_t\n");
|
|
goto err;
|
|
}
|
|
|
|
/* safety bump of cseq if prack was involved in call setup */
|
|
if(cell->iflags & DLG_IFLAG_PRACK) {
|
|
dialog_info->loc_seq.value += 80;
|
|
}
|
|
|
|
LM_DBG("sending BYE to %s\n",
|
|
(dir == DLG_CALLER_LEG) ? "caller" : "callee");
|
|
|
|
iuid = dlg_get_iuid_shm_clone(cell);
|
|
if(iuid == NULL) {
|
|
LM_ERR("failed to create dialog unique id clone\n");
|
|
goto err;
|
|
}
|
|
|
|
lhdrs = *hdrs;
|
|
|
|
if(dir == DLG_CALLEE_LEG && dlg_lreq_callee_headers.len > 0) {
|
|
/* space allocated in hdrs->s by build_extra_hdrs() */
|
|
memcpy(lhdrs.s + lhdrs.len, dlg_lreq_callee_headers.s,
|
|
dlg_lreq_callee_headers.len);
|
|
lhdrs.len += dlg_lreq_callee_headers.len;
|
|
if(dlg_lreq_callee_headers.s[dlg_lreq_callee_headers.len - 1] != '\n') {
|
|
memcpy(lhdrs.s + lhdrs.len, CRLF, CRLF_LEN);
|
|
lhdrs.len += CRLF_LEN;
|
|
}
|
|
}
|
|
|
|
set_uac_req(&uac_r, &met, &lhdrs, NULL, dialog_info, TMCB_LOCAL_COMPLETED,
|
|
bye_reply_cb, (void *)iuid);
|
|
result = d_tmb.t_request_within(&uac_r);
|
|
|
|
if(result < 0) {
|
|
LM_ERR("failed to send the BYE request\n");
|
|
goto err;
|
|
}
|
|
|
|
free_tm_dlg(dialog_info);
|
|
|
|
LM_DBG("BYE sent to %s\n", (dir == 0) ? "caller" : "callee");
|
|
return 0;
|
|
|
|
err:
|
|
if(dialog_info)
|
|
free_tm_dlg(dialog_info);
|
|
return -1;
|
|
}
|
|
|
|
dlg_t *build_dlg_t_early(
|
|
struct sip_msg *msg, struct dlg_cell *cell, int branch_id, str *rr_set)
|
|
{
|
|
|
|
dlg_t *td = NULL;
|
|
str cseq;
|
|
unsigned int loc_seq;
|
|
char nbuf[MAX_URI_SIZE];
|
|
char dbuf[80];
|
|
str nuri = STR_NULL;
|
|
str duri = STR_NULL;
|
|
size_t sz;
|
|
char *p;
|
|
unsigned int own_rr = 0, skip_recs = 0;
|
|
|
|
if(cell->state != DLG_STATE_UNCONFIRMED && cell->state != DLG_STATE_EARLY) {
|
|
LM_ERR("invalid state for build_dlg_state: %d"
|
|
" (only working for unconfirmed or early dialogs)\n",
|
|
cell->state);
|
|
goto error;
|
|
}
|
|
|
|
if(msg == NULL || msg->first_line.type != SIP_REPLY) {
|
|
if(!cell->t) {
|
|
LM_ERR("no transaction associated\n");
|
|
goto error;
|
|
}
|
|
|
|
if(branch_id <= 0 || branch_id > cell->t->nr_of_outgoings) {
|
|
LM_ERR("invalid branch %d (%d branches in transaction)\n",
|
|
branch_id, cell->t->nr_of_outgoings);
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
if(!msg->contact
|
|
&& (parse_headers(msg, HDR_CONTACT_F, 0) < 0 || !msg->contact)) {
|
|
LM_ERR("bad sip message or missing Contact hdr\n");
|
|
goto error;
|
|
}
|
|
|
|
if(parse_contact(msg->contact) < 0
|
|
|| ((contact_body_t *)msg->contact->parsed)->contacts == NULL) {
|
|
LM_ERR("bad Contact HDR\n");
|
|
goto error;
|
|
}
|
|
|
|
/*try to restore alias parameter if no route set */
|
|
nuri.s = nbuf;
|
|
nuri.len = MAX_URI_SIZE;
|
|
duri.s = dbuf;
|
|
duri.len = 80;
|
|
if(uri_restore_rcv_alias(
|
|
&((contact_body_t *)msg->contact->parsed)->contacts->uri, &nuri,
|
|
&duri)
|
|
< 0) {
|
|
nuri.len = 0;
|
|
duri.len = 0;
|
|
}
|
|
|
|
if(nuri.len > 0 && duri.len > 0) {
|
|
sz = sizeof(dlg_t) + (nuri.len + duri.len + 2) * sizeof(char);
|
|
} else {
|
|
sz = sizeof(dlg_t);
|
|
}
|
|
|
|
td = (dlg_t *)pkg_malloc(sz);
|
|
if(!td) {
|
|
LM_ERR("out of pkg memory\n");
|
|
return NULL;
|
|
}
|
|
memset(td, 0, sz);
|
|
|
|
/*route set*/
|
|
if(msg->record_route) {
|
|
if(cell->t) {
|
|
LM_DBG("transaction exists\n");
|
|
own_rr = (cell->t->flags & TM_UAC_FLAG_R2) ? 2
|
|
: (cell->t->flags & TM_UAC_FLAG_RR) ? 1
|
|
: 0;
|
|
} else {
|
|
own_rr = (msg->flags & TM_UAC_FLAG_R2) ? 2
|
|
: (msg->flags & TM_UAC_FLAG_RR) ? 1
|
|
: 0;
|
|
}
|
|
skip_recs = cell->from_rr_nb + own_rr;
|
|
|
|
LM_DBG("skipping %u records, %u of myself\n", skip_recs, own_rr);
|
|
|
|
if(print_rr_body(msg->record_route, rr_set, DLG_CALLEE_LEG, &skip_recs)
|
|
!= 0) {
|
|
LM_ERR("failed to print route records \n");
|
|
goto error;
|
|
}
|
|
LM_DBG("new route set: %.*s\n", STR_FMT(rr_set));
|
|
|
|
if(parse_rr_body(rr_set->s, rr_set->len, &td->route_set) != 0) {
|
|
LM_ERR("failed to parse route set\n");
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
/*local sequence number*/
|
|
cseq = cell->cseq[DLG_CALLER_LEG];
|
|
|
|
if(cseq.len > 0) {
|
|
LM_DBG("CSeq is %.*s\n", cseq.len, cseq.s);
|
|
if(str2int(&cseq, &loc_seq) != 0) {
|
|
LM_ERR("invalid cseq\n");
|
|
goto error;
|
|
}
|
|
} else {
|
|
LM_DBG("CSeq not set yet, assuming 1\n");
|
|
loc_seq = 1;
|
|
}
|
|
|
|
/*we don not increase here the cseq as this will be done by TM*/
|
|
td->loc_seq.value = loc_seq;
|
|
td->loc_seq.is_set = 1;
|
|
|
|
LM_DBG("nuri: %.*s\n", STR_FMT(&nuri));
|
|
LM_DBG("duri: %.*s\n", STR_FMT(&duri));
|
|
|
|
if(nuri.len > 0 && duri.len > 0) {
|
|
/* req uri */
|
|
p = (char *)td + sizeof(dlg_t);
|
|
strncpy(p, nuri.s, nuri.len);
|
|
p[nuri.len] = '\0';
|
|
td->rem_target.s = p;
|
|
td->rem_target.len = nuri.len;
|
|
/* dst uri */
|
|
p += nuri.len + 1;
|
|
strncpy(p, duri.s, duri.len);
|
|
p[duri.len] = '\0';
|
|
td->dst_uri.s = p;
|
|
td->dst_uri.len = duri.len;
|
|
} else {
|
|
td->rem_target =
|
|
((contact_body_t *)msg->contact->parsed)->contacts->uri;
|
|
}
|
|
|
|
td->rem_uri = cell->from_uri;
|
|
td->loc_uri = cell->to_uri;
|
|
LM_DBG("rem_uri: %.*s\n", STR_FMT(&td->rem_uri));
|
|
LM_DBG("loc_uri: %.*s\n", STR_FMT(&td->loc_uri));
|
|
|
|
LM_DBG("rem_target: %.*s\n", STR_FMT(&td->rem_target));
|
|
LM_DBG("dst_uri: %.*s\n", STR_FMT(&td->dst_uri));
|
|
|
|
td->id.call_id = cell->callid;
|
|
td->id.rem_tag = cell->tag[DLG_CALLER_LEG];
|
|
td->id.loc_tag = cell->tag[DLG_CALLEE_LEG];
|
|
|
|
td->state = DLG_EARLY;
|
|
td->send_sock = cell->bind_addr[DLG_CALLER_LEG];
|
|
|
|
return td;
|
|
|
|
error:
|
|
LM_ERR("Error occurred creating early dialog\n");
|
|
free_tm_dlg(td);
|
|
return NULL;
|
|
}
|
|
|
|
int dlg_request_within(struct sip_msg *msg, struct dlg_cell *dlg, int side,
|
|
str *method, str *hdrs, str *content_type, str *content)
|
|
{
|
|
uac_req_t uac_r;
|
|
dlg_t *dialog_info;
|
|
int result;
|
|
dlg_iuid_t *iuid = NULL;
|
|
char rr_set_s[MAX_URI_SIZE];
|
|
str rr_set = {rr_set_s, 0};
|
|
str allheaders = {0, 0};
|
|
str content_type_hdr = {"Content-Type: ", 14};
|
|
int idx = 0;
|
|
memset(rr_set_s, 0, 500);
|
|
|
|
/* Special treatment for callee in early state*/
|
|
if(dlg->state != DLG_STATE_CONFIRMED_NA && dlg->state != DLG_STATE_CONFIRMED
|
|
&& side == DLG_CALLEE_LEG) {
|
|
LM_DBG("Send request to callee in early state...\n");
|
|
|
|
if(dlg->t == NULL && d_tmb.t_gett) {
|
|
dlg->t = d_tmb.t_gett();
|
|
if(dlg->t && dlg->t != T_UNDEFINED)
|
|
idx = dlg->t->nr_of_outgoings;
|
|
}
|
|
LM_DBG("Branch %i\n", idx);
|
|
|
|
/*verify direction*/
|
|
if((dialog_info = build_dlg_t_early(msg, dlg, idx, &rr_set)) == 0) {
|
|
LM_ERR("failed to create dlg_t\n");
|
|
goto err;
|
|
}
|
|
} else {
|
|
LM_DBG("Send request to caller or in confirmed state...\n");
|
|
/*verify direction*/
|
|
if((dialog_info = build_dlg_t(dlg, side)) == 0) {
|
|
LM_ERR("failed to create dlg_t\n");
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
LM_DBG("sending %.*s to %s\n", method->len, method->s,
|
|
(side == DLG_CALLER_LEG) ? "caller" : "callee");
|
|
|
|
iuid = dlg_get_iuid_shm_clone(dlg);
|
|
if(iuid == NULL) {
|
|
LM_ERR("failed to create dialog unique id clone\n");
|
|
goto err;
|
|
}
|
|
|
|
if(hdrs && hdrs->len > 0) {
|
|
LM_DBG("Extra headers: %.*s\n", STR_FMT(hdrs));
|
|
allheaders.len += hdrs->len;
|
|
}
|
|
|
|
if(content_type && content_type->s && content && content->s) {
|
|
LM_DBG("Content-Type: %.*s\n", STR_FMT(content_type));
|
|
allheaders.len += content_type_hdr.len + content_type->len + 2;
|
|
}
|
|
if(allheaders.len > 0) {
|
|
allheaders.s = (char *)pkg_malloc(allheaders.len);
|
|
if(allheaders.s == NULL) {
|
|
PKG_MEM_ERROR;
|
|
goto err;
|
|
}
|
|
allheaders.len = 0;
|
|
if(hdrs && hdrs->len > 0) {
|
|
memcpy(allheaders.s, hdrs->s, hdrs->len);
|
|
allheaders.len += hdrs->len;
|
|
}
|
|
if(content_type && content_type->s && content && content->s) {
|
|
memcpy(allheaders.s + allheaders.len, content_type_hdr.s,
|
|
content_type_hdr.len);
|
|
allheaders.len += content_type_hdr.len;
|
|
memcpy(allheaders.s + allheaders.len, content_type->s,
|
|
content_type->len);
|
|
allheaders.len += content_type->len;
|
|
memcpy(allheaders.s + allheaders.len, "\r\n", 2);
|
|
allheaders.len += 2;
|
|
}
|
|
LM_DBG("All headers: %.*s\n", STR_FMT(&allheaders));
|
|
}
|
|
|
|
set_uac_req(&uac_r, method, allheaders.len ? &allheaders : NULL,
|
|
(content && content->len) ? content : NULL, dialog_info,
|
|
TMCB_LOCAL_COMPLETED, NULL, NULL);
|
|
|
|
result = d_tmb.t_request_within(&uac_r);
|
|
|
|
if(allheaders.s)
|
|
pkg_free(allheaders.s);
|
|
|
|
if(result < 0) {
|
|
LM_ERR("failed to send request\n");
|
|
goto err;
|
|
}
|
|
|
|
free_tm_dlg(dialog_info);
|
|
|
|
LM_DBG("%.*s sent to %s\n", method->len, method->s,
|
|
(side == DLG_CALLER_LEG) ? "caller" : "callee");
|
|
|
|
return 0;
|
|
err:
|
|
if(dialog_info)
|
|
free_tm_dlg(dialog_info);
|
|
return -1;
|
|
}
|
|
|
|
/* send keep-alive
|
|
* dlg - pointer to a struct dlg_cell
|
|
* dir - direction: the request will be sent to:
|
|
* DLG_CALLER_LEG (0): caller
|
|
* DLG_CALLEE_LEG (1): callee
|
|
*/
|
|
int dlg_send_ka(dlg_cell_t *dlg, int dir)
|
|
{
|
|
uac_req_t uac_r;
|
|
dlg_t *di;
|
|
str met = {"OPTIONS", 7};
|
|
int result;
|
|
dlg_iuid_t *iuid = NULL;
|
|
|
|
if(dlg_filter_mode & DLG_FILTER_LOCALONLY) {
|
|
if(dlg->bind_addr[dir] == NULL) {
|
|
LM_DBG("skipping dialog without bind address\n");
|
|
return 0;
|
|
}
|
|
|
|
if(lookup_local_socket(&(dlg->bind_addr[dir]->sock_str)) == NULL) {
|
|
LM_DBG("skipping non local dialog\n");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* do not send KA request for non-confirmed dialogs (not supported) */
|
|
if(dlg->state != DLG_STATE_CONFIRMED) {
|
|
LM_DBG("skipping non-confirmed dialogs\n");
|
|
return 0;
|
|
}
|
|
|
|
/* build tm dlg by direction */
|
|
if((di = build_dlg_t(dlg, dir)) == 0) {
|
|
LM_ERR("failed to create dlg_t\n");
|
|
goto err;
|
|
}
|
|
|
|
/* tm increases cseq value, decrease it no to make it invalid
|
|
* - dialog is ended on timeout (408) or C/L does not exist (481) */
|
|
if(di->loc_seq.value > 1)
|
|
di->loc_seq.value -= 2;
|
|
else
|
|
di->loc_seq.value -= 1;
|
|
|
|
LM_DBG("sending OPTIONS to %s\n",
|
|
(dir == DLG_CALLER_LEG) ? "caller" : "callee");
|
|
|
|
iuid = dlg_get_iuid_shm_clone(dlg);
|
|
if(iuid == NULL) {
|
|
LM_ERR("failed to create dialog unique id clone\n");
|
|
goto err;
|
|
}
|
|
|
|
if(dir == DLG_CALLEE_LEG && dlg_lreq_callee_headers.len > 0) {
|
|
set_uac_req(&uac_r, &met, &dlg_lreq_callee_headers, NULL, di,
|
|
TMCB_LOCAL_COMPLETED, dlg_ka_cb_dst, (void *)iuid);
|
|
} else {
|
|
set_uac_req(&uac_r, &met, NULL, NULL, di, TMCB_LOCAL_COMPLETED,
|
|
(dir == DLG_CALLEE_LEG) ? dlg_ka_cb_dst : dlg_ka_cb_src,
|
|
(void *)iuid);
|
|
}
|
|
result = d_tmb.t_request_within(&uac_r);
|
|
|
|
if(result < 0) {
|
|
LM_ERR("failed to send the OPTIONS request\n");
|
|
goto err;
|
|
}
|
|
|
|
free_tm_dlg(di);
|
|
|
|
LM_DBG("keep-alive sent to %s\n", (dir == 0) ? "caller" : "callee");
|
|
return 0;
|
|
|
|
err:
|
|
if(di)
|
|
free_tm_dlg(di);
|
|
return -1;
|
|
}
|
|
|
|
|
|
int dlg_bye(struct dlg_cell *dlg, str *hdrs, int side)
|
|
{
|
|
str all_hdrs = {0, 0};
|
|
int ret;
|
|
|
|
if(side == DLG_CALLER_LEG) {
|
|
if(dlg->dflags & DLG_FLAG_CALLERBYE)
|
|
return -1;
|
|
dlg->dflags |= DLG_FLAG_CALLERBYE;
|
|
} else {
|
|
if(dlg->dflags & DLG_FLAG_CALLEEBYE)
|
|
return -1;
|
|
dlg->dflags |= DLG_FLAG_CALLEEBYE;
|
|
}
|
|
if((build_extra_hdr(dlg, hdrs, &all_hdrs)) != 0) {
|
|
LM_ERR("failed to build dlg headers\n");
|
|
return -1;
|
|
}
|
|
ret = send_bye(dlg, side, &all_hdrs);
|
|
pkg_free(all_hdrs.s);
|
|
|
|
dlg_run_event_route(dlg, NULL, dlg->state, DLG_STATE_DELETED);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int dlg_bye_all(struct dlg_cell *dlg, str *hdrs)
|
|
{
|
|
str all_hdrs = {0, 0};
|
|
int ret;
|
|
|
|
/* run dialog terminated callbacks */
|
|
run_dlg_callbacks(DLGCB_TERMINATED, dlg, NULL, NULL, DLG_DIR_NONE, 0);
|
|
|
|
if((build_extra_hdr(dlg, hdrs, &all_hdrs)) != 0) {
|
|
LM_ERR("failed to build dlg headers\n");
|
|
return -1;
|
|
}
|
|
|
|
ret = send_bye(dlg, DLG_CALLER_LEG, &all_hdrs);
|
|
ret |= send_bye(dlg, DLG_CALLEE_LEG, &all_hdrs);
|
|
|
|
pkg_free(all_hdrs.s);
|
|
|
|
dlg_run_event_route(dlg, NULL, dlg->state, DLG_STATE_DELETED);
|
|
|
|
return ret;
|
|
}
|