2016-03-22 13:15:17 +00:00
|
|
|
/*
|
2017-03-10 15:05:17 +00:00
|
|
|
* (C) 2016-2017 by Holger Hans Peter Freyther
|
2016-03-22 13:15:17 +00:00
|
|
|
*
|
|
|
|
* All Rights Reserved
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program 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 Affero General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "sip.h"
|
|
|
|
#include "app.h"
|
2016-03-26 05:17:29 +00:00
|
|
|
#include "call.h"
|
|
|
|
#include "logging.h"
|
2016-03-26 19:02:06 +00:00
|
|
|
#include "sdp.h"
|
2016-03-26 05:17:29 +00:00
|
|
|
|
|
|
|
#include <osmocom/core/utils.h>
|
2020-09-09 15:39:09 +00:00
|
|
|
#include <osmocom/core/socket.h>
|
2021-01-31 04:24:29 +00:00
|
|
|
#include <osmocom/gsm/tlv.h>
|
2016-03-26 05:17:29 +00:00
|
|
|
|
2016-03-26 18:44:03 +00:00
|
|
|
#include <sofia-sip/sip_status.h>
|
2018-04-15 19:51:41 +00:00
|
|
|
#include <sofia-sip/su_log.h>
|
2019-07-30 13:04:21 +00:00
|
|
|
#include <sofia-sip/sdp.h>
|
2016-03-22 13:15:17 +00:00
|
|
|
|
|
|
|
#include <talloc.h>
|
|
|
|
|
|
|
|
#include <string.h>
|
2020-09-09 15:39:09 +00:00
|
|
|
#include <netinet/in.h>
|
2016-03-22 13:15:17 +00:00
|
|
|
|
|
|
|
extern void *tall_mncc_ctx;
|
|
|
|
|
2016-03-26 19:55:02 +00:00
|
|
|
static void sip_release_call(struct call_leg *_leg);
|
2016-03-26 21:11:06 +00:00
|
|
|
static void sip_ring_call(struct call_leg *_leg);
|
|
|
|
static void sip_connect_call(struct call_leg *_leg);
|
2017-02-22 12:50:11 +00:00
|
|
|
static void sip_dtmf_call(struct call_leg *_leg, int keypad);
|
2019-07-30 14:26:03 +00:00
|
|
|
static void sip_hold_call(struct call_leg *_leg);
|
|
|
|
static void sip_retrieve_call(struct call_leg *_leg);
|
|
|
|
|
forward SDP between SIP and MNCC
We have added support for sending SDP via MNCC a long time ago, but so
far the SDP section remained empty. Now, implement actually forwarding
SDP codec information between SIP and MNCC.
The aim is to let the MSC know about all codec choices the remote SIP
call leg has to offer, so that finding a codec match between local and
remote call leg becomes possible.
Store any SDP info contained in incoming SIP and MNCC messages, and send
the stored SDP to the other call leg in all outgoing SIP and MNCC
messages.
In sdp_create_file(), we used to compose fixed SDP -- instead, take the
other call leg's SDP as-is, only make sure to modify the mode (e.g.
"a=sendrecv") to reflect the current call hold state.
The RTP address and codec info in the MNCC structures is now essentially
a redundant / possibly less accurate copy of the SDP info, but leave all
of that as-is, for backwards compat.
There is codec checking that may reject unexpected codecs. The
overall/future aim is to leave all codec checking up to the MSC, but so
far just leave current behaviour unchanged, until we notice problems.
Related: SYS#5066
Related: osmo-ttcn3-hacks Ib2ae8449e673f5027f01d428d3718c006f76d93e
Change-Id: I3df5d06f38ee2d122706a9ebffde7db4f2bd6bae
2019-08-15 04:12:54 +00:00
|
|
|
static const char *sip_get_sdp(const sip_t *sip)
|
|
|
|
{
|
|
|
|
if (!sip || !sip->sip_payload)
|
|
|
|
return NULL;
|
|
|
|
return sip->sip_payload->pl_data;
|
|
|
|
}
|
2016-03-26 19:55:02 +00:00
|
|
|
|
2019-07-30 13:58:55 +00:00
|
|
|
/* Find a SIP Call leg by given nua_handle */
|
|
|
|
static struct sip_call_leg *sip_find_leg(nua_handle_t *nh)
|
|
|
|
{
|
|
|
|
struct call *call;
|
|
|
|
|
|
|
|
llist_for_each_entry(call, &g_call_list, entry) {
|
|
|
|
if (call->initial && call->initial->type == CALL_TYPE_SIP) {
|
|
|
|
struct sip_call_leg *leg = (struct sip_call_leg *) call->initial;
|
|
|
|
if (leg->nua_handle == nh)
|
|
|
|
return leg;
|
|
|
|
}
|
|
|
|
if (call->remote && call->remote->type == CALL_TYPE_SIP) {
|
|
|
|
struct sip_call_leg *leg = (struct sip_call_leg *) call->remote;
|
|
|
|
if (leg->nua_handle == nh)
|
|
|
|
return leg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-03-10 15:05:17 +00:00
|
|
|
static void call_progress(struct sip_call_leg *leg, const sip_t *sip, int status)
|
2016-03-26 05:17:29 +00:00
|
|
|
{
|
|
|
|
struct call_leg *other = call_leg_other(&leg->base);
|
|
|
|
|
|
|
|
if (!other)
|
|
|
|
return;
|
|
|
|
|
2017-03-10 15:05:17 +00:00
|
|
|
/* Extract SDP for session in progress with matching codec */
|
|
|
|
if (status == 183)
|
|
|
|
sdp_extract_sdp(leg, sip, false);
|
|
|
|
|
2019-08-07 00:13:06 +00:00
|
|
|
LOGP(DSIP, LOGL_INFO, "leg(%p) is now progressing.\n", leg);
|
2016-03-26 05:17:29 +00:00
|
|
|
other->ring_call(other);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void call_connect(struct sip_call_leg *leg, const sip_t *sip)
|
|
|
|
{
|
|
|
|
/* extract SDP file and if compatible continue */
|
|
|
|
struct call_leg *other = call_leg_other(&leg->base);
|
|
|
|
|
|
|
|
if (!other) {
|
|
|
|
LOGP(DSIP, LOGL_ERROR, "leg(%p) connected but leg gone\n", leg);
|
|
|
|
nua_cancel(leg->nua_handle, TAG_END());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-03-31 14:28:21 +00:00
|
|
|
if (!sdp_extract_sdp(leg, sip, false)) {
|
2016-03-26 05:17:29 +00:00
|
|
|
LOGP(DSIP, LOGL_ERROR, "leg(%p) incompatible audio, releasing\n", leg);
|
|
|
|
nua_cancel(leg->nua_handle, TAG_END());
|
|
|
|
other->release_call(other);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-08-07 00:13:06 +00:00
|
|
|
LOGP(DSIP, LOGL_INFO, "leg(%p) is now connected(%s).\n", leg, sip->sip_call_id->i_id);
|
2016-03-26 05:17:29 +00:00
|
|
|
leg->state = SIP_CC_CONNECTED;
|
|
|
|
other->connect_call(other);
|
|
|
|
nua_ack(leg->nua_handle, TAG_END());
|
|
|
|
}
|
|
|
|
|
2016-03-26 18:44:03 +00:00
|
|
|
static void new_call(struct sip_agent *agent, nua_handle_t *nh,
|
|
|
|
const sip_t *sip)
|
|
|
|
{
|
2016-03-26 19:55:02 +00:00
|
|
|
struct call *call;
|
|
|
|
struct sip_call_leg *leg;
|
|
|
|
const char *from = NULL, *to = NULL;
|
2020-09-09 15:39:09 +00:00
|
|
|
char ip_addr[INET6_ADDRSTRLEN];
|
2021-10-27 13:28:36 +00:00
|
|
|
bool xgcr_hdr_present = false;
|
|
|
|
uint8_t xgcr_hdr[28] = { 0 };
|
2016-03-26 19:55:02 +00:00
|
|
|
|
2019-08-07 00:13:06 +00:00
|
|
|
LOGP(DSIP, LOGL_INFO, "Incoming call(%s) handle(%p)\n", sip->sip_call_id->i_id, nh);
|
2016-03-26 18:44:03 +00:00
|
|
|
|
2021-01-31 04:24:29 +00:00
|
|
|
sip_unknown_t *unknown_header = sip->sip_unknown;
|
|
|
|
while (unknown_header != NULL) {
|
|
|
|
if (!strcmp("X-Global-Call-Ref", unknown_header->un_name)) {
|
2021-10-27 13:28:36 +00:00
|
|
|
osmo_hexparse(unknown_header->un_value, xgcr_hdr, sizeof(xgcr_hdr));
|
|
|
|
xgcr_hdr_present = true;
|
2021-01-31 04:24:29 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
unknown_header = unknown_header->un_next;
|
|
|
|
}
|
|
|
|
|
2016-03-26 19:02:06 +00:00
|
|
|
if (!sdp_screen_sdp(sip)) {
|
2016-03-26 18:44:03 +00:00
|
|
|
LOGP(DSIP, LOGL_ERROR, "No supported codec.\n");
|
|
|
|
nua_respond(nh, SIP_406_NOT_ACCEPTABLE, TAG_END());
|
|
|
|
nua_handle_destroy(nh);
|
2016-03-26 19:30:14 +00:00
|
|
|
return;
|
2016-03-26 18:44:03 +00:00
|
|
|
}
|
|
|
|
|
2016-03-26 19:55:02 +00:00
|
|
|
call = call_sip_create();
|
2022-07-18 11:44:42 +00:00
|
|
|
OSMO_ASSERT(call);
|
2016-03-26 19:55:02 +00:00
|
|
|
|
2021-10-27 13:28:36 +00:00
|
|
|
/* Decode Decode the Global Call Reference (if present) */
|
|
|
|
if (xgcr_hdr_present) {
|
|
|
|
if (osmo_dec_gcr(&call->gcr, xgcr_hdr, sizeof(xgcr_hdr)) < 0) {
|
|
|
|
LOGP(DSIP, LOGL_ERROR, "Failed to parse X-Global-Call-Ref.\n");
|
|
|
|
nua_respond(nh, SIP_406_NOT_ACCEPTABLE, TAG_END());
|
|
|
|
nua_handle_destroy(nh);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
call->gcr_present = true;
|
|
|
|
}
|
2021-01-31 04:24:29 +00:00
|
|
|
|
2016-03-26 19:55:02 +00:00
|
|
|
if (sip->sip_to)
|
|
|
|
to = sip->sip_to->a_url->url_user;
|
|
|
|
if (sip->sip_from)
|
|
|
|
from = sip->sip_from->a_url->url_user;
|
|
|
|
|
|
|
|
if (!to || !from) {
|
|
|
|
LOGP(DSIP, LOGL_ERROR, "Unknown from/to for invite.\n");
|
|
|
|
nua_respond(nh, SIP_406_NOT_ACCEPTABLE, TAG_END());
|
|
|
|
nua_handle_destroy(nh);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
leg = (struct sip_call_leg *) call->initial;
|
|
|
|
leg->state = SIP_CC_DLG_CNFD;
|
|
|
|
leg->dir = SIP_DIR_MO;
|
|
|
|
|
2016-03-31 14:28:21 +00:00
|
|
|
/*
|
|
|
|
* FIXME/TODO.. we need to select the codec at some point. But it is
|
|
|
|
* not this place. It starts with the TCH/F vs. TCH/H selection based
|
|
|
|
* on the offered codecs, and then RTP_CREATE should have it. So both
|
|
|
|
* are GSM related... and do not belong here. Just pick the first codec
|
2019-08-06 14:45:12 +00:00
|
|
|
* so the IP address, port and payload type is set.
|
2016-03-31 14:28:21 +00:00
|
|
|
*/
|
|
|
|
if (!sdp_extract_sdp(leg, sip, true)) {
|
|
|
|
LOGP(DSIP, LOGL_ERROR, "leg(%p) no audio, releasing\n", leg);
|
|
|
|
nua_respond(nh, SIP_406_NOT_ACCEPTABLE, TAG_END());
|
|
|
|
nua_handle_destroy(nh);
|
|
|
|
call_leg_release(&leg->base);
|
|
|
|
return;
|
|
|
|
}
|
2019-08-07 00:13:06 +00:00
|
|
|
LOGP(DSIP, LOGL_INFO, "SDP Extracted: IP=(%s) PORT=(%u) PAYLOAD=(%u).\n",
|
2020-09-09 15:39:09 +00:00
|
|
|
osmo_sockaddr_ntop((const struct sockaddr *)&leg->base.addr, ip_addr),
|
|
|
|
osmo_sockaddr_port((const struct sockaddr *)&leg->base.addr),
|
2019-07-30 14:30:56 +00:00
|
|
|
leg->base.payload_type);
|
2016-03-31 14:28:21 +00:00
|
|
|
|
2016-03-26 19:55:02 +00:00
|
|
|
leg->base.release_call = sip_release_call;
|
2016-03-26 21:11:06 +00:00
|
|
|
leg->base.ring_call = sip_ring_call;
|
|
|
|
leg->base.connect_call = sip_connect_call;
|
2017-02-22 12:50:11 +00:00
|
|
|
leg->base.dtmf = sip_dtmf_call;
|
2019-07-30 14:26:03 +00:00
|
|
|
leg->base.hold_call = sip_hold_call;
|
|
|
|
leg->base.retrieve_call = sip_retrieve_call;
|
2016-03-26 19:55:02 +00:00
|
|
|
leg->agent = agent;
|
|
|
|
leg->nua_handle = nh;
|
|
|
|
nua_handle_bind(nh, leg);
|
|
|
|
leg->sdp_payload = talloc_strdup(leg, sip->sip_payload->pl_data);
|
2016-03-26 20:09:07 +00:00
|
|
|
|
forward SDP between SIP and MNCC
We have added support for sending SDP via MNCC a long time ago, but so
far the SDP section remained empty. Now, implement actually forwarding
SDP codec information between SIP and MNCC.
The aim is to let the MSC know about all codec choices the remote SIP
call leg has to offer, so that finding a codec match between local and
remote call leg becomes possible.
Store any SDP info contained in incoming SIP and MNCC messages, and send
the stored SDP to the other call leg in all outgoing SIP and MNCC
messages.
In sdp_create_file(), we used to compose fixed SDP -- instead, take the
other call leg's SDP as-is, only make sure to modify the mode (e.g.
"a=sendrecv") to reflect the current call hold state.
The RTP address and codec info in the MNCC structures is now essentially
a redundant / possibly less accurate copy of the SDP info, but leave all
of that as-is, for backwards compat.
There is codec checking that may reject unexpected codecs. The
overall/future aim is to leave all codec checking up to the MSC, but so
far just leave current behaviour unchanged, until we notice problems.
Related: SYS#5066
Related: osmo-ttcn3-hacks Ib2ae8449e673f5027f01d428d3718c006f76d93e
Change-Id: I3df5d06f38ee2d122706a9ebffde7db4f2bd6bae
2019-08-15 04:12:54 +00:00
|
|
|
call_leg_rx_sdp(&leg->base, sip_get_sdp(sip));
|
|
|
|
|
2016-04-04 17:52:41 +00:00
|
|
|
app_route_call(call,
|
|
|
|
talloc_strdup(leg, from),
|
|
|
|
talloc_strdup(leg, to));
|
2016-03-26 18:44:03 +00:00
|
|
|
}
|
|
|
|
|
2019-07-30 13:58:55 +00:00
|
|
|
static void sip_handle_reinvite(struct sip_call_leg *leg, nua_handle_t *nh, const sip_t *sip) {
|
|
|
|
|
|
|
|
char *sdp;
|
|
|
|
sdp_mode_t mode = sdp_sendrecv;
|
2020-09-09 15:39:09 +00:00
|
|
|
char ip_addr[INET6_ADDRSTRLEN];
|
|
|
|
struct sockaddr_storage prev_addr = leg->base.addr;
|
2019-07-30 13:58:55 +00:00
|
|
|
|
2019-08-07 00:13:06 +00:00
|
|
|
LOGP(DSIP, LOGL_INFO, "re-INVITE for call %s\n", sip->sip_call_id->i_id);
|
2019-07-30 13:58:55 +00:00
|
|
|
|
|
|
|
struct call_leg *other = call_leg_other(&leg->base);
|
2019-08-07 01:20:53 +00:00
|
|
|
|
|
|
|
if (!other) {
|
|
|
|
LOGP(DMNCC, LOGL_ERROR, "leg(%p) other leg gone!\n", leg);
|
|
|
|
sip_release_call(&leg->base);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-07-30 13:58:55 +00:00
|
|
|
if (!sdp_get_sdp_mode(sip, &mode)) {
|
|
|
|
/* re-INVITE with no SDP.
|
|
|
|
* We should respond with SDP reflecting current session
|
|
|
|
*/
|
|
|
|
sdp = sdp_create_file(leg, other, sdp_sendrecv);
|
|
|
|
nua_respond(nh, SIP_200_OK,
|
|
|
|
NUTAG_MEDIA_ENABLE(0),
|
|
|
|
SIPTAG_CONTENT_TYPE_STR("application/sdp"),
|
|
|
|
SIPTAG_PAYLOAD_STR(sdp),
|
|
|
|
TAG_END());
|
|
|
|
talloc_free(sdp);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-09-09 15:39:09 +00:00
|
|
|
LOGP(DSIP, LOGL_DEBUG, "pre re-INVITE have IP:port (%s:%u)\n",
|
2021-06-01 02:17:49 +00:00
|
|
|
osmo_sockaddr_ntop((struct sockaddr*)&prev_addr, ip_addr),
|
|
|
|
osmo_sockaddr_port((struct sockaddr*)&prev_addr));
|
2019-07-30 14:30:56 +00:00
|
|
|
|
forward SDP between SIP and MNCC
We have added support for sending SDP via MNCC a long time ago, but so
far the SDP section remained empty. Now, implement actually forwarding
SDP codec information between SIP and MNCC.
The aim is to let the MSC know about all codec choices the remote SIP
call leg has to offer, so that finding a codec match between local and
remote call leg becomes possible.
Store any SDP info contained in incoming SIP and MNCC messages, and send
the stored SDP to the other call leg in all outgoing SIP and MNCC
messages.
In sdp_create_file(), we used to compose fixed SDP -- instead, take the
other call leg's SDP as-is, only make sure to modify the mode (e.g.
"a=sendrecv") to reflect the current call hold state.
The RTP address and codec info in the MNCC structures is now essentially
a redundant / possibly less accurate copy of the SDP info, but leave all
of that as-is, for backwards compat.
There is codec checking that may reject unexpected codecs. The
overall/future aim is to leave all codec checking up to the MSC, but so
far just leave current behaviour unchanged, until we notice problems.
Related: SYS#5066
Related: osmo-ttcn3-hacks Ib2ae8449e673f5027f01d428d3718c006f76d93e
Change-Id: I3df5d06f38ee2d122706a9ebffde7db4f2bd6bae
2019-08-15 04:12:54 +00:00
|
|
|
call_leg_rx_sdp(&leg->base, sip_get_sdp(sip));
|
|
|
|
|
2019-07-30 13:58:55 +00:00
|
|
|
if (mode == sdp_sendonly) {
|
|
|
|
/* SIP side places call on HOLD */
|
|
|
|
sdp = sdp_create_file(leg, other, sdp_recvonly);
|
|
|
|
/* TODO: Tell core network to stop sending RTP ? */
|
|
|
|
} else {
|
|
|
|
/* SIP re-INVITE may want to change media, IP, port */
|
|
|
|
if (!sdp_extract_sdp(leg, sip, true)) {
|
|
|
|
LOGP(DSIP, LOGL_ERROR, "leg(%p) no audio, releasing\n", leg);
|
|
|
|
nua_respond(nh, SIP_406_NOT_ACCEPTABLE, TAG_END());
|
|
|
|
nua_handle_destroy(nh);
|
|
|
|
call_leg_release(&leg->base);
|
|
|
|
return;
|
|
|
|
}
|
2020-09-09 15:39:09 +00:00
|
|
|
LOGP(DSIP, LOGL_DEBUG, "Media IP:port in re-INVITE: (%s:%u)\n",
|
|
|
|
osmo_sockaddr_ntop((struct sockaddr*)&leg->base.addr, ip_addr),
|
|
|
|
osmo_sockaddr_port((struct sockaddr*)&leg->base.addr));
|
|
|
|
if (osmo_sockaddr_cmp((struct osmo_sockaddr *)&prev_addr,
|
2021-06-01 02:16:18 +00:00
|
|
|
(struct osmo_sockaddr *)&leg->base.addr)) {
|
2021-06-01 02:17:49 +00:00
|
|
|
LOGP(DSIP, LOGL_INFO, "re-INVITE changes media connection to %s:%u\n",
|
|
|
|
osmo_sockaddr_ntop((struct sockaddr*)&leg->base.addr, ip_addr),
|
|
|
|
osmo_sockaddr_port((struct sockaddr*)&leg->base.addr));
|
2019-08-01 13:18:56 +00:00
|
|
|
if (other->update_rtp)
|
|
|
|
other->update_rtp(leg->base.call->remote);
|
2021-06-01 02:17:49 +00:00
|
|
|
} else {
|
|
|
|
LOGP(DSIP, LOGL_INFO, "re-INVITE does not change media connection (%s:%u)\n",
|
|
|
|
osmo_sockaddr_ntop((struct sockaddr*)&prev_addr, ip_addr),
|
|
|
|
osmo_sockaddr_port((struct sockaddr*)&prev_addr));
|
2019-07-30 14:30:56 +00:00
|
|
|
}
|
2019-07-30 13:58:55 +00:00
|
|
|
sdp = sdp_create_file(leg, other, sdp_sendrecv);
|
|
|
|
}
|
|
|
|
|
|
|
|
LOGP(DSIP, LOGL_DEBUG, "Sending 200 response to re-INVITE for mode(%u)\n", mode);
|
|
|
|
nua_respond(nh, SIP_200_OK,
|
|
|
|
NUTAG_MEDIA_ENABLE(0),
|
|
|
|
SIPTAG_CONTENT_TYPE_STR("application/sdp"),
|
|
|
|
SIPTAG_PAYLOAD_STR(sdp),
|
|
|
|
TAG_END());
|
|
|
|
talloc_free(sdp);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-08-31 13:23:48 +00:00
|
|
|
/* Sofia SIP definitions come with error code numbers and strings, this
|
|
|
|
* map allows us to reuse the existing definitions.
|
|
|
|
* The map is in priority order. The first matching entry found
|
|
|
|
* will be used.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static struct cause_map {
|
|
|
|
int sip_status;
|
|
|
|
const char *sip_phrase;
|
|
|
|
int gsm48_cause;
|
|
|
|
const char *q850_reason;
|
|
|
|
} cause_map[] = {
|
|
|
|
{ SIP_200_OK, GSM48_CC_CAUSE_NORM_CALL_CLEAR, "Normal Call Clearing" },
|
|
|
|
{ SIP_403_FORBIDDEN, GSM48_CC_CAUSE_CALL_REJECTED, "Call Rejected" },
|
|
|
|
{ SIP_401_UNAUTHORIZED, GSM48_CC_CAUSE_CALL_REJECTED, "Call Rejected" },
|
|
|
|
{ SIP_402_PAYMENT_REQUIRED, GSM48_CC_CAUSE_CALL_REJECTED, "Call Rejected" },
|
|
|
|
{ SIP_407_PROXY_AUTH_REQUIRED, GSM48_CC_CAUSE_CALL_REJECTED, "Call Rejected" },
|
|
|
|
{ SIP_603_DECLINE, GSM48_CC_CAUSE_CALL_REJECTED, "Call Rejected" },
|
|
|
|
{ SIP_406_NOT_ACCEPTABLE, GSM48_CC_CAUSE_CHAN_UNACCEPT, "Channel Unacceptable" },
|
|
|
|
{ SIP_404_NOT_FOUND, GSM48_CC_CAUSE_UNASSIGNED_NR, "Unallocated Number" },
|
|
|
|
{ SIP_485_AMBIGUOUS, GSM48_CC_CAUSE_NO_ROUTE, "No Route to Destination" },
|
|
|
|
{ SIP_604_DOES_NOT_EXIST_ANYWHERE, GSM48_CC_CAUSE_NO_ROUTE, "No Route to Destination" },
|
|
|
|
{ SIP_504_GATEWAY_TIME_OUT, GSM48_CC_CAUSE_RECOVERY_TIMER, "Recovery on Timer Expiry" },
|
|
|
|
{ SIP_408_REQUEST_TIMEOUT, GSM48_CC_CAUSE_RECOVERY_TIMER, "Recovery on Timer Expiry" },
|
|
|
|
{ SIP_410_GONE, GSM48_CC_CAUSE_NUMBER_CHANGED, "Number Changed" },
|
|
|
|
{ SIP_416_UNSUPPORTED_URI, GSM48_CC_CAUSE_INVAL_TRANS_ID, "Invalid Call Reference Value" },
|
|
|
|
{ SIP_420_BAD_EXTENSION, GSM48_CC_CAUSE_INTERWORKING, "Interworking, Unspecified" },
|
|
|
|
{ SIP_414_REQUEST_URI_TOO_LONG, GSM48_CC_CAUSE_INTERWORKING, "Interworking, Unspecified" },
|
|
|
|
{ SIP_413_REQUEST_TOO_LARGE, GSM48_CC_CAUSE_INTERWORKING, "Interworking, Unspecified" },
|
|
|
|
{ SIP_421_EXTENSION_REQUIRED, GSM48_CC_CAUSE_INTERWORKING, "Interworking, Unspecified" },
|
|
|
|
{ SIP_423_INTERVAL_TOO_BRIEF, GSM48_CC_CAUSE_INTERWORKING, "Interworking, Unspecified" },
|
|
|
|
{ SIP_505_VERSION_NOT_SUPPORTED, GSM48_CC_CAUSE_INTERWORKING, "Interworking, Unspecified" },
|
|
|
|
{ SIP_513_MESSAGE_TOO_LARGE, GSM48_CC_CAUSE_INTERWORKING, "Interworking, Unspecified" },
|
|
|
|
{ SIP_480_TEMPORARILY_UNAVAILABLE, GSM48_CC_CAUSE_USER_NOTRESPOND, "No User Responding" },
|
|
|
|
{ SIP_503_SERVICE_UNAVAILABLE, GSM48_CC_CAUSE_RESOURCE_UNAVAIL,"Resource Unavailable, Unspecified" },
|
|
|
|
{ SIP_503_SERVICE_UNAVAILABLE, GSM48_CC_CAUSE_TEMP_FAILURE, "Temporary Failure" },
|
|
|
|
{ SIP_503_SERVICE_UNAVAILABLE, GSM48_CC_CAUSE_SWITCH_CONG, "Switching Equipment Congestion" },
|
|
|
|
{ SIP_400_BAD_REQUEST, GSM48_CC_CAUSE_TEMP_FAILURE, "Temporary Failure" },
|
|
|
|
{ SIP_481_NO_CALL, GSM48_CC_CAUSE_TEMP_FAILURE, "Temporary Failure" },
|
|
|
|
{ SIP_500_INTERNAL_SERVER_ERROR, GSM48_CC_CAUSE_TEMP_FAILURE, "Temporary Failure" },
|
|
|
|
{ SIP_486_BUSY_HERE, GSM48_CC_CAUSE_USER_BUSY, "User Busy" },
|
|
|
|
{ SIP_600_BUSY_EVERYWHERE, GSM48_CC_CAUSE_USER_BUSY, "User Busy" },
|
|
|
|
{ SIP_484_ADDRESS_INCOMPLETE, GSM48_CC_CAUSE_INV_NR_FORMAT, "Invalid Number Format (addr incomplete)" },
|
|
|
|
{ SIP_488_NOT_ACCEPTABLE, GSM48_CC_CAUSE_INCOMPAT_DEST, "Incompatible Destination" },
|
|
|
|
{ SIP_606_NOT_ACCEPTABLE, GSM48_CC_CAUSE_INCOMPAT_DEST, "Incompatible Destination" },
|
|
|
|
{ SIP_502_BAD_GATEWAY, GSM48_CC_CAUSE_DEST_OOO, "Destination Out of Order" },
|
|
|
|
{ SIP_503_SERVICE_UNAVAILABLE, GSM48_CC_CAUSE_NETWORK_OOO, "Network Out of Order" },
|
|
|
|
{ SIP_405_METHOD_NOT_ALLOWED, GSM48_CC_CAUSE_SERV_OPT_UNAVAIL,"Service or Option Not Implemented" },
|
|
|
|
{ SIP_501_NOT_IMPLEMENTED, GSM48_CC_CAUSE_SERV_OPT_UNIMPL, "Service or Option Not Implemented" },
|
|
|
|
{ SIP_415_UNSUPPORTED_MEDIA, GSM48_CC_CAUSE_SERV_OPT_UNIMPL, "Service or Option Not Implemented" },
|
|
|
|
{ SIP_406_NOT_ACCEPTABLE, GSM48_CC_CAUSE_SERV_OPT_UNIMPL, "Service or Option Not Implemented" },
|
|
|
|
{ SIP_482_LOOP_DETECTED, GSM48_CC_CAUSE_PRE_EMPTION, "Exchange Routing Error" },
|
|
|
|
{ SIP_483_TOO_MANY_HOPS, GSM48_CC_CAUSE_PRE_EMPTION, "Exchange Routing Error" },
|
|
|
|
{ SIP_503_SERVICE_UNAVAILABLE, GSM48_CC_CAUSE_BEARER_CA_UNAVAIL,"Bearer Capability Not Available" },
|
|
|
|
{ SIP_480_TEMPORARILY_UNAVAILABLE, GSM48_CC_CAUSE_NORMAL_UNSPEC, "Normal, Unspecified" }
|
|
|
|
};
|
|
|
|
|
|
|
|
static int status2cause(int status)
|
|
|
|
{
|
|
|
|
uint8_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(cause_map) - 1; i++) {
|
|
|
|
if (cause_map[i].sip_status == status) {
|
|
|
|
return cause_map[i].gsm48_cause;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return GSM48_CC_CAUSE_NORMAL_UNSPEC;
|
|
|
|
}
|
|
|
|
|
2016-03-22 13:15:17 +00:00
|
|
|
void nua_callback(nua_event_t event, int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[])
|
|
|
|
{
|
2023-09-07 03:20:03 +00:00
|
|
|
LOGP(DSIP, LOGL_DEBUG, "SIP event[%s] status(%d) phrase(%s) SDP(%s) %p\n",
|
|
|
|
nua_event_name(event), status, phrase, sip_get_sdp(sip), hmagic);
|
2016-03-26 05:17:29 +00:00
|
|
|
|
|
|
|
if (event == nua_r_invite) {
|
|
|
|
struct sip_call_leg *leg;
|
|
|
|
leg = (struct sip_call_leg *) hmagic;
|
|
|
|
|
forward SDP between SIP and MNCC
We have added support for sending SDP via MNCC a long time ago, but so
far the SDP section remained empty. Now, implement actually forwarding
SDP codec information between SIP and MNCC.
The aim is to let the MSC know about all codec choices the remote SIP
call leg has to offer, so that finding a codec match between local and
remote call leg becomes possible.
Store any SDP info contained in incoming SIP and MNCC messages, and send
the stored SDP to the other call leg in all outgoing SIP and MNCC
messages.
In sdp_create_file(), we used to compose fixed SDP -- instead, take the
other call leg's SDP as-is, only make sure to modify the mode (e.g.
"a=sendrecv") to reflect the current call hold state.
The RTP address and codec info in the MNCC structures is now essentially
a redundant / possibly less accurate copy of the SDP info, but leave all
of that as-is, for backwards compat.
There is codec checking that may reject unexpected codecs. The
overall/future aim is to leave all codec checking up to the MSC, but so
far just leave current behaviour unchanged, until we notice problems.
Related: SYS#5066
Related: osmo-ttcn3-hacks Ib2ae8449e673f5027f01d428d3718c006f76d93e
Change-Id: I3df5d06f38ee2d122706a9ebffde7db4f2bd6bae
2019-08-15 04:12:54 +00:00
|
|
|
call_leg_rx_sdp(&leg->base, sip_get_sdp(sip));
|
|
|
|
|
2016-03-26 05:17:29 +00:00
|
|
|
/* MT call is moving forward */
|
|
|
|
|
|
|
|
/* The dialogue is now confirmed */
|
|
|
|
if (leg->state == SIP_CC_INITIAL)
|
|
|
|
leg->state = SIP_CC_DLG_CNFD;
|
|
|
|
|
2017-02-11 03:53:16 +00:00
|
|
|
if (status == 180 || status == 183)
|
2017-03-10 15:05:17 +00:00
|
|
|
call_progress(leg, sip, status);
|
2019-07-30 13:58:55 +00:00
|
|
|
else if (status == 200) {
|
2019-08-06 15:50:20 +00:00
|
|
|
if (leg->state == SIP_CC_CONNECTED || leg->state == SIP_CC_HOLD) {
|
|
|
|
/* This 200 is a response to our re-INVITE on
|
|
|
|
* a connected call. We just need to ACK it. */
|
2019-07-30 13:58:55 +00:00
|
|
|
nua_ack(leg->nua_handle, TAG_END());
|
2019-08-06 15:50:20 +00:00
|
|
|
} else {
|
2019-07-30 13:58:55 +00:00
|
|
|
call_connect(leg, sip);
|
2019-08-06 15:50:20 +00:00
|
|
|
}
|
2019-07-30 13:58:55 +00:00
|
|
|
}
|
2016-03-26 05:17:29 +00:00
|
|
|
else if (status >= 300) {
|
|
|
|
struct call_leg *other = call_leg_other(&leg->base);
|
|
|
|
|
2023-03-22 13:39:11 +00:00
|
|
|
if (status < 400)
|
|
|
|
LOGP(DSIP, LOGL_NOTICE, "INVITE got status(%d), releasing leg(%p) as redirect is not"
|
|
|
|
" implemented\n", status, leg);
|
|
|
|
else
|
|
|
|
LOGP(DSIP, LOGL_ERROR, "INVITE got status(%d), releasing leg(%p)\n", status, leg);
|
|
|
|
|
2016-03-26 05:17:29 +00:00
|
|
|
nua_cancel(leg->nua_handle, TAG_END());
|
|
|
|
nua_handle_destroy(leg->nua_handle);
|
|
|
|
call_leg_release(&leg->base);
|
|
|
|
|
2018-08-31 13:44:30 +00:00
|
|
|
if (other) {
|
2019-08-07 00:13:06 +00:00
|
|
|
LOGP(DSIP, LOGL_INFO, "Releasing MNCC leg (%p) with status(%d)\n", other, status);
|
2018-08-31 13:44:30 +00:00
|
|
|
other->cause = status2cause(status);
|
2016-03-26 05:17:29 +00:00
|
|
|
other->release_call(other);
|
2018-08-31 13:44:30 +00:00
|
|
|
}
|
2016-03-26 05:17:29 +00:00
|
|
|
}
|
2019-07-30 13:58:55 +00:00
|
|
|
} else if (event == nua_i_ack) {
|
|
|
|
/* SDP comes back to us in 200 ACK after we
|
|
|
|
* respond to the re-INVITE query. */
|
|
|
|
if (sip->sip_payload && sip->sip_payload->pl_data) {
|
|
|
|
struct sip_call_leg *leg = sip_find_leg(nh);
|
forward SDP between SIP and MNCC
We have added support for sending SDP via MNCC a long time ago, but so
far the SDP section remained empty. Now, implement actually forwarding
SDP codec information between SIP and MNCC.
The aim is to let the MSC know about all codec choices the remote SIP
call leg has to offer, so that finding a codec match between local and
remote call leg becomes possible.
Store any SDP info contained in incoming SIP and MNCC messages, and send
the stored SDP to the other call leg in all outgoing SIP and MNCC
messages.
In sdp_create_file(), we used to compose fixed SDP -- instead, take the
other call leg's SDP as-is, only make sure to modify the mode (e.g.
"a=sendrecv") to reflect the current call hold state.
The RTP address and codec info in the MNCC structures is now essentially
a redundant / possibly less accurate copy of the SDP info, but leave all
of that as-is, for backwards compat.
There is codec checking that may reject unexpected codecs. The
overall/future aim is to leave all codec checking up to the MSC, but so
far just leave current behaviour unchanged, until we notice problems.
Related: SYS#5066
Related: osmo-ttcn3-hacks Ib2ae8449e673f5027f01d428d3718c006f76d93e
Change-Id: I3df5d06f38ee2d122706a9ebffde7db4f2bd6bae
2019-08-15 04:12:54 +00:00
|
|
|
if (leg) {
|
|
|
|
call_leg_rx_sdp(&leg->base, sip_get_sdp(sip));
|
2019-07-30 13:58:55 +00:00
|
|
|
sip_handle_reinvite(leg, nh, sip);
|
forward SDP between SIP and MNCC
We have added support for sending SDP via MNCC a long time ago, but so
far the SDP section remained empty. Now, implement actually forwarding
SDP codec information between SIP and MNCC.
The aim is to let the MSC know about all codec choices the remote SIP
call leg has to offer, so that finding a codec match between local and
remote call leg becomes possible.
Store any SDP info contained in incoming SIP and MNCC messages, and send
the stored SDP to the other call leg in all outgoing SIP and MNCC
messages.
In sdp_create_file(), we used to compose fixed SDP -- instead, take the
other call leg's SDP as-is, only make sure to modify the mode (e.g.
"a=sendrecv") to reflect the current call hold state.
The RTP address and codec info in the MNCC structures is now essentially
a redundant / possibly less accurate copy of the SDP info, but leave all
of that as-is, for backwards compat.
There is codec checking that may reject unexpected codecs. The
overall/future aim is to leave all codec checking up to the MSC, but so
far just leave current behaviour unchanged, until we notice problems.
Related: SYS#5066
Related: osmo-ttcn3-hacks Ib2ae8449e673f5027f01d428d3718c006f76d93e
Change-Id: I3df5d06f38ee2d122706a9ebffde7db4f2bd6bae
2019-08-15 04:12:54 +00:00
|
|
|
}
|
2019-07-30 13:58:55 +00:00
|
|
|
}
|
2016-03-26 05:17:29 +00:00
|
|
|
} else if (event == nua_r_bye || event == nua_r_cancel) {
|
|
|
|
/* our bye or hang up is answered */
|
|
|
|
struct sip_call_leg *leg = (struct sip_call_leg *) hmagic;
|
2019-08-07 00:13:06 +00:00
|
|
|
LOGP(DSIP, LOGL_INFO, "leg(%p) got resp to %s\n",
|
2016-03-26 05:17:29 +00:00
|
|
|
leg, event == nua_r_bye ? "bye" : "cancel");
|
|
|
|
nua_handle_destroy(leg->nua_handle);
|
|
|
|
call_leg_release(&leg->base);
|
|
|
|
} else if (event == nua_i_bye) {
|
|
|
|
/* our remote has hung up */
|
|
|
|
struct sip_call_leg *leg = (struct sip_call_leg *) hmagic;
|
|
|
|
struct call_leg *other = call_leg_other(&leg->base);
|
|
|
|
|
2019-08-07 00:13:06 +00:00
|
|
|
LOGP(DSIP, LOGL_INFO, "leg(%p) got bye, releasing.\n", leg);
|
2016-03-26 05:17:29 +00:00
|
|
|
nua_handle_destroy(leg->nua_handle);
|
|
|
|
call_leg_release(&leg->base);
|
|
|
|
|
|
|
|
if (other)
|
|
|
|
other->release_call(other);
|
2016-03-26 18:44:03 +00:00
|
|
|
} else if (event == nua_i_invite) {
|
2019-07-30 13:58:55 +00:00
|
|
|
/* new incoming leg or re-INVITE */
|
2019-08-07 00:13:06 +00:00
|
|
|
LOGP(DSIP, LOGL_INFO, "Processing INVITE Call-ID: %s\n", sip->sip_call_id->i_id);
|
2019-07-30 13:58:55 +00:00
|
|
|
|
|
|
|
if (status == 100) {
|
|
|
|
struct sip_call_leg *leg = sip_find_leg(nh);
|
forward SDP between SIP and MNCC
We have added support for sending SDP via MNCC a long time ago, but so
far the SDP section remained empty. Now, implement actually forwarding
SDP codec information between SIP and MNCC.
The aim is to let the MSC know about all codec choices the remote SIP
call leg has to offer, so that finding a codec match between local and
remote call leg becomes possible.
Store any SDP info contained in incoming SIP and MNCC messages, and send
the stored SDP to the other call leg in all outgoing SIP and MNCC
messages.
In sdp_create_file(), we used to compose fixed SDP -- instead, take the
other call leg's SDP as-is, only make sure to modify the mode (e.g.
"a=sendrecv") to reflect the current call hold state.
The RTP address and codec info in the MNCC structures is now essentially
a redundant / possibly less accurate copy of the SDP info, but leave all
of that as-is, for backwards compat.
There is codec checking that may reject unexpected codecs. The
overall/future aim is to leave all codec checking up to the MSC, but so
far just leave current behaviour unchanged, until we notice problems.
Related: SYS#5066
Related: osmo-ttcn3-hacks Ib2ae8449e673f5027f01d428d3718c006f76d93e
Change-Id: I3df5d06f38ee2d122706a9ebffde7db4f2bd6bae
2019-08-15 04:12:54 +00:00
|
|
|
if (leg) {
|
|
|
|
call_leg_rx_sdp(&leg->base, sip_get_sdp(sip));
|
2019-07-30 13:58:55 +00:00
|
|
|
sip_handle_reinvite(leg, nh, sip);
|
forward SDP between SIP and MNCC
We have added support for sending SDP via MNCC a long time ago, but so
far the SDP section remained empty. Now, implement actually forwarding
SDP codec information between SIP and MNCC.
The aim is to let the MSC know about all codec choices the remote SIP
call leg has to offer, so that finding a codec match between local and
remote call leg becomes possible.
Store any SDP info contained in incoming SIP and MNCC messages, and send
the stored SDP to the other call leg in all outgoing SIP and MNCC
messages.
In sdp_create_file(), we used to compose fixed SDP -- instead, take the
other call leg's SDP as-is, only make sure to modify the mode (e.g.
"a=sendrecv") to reflect the current call hold state.
The RTP address and codec info in the MNCC structures is now essentially
a redundant / possibly less accurate copy of the SDP info, but leave all
of that as-is, for backwards compat.
There is codec checking that may reject unexpected codecs. The
overall/future aim is to leave all codec checking up to the MSC, but so
far just leave current behaviour unchanged, until we notice problems.
Related: SYS#5066
Related: osmo-ttcn3-hacks Ib2ae8449e673f5027f01d428d3718c006f76d93e
Change-Id: I3df5d06f38ee2d122706a9ebffde7db4f2bd6bae
2019-08-15 04:12:54 +00:00
|
|
|
} else {
|
2019-07-30 13:58:55 +00:00
|
|
|
new_call((struct sip_agent *) magic, nh, sip);
|
forward SDP between SIP and MNCC
We have added support for sending SDP via MNCC a long time ago, but so
far the SDP section remained empty. Now, implement actually forwarding
SDP codec information between SIP and MNCC.
The aim is to let the MSC know about all codec choices the remote SIP
call leg has to offer, so that finding a codec match between local and
remote call leg becomes possible.
Store any SDP info contained in incoming SIP and MNCC messages, and send
the stored SDP to the other call leg in all outgoing SIP and MNCC
messages.
In sdp_create_file(), we used to compose fixed SDP -- instead, take the
other call leg's SDP as-is, only make sure to modify the mode (e.g.
"a=sendrecv") to reflect the current call hold state.
The RTP address and codec info in the MNCC structures is now essentially
a redundant / possibly less accurate copy of the SDP info, but leave all
of that as-is, for backwards compat.
There is codec checking that may reject unexpected codecs. The
overall/future aim is to leave all codec checking up to the MSC, but so
far just leave current behaviour unchanged, until we notice problems.
Related: SYS#5066
Related: osmo-ttcn3-hacks Ib2ae8449e673f5027f01d428d3718c006f76d93e
Change-Id: I3df5d06f38ee2d122706a9ebffde7db4f2bd6bae
2019-08-15 04:12:54 +00:00
|
|
|
}
|
2019-07-30 13:58:55 +00:00
|
|
|
}
|
2016-03-26 18:44:03 +00:00
|
|
|
} else if (event == nua_i_cancel) {
|
2016-03-26 19:55:02 +00:00
|
|
|
struct sip_call_leg *leg;
|
|
|
|
struct call_leg *other;
|
|
|
|
|
2019-08-07 00:13:06 +00:00
|
|
|
LOGP(DSIP, LOGL_INFO, "Cancelled on leg(%p)\n", hmagic);
|
2016-03-26 19:55:02 +00:00
|
|
|
|
|
|
|
leg = (struct sip_call_leg *) hmagic;
|
|
|
|
other = call_leg_other(&leg->base);
|
|
|
|
|
|
|
|
nua_handle_destroy(leg->nua_handle);
|
|
|
|
call_leg_release(&leg->base);
|
|
|
|
if (other)
|
|
|
|
other->release_call(other);
|
2019-07-30 14:30:56 +00:00
|
|
|
} else {
|
|
|
|
LOGP(DSIP, LOGL_DEBUG, "Did not handle event[%s] status(%d)\n", nua_event_name(event), status);
|
2016-03-26 05:17:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-31 13:23:48 +00:00
|
|
|
static void cause2status(int cause, int *sip_status, const char **sip_phrase, const char **reason_text)
|
|
|
|
{
|
|
|
|
uint8_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(cause_map) - 1; i++) {
|
|
|
|
if (cause_map[i].gsm48_cause == cause) {
|
|
|
|
*sip_status = cause_map[i].sip_status;
|
|
|
|
*sip_phrase = cause_map[i].sip_phrase;
|
|
|
|
*reason_text = cause_map[i].q850_reason;
|
2018-09-01 07:05:39 +00:00
|
|
|
LOGP(DSIP, LOGL_DEBUG, "%s(): Mapping cause(%s) to status(%d)\n",
|
|
|
|
__func__, gsm48_cc_cause_name(cause), *sip_status);
|
2018-08-31 13:23:48 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2018-09-01 07:05:39 +00:00
|
|
|
LOGP(DSIP, LOGL_ERROR, "%s(): Cause(%s) not found in map.\n", __func__, gsm48_cc_cause_name(cause));
|
2018-08-31 13:23:48 +00:00
|
|
|
*sip_status = cause_map[i].sip_status;
|
|
|
|
*sip_phrase = cause_map[i].sip_phrase;
|
|
|
|
*reason_text = cause_map[i].q850_reason;
|
|
|
|
}
|
2016-03-26 05:17:29 +00:00
|
|
|
|
|
|
|
static void sip_release_call(struct call_leg *_leg)
|
|
|
|
{
|
|
|
|
struct sip_call_leg *leg;
|
2018-08-31 13:44:30 +00:00
|
|
|
char reason[64];
|
|
|
|
int sip_cause;
|
|
|
|
const char *sip_phrase;
|
|
|
|
const char *reason_text;
|
2016-03-26 05:17:29 +00:00
|
|
|
|
|
|
|
OSMO_ASSERT(_leg->type == CALL_TYPE_SIP);
|
|
|
|
leg = (struct sip_call_leg *) _leg;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If a dialogue is not confirmed yet, we can probably not do much
|
|
|
|
* but wait for the timeout. For a confirmed one we can send cancel
|
|
|
|
* and for a connected one bye. I don't see how sofia-sip is going
|
|
|
|
* to help us here.
|
|
|
|
*/
|
2018-08-31 13:44:30 +00:00
|
|
|
|
2019-08-07 00:13:06 +00:00
|
|
|
LOGP(DSIP, LOGL_INFO, "%s(): Release with MNCC cause(%s)\n", __func__, gsm48_cc_cause_name(_leg->cause));
|
2018-08-31 13:44:30 +00:00
|
|
|
cause2status(_leg->cause, &sip_cause, &sip_phrase, &reason_text);
|
|
|
|
snprintf(reason, sizeof reason, "Q.850;cause=%u;text=\"%s\"", _leg->cause, reason_text);
|
|
|
|
|
2016-03-26 05:17:29 +00:00
|
|
|
switch (leg->state) {
|
|
|
|
case SIP_CC_INITIAL:
|
2019-08-07 00:13:06 +00:00
|
|
|
LOGP(DSIP, LOGL_INFO, "Cancelling leg(%p) in initial state\n", leg);
|
2016-03-26 05:17:29 +00:00
|
|
|
nua_handle_destroy(leg->nua_handle);
|
|
|
|
call_leg_release(&leg->base);
|
|
|
|
break;
|
|
|
|
case SIP_CC_DLG_CNFD:
|
2019-08-07 00:13:06 +00:00
|
|
|
LOGP(DSIP, LOGL_INFO, "Cancelling leg(%p) in confirmed state\n", leg);
|
2016-03-26 20:09:07 +00:00
|
|
|
if (leg->dir == SIP_DIR_MT)
|
|
|
|
nua_cancel(leg->nua_handle, TAG_END());
|
|
|
|
else {
|
2018-08-31 13:44:30 +00:00
|
|
|
nua_respond(leg->nua_handle, sip_cause, sip_phrase,
|
|
|
|
SIPTAG_REASON_STR(reason),
|
2016-03-26 20:09:07 +00:00
|
|
|
TAG_END());
|
|
|
|
nua_handle_destroy(leg->nua_handle);
|
|
|
|
call_leg_release(&leg->base);
|
|
|
|
}
|
2016-03-26 05:17:29 +00:00
|
|
|
break;
|
|
|
|
case SIP_CC_CONNECTED:
|
2019-07-30 14:26:03 +00:00
|
|
|
case SIP_CC_HOLD:
|
2019-08-06 14:45:12 +00:00
|
|
|
LOGP(DSIP, LOGL_NOTICE, "Ending leg(%p) in connected state.\n", leg);
|
2016-03-26 05:17:29 +00:00
|
|
|
nua_bye(leg->nua_handle, TAG_END());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-26 21:11:06 +00:00
|
|
|
static void sip_ring_call(struct call_leg *_leg)
|
|
|
|
{
|
|
|
|
struct sip_call_leg *leg;
|
|
|
|
|
|
|
|
OSMO_ASSERT(_leg->type == CALL_TYPE_SIP);
|
|
|
|
leg = (struct sip_call_leg *) _leg;
|
|
|
|
|
forward SDP between SIP and MNCC
We have added support for sending SDP via MNCC a long time ago, but so
far the SDP section remained empty. Now, implement actually forwarding
SDP codec information between SIP and MNCC.
The aim is to let the MSC know about all codec choices the remote SIP
call leg has to offer, so that finding a codec match between local and
remote call leg becomes possible.
Store any SDP info contained in incoming SIP and MNCC messages, and send
the stored SDP to the other call leg in all outgoing SIP and MNCC
messages.
In sdp_create_file(), we used to compose fixed SDP -- instead, take the
other call leg's SDP as-is, only make sure to modify the mode (e.g.
"a=sendrecv") to reflect the current call hold state.
The RTP address and codec info in the MNCC structures is now essentially
a redundant / possibly less accurate copy of the SDP info, but leave all
of that as-is, for backwards compat.
There is codec checking that may reject unexpected codecs. The
overall/future aim is to leave all codec checking up to the MSC, but so
far just leave current behaviour unchanged, until we notice problems.
Related: SYS#5066
Related: osmo-ttcn3-hacks Ib2ae8449e673f5027f01d428d3718c006f76d93e
Change-Id: I3df5d06f38ee2d122706a9ebffde7db4f2bd6bae
2019-08-15 04:12:54 +00:00
|
|
|
/* 180 Ringing should not contain any SDP. */
|
2016-03-26 21:11:06 +00:00
|
|
|
nua_respond(leg->nua_handle, SIP_180_RINGING, TAG_END());
|
|
|
|
}
|
|
|
|
|
|
|
|
static void sip_connect_call(struct call_leg *_leg)
|
|
|
|
{
|
|
|
|
struct call_leg *other;
|
|
|
|
struct sip_call_leg *leg;
|
|
|
|
char *sdp;
|
|
|
|
|
|
|
|
OSMO_ASSERT(_leg->type == CALL_TYPE_SIP);
|
|
|
|
leg = (struct sip_call_leg *) _leg;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TODO/FIXME: check if resulting codec is compatible..
|
|
|
|
*/
|
|
|
|
|
|
|
|
other = call_leg_other(&leg->base);
|
|
|
|
if (!other) {
|
|
|
|
sip_release_call(&leg->base);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-07-30 13:04:21 +00:00
|
|
|
sdp = sdp_create_file(leg, other, sdp_sendrecv);
|
2016-03-26 21:11:06 +00:00
|
|
|
|
|
|
|
leg->state = SIP_CC_CONNECTED;
|
|
|
|
nua_respond(leg->nua_handle, SIP_200_OK,
|
2016-03-31 14:04:47 +00:00
|
|
|
NUTAG_MEDIA_ENABLE(0),
|
2016-03-26 21:11:06 +00:00
|
|
|
SIPTAG_CONTENT_TYPE_STR("application/sdp"),
|
|
|
|
SIPTAG_PAYLOAD_STR(sdp),
|
|
|
|
TAG_END());
|
|
|
|
talloc_free(sdp);
|
|
|
|
}
|
|
|
|
|
2017-02-22 12:50:11 +00:00
|
|
|
static void sip_dtmf_call(struct call_leg *_leg, int keypad)
|
|
|
|
{
|
|
|
|
struct sip_call_leg *leg;
|
|
|
|
char *buf;
|
|
|
|
|
|
|
|
OSMO_ASSERT(_leg->type == CALL_TYPE_SIP);
|
|
|
|
leg = (struct sip_call_leg *) _leg;
|
|
|
|
|
|
|
|
buf = talloc_asprintf(leg, "Signal=%c\nDuration=160\n", keypad);
|
|
|
|
nua_info(leg->nua_handle,
|
|
|
|
NUTAG_MEDIA_ENABLE(0),
|
|
|
|
SIPTAG_CONTENT_TYPE_STR("application/dtmf-relay"),
|
|
|
|
SIPTAG_PAYLOAD_STR(buf), TAG_END());
|
|
|
|
talloc_free(buf);
|
|
|
|
}
|
|
|
|
|
2019-07-30 14:26:03 +00:00
|
|
|
static void sip_hold_call(struct call_leg *_leg)
|
|
|
|
{
|
|
|
|
struct sip_call_leg *leg;
|
|
|
|
struct call_leg *other_leg;
|
|
|
|
OSMO_ASSERT(_leg->type == CALL_TYPE_SIP);
|
|
|
|
leg = (struct sip_call_leg *) _leg;
|
|
|
|
other_leg = call_leg_other(&leg->base);
|
2019-08-07 01:20:53 +00:00
|
|
|
if (!other_leg) {
|
|
|
|
LOGP(DMNCC, LOGL_ERROR, "leg(%p) other leg gone!\n", leg);
|
|
|
|
sip_release_call(&leg->base);
|
|
|
|
return;
|
|
|
|
}
|
2019-07-30 14:26:03 +00:00
|
|
|
char *sdp = sdp_create_file(leg, other_leg, sdp_sendonly);
|
|
|
|
nua_invite(leg->nua_handle,
|
|
|
|
NUTAG_MEDIA_ENABLE(0),
|
|
|
|
SIPTAG_CONTENT_TYPE_STR("application/sdp"),
|
|
|
|
SIPTAG_PAYLOAD_STR(sdp),
|
|
|
|
TAG_END());
|
|
|
|
talloc_free(sdp);
|
|
|
|
leg->state = SIP_CC_HOLD;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void sip_retrieve_call(struct call_leg *_leg)
|
|
|
|
{
|
|
|
|
struct sip_call_leg *leg;
|
|
|
|
struct call_leg *other_leg;
|
|
|
|
OSMO_ASSERT(_leg->type == CALL_TYPE_SIP);
|
|
|
|
leg = (struct sip_call_leg *) _leg;
|
|
|
|
other_leg = call_leg_other(&leg->base);
|
2019-08-07 01:20:53 +00:00
|
|
|
if (!other_leg) {
|
|
|
|
LOGP(DMNCC, LOGL_ERROR, "leg(%p) other leg gone!\n", leg);
|
|
|
|
sip_release_call(&leg->base);
|
|
|
|
return;
|
|
|
|
}
|
2019-07-30 14:26:03 +00:00
|
|
|
char *sdp = sdp_create_file(leg, other_leg, sdp_sendrecv);
|
|
|
|
nua_invite(leg->nua_handle,
|
|
|
|
NUTAG_MEDIA_ENABLE(0),
|
|
|
|
SIPTAG_CONTENT_TYPE_STR("application/sdp"),
|
|
|
|
SIPTAG_PAYLOAD_STR(sdp),
|
|
|
|
TAG_END());
|
|
|
|
talloc_free(sdp);
|
|
|
|
leg->state = SIP_CC_CONNECTED;
|
|
|
|
}
|
|
|
|
|
2016-03-26 05:17:29 +00:00
|
|
|
static int send_invite(struct sip_agent *agent, struct sip_call_leg *leg,
|
|
|
|
const char *calling_num, const char *called_num)
|
|
|
|
{
|
|
|
|
struct call_leg *other = leg->base.call->initial;
|
|
|
|
|
2017-03-07 15:01:04 +00:00
|
|
|
char *from = talloc_asprintf(leg, "sip:%s@%s:%d",
|
2016-03-26 05:17:29 +00:00
|
|
|
calling_num,
|
2017-03-07 15:01:04 +00:00
|
|
|
agent->app->sip.local_addr,
|
|
|
|
agent->app->sip.local_port);
|
|
|
|
char *to = talloc_asprintf(leg, "sip:%s@%s:%d",
|
2016-03-26 05:17:29 +00:00
|
|
|
called_num,
|
2017-03-07 15:01:04 +00:00
|
|
|
agent->app->sip.remote_addr,
|
|
|
|
agent->app->sip.remote_port);
|
2019-07-30 13:04:21 +00:00
|
|
|
char *sdp = sdp_create_file(leg, other, sdp_sendrecv);
|
2016-03-26 05:17:29 +00:00
|
|
|
|
2021-10-27 13:28:36 +00:00
|
|
|
/* Encode the Global Call Reference (if present) */
|
|
|
|
char *x_gcr = NULL;
|
|
|
|
|
|
|
|
if (leg->base.call->gcr_present) {
|
|
|
|
struct msgb *msg = msgb_alloc(16, "SIP GCR");
|
|
|
|
|
|
|
|
if (msg != NULL && osmo_enc_gcr(msg, &leg->base.call->gcr) > 0)
|
|
|
|
x_gcr = talloc_asprintf(leg, "X-Global-Call-Ref: %s", msgb_hexdump(msg));
|
|
|
|
else
|
|
|
|
LOGP(DSIP, LOGL_ERROR, "Failed to encode GCR for leg(%p)\n", leg);
|
|
|
|
msgb_free(msg);
|
|
|
|
}
|
2021-01-31 04:24:29 +00:00
|
|
|
|
2016-03-26 05:17:29 +00:00
|
|
|
leg->state = SIP_CC_INITIAL;
|
2016-03-26 17:55:33 +00:00
|
|
|
leg->dir = SIP_DIR_MT;
|
2016-03-26 05:17:29 +00:00
|
|
|
nua_invite(leg->nua_handle,
|
|
|
|
SIPTAG_FROM_STR(from),
|
|
|
|
SIPTAG_TO_STR(to),
|
|
|
|
NUTAG_MEDIA_ENABLE(0),
|
|
|
|
SIPTAG_CONTENT_TYPE_STR("application/sdp"),
|
2021-10-27 13:28:36 +00:00
|
|
|
TAG_IF(x_gcr, SIPTAG_HEADER_STR(x_gcr)),
|
2016-03-26 05:17:29 +00:00
|
|
|
SIPTAG_PAYLOAD_STR(sdp),
|
|
|
|
TAG_END());
|
|
|
|
|
|
|
|
leg->base.call->remote = &leg->base;
|
|
|
|
talloc_free(from);
|
|
|
|
talloc_free(to);
|
|
|
|
talloc_free(sdp);
|
2021-10-27 13:26:05 +00:00
|
|
|
talloc_free(x_gcr);
|
2016-03-26 05:17:29 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-04-04 17:52:41 +00:00
|
|
|
int sip_create_remote_leg(struct sip_agent *agent, struct call *call)
|
2016-03-26 05:17:29 +00:00
|
|
|
{
|
|
|
|
struct sip_call_leg *leg;
|
|
|
|
|
|
|
|
leg = talloc_zero(call, struct sip_call_leg);
|
|
|
|
if (!leg) {
|
|
|
|
LOGP(DSIP, LOGL_ERROR, "Failed to allocate leg for call(%u)\n",
|
|
|
|
call->id);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
leg->base.type = CALL_TYPE_SIP;
|
|
|
|
leg->base.call = call;
|
|
|
|
leg->base.release_call = sip_release_call;
|
2017-02-22 12:50:11 +00:00
|
|
|
leg->base.dtmf = sip_dtmf_call;
|
2019-08-06 15:56:30 +00:00
|
|
|
leg->base.hold_call = sip_hold_call;
|
|
|
|
leg->base.retrieve_call = sip_retrieve_call;
|
2016-03-26 05:17:29 +00:00
|
|
|
leg->agent = agent;
|
|
|
|
|
|
|
|
leg->nua_handle = nua_handle(agent->nua, leg, TAG_END());
|
|
|
|
if (!leg->nua_handle) {
|
|
|
|
LOGP(DSIP, LOGL_ERROR, "Failed to allocate nua for call(%u)\n",
|
|
|
|
call->id);
|
|
|
|
talloc_free(leg);
|
|
|
|
return -2;
|
|
|
|
}
|
|
|
|
|
2016-04-04 17:52:41 +00:00
|
|
|
return send_invite(agent, leg, call->source, call->dest);
|
2016-03-22 13:15:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
char *make_sip_uri(struct sip_agent *agent)
|
|
|
|
{
|
|
|
|
const char *hostname = agent->app->sip.local_addr;
|
|
|
|
|
|
|
|
/* We need to map 0.0.0.0 to '*' to bind everywhere */
|
|
|
|
if (strcmp(hostname, "0.0.0.0") == 0)
|
|
|
|
hostname = "*";
|
|
|
|
|
|
|
|
return talloc_asprintf(tall_mncc_ctx, "sip:%s:%d",
|
|
|
|
agent->app->sip.local_addr,
|
|
|
|
agent->app->sip.local_port);
|
|
|
|
}
|
|
|
|
|
2018-04-15 19:51:41 +00:00
|
|
|
/* http://sofia-sip.sourceforge.net/refdocs/debug_logs.html */
|
|
|
|
static void sip_logger(void *stream, char const *fmt, va_list ap)
|
|
|
|
{
|
|
|
|
/* this is ugly, as unfortunately sofia-sip does not pass the log level to
|
|
|
|
* the log handler call-back function, so we have no clue what log level the
|
|
|
|
* currently logged message was sent for :( As a result, we can only use one
|
|
|
|
* hard-coded LOGL_NOTICE here */
|
2019-11-18 14:41:28 +00:00
|
|
|
if (!log_check_level(DSIP, LOGL_NOTICE))
|
|
|
|
return;
|
|
|
|
/* The sofia-sip log line *sometimes* lacks a terminating '\n'. Add it. */
|
|
|
|
char log_line[256];
|
|
|
|
int rc = vsnprintf(log_line, sizeof(log_line), fmt, ap);
|
|
|
|
if (rc > 0) {
|
|
|
|
/* since we're explicitly checking for sizeof(log_line), we can use vsnprintf()'s return value (which,
|
|
|
|
* alone, would possibly cause writing past the buffer's end). */
|
|
|
|
char *end = log_line + OSMO_MIN(rc, sizeof(log_line) - 2);
|
|
|
|
osmo_strlcpy(end, "\n", 2);
|
|
|
|
LOGP(DSIP, LOGL_NOTICE, "%s", log_line);
|
|
|
|
} else
|
|
|
|
LOGP(DSIP, LOGL_NOTICE, "unknown logging from sip\n");
|
2018-04-15 19:51:41 +00:00
|
|
|
}
|
|
|
|
|
2016-03-22 13:15:17 +00:00
|
|
|
void sip_agent_init(struct sip_agent *agent, struct app_config *app)
|
|
|
|
{
|
|
|
|
agent->app = app;
|
|
|
|
|
|
|
|
su_init();
|
|
|
|
su_home_init(&agent->home);
|
2018-04-15 19:51:41 +00:00
|
|
|
su_log_redirect(su_log_default, &sip_logger, NULL);
|
|
|
|
su_log_redirect(su_log_global, &sip_logger, NULL);
|
2016-03-22 13:15:17 +00:00
|
|
|
agent->root = su_glib_root_create(NULL);
|
|
|
|
su_root_threading(agent->root, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int sip_agent_start(struct sip_agent *agent)
|
|
|
|
{
|
|
|
|
char *sip_uri = make_sip_uri(agent);
|
|
|
|
|
|
|
|
agent->nua = nua_create(agent->root,
|
|
|
|
nua_callback, agent,
|
|
|
|
NUTAG_URL(sip_uri),
|
2016-03-26 05:19:42 +00:00
|
|
|
NUTAG_AUTOACK(0),
|
|
|
|
NUTAG_AUTOALERT(0),
|
|
|
|
NUTAG_AUTOANSWER(0),
|
2016-03-22 13:15:17 +00:00
|
|
|
TAG_END());
|
|
|
|
talloc_free(sip_uri);
|
|
|
|
return agent->nua ? 0 : -1;
|
|
|
|
}
|