WIOP... connect call.. and handle other parts of it too

This commit is contained in:
Holger Hans Peter Freyther 2016-03-25 21:15:35 +01:00
parent 9b6b73207d
commit 9f5edf62bf
2 changed files with 185 additions and 2 deletions

View File

@ -65,6 +65,12 @@ struct call_leg {
void (*release_call)(struct call_leg *);
};
enum sip_cc_state {
SIP_CC_INITIAL,
SIP_CC_DLG_CNFD,
SIP_CC_CONNECTED,
};
struct sip_call_leg {
/* base class */
struct call_leg base;
@ -74,6 +80,8 @@ struct sip_call_leg {
/* per instance members */
struct nua_handle_s *nua_handle;
enum sip_cc_state state;
const char *wanted_codec;
};
enum mncc_cc_state {

179
src/sip.c
View File

@ -25,16 +25,171 @@
#include <osmocom/core/utils.h>
#include <sofia-sip/sdp.h>
#include <talloc.h>
#include <string.h>
extern void *tall_mncc_ctx;
static bool extract_sdp(struct sip_call_leg *leg, const sip_t *sip)
{
sdp_connection_t *conn;
sdp_session_t *sdp;
sdp_parser_t *parser;
sdp_media_t *media;
const char *sdp_data;
bool found_conn = false, found_map = false;
if (!sip->sip_payload || !sip->sip_payload->pl_data) {
LOGP(DSIP, LOGL_ERROR, "leg(%p) but no SDP file\n", leg);
return false;
}
sdp_data = sip->sip_payload->pl_data;
parser = sdp_parse(NULL, sdp_data, strlen(sdp_data), 0);
if (!parser) {
LOGP(DSIP, LOGL_ERROR, "leg(%p) failed to parse SDP\n",
leg);
return false;
}
sdp = sdp_session(parser);
if (!sdp) {
LOGP(DSIP, LOGL_ERROR, "leg(%p) no sdp session\n", leg);
sdp_parser_free(parser);
return false;
}
for (conn = sdp->sdp_connection; conn; conn = conn->c_next) {
struct in_addr addr;
if (conn->c_addrtype != sdp_addr_ip4)
continue;
inet_aton(conn->c_address, &addr);
leg->base.ip = addr.s_addr;
found_conn = true;
break;
}
for (media = sdp->sdp_media; media; media = media->m_next) {
sdp_rtpmap_t *map;
if (media->m_proto != sdp_proto_rtp)
continue;
if (media->m_type != sdp_media_audio)
continue;
for (map = media->m_rtpmaps; map; map = map->rm_next) {
if (strcasecmp(map->rm_encoding, leg->wanted_codec) != 0)
continue;
leg->base.port = media->m_port;
leg->base.payload_type = map->rm_pt;
found_map = true;
break;
}
if (found_map)
break;
}
if (!found_conn || !found_map) {
LOGP(DSIP, LOGL_ERROR, "leg(%p) did not find %d/%d\n",
leg, found_conn, found_map);
sdp_parser_free(parser);
return false;
}
sdp_parser_free(parser);
return true;
}
static void call_progress(struct sip_call_leg *leg, const sip_t *sip)
{
struct call_leg *other = call_leg_other(&leg->base);
if (!other)
return;
LOGP(DSIP, LOGL_NOTICE, "leg(%p) is now rining.\n", leg);
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;
}
if (!extract_sdp(leg, sip)) {
LOGP(DSIP, LOGL_ERROR, "leg(%p) incompatible audio, releasing\n", leg);
nua_cancel(leg->nua_handle, TAG_END());
other->release_call(other);
return;
}
LOGP(DSIP, LOGL_NOTICE, "leg(%p) is now connected.\n", leg);
leg->state = SIP_CC_CONNECTED;
other->connect_call(other);
nua_ack(leg->nua_handle, TAG_END());
}
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[])
{
LOGP(DSIP, LOGL_DEBUG, "SIP event(%u) status(%d) phrase(%s) %p\n",
event, status, phrase, hmagic);
if (event == nua_r_invite) {
struct sip_call_leg *leg;
leg = (struct sip_call_leg *) hmagic;
/* MT call is moving forward */
/* The dialogue is now confirmed */
if (leg->state == SIP_CC_INITIAL)
leg->state = SIP_CC_DLG_CNFD;
if (status == 180)
call_progress(leg, sip);
else if (status == 200)
call_connect(leg, sip);
else if (status >= 300) {
struct call_leg *other = call_leg_other(&leg->base);
LOGP(DSIP, LOGL_ERROR, "leg(%p) unknown err, releasing.\n", leg);
nua_cancel(leg->nua_handle, TAG_END());
nua_handle_destroy(leg->nua_handle);
call_leg_release(&leg->base);
if (other)
other->release_call(other);
}
} 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;
LOGP(DSIP, LOGL_NOTICE, "leg(%p) got resp to %s\n",
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);
LOGP(DSIP, LOGL_ERROR, "leg(%p) got bye, releasing.\n", leg);
nua_handle_destroy(leg->nua_handle);
call_leg_release(&leg->base);
if (other)
other->release_call(other);
}
}
@ -51,7 +206,21 @@ static void sip_release_call(struct call_leg *_leg)
* and for a connected one bye. I don't see how sofia-sip is going
* to help us here.
*/
nua_cancel(leg->nua_handle, TAG_END());
switch (leg->state) {
case SIP_CC_INITIAL:
LOGP(DSIP, LOGL_NOTICE, "Canceling leg(%p) in int state\n", leg);
nua_handle_destroy(leg->nua_handle);
call_leg_release(&leg->base);
break;
case SIP_CC_DLG_CNFD:
LOGP(DSIP, LOGL_NOTICE, "Canceling leg(%p) in cnfd state\n", leg);
nua_cancel(leg->nua_handle, TAG_END());
break;
case SIP_CC_CONNECTED:
LOGP(DSIP, LOGL_NOTICE, "Ending leg(%p) in con\n", leg);
nua_bye(leg->nua_handle, TAG_END());
break;
}
}
static const char *media_name(int ptmsg)
@ -65,6 +234,8 @@ static int send_invite(struct sip_agent *agent, struct sip_call_leg *leg,
struct call_leg *other = leg->base.call->initial;
struct in_addr net = { .s_addr = ntohl(other->ip) };
leg->wanted_codec = media_name(other->payload_msg_type);
char *from = talloc_asprintf(leg, "sip:%s@%s",
calling_num,
agent->app->sip.local_addr);
@ -82,8 +253,9 @@ static int send_invite(struct sip_agent *agent, struct sip_call_leg *leg,
inet_ntoa(net), inet_ntoa(net), /* never use diff. addr! */
other->port, other->payload_type,
other->payload_type,
media_name(other->payload_msg_type));
leg->wanted_codec);
leg->state = SIP_CC_INITIAL;
nua_invite(leg->nua_handle,
SIPTAG_FROM_STR(from),
SIPTAG_TO_STR(to),
@ -157,6 +329,9 @@ int sip_agent_start(struct sip_agent *agent)
agent->nua = nua_create(agent->root,
nua_callback, agent,
NUTAG_URL(sip_uri),
NUTAG_AUTOACK(0),
NUTAG_AUTOALERT(0),
NUTAG_AUTOANSWER(0),
TAG_END());
talloc_free(sip_uri);
return agent->nua ? 0 : -1;