Commit fefec622 authored by Pekka Pessi's avatar Pekka Pessi

nua: do not ignore 2nd answer if SDP has changed

Re-initializing the O/A exchange if SDP changes in 200 OK.

This will get hosed with 100rel and forking, but...
parent 7304d8a2
......@@ -242,19 +242,24 @@ int s2_check_event(nua_event_t event, int status)
return e != NULL;
}
enum nua_callstate s2_event_callstate(struct event *e)
{
if (e) {
tagi_t const *tagi = tl_find(e->data->e_tags, nutag_callstate);
if (tagi)
return (enum nua_callstate) tagi->t_value;
}
return -1;
}
int s2_check_callstate(enum nua_callstate state)
{
int retval = 0;
tagi_t const *tagi;
struct event *e;
e = s2_wait_for_event(nua_i_state, 0);
if (e) {
tagi = tl_find(e->data->e_tags, nutag_callstate);
if (tagi) {
retval = (tag_value_t)state == tagi->t_value;
}
}
retval = state == s2_event_callstate(e);
s2_free_event(e);
return retval;
}
......
......@@ -81,6 +81,7 @@ void s2_flush_events(void);
struct event *s2_next_event(void);
struct event *s2_wait_for_event(nua_event_t event, int status);
int s2_check_event(nua_event_t event, int status);
enum nua_callstate s2_event_callstate(struct event *e);
int s2_check_callstate(enum nua_callstate state);
int s2_check_substate(struct event *e, enum nua_substate state);
......
......@@ -56,20 +56,14 @@
/* ====================================================================== */
static nua_t *nua;
static soa_session_t *soa = NULL;
static struct dialog *dialog = NULL;
static soa_session_t *soa = NULL, *soa2 = NULL;
static struct dialog *dialog = NULL, *dialog2 = NULL;
#define CRLF "\r\n"
static void call_setup(void)
static void endpoint_setup(void)
{
nua = s2_nua_setup("call",
SIPTAG_ORGANIZATION_STR("Pussy Galore's Flying Circus"),
NUTAG_OUTBOUND("no-options-keepalive, no-validate"),
TAG_END());
soa = soa_create(NULL, s2base->root, NULL);
fail_if(!soa);
soa_set_params(soa,
......@@ -77,7 +71,18 @@ static void call_setup(void)
"m=video 5010 RTP/AVP 34" CRLF),
TAG_END());
dialog = su_home_new(sizeof *dialog); fail_if(!dialog);
dialog = su_home_new(sizeof *dialog);
fail_if(!dialog);
}
static void call_setup(void)
{
nua = s2_nua_setup("call",
SIPTAG_ORGANIZATION_STR("Pussy Galore's Flying Circus"),
NUTAG_OUTBOUND("no-options-keepalive, no-validate"),
TAG_END());
endpoint_setup();
s2_register_setup();
}
......@@ -148,6 +153,27 @@ static void process_answer(struct message *message)
fail_if(soa_process_answer(soa, NULL) < 0);
}
/* Return true if answer is sent */
static inline int is_answer_sent(tagi_t const *tags)
{
tagi_t const *ti = tl_find(tags, nutag_answer_sent);
return ti ? ti->t_value : 0;
}
/* Return true if offer is recv */
static inline int is_offer_recv(tagi_t const *tags)
{
tagi_t const *ti = tl_find(tags, nutag_offer_recv);
return ti ? ti->t_value : 0;
}
/* Return true if answer is recv */
static inline int is_answer_recv(tagi_t const *tags)
{
tagi_t const *ti = tl_find(tags, nutag_answer_recv);
return ti ? ti->t_value : 0;
}
static void
respond_with_sdp(struct message *request,
struct dialog *dialog,
......@@ -252,6 +278,7 @@ invite_by_nua(nua_handle_t *nh,
tag_type_t tag, tag_value_t value, ...)
{
struct message *invite;
struct event *i_state;
ta_list ta;
ta_start(ta, tag, value);
......@@ -270,7 +297,9 @@ invite_by_nua(nua_handle_t *nh,
respond_with_sdp(invite, dialog, SIP_200_OK, TAG_END());
s2_sip_free_message(invite);
fail_unless_event(nua_r_invite, 200);
fail_unless(s2_check_callstate(nua_callstate_ready));
i_state = s2_wait_for_event(nua_i_state, 0);
fail_unless(i_state != NULL);
fail_if(is_answer_recv(i_state->data->e_tags));
fail_unless(s2_sip_check_request(SIP_METHOD_ACK));
}
......@@ -441,7 +470,6 @@ START_TEST(call_2_1_1)
}
END_TEST
START_TEST(call_2_1_2_1)
{
nua_handle_t *nh;
......@@ -480,6 +508,75 @@ START_TEST(call_2_1_2_2)
END_TEST
START_TEST(call_2_1_2_3)
{
nua_handle_t *nh;
struct message *invite;
struct event *i_state;
S2_CASE("2.1.1", "Outgoing call with two answers",
"NUA sends INVITE, receives 180 and 200 with different SDP");
nh = nua_handle(nua, NULL, SIPTAG_TO(s2sip->aor), TAG_END());
invite = invite_sent_by_nua(
nh,
SOATAG_USER_SDP_STR("m=audio 5004 RTP/AVP 4 3" CRLF
"m=video 5006 RTP/AVP 31" CRLF),
TAG_END());
process_offer(invite);
respond_with_sdp(
invite, dialog, SIP_180_RINGING,
SIPTAG_CONTENT_DISPOSITION_STR("session;handling=optional"),
TAG_END());
fail_unless_event(nua_r_invite, 180);
i_state = s2_wait_for_event(nua_i_state, 0);
fail_unless(i_state != NULL);
fail_unless(s2_event_callstate(i_state) == nua_callstate_proceeding);
fail_if(!is_answer_recv(i_state->data->e_tags));
s2_free_event(i_state);
dialog2 = dialog, dialog = NULL; soa2 = soa, soa = NULL;
endpoint_setup();
soa_set_params(soa,
SOATAG_USER_SDP_STR("m=audio 5008 RTP/AVP 4" CRLF
"m=video 5010 RTP/AVP 31" CRLF),
TAG_END());
/* Now from another endpoint */
process_offer(invite);
respond_with_sdp(
invite, dialog, SIP_180_RINGING,
SIPTAG_CONTENT_DISPOSITION_STR("session;handling=optional"),
TAG_END());
fail_unless_event(nua_r_invite, 180);
i_state = s2_wait_for_event(nua_i_state, 0);
fail_unless(i_state != NULL);
fail_unless(s2_event_callstate(i_state) == nua_callstate_proceeding);
fail_if(is_answer_recv(i_state->data->e_tags));
s2_free_event(i_state);
respond_with_sdp(invite, dialog, SIP_200_OK, TAG_END());
s2_sip_free_message(invite);
fail_unless_event(nua_r_invite, 200);
i_state = s2_wait_for_event(nua_i_state, 0);
fail_unless(i_state != NULL);
fail_if(!is_answer_recv(i_state->data->e_tags));
s2_free_event(i_state);
fail_unless(s2_sip_check_request(SIP_METHOD_ACK));
bye_by_nua(nh, TAG_END());
nua_handle_destroy(nh);
}
END_TEST
START_TEST(call_2_1_3_1)
{
nua_handle_t *nh;
......@@ -694,6 +791,7 @@ TCase *invite_tcase(int threading)
tcase_add_test(tc, call_2_1_1);
tcase_add_test(tc, call_2_1_2_1);
tcase_add_test(tc, call_2_1_2_2);
tcase_add_test(tc, call_2_1_2_3);
tcase_add_test(tc, call_2_1_3_1);
tcase_add_test(tc, call_2_1_3_2);
tcase_add_test(tc, call_2_1_4);
......
......@@ -946,29 +946,58 @@ static int nua_session_client_response(nua_client_request_t *cr,
SU_DEBUG_5(("nua(%p): %s: %s %s in %u %s\n", \
(void *)nh, cr->cr_method_name, (m), received, status, phrase))
if (!ss || !sip || 300 <= status)
/* Xyzzy */;
else if (!session_get_description(sip, &sdp, &len))
/* No SDP */;
else if (cr->cr_answer_recv) {
/* Ignore spurious answers after completing O/A */
LOG3("ignoring duplicate");
sdp = NULL;
}
else if (cr->cr_offer_sent) {
/* case 1: answer to our offer */
if (!ss || 300 <= status || !session_get_description(sip, &sdp, &len))
return nua_base_client_response(cr, status, phrase, sip, NULL);
if (cr->cr_offer_sent) {
/* case 1: answer to our offer? */
int new_answer, previous_answer = cr->cr_answer_recv;
cr->cr_answer_recv = status;
received = Answer;
if (nh->nh_soa == NULL)
if (nh->nh_soa == NULL) {
LOG5("got SDP");
else if (soa_set_remote_sdp(nh->nh_soa, NULL, sdp, len) < 0) {
goto response;
}
if (previous_answer && status < 200) {
/* Ignore extra answers in unreliable provisional responses */
LOG5("ignoring extra");
sdp = NULL;
received = NULL;
goto response;
}
new_answer = soa_set_remote_sdp(nh->nh_soa, NULL, sdp, len);
if (new_answer < 0) {
LOG3("error parsing SDP");
sdp = NULL;
cr->cr_graceful = 1;
ss->ss_reason = "SIP;cause=400;text=\"Malformed Session Description\"";
goto response;
}
else if (soa_process_answer(nh->nh_soa, NULL) < 0) {
if (previous_answer) {
if (!new_answer) {
/* Ignore duplicate answers */
LOG5("ignoring duplicate");
sdp = NULL;
received = NULL;
goto response;
}
else {
if (soa_init_offer_answer(nh->nh_soa) < 0 ||
soa_generate_offer(nh->nh_soa, 1, NULL) < 0 ||
soa_set_remote_sdp(nh->nh_soa, NULL, sdp, len) < 0) {
LOG5("error reinitializing session");
sdp = NULL;
goto response;
}
}
}
if (soa_process_answer(nh->nh_soa, NULL) < 0) {
LOG5("error processing SDP");
/* XXX */
sdp = NULL;
......@@ -1002,7 +1031,8 @@ static int nua_session_client_response(nua_client_request_t *cr,
LOG5("got SDP");
}
if (ss && received)
response:
if (received)
ss->ss_oa_recv = received;
if (sdp && nh->nh_soa)
......@@ -4544,16 +4574,20 @@ int session_get_description(sip_t const *sip,
char const **return_sdp,
size_t *return_len)
{
sip_payload_t const *pl = sip->sip_payload;
sip_content_type_t const *ct = sip->sip_content_type;
sip_payload_t const *pl;
sip_content_type_t const *ct;
int matching_content_type = 0;
if (pl == NULL)
if (sip == NULL)
return 0;
if (pl->pl_len == 0 || pl->pl_data == NULL)
pl = sip->sip_payload;
if (pl == NULL || pl->pl_len == 0 || pl->pl_data == NULL)
return 0;
ct = sip->sip_content_type;
if (ct == NULL)
/* Be bug-compatible with our old gateways */
SU_DEBUG_3(("nua: no %s, assuming %s\n",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment