Skip to content

Commit

Permalink
b2b_logic: Support for rfc3515 NOTIFY and rollback on failed transfers
Browse files Browse the repository at this point in the history
  • Loading branch information
Nick Altmann committed Feb 2, 2021
1 parent 1c3e6e3 commit 9faaa31
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 11 deletions.
48 changes: 47 additions & 1 deletion modules/b2b_logic/b2b_logic.c
Expand Up @@ -57,6 +57,7 @@ static void mod_destroy(void);
static int child_init(int rank);
static int fixup_init_flags(void** param);
static int fixup_free_init_flags(void** param);
static int fixup_bridge_flags(void** param);
static int fixup_init_id(void** param);
static int fixup_check_avp(void** param);
static int fixup_route(void** param);
Expand Down Expand Up @@ -208,7 +209,8 @@ static cmd_export_t cmds[]=
{CMD_PARAM_STR,0,0},
{CMD_PARAM_STR,0,0},
{CMD_PARAM_STR|CMD_PARAM_OPT,0,0},
{CMD_PARAM_INT|CMD_PARAM_OPT,0,0}, {0,0,0}},
{CMD_PARAM_STR|CMD_PARAM_OPT|CMD_PARAM_FIX_NULL,
fixup_bridge_flags, fixup_free_init_flags}, {0,0,0}},
REQUEST_ROUTE},
{"b2b_bridge_request", (cmd_function)b2b_bridge_request,
{{CMD_PARAM_STR,0,0}, {CMD_PARAM_INT,0,0}, {0,0,0}},
Expand Down Expand Up @@ -727,6 +729,50 @@ static int fixup_free_init_flags(void** param)
return 0;
}

static int fixup_bridge_flags(void** param)
{
str *s = (str*)*param;
int st;
struct b2b_bridge_params *bridge_params;

bridge_params = pkg_malloc(sizeof *bridge_params);
if (!bridge_params) {
LM_ERR("out of pkg memory\n");
return -1;
}
memset(bridge_params, 0, sizeof *bridge_params);

bridge_params->lifetime = 0;

*param = (void*)bridge_params;

if (!s)
return 0;

for( st=0 ; st< s->len ; st++ ) {
switch (s->s[st])
{
case 't':
while (st<s->len-1 && isdigit(s->s[st+1])) {
bridge_params->lifetime =
bridge_params->lifetime*10 + s->s[st+1] - '0';
st++;
}
break;
case 'n':
bridge_params->flags |= B2BL_BR_FLAG_NOTIFY;
break;
case 'f':
bridge_params->flags |= B2BL_BR_FLAG_RETURN_AFTER_FAILURE;
break;
default:
LM_WARN("unknown option `%c'\n", s->s[st]);
}
}

return 0;
}

static int fixup_init_id(void** param)
{
str *s = (str*)*param;
Expand Down
10 changes: 10 additions & 0 deletions modules/b2b_logic/b2b_logic.h
Expand Up @@ -53,6 +53,10 @@
#define B2BL_FLAG_TRANSPARENT_TO 0x02
#define B2BL_FLAG_USE_INIT_SDP 0x04

/* B2BL_BR_FLAGS constants */
#define B2BL_BR_FLAG_NOTIFY 0x01
#define B2BL_BR_FLAG_RETURN_AFTER_FAILURE 0x02

/* modes to write in db */
#define NO_DB 0
#define WRITE_THROUGH 1
Expand Down Expand Up @@ -84,6 +88,12 @@ struct b2b_params
str *init_body_type;
};

struct b2b_bridge_params
{
unsigned int flags;
unsigned int lifetime;
};

enum pv_entity_field {
PV_ENTITY_KEY,
PV_ENTITY_CALLID,
Expand Down
22 changes: 18 additions & 4 deletions modules/b2b_logic/doc/b2b_logic_admin.xml
Expand Up @@ -635,7 +635,7 @@ b2b_client_new("client1", "sip:alice@opensips.org");

<section id="func_b2b_bridge" xreflabel="b2b_bridge()">
<title>
<function moreinfo="none">b2b_bridge(entity1, entity2, [provmedia_uri], [lifetime])</function>
<function moreinfo="none">b2b_bridge(entity1, entity2, [provmedia_uri], [flags])</function>
</title>
<para>
This function bridges two entities, in the context of an existing B2B session
Expand All @@ -660,9 +660,23 @@ b2b_client_new("client1", "sip:alice@opensips.org");
media server to be connected with the caller while the callee answers.
</para></listitem>
<listitem><para>
<emphasis>lifetime (int, optional)</emphasis> - maximum duration of the B2B
session. If the lifetime expires, the B2BUA will send BYE messages to both
ends and delete the record.
<emphasis>flags (string, optional)</emphasis> - meanings of the flags is as follows:
<itemizedlist>
<listitem><para>
<emphasis>t[nn]</emphasis> - Maximum duration of the B2B
session. If the lifetime expires, the B2BUA will send BYE messages to both
ends and delete the record.
Example: t300.
</para></listitem>
<listitem><para>
<emphasis>n</emphasis> - Enable rfc3515 NOTIFY to inform the agent sending the
REFER of the status of the reference.
</para></listitem>
<listitem><para>
<emphasis>f</emphasis> - Rollback call to state before bridging in case of transfer
failed, don't hangup the call (default behaviour).
</para></listitem>
</itemizedlist>
</para></listitem>
</itemizedlist>
<para>
Expand Down
85 changes: 79 additions & 6 deletions modules/b2b_logic/logic.c
Expand Up @@ -84,6 +84,7 @@ static str method_invite= {INVITE, INVITE_LEN};
static str method_ack = {ACK, ACK_LEN};
static str method_bye = {BYE, BYE_LEN};
static str method_cancel= {CANCEL, CANCEL_LEN};
static str method_notify= {NOTIFY, NOTIFY_LEN};

static str ok = str_init("OK");
static str notAcceptable = str_init("Not Acceptable");
Expand Down Expand Up @@ -473,9 +474,25 @@ int process_bridge_dialog_end(b2bl_tuple_t* tuple, unsigned int hash_index,
}
else
{
/* the entity to connect replied with negative reply */
b2b_end_dialog(tuple->bridge_entities[0], tuple, hash_index);
b2b_mark_todel(tuple);
if(tuple->bridge_flags & B2BL_BR_FLAG_RETURN_AFTER_FAILURE)
{
/* Delete failed entity */
b2bl_delete_entity(bentity, tuple, hash_index, 1);

/* Restore initial bridge */
tuple->bridge_entities[1] = tuple->bridge_entities[0];
tuple->bridge_entities[0] = tuple->servers[0];

tuple->bridge_entities[1]->peer = tuple->bridge_entities[0];
tuple->bridge_entities[0]->peer = tuple->bridge_entities[1];

/* Disable bridging state */
tuple->state = B2B_NOTDEF_STATE;
} else {
/* the entity to connect replied with negative reply */
b2b_end_dialog(tuple->bridge_entities[0], tuple, hash_index);
b2b_mark_todel(tuple);
}
}
}
else
Expand Down Expand Up @@ -512,6 +529,45 @@ int process_bridge_bye(struct sip_msg* msg, b2bl_tuple_t* tuple,
return process_bridge_dialog_end(tuple, hash_index, entity_no, entity);
}

int process_bridge_notify(b2bl_entity_id_t *entity, unsigned int hash_index, struct sip_msg* msg)
{
b2b_req_data_t req_data;
static char def_hdrs[] = "Event: refer\r\nContent-Type: message/sipfrag\r\nSubscription-State: ";
static char buf[BUF_LEN];
static str body = str_init("SIP/2.0 100 Trying");
static str hdrs = {buf, 0};

memset(&req_data, 0, sizeof(b2b_req_data_t));
PREP_REQ_DATA(entity);
req_data.method = &method_notify;
req_data.client_headers = &entity->hdrs;
req_data.body = 0;
if (!msg) {
hdrs.len = snprintf(buf, BUF_LEN, "%sactive;expires=%d\r\n", def_hdrs, 60);
} else {
body.s = msg->first_line.u.reply.version.s;
body.len = msg->first_line.u.reply.version.len +
msg->first_line.u.reply.status.len +
msg->first_line.u.reply.reason.len + 2;
hdrs.len = snprintf(buf, BUF_LEN, "%sterminated;reason=noresource\r\n", def_hdrs);
}
LM_DBG("Sending notify [%.*s]\n", body.len, body.s);
if ((unsigned)hdrs.len >= BUF_LEN) {
LM_ERR("Buffer is too small\n");
return -1;
}
req_data.extra_headers = &hdrs;
req_data.body = &body;
b2bl_htable[hash_index].locked_by = process_no;
if (b2b_api.send_request(&req_data) < 0) {
LM_ERR("Failed to send NOTIFY\n");
b2bl_htable[hash_index].locked_by = -1;
return -1;
}
b2bl_htable[hash_index].locked_by = -1;

return 0;
}

int process_bridge_negreply(b2bl_tuple_t* tuple,
unsigned int hash_index, b2bl_entity_id_t* entity, struct sip_msg* msg)
Expand Down Expand Up @@ -643,6 +699,7 @@ static b2bl_entity_id_t* b2bl_new_client(str* to_uri, str *proxy, str* from_uri,

return entity;
}

int process_bridge_200OK(struct sip_msg* msg, str* extra_headers,
str* body, b2bl_tuple_t* tuple, unsigned int hash_index,
b2bl_entity_id_t* entity)
Expand Down Expand Up @@ -1123,15 +1180,26 @@ int _b2b_handle_reply(struct sip_msg *msg, b2bl_tuple_t *tuple,
if (tuple->state == B2B_BRIDGING_STATE) {
LM_DBG("Received a reply [%d] while in BRIDGING scenario\n",
statuscode);

/* if the scenario state is B2B_BRIDGING_STATE -> we should have a reply for INVITE */
/* extract the method from Cseq header */

if(method_value == METHOD_NOTIFY) goto done1; /* Silently ignore reply on NOTIFY */

if(method_value != METHOD_INVITE)
{
LM_ERR("Wrong scenario state [B2B_BRIDGING_STATE] for this"
" reply(for method %d)\n", method_value);
goto error;
}

if(statuscode >= 200 && entity == tuple->bridge_entities[1]) /* Reply from new bridge entity */
{
process_bridge_notify(tuple->servers[0], cur_route_ctx.hash_index, msg);
if(statuscode == 200 || !(tuple->bridge_flags & B2BL_BR_FLAG_RETURN_AFTER_FAILURE))
b2bl_delete_entity(tuple->servers[0], tuple, tuple->hash_index, 1);
}

/* if a negative reply */
if(statuscode >= 300)
{
Expand Down Expand Up @@ -1996,7 +2064,7 @@ static struct b2bl_new_entity *get_ent_to_bridge(b2bl_tuple_t *tuple,
}

int b2b_scenario_bridge(struct sip_msg *msg, str *br_ent1_str, str *br_ent2_str,
str *provmedia_uri, int *lifetime)
str *provmedia_uri, struct b2b_bridge_params *params)
{
b2bl_tuple_t *tuple;
b2bl_entity_id_t *entity, *e = NULL, *old_entity = NULL;
Expand All @@ -2019,6 +2087,8 @@ int b2b_scenario_bridge(struct sip_msg *msg, str *br_ent1_str, str *br_ent2_str,
goto done;
}

tuple->bridge_flags = params->flags;

entity = b2bl_search_entity(tuple, &cur_route_ctx.entity_key,
cur_route_ctx.entity_type, &entity_head);
if(entity == NULL)
Expand Down Expand Up @@ -2057,8 +2127,11 @@ int b2b_scenario_bridge(struct sip_msg *msg, str *br_ent1_str, str *br_ent2_str,
} else if (!new_br_ent[1])
goto done;

if (params->flags & B2BL_BR_FLAG_NOTIFY)
process_bridge_notify(entity, cur_route_ctx.hash_index, NULL);

if (process_bridge_action(msg, tuple, cur_route_ctx.hash_index,
old_entity, new_br_ent, provmedia_uri, lifetime ? *lifetime : 0) < 0) {
old_entity, new_br_ent, provmedia_uri, params->lifetime) < 0) {
LM_ERR("Failed to process bridge action\n");
goto done;
}
Expand Down Expand Up @@ -2744,7 +2817,7 @@ int process_bridge_action(struct sip_msg* msg, b2bl_tuple_t* tuple,
else
tuple->lifetime = -1;

LM_DBG("be[0]= %p, be1=[%p]\n", tuple->bridge_entities[0], tuple->bridge_entities[1]);
LM_DBG("be[0] = [%p], be[1] = [%p]\n", tuple->bridge_entities[0], tuple->bridge_entities[1]);
return 0;

error1:
Expand Down
1 change: 1 addition & 0 deletions modules/b2b_logic/records.h
Expand Up @@ -100,6 +100,7 @@ typedef struct b2bl_tuple
b2bl_entity_id_t* servers[MAX_B2BL_ENT];
b2bl_entity_id_t* clients[MAX_B2BL_ENT];
b2bl_entity_id_t* bridge_entities[MAX_BRIDGE_ENT];
int bridge_flags;
int to_del;
str* extra_headers;
struct b2bl_tuple* next;
Expand Down

0 comments on commit 9faaa31

Please sign in to comment.