Michael Jerris 833500ae64 Fri Nov 21 04:52:55 CST 2008 Pekka Pessi <first.last@nokia.com>
* nua: try to cope if a re-INVITE nor its ACK contain SDP
  
  Some SIP user-agents use INVITE without SDP offer to refresh session.
  By default, NUA sends an offer in 200 OK to such an INVITE and expects
  an answer back in ACK. Now nua tries to recover from such a Offer/Answer
  protocol error.
  
  Also, if NUTAG_REFRESH_WITHOUT_SDP(1) tag is used, and if the re-INVITE was
  received without SDP, no SDP offer is sent in 200 OK.
  
  Thanks for Anthony Minessale for reporting the problem.



git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@10829 d0543943-73ff-0310-b7d9-9358b9ac24b2
2008-12-16 21:20:22 +00:00

3052 lines
77 KiB
C

/*
* This file is part of the Sofia-SIP package
*
* Copyright (C) 2008 Nokia Corporation.
*
* Contact: Pekka Pessi <pekka.pessi@nokia.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
/**@CFILE check_session.c
*
* @brief NUA module tests for SIP session handling
*
* @author Pekka Pessi <Pekka.Pessi@nokia.com>
*
* @copyright (C) 2008 Nokia Corporation.
*/
#include "config.h"
#include "check_nua.h"
#include "test_s2.h"
#include <sofia-sip/sip_status.h>
#include <sofia-sip/sip_header.h>
#include <sofia-sip/soa.h>
#include <sofia-sip/su_tagarg.h>
#include <sofia-sip/su_tag_io.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
/* define XXX as 1 in order to see all failing test cases */
#define XXX (0)
/* ====================================================================== */
/* Call cases */
static nua_t *nua;
static soa_session_t *soa = NULL;
static struct dialog *dialog = NULL;
#define CRLF "\r\n"
static void call_setup(void)
{
s2_case("0.1.1", "Setup for Call Tests", "");
nua = s2_nua_setup(SIPTAG_ORGANIZATION_STR("Pussy Galore's Flying Circus"),
NUTAG_OUTBOUND("no-options-keepalive, no-validate"),
TAG_END());
soa = soa_create(NULL, s2->root, NULL);
fail_if(!soa);
soa_set_params(soa,
SOATAG_USER_SDP_STR("m=audio 5008 RTP/AVP 8 0" CRLF
"m=video 5010 RTP/AVP 34" CRLF),
TAG_END());
dialog = su_home_new(sizeof *dialog); fail_if(!dialog);
s2_register_setup();
}
static void call_teardown(void)
{
s2_case("0.1.2", "Teardown Call Test Setup", "");
mark_point();
s2_register_teardown();
nua_shutdown(nua);
fail_unless(s2_check_event(nua_r_shutdown, 200));
s2_nua_teardown();
}
static void save_sdp_to_soa(struct message *message)
{
sip_payload_t *pl;
char const *body;
isize_t bodylen;
fail_if(!message);
fail_if(!message->sip->sip_content_length);
fail_if(!message->sip->sip_content_type);
fail_if(strcmp(message->sip->sip_content_type->c_type,
"application/sdp"));
fail_if(!message->sip->sip_payload);
pl = message->sip->sip_payload;
body = pl->pl_data, bodylen = pl->pl_len;
fail_if(soa_set_remote_sdp(soa, NULL, body, (issize_t)bodylen) < 0);
}
static void process_offer(struct message *message)
{
save_sdp_to_soa(message);
fail_if(soa_generate_answer(soa, NULL) < 0);
}
static void process_answer(struct message *message)
{
save_sdp_to_soa(message);
fail_if(soa_process_answer(soa, NULL) < 0);
}
static void
respond_with_sdp(struct message *request,
struct dialog *dialog,
int status, char const *phrase,
tag_type_t tag, tag_value_t value, ...)
{
ta_list ta;
char const *body;
isize_t bodylen;
fail_if(soa_get_local_sdp(soa, NULL, &body, &bodylen) != 1);
ta_start(ta, tag, value);
s2_respond_to(request, dialog, status, phrase,
SIPTAG_CONTENT_TYPE_STR("application/sdp"),
SIPTAG_PAYLOAD_STR(body),
SIPTAG_CONTENT_DISPOSITION_STR("session"),
ta_tags(ta));
ta_end(ta);
}
static void
request_with_sdp(struct dialog *dialog,
sip_method_t method, char const *name,
tport_t *tport,
tag_type_t tag, tag_value_t value, ...)
{
ta_list ta;
char const *body;
isize_t bodylen;
fail_if(soa_get_local_sdp(soa, NULL, &body, &bodylen) != 1);
ta_start(ta, tag, value);
fail_if(
s2_request_to(dialog, method, name, tport,
SIPTAG_CONTENT_TYPE_STR("application/sdp"),
SIPTAG_PAYLOAD_STR(body),
ta_tags(ta)));
ta_end(ta);
}
static struct message *
invite_sent_by_nua(nua_handle_t *nh,
tag_type_t tag, tag_value_t value, ...)
{
ta_list ta;
ta_start(ta, tag, value);
nua_invite(nh, SOATAG_USER_SDP_STR("m=audio 5004 RTP/AVP 0 8"),
ta_tags(ta));
ta_end(ta);
fail_unless(s2_check_callstate(nua_callstate_calling));
return s2_wait_for_request(SIP_METHOD_INVITE);
}
static uint32_t s2_rseq;
static struct message *
respond_with_100rel(struct message *invite,
struct dialog *d,
int with_sdp,
int status, char const *phrase,
tag_type_t tag, tag_value_t value, ...)
{
ta_list ta;
sip_rseq_t rs[1];
assert(100 < status && status < 200);
sip_rseq_init(rs);
rs->rs_response = ++s2_rseq;
ta_start(ta, tag, value);
if (with_sdp) {
respond_with_sdp(
invite, dialog, status, phrase,
SIPTAG_REQUIRE_STR("100rel"),
SIPTAG_RSEQ(rs),
ta_tags(ta));
}
else {
s2_respond_to(
invite, dialog, status, phrase,
SIPTAG_REQUIRE_STR("100rel"),
SIPTAG_RSEQ(rs),
ta_tags(ta));
}
ta_end(ta);
fail_unless(s2_check_event(nua_r_invite, status));
return s2_wait_for_request(SIP_METHOD_PRACK);
}
static void
invite_by_nua(nua_handle_t *nh,
tag_type_t tag, tag_value_t value, ...)
{
struct message *invite;
ta_list ta;
ta_start(ta, tag, value);
invite = invite_sent_by_nua(nh, ta_tags(ta));
ta_end(ta);
process_offer(invite);
respond_with_sdp(
invite, dialog, SIP_180_RINGING,
SIPTAG_CONTENT_DISPOSITION_STR("session;handling=optional"),
TAG_END());
fail_unless(s2_check_event(nua_r_invite, 180));
fail_unless(s2_check_callstate(nua_callstate_proceeding));
respond_with_sdp(invite, dialog, SIP_200_OK, TAG_END());
s2_free_message(invite);
fail_unless(s2_check_event(nua_r_invite, 200));
fail_unless(s2_check_callstate(nua_callstate_ready));
fail_unless(s2_check_request(SIP_METHOD_ACK));
}
static nua_handle_t *
invite_to_nua(tag_type_t tag, tag_value_t value, ...)
{
ta_list ta;
struct event *invite;
struct message *response;
nua_handle_t *nh;
soa_generate_offer(soa, 1, NULL);
ta_start(ta, tag, value);
request_with_sdp(dialog, SIP_METHOD_INVITE, NULL, ta_tags(ta));
ta_end(ta);
invite = s2_wait_for_event(nua_i_invite, 100); fail_unless(invite != NULL);
fail_unless(s2_check_callstate(nua_callstate_received));
nh = invite->nh;
fail_if(!nh);
s2_free_event(invite);
response = s2_wait_for_response(100, SIP_METHOD_INVITE);
fail_if(!response);
nua_respond(nh, SIP_180_RINGING,
SOATAG_USER_SDP_STR("m=audio 5004 RTP/AVP 0 8"),
TAG_END());
fail_unless(s2_check_callstate(nua_callstate_early));
response = s2_wait_for_response(180, SIP_METHOD_INVITE);
fail_if(!response);
s2_update_dialog(dialog, response);
process_answer(response);
s2_free_message(response);
nua_respond(nh, SIP_200_OK, TAG_END());
fail_unless(s2_check_callstate(nua_callstate_completed));
response = s2_wait_for_response(200, SIP_METHOD_INVITE);
fail_if(!response);
s2_update_dialog(dialog, response);
s2_free_message(response);
fail_if(s2_request_to(dialog, SIP_METHOD_ACK, NULL, TAG_END()));
fail_unless(s2_check_event(nua_i_ack, 200));
fail_unless(s2_check_callstate(nua_callstate_ready));
return nh;
}
static void
bye_by_nua(nua_handle_t *nh,
tag_type_t tag, tag_value_t value, ...)
{
ta_list ta;
struct message *bye;
ta_start(ta, tag, value);
nua_bye(nh, ta_tags(ta));
ta_end(ta);
bye = s2_wait_for_request(SIP_METHOD_BYE);
fail_if(!bye);
s2_respond_to(bye, dialog, SIP_200_OK, TAG_END());
s2_free_message(bye);
fail_unless(s2_check_event(nua_r_bye, 200));
fail_unless(s2_check_callstate(nua_callstate_terminated));
}
static void
bye_by_nua_challenged(nua_handle_t *nh,
tag_type_t tag, tag_value_t value, ...)
{
ta_list ta;
struct message *bye;
s2_flush_events();
ta_start(ta, tag, value);
nua_bye(nh, ta_tags(ta));
ta_end(ta);
bye = s2_wait_for_request(SIP_METHOD_BYE);
fail_if(!bye);
s2_respond_to(bye, dialog, SIP_407_PROXY_AUTH_REQUIRED,
SIPTAG_PROXY_AUTHENTICATE_STR(s2_auth_digest_str),
TAG_END());
s2_free_message(bye);
fail_unless(s2_check_event(nua_r_bye, 407));
nua_authenticate(nh, NUTAG_AUTH("Digest:\"s2test\":abc:abc"), TAG_END());
bye = s2_wait_for_request(SIP_METHOD_BYE);
fail_if(!bye);
s2_respond_to(bye, dialog, SIP_200_OK, TAG_END());
s2_free_message(bye);
fail_unless(s2_check_event(nua_r_bye, 200));
fail_unless(s2_check_callstate(nua_callstate_terminated));
fail_if(s2->events);
}
static void
cancel_by_nua(nua_handle_t *nh,
struct message *invite,
struct dialog *dialog,
tag_type_t tag, tag_value_t value, ...)
{
ta_list ta;
struct message *cancel;
ta_start(ta, tag, value);
nua_cancel(nh, ta_tags(ta));
ta_end(ta);
cancel = s2_wait_for_request(SIP_METHOD_CANCEL);
fail_if(!cancel);
s2_respond_to(cancel, dialog, SIP_200_OK, TAG_END());
s2_free_message(cancel);
fail_unless(s2_check_event(nua_r_cancel, 200));
s2_respond_to(invite, dialog, SIP_487_REQUEST_CANCELLED, TAG_END());
fail_unless(s2_check_request(SIP_METHOD_ACK));
fail_unless(s2_check_event(nua_r_invite, 487));
}
static void
bye_to_nua(nua_handle_t *nh,
tag_type_t tag, tag_value_t value, ...)
{
ta_list ta;
ta_start(ta, tag, value);
fail_if(s2_request_to(dialog, SIP_METHOD_BYE, NULL, ta_tags(ta)));
ta_end(ta);
fail_unless(s2_check_event(nua_i_bye, 200));
fail_unless(s2_check_callstate(nua_callstate_terminated));
fail_unless(s2_check_response(200, SIP_METHOD_BYE));
}
/* ====================================================================== */
/* 2 - Call cases */
/* 2.1 - Basic call cases */
START_TEST(call_2_1_1)
{
nua_handle_t *nh;
s2_case("2.1.1", "Basic call",
"NUA sends INVITE, NUA sends BYE");
nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local), TAG_END());
invite_by_nua(nh, TAG_END());
bye_by_nua(nh, TAG_END());
nua_handle_destroy(nh);
}
END_TEST
START_TEST(call_2_1_2_1)
{
nua_handle_t *nh;
s2_case("2.1.2.1", "Basic call",
"NUA sends INVITE, NUA receives BYE");
nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local), TAG_END());
invite_by_nua(nh, TAG_END());
bye_to_nua(nh, TAG_END());
nua_handle_destroy(nh);
}
END_TEST
START_TEST(call_2_1_2_2)
{
nua_handle_t *nh;
s2_case("2.1.2.2", "Basic call over TCP",
"NUA sends INVITE, NUA receives BYE");
nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local),
TAG_END());
invite_by_nua(nh,
NUTAG_PROXY(s2->tcp.contact->m_url),
TAG_END());
bye_to_nua(nh, TAG_END());
nua_handle_destroy(nh);
}
END_TEST
START_TEST(call_2_1_3_1)
{
nua_handle_t *nh;
s2_case("2.1.3.1", "Incoming call",
"NUA receives INVITE and BYE");
nh = invite_to_nua(TAG_END());
bye_to_nua(nh, TAG_END());
nua_handle_destroy(nh);
}
END_TEST
START_TEST(call_2_1_3_2)
{
nua_handle_t *nh;
s2_case("2.1.3.2", "Incoming call over TCP",
"NUA receives INVITE and BYE");
dialog->tport = s2->tcp.tport;
nh = invite_to_nua(TAG_END());
bye_to_nua(nh, TAG_END());
nua_handle_destroy(nh);
}
END_TEST
START_TEST(call_2_1_4)
{
nua_handle_t *nh;
s2_case("2.1.4", "Incoming call",
"NUA receives INVITE and sends BYE");
nh = invite_to_nua(TAG_END());
bye_by_nua(nh, TAG_END());
nua_handle_destroy(nh);
}
END_TEST
START_TEST(call_2_1_5)
{
nua_handle_t *nh;
s2_case("2.1.5", "Incoming call",
"NUA receives INVITE and sends BYE, BYE is challenged");
nh = invite_to_nua(TAG_END());
bye_by_nua_challenged(nh, TAG_END());
nua_handle_destroy(nh);
}
END_TEST
START_TEST(call_2_1_6)
{
nua_handle_t *nh;
struct message *bye;
struct event *invite;
struct message *response;
s2_case("2.1.6", "Basic call",
"NUA received INVITE, "
"NUA responds (and saves proxy for dialog), "
"NUA sends BYE");
soa_generate_offer(soa, 1, NULL);
request_with_sdp(dialog, SIP_METHOD_INVITE, NULL, TAG_END());
invite = s2_wait_for_event(nua_i_invite, 100); fail_unless(invite != NULL);
fail_unless(s2_check_callstate(nua_callstate_received));
nh = invite->nh;
fail_if(!nh);
s2_free_event(invite);
response = s2_wait_for_response(100, SIP_METHOD_INVITE);
fail_if(!response);
nua_respond(nh, SIP_180_RINGING,
/* Dialog-specific proxy is saved */
NUTAG_PROXY(s2->tcp.contact->m_url),
SOATAG_USER_SDP_STR("m=audio 5004 RTP/AVP 0 8"),
TAG_END());
fail_unless(s2_check_callstate(nua_callstate_early));
response = s2_wait_for_response(180, SIP_METHOD_INVITE);
fail_if(!response);
s2_update_dialog(dialog, response);
process_answer(response);
s2_free_message(response);
nua_respond(nh, SIP_200_OK, TAG_END());
fail_unless(s2_check_callstate(nua_callstate_completed));
response = s2_wait_for_response(200, SIP_METHOD_INVITE);
fail_if(!response);
s2_update_dialog(dialog, response);
s2_free_message(response);
fail_if(s2_request_to(dialog, SIP_METHOD_ACK, NULL, TAG_END()));
fail_unless(s2_check_event(nua_i_ack, 200));
fail_unless(s2_check_callstate(nua_callstate_ready));
nua_bye(nh, TAG_END());
bye = s2_wait_for_request(SIP_METHOD_BYE);
fail_if(!bye);
/* Check that NUA used dialog-specific proxy with BYE */
fail_unless(tport_is_tcp(bye->tport));
s2_respond_to(bye, dialog, SIP_200_OK, TAG_END());
s2_free_message(bye);
fail_unless(s2_check_event(nua_r_bye, 200));
fail_unless(s2_check_callstate(nua_callstate_terminated));
nua_handle_destroy(nh);
}
END_TEST
START_TEST(call_2_1_7)
{
nua_handle_t *nh, *nh2;
sip_replaces_t *replaces;
s2_case("2.1.7", "Call lookup",
"Test dialog and call-id lookup");
nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local), TAG_END());
invite_by_nua(nh, TAG_END());
nh2 = nua_handle_by_call_id(nua, dialog->call_id->i_id);
fail_if(!nh2); fail_if(nh != nh2); nua_handle_unref(nh2);
replaces = sip_replaces_format(NULL, "%s;from-tag=%s;to-tag=%s",
dialog->call_id->i_id,
dialog->local->a_tag,
dialog->remote->a_tag);
fail_if(!replaces);
nh2 = nua_handle_by_replaces(nua, replaces);
fail_if(!nh2); fail_if(nh != nh2); nua_handle_unref(nh2);
msg_header_free_all(NULL, (msg_header_t *)replaces);
bye_by_nua(nh, TAG_END());
nua_handle_destroy(nh);
}
END_TEST
START_TEST(call_2_1_8)
{
nua_handle_t *nh;
struct message *invite, *ack;
s2_case("2.1.8", "Call using NUTAG_PROXY()",
"Test handle-specific NUTAG_PROXY().");
nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local), TAG_END());
invite = invite_sent_by_nua(
nh, SOATAG_USER_SDP_STR("m=audio 5004 RTP/AVP 0 8"),
NUTAG_PROXY(s2->tcp.contact->m_url), TAG_END());
process_offer(invite);
respond_with_sdp(
invite, dialog, SIP_180_RINGING,
SIPTAG_CONTENT_DISPOSITION_STR("session;handling=optional"),
TAG_END());
fail_unless(s2_check_event(nua_r_invite, 180));
fail_unless(s2_check_callstate(nua_callstate_proceeding));
respond_with_sdp(invite, dialog, SIP_200_OK, TAG_END());
s2_free_message(invite);
fail_unless(s2_check_event(nua_r_invite, 200));
fail_unless(s2_check_callstate(nua_callstate_ready));
ack = s2_wait_for_request(SIP_METHOD_ACK);
fail_unless(ack && tport_is_tcp(ack->tport));
bye_by_nua(nh, TAG_END());
nua_handle_destroy(nh);
}
END_TEST
TCase *invite_tcase(void)
{
TCase *tc = tcase_create("2.1 - Basic INVITE");
tcase_add_checked_fixture(tc, call_setup, call_teardown);
{
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_3_1);
tcase_add_test(tc, call_2_1_3_2);
tcase_add_test(tc, call_2_1_4);
tcase_add_test(tc, call_2_1_5);
tcase_add_test(tc, call_2_1_6);
tcase_add_test(tc, call_2_1_7);
tcase_add_test(tc, call_2_1_8);
}
return tc;
}
/* ---------------------------------------------------------------------- */
/* 2.2 - Call CANCEL cases */
START_TEST(cancel_2_2_1)
{
nua_handle_t *nh;
struct message *invite, *cancel;
s2_case("2.2.1", "Cancel call",
"NUA is caller, NUA sends CANCEL immediately");
nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local), TAG_END());
nua_invite(nh, SOATAG_USER_SDP_STR("m=audio 5004 RTP/AVP 0 8"),
TAG_END());
fail_unless(s2_check_callstate(nua_callstate_calling));
nua_cancel(nh, TAG_END());
invite = s2_wait_for_request(SIP_METHOD_INVITE);
fail_if(!invite);
fail_if(s2->received != NULL);
s2_respond_to(invite, dialog, SIP_100_TRYING, TAG_END());
cancel = s2_wait_for_request(SIP_METHOD_CANCEL);
fail_if(!cancel);
s2_respond_to(invite, dialog, SIP_487_REQUEST_CANCELLED, TAG_END());
s2_respond_to(cancel, dialog, SIP_200_OK, TAG_END());
fail_unless(s2_check_request(SIP_METHOD_ACK));
fail_unless(s2_check_event(nua_r_invite, 487));
fail_unless(s2_check_callstate(nua_callstate_terminated));
fail_unless(s2_check_event(nua_r_cancel, 200));
fail_if(s2->events != NULL);
nua_handle_destroy(nh);
}
END_TEST
START_TEST(cancel_2_2_2)
{
nua_handle_t *nh;
struct message *invite;
s2_case("2.2.2", "Canceled call",
"NUA is caller, NUA sends CANCEL after receiving 100");
nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local), TAG_END());
nua_invite(nh, SOATAG_USER_SDP_STR("m=audio 5004 RTP/AVP 0 8"),
TAG_END());
fail_unless(s2_check_callstate(nua_callstate_calling));
invite = s2_wait_for_request(SIP_METHOD_INVITE);
process_offer(invite);
s2_respond_to(invite, dialog, SIP_100_TRYING, TAG_END());
cancel_by_nua(nh, invite, dialog, TAG_END());
fail_unless(s2_check_callstate(nua_callstate_terminated));
nua_handle_destroy(nh);
}
END_TEST
START_TEST(cancel_2_2_3)
{
nua_handle_t *nh;
struct message *invite;
s2_case("2.2.3", "Canceled call",
"NUA is caller, NUA sends CANCEL after receiving 180");
nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local), TAG_END());
nua_invite(nh, SOATAG_USER_SDP_STR("m=audio 5004 RTP/AVP 0 8"),
TAG_END());
fail_unless(s2_check_callstate(nua_callstate_calling));
invite = s2_wait_for_request(SIP_METHOD_INVITE);
process_offer(invite);
respond_with_sdp(
invite, dialog, SIP_180_RINGING,
SIPTAG_CONTENT_DISPOSITION_STR("session;handling=optional"),
TAG_END());
fail_unless(s2_check_event(nua_r_invite, 180));
fail_unless(s2_check_callstate(nua_callstate_proceeding));
cancel_by_nua(nh, invite, dialog, TAG_END());
fail_unless(s2_check_callstate(nua_callstate_terminated));
nua_handle_destroy(nh);
}
END_TEST
START_TEST(cancel_2_2_4)
{
nua_handle_t *nh;
struct message *invite, *cancel;
s2_case("2.2.4", "Cancel and 200 OK glare",
"NUA is caller, NUA sends CANCEL after receiving 180 "
"but UAS already sent 200 OK.");
nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local), TAG_END());
nua_invite(nh, SOATAG_USER_SDP_STR("m=audio 5004 RTP/AVP 0 8"),
TAG_END());
fail_unless(s2_check_callstate(nua_callstate_calling));
invite = s2_wait_for_request(SIP_METHOD_INVITE);
process_offer(invite);
respond_with_sdp(
invite, dialog, SIP_180_RINGING,
SIPTAG_CONTENT_DISPOSITION_STR("session;handling=optional"),
TAG_END());
fail_unless(s2_check_event(nua_r_invite, 180));
fail_unless(s2_check_callstate(nua_callstate_proceeding));
nua_cancel(nh, TAG_END());
cancel = s2_wait_for_request(SIP_METHOD_CANCEL);
fail_if(!cancel);
respond_with_sdp(invite, dialog, SIP_200_OK, TAG_END());
s2_respond_to(cancel, dialog, SIP_481_NO_TRANSACTION, TAG_END());
s2_free_message(cancel);
fail_unless(s2_check_event(nua_r_cancel, 481));
fail_unless(s2_check_request(SIP_METHOD_ACK));
fail_unless(s2_check_callstate(nua_callstate_ready));
bye_by_nua(nh, TAG_END());
nua_handle_destroy(nh);
}
END_TEST
START_TEST(cancel_2_2_5)
{
nua_handle_t *nh;
struct message *invite, *cancel, *bye;
s2_case(
"2.2.5", "Cancel and 200 OK glare",
"NUA is caller, "
"NUA uses nua_bye() to send CANCEL after receiving 180\n"
"but UAS already sent 200 OK.\n"
"Test case checks that NUA really sends BYE after nua_bye() is called\n");
nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local), TAG_END());
nua_invite(nh, SOATAG_USER_SDP_STR("m=audio 5004 RTP/AVP 0 8"),
NUTAG_AUTOACK(0),
TAG_END());
fail_unless(s2_check_callstate(nua_callstate_calling));
invite = s2_wait_for_request(SIP_METHOD_INVITE);
process_offer(invite);
respond_with_sdp(
invite, dialog, SIP_180_RINGING,
SIPTAG_CONTENT_DISPOSITION_STR("session;handling=optional"),
TAG_END());
fail_unless(s2_check_event(nua_r_invite, 180));
fail_unless(s2_check_callstate(nua_callstate_proceeding));
nua_bye(nh, TAG_END());
cancel = s2_wait_for_request(SIP_METHOD_CANCEL);
fail_if(!cancel);
respond_with_sdp(invite, dialog, SIP_200_OK, TAG_END());
s2_respond_to(cancel, dialog, SIP_481_NO_TRANSACTION, TAG_END());
s2_free_message(cancel);
fail_unless(s2_check_event(nua_r_cancel, 481));
fail_unless(s2_check_request(SIP_METHOD_ACK));
fail_unless(s2_check_callstate(nua_callstate_terminating));
bye = s2_wait_for_request(SIP_METHOD_BYE);
fail_if(!bye);
s2_respond_to(bye, dialog, SIP_200_OK, TAG_END());
s2_free_message(bye);
fail_unless(s2_check_event(nua_r_bye, 200));
fail_unless(s2_check_callstate(nua_callstate_terminated));
nua_handle_destroy(nh);
}
END_TEST
START_TEST(cancel_2_2_6)
{
nua_handle_t *nh;
struct event *invite;
struct message *response;
s2_case("2.2.6", "Cancel call",
"NUA is callee, sends 100, 180, INVITE gets canceled");
soa_generate_offer(soa, 1, NULL);
request_with_sdp(dialog, SIP_METHOD_INVITE, NULL, TAG_END());
invite = s2_wait_for_event(nua_i_invite, 100); fail_unless(invite != NULL);
fail_unless(s2_check_callstate(nua_callstate_received));
nh = invite->nh; fail_if(!nh);
s2_free_event(invite);
response = s2_wait_for_response(100, SIP_METHOD_INVITE);
fail_if(!response);
nua_respond(nh, SIP_180_RINGING,
SOATAG_USER_SDP_STR("m=audio 5004 RTP/AVP 0 8"),
TAG_END());
fail_unless(s2_check_callstate(nua_callstate_early));
response = s2_wait_for_response(180, SIP_METHOD_INVITE);
fail_if(!response);
s2_update_dialog(dialog, response);
process_answer(response);
s2_free_message(response);
fail_if(s2_request_to(dialog, SIP_METHOD_CANCEL, NULL, TAG_END()));
fail_unless(s2_check_event(nua_i_cancel, 200));
fail_unless(s2_check_callstate(nua_callstate_terminated));
response = s2_wait_for_response(200, SIP_METHOD_CANCEL);
fail_if(!response);
s2_free_message(response);
response = s2_wait_for_response(487, SIP_METHOD_INVITE);
fail_if(s2_request_to(dialog, SIP_METHOD_ACK, NULL,
SIPTAG_VIA(sip_object(dialog->invite)->sip_via),
TAG_END()));
nua_handle_destroy(nh);
}
END_TEST
START_TEST(cancel_2_2_7)
{
nua_handle_t *nh;
struct event *invite;
struct message *response;
char const *via = "SIP/2.0/UDP host.in.invalid;rport";
s2_case("2.2.7", "Call gets canceled",
"NUA is callee, sends 100, 180, INVITE gets canceled. "
"Using RFC 2543 dialog and transaction matching.");
soa_generate_offer(soa, 1, NULL);
request_with_sdp(dialog, SIP_METHOD_INVITE, NULL,
SIPTAG_VIA_STR(via),
TAG_END());
invite = s2_wait_for_event(nua_i_invite, 100); fail_unless(invite != NULL);
fail_unless(s2_check_callstate(nua_callstate_received));
nh = invite->nh; fail_if(!nh);
s2_free_event(invite);
response = s2_wait_for_response(100, SIP_METHOD_INVITE);
fail_if(!response);
nua_respond(nh, SIP_180_RINGING,
SOATAG_USER_SDP_STR("m=audio 5004 RTP/AVP 0 8"),
TAG_END());
fail_unless(s2_check_callstate(nua_callstate_early));
response = s2_wait_for_response(180, SIP_METHOD_INVITE);
fail_if(!response);
s2_update_dialog(dialog, response);
process_answer(response);
s2_free_message(response);
fail_if(s2_request_to(dialog, SIP_METHOD_CANCEL, NULL, TAG_END()));
fail_unless(s2_check_event(nua_i_cancel, 200));
fail_unless(s2_check_callstate(nua_callstate_terminated));
response = s2_wait_for_response(200, SIP_METHOD_CANCEL);
fail_if(!response);
s2_free_message(response);
response = s2_wait_for_response(487, SIP_METHOD_INVITE);
fail_if(s2_request_to(dialog, SIP_METHOD_ACK, NULL,
SIPTAG_VIA(sip_object(dialog->invite)->sip_via),
TAG_END()));
nua_handle_destroy(nh);
}
END_TEST
TCase *cancel_tcase(void)
{
TCase *tc = tcase_create("2.2 - CANCEL");
tcase_add_checked_fixture(tc, call_setup, call_teardown);
tcase_add_test(tc, cancel_2_2_1);
tcase_add_test(tc, cancel_2_2_2);
tcase_add_test(tc, cancel_2_2_3);
tcase_add_test(tc, cancel_2_2_4);
if (XXX) tcase_add_test(tc, cancel_2_2_5);
tcase_add_test(tc, cancel_2_2_6);
tcase_add_test(tc, cancel_2_2_7);
return tc;
}
/* ---------------------------------------------------------------------- */
/* 2.3 - Session timers */
static void invite_timer_round(nua_handle_t *nh,
char const *session_expires)
{
struct message *invite, *ack;
fail_unless(s2_check_callstate(nua_callstate_calling));
invite = s2_wait_for_request(SIP_METHOD_INVITE);
process_offer(invite);
respond_with_sdp(
invite, dialog, SIP_200_OK,
SIPTAG_SESSION_EXPIRES_STR(session_expires),
SIPTAG_REQUIRE_STR("timer"),
TAG_END());
s2_free_message(invite);
fail_unless(s2_check_event(nua_r_invite, 200));
fail_unless(s2_check_callstate(nua_callstate_ready));
ack = s2_wait_for_request(SIP_METHOD_ACK);
s2_free_message(ack);
}
START_TEST(call_2_3_1)
{
nua_handle_t *nh;
s2_case("2.3.1", "Incoming call with call timers",
"NUA receives INVITE, "
"activates call timers, "
"sends re-INVITE twice, "
"sends BYE.");
nh = invite_to_nua(
SIPTAG_SESSION_EXPIRES_STR("300;refresher=uas"),
SIPTAG_REQUIRE_STR("timer"),
TAG_END());
s2_fast_forward(300);
invite_timer_round(nh, "300;refresher=uac");
s2_fast_forward(300);
invite_timer_round(nh, "300;refresher=uac");
bye_by_nua(nh, TAG_END());
nua_handle_destroy(nh);
}
END_TEST
START_TEST(call_2_3_2)
{
nua_handle_t *nh;
s2_case("2.3.2", "Incoming call with call timers",
"NUA receives INVITE, "
"activates call timers, "
"sends re-INVITE, "
"sends BYE.");
nh = invite_to_nua(
SIPTAG_SESSION_EXPIRES_STR("300;refresher=uas"),
SIPTAG_REQUIRE_STR("timer"),
TAG_END());
s2_fast_forward(300);
invite_timer_round(nh, "300");
s2_fast_forward(300);
invite_timer_round(nh, "300");
bye_by_nua(nh, TAG_END());
nua_handle_destroy(nh);
}
END_TEST
START_TEST(call_2_3_3)
{
nua_handle_t *nh;
struct message *response;
s2_case("2.3.3", "Handling re-INVITE without SDP gracefully",
"NUA receives INVITE, "
"re-INVITE without SDP (w/o NUTAG_REFRESH_WITHOUT_SDP(), "
"re-INVITE without SDP (using NUTAG_REFRESH_WITHOUT_SDP(), "
"sends BYE.");
nh = invite_to_nua(
TAG_END());
s2_request_to(dialog, SIP_METHOD_INVITE, NULL,
SIPTAG_USER_AGENT_STR("evil (evil) evil"),
TAG_END());
nua_respond(nh, SIP_200_OK, TAG_END());
fail_unless(s2_check_callstate(nua_callstate_completed));
response = s2_wait_for_response(200, SIP_METHOD_INVITE);
fail_if(!response);
s2_update_dialog(dialog, response);
fail_if(!response->sip->sip_content_type);
s2_free_message(response);
fail_if(s2_request_to(dialog, SIP_METHOD_ACK, NULL, TAG_END()));
fail_unless(s2_check_event(nua_i_ack, 200));
fail_unless(s2_check_callstate(nua_callstate_ready));
s2_fast_forward(10);
nua_set_hparams(nh, NUTAG_REFRESH_WITHOUT_SDP(1), TAG_END());
fail_unless(s2_check_event(nua_r_set_params, 200));
s2_request_to(dialog, SIP_METHOD_INVITE, NULL,
SIPTAG_USER_AGENT_STR("evil (evil) evil"),
TAG_END());
nua_respond(nh, SIP_200_OK, TAG_END());
fail_unless(s2_check_callstate(nua_callstate_completed));
response = s2_wait_for_response(200, SIP_METHOD_INVITE);
fail_if(!response);
s2_update_dialog(dialog, response);
fail_if(response->sip->sip_content_type);
s2_free_message(response);
fail_if(s2_request_to(dialog, SIP_METHOD_ACK, NULL, TAG_END()));
fail_unless(s2_check_event(nua_i_ack, 200));
fail_unless(s2_check_callstate(nua_callstate_ready));
bye_by_nua(nh, TAG_END());
nua_handle_destroy(nh);
}
END_TEST
TCase *session_timer_tcase(void)
{
TCase *tc = tcase_create("2.3 - Session timers");
tcase_add_checked_fixture(tc, call_setup, call_teardown);
{
tcase_add_test(tc, call_2_3_1);
tcase_add_test(tc, call_2_3_2);
tcase_add_test(tc, call_2_3_3);
}
return tc;
}
/* ====================================================================== */
/* 2.4 - 100rel */
START_TEST(call_2_4_1)
{
nua_handle_t *nh;
struct message *invite, *prack;
int with_sdp;
s2_case("2.4.1", "Call with 100rel",
"NUA sends INVITE, "
"receives 183, sends PRACK, receives 200 for it, "
"receives 180, sends PRACK, receives 200 for it, "
"receives 200, send ACK.");
nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local), TAG_END());
invite = invite_sent_by_nua(
nh, SOATAG_USER_SDP_STR("m=audio 5004 RTP/AVP 0 8"),
TAG_END());
process_offer(invite);
prack = respond_with_100rel(invite, dialog, with_sdp = 1,
SIP_183_SESSION_PROGRESS,
TAG_END());
s2_respond_to(prack, dialog, SIP_200_OK, TAG_END());
s2_free_message(prack), prack = NULL;
fail_unless(s2_check_callstate(nua_callstate_proceeding));
fail_unless(s2_check_event(nua_r_prack, 200));
prack = respond_with_100rel(invite, dialog, with_sdp = 0,
SIP_180_RINGING,
TAG_END());
s2_respond_to(prack, dialog, SIP_200_OK, TAG_END());
s2_free_message(prack), prack = NULL;
fail_unless(s2_check_callstate(nua_callstate_proceeding));
fail_unless(s2_check_event(nua_r_prack, 200));
s2_respond_to(invite, dialog, SIP_200_OK, TAG_END());
s2_free_message(invite);
fail_unless(s2_check_event(nua_r_invite, 200));
fail_unless(s2_check_callstate(nua_callstate_ready));
fail_unless(s2_check_request(SIP_METHOD_ACK));
bye_to_nua(nh, TAG_END());
nua_handle_destroy(nh);
}
END_TEST
START_TEST(call_2_4_2)
{
nua_handle_t *nh;
struct message *invite, *prack;
int with_sdp;
s2_case("2.4.2", "Call with 100rel",
"NUA sends INVITE, "
"receives 183, sends PRACK, receives 200 for it, "
"receives 180, sends PRACK, receives 200 for it, "
"receives 200, send ACK.");
nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local), TAG_END());
invite = invite_sent_by_nua(
nh,
NUTAG_MEDIA_ENABLE(0),
SIPTAG_CONTENT_TYPE_STR("application/sdp"),
SIPTAG_PAYLOAD_STR(
"v=0" CRLF
"o=- 6805647540234172778 5821668777690722690 IN IP4 127.0.0.1" CRLF
"s=-" CRLF
"c=IN IP4 127.0.0.1" CRLF
"m=audio 5004 RTP/AVP 0 8" CRLF),
TAG_END());
prack = respond_with_100rel(invite, dialog, with_sdp = 0,
SIP_183_SESSION_PROGRESS,
TAG_END());
s2_respond_to(prack, dialog, SIP_200_OK, TAG_END());
s2_free_message(prack), prack = NULL;
fail_unless(s2_check_callstate(nua_callstate_proceeding));
fail_unless(s2_check_event(nua_r_prack, 200));
prack = respond_with_100rel(invite, dialog, with_sdp = 0,
SIP_180_RINGING,
TAG_END());
s2_respond_to(prack, dialog, SIP_200_OK, TAG_END());
s2_free_message(prack), prack = NULL;
fail_unless(s2_check_callstate(nua_callstate_proceeding));
fail_unless(s2_check_event(nua_r_prack, 200));
s2_respond_to(invite, dialog, SIP_200_OK, TAG_END());
s2_free_message(invite);
fail_unless(s2_check_event(nua_r_invite, 200));
fail_unless(s2_check_callstate(nua_callstate_ready));
fail_unless(s2_check_request(SIP_METHOD_ACK));
bye_to_nua(nh, TAG_END());
nua_handle_destroy(nh);
}
END_TEST
TCase *invite_100rel_tcase(void)
{
TCase *tc = tcase_create("2.4 - INVITE with 100rel");
tcase_add_checked_fixture(tc, call_setup, call_teardown);
{
tcase_add_test(tc, call_2_4_1);
tcase_add_test(tc, call_2_4_2);
}
return tc;
}
/* ====================================================================== */
/* 2.5 - Call with preconditions */
START_TEST(call_2_5_1)
{
nua_handle_t *nh;
struct message *invite, *prack, *update;
int with_sdp;
s2_case("2.5.1", "Call with preconditions",
"NUA sends INVITE, "
"receives 183, sends PRACK, receives 200 for it, "
"sends UPDATE, receives 200 for it, "
"receives 180, sends PRACK, receives 200 for it, "
"receives 200, send ACK.");
nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local), TAG_END());
invite = invite_sent_by_nua(
nh, SOATAG_USER_SDP_STR("m=audio 5004 RTP/AVP 0 8"),
SIPTAG_REQUIRE_STR("precondition"),
TAG_END());
process_offer(invite);
prack = respond_with_100rel(invite, dialog, with_sdp = 1,
SIP_183_SESSION_PROGRESS,
TAG_END());
fail_unless(s2_check_callstate(nua_callstate_proceeding));
process_offer(prack);
respond_with_sdp(
prack, dialog, SIP_200_OK,
SIPTAG_REQUIRE_STR("100rel"),
TAG_END());
s2_free_message(prack), prack = NULL;
fail_unless(s2_check_event(nua_r_prack, 200));
fail_unless(s2_check_callstate(nua_callstate_proceeding));
update = s2_wait_for_request(SIP_METHOD_UPDATE);
/* UPDATE sent by stack, stack sends event for it */
fail_unless(s2_check_callstate(nua_callstate_proceeding));
process_offer(update);
respond_with_sdp(
update, dialog, SIP_200_OK,
TAG_END());
s2_free_message(update), update = NULL;
fail_unless(s2_check_event(nua_r_update, 200));
fail_unless(s2_check_callstate(nua_callstate_proceeding));
prack = respond_with_100rel(invite, dialog, with_sdp = 0,
SIP_180_RINGING,
TAG_END());
s2_respond_to(prack, dialog, SIP_200_OK, TAG_END());
s2_free_message(prack), prack = NULL;
fail_unless(s2_check_callstate(nua_callstate_proceeding));
fail_unless(s2_check_event(nua_r_prack, 200));
s2_respond_to(invite, dialog, SIP_200_OK, TAG_END());
s2_free_message(invite);
fail_unless(s2_check_event(nua_r_invite, 200));
fail_unless(s2_check_callstate(nua_callstate_ready));
fail_unless(s2_check_request(SIP_METHOD_ACK));
bye_to_nua(nh, TAG_END());
nua_handle_destroy(nh);
}
END_TEST
START_TEST(call_2_5_2)
{
nua_handle_t *nh;
struct message *invite, *prack, *update;
sip_rseq_t rs[1];
sip_rack_t rack[1];
s2_case("2.5.2", "Call with preconditions - send 200 w/ ongoing PRACK ",
"NUA sends INVITE, "
"receives 183, sends PRACK, "
"receives 200 to INVITE, "
"receives 200 to PRACK, "
"sends ACK, "
"sends UPDATE, "
"receives 200 to UPDATE.");
nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local), TAG_END());
invite = invite_sent_by_nua(
nh,
SOATAG_USER_SDP_STR("m=audio 5004 RTP/AVP 0 8"),
SIPTAG_REQUIRE_STR("precondition"),
NUTAG_APPL_METHOD("PRACK"),
TAG_END());
process_offer(invite);
sip_rseq_init(rs)->rs_response = ++s2_rseq;
respond_with_sdp(
invite, dialog, SIP_183_SESSION_PROGRESS,
SIPTAG_REQUIRE_STR("100rel"),
SIPTAG_RSEQ(rs),
TAG_END());
fail_unless(s2_check_event(nua_r_invite, 183));
fail_unless(s2_check_callstate(nua_callstate_proceeding));
sip_rack_init(rack)->ra_response = s2_rseq;
rack->ra_cseq = invite->sip->sip_cseq->cs_seq;
rack->ra_method = invite->sip->sip_cseq->cs_method;
rack->ra_method_name = invite->sip->sip_cseq->cs_method_name;
nua_prack(nh, SIPTAG_RACK(rack), TAG_END());
prack = s2_wait_for_request(SIP_METHOD_PRACK);
process_offer(prack);
s2_respond_to(invite, dialog, SIP_200_OK, TAG_END());
s2_free_message(invite);
fail_unless(s2_check_event(nua_r_invite, 200));
fail_unless(s2_check_callstate(nua_callstate_completing));
respond_with_sdp(
prack, dialog, SIP_200_OK,
TAG_END());
s2_free_message(prack), prack = NULL;
fail_unless(s2_check_event(nua_r_prack, 200));
fail_unless(s2_check_callstate(nua_callstate_ready));
fail_unless(s2_check_request(SIP_METHOD_ACK));
update = s2_wait_for_request(SIP_METHOD_UPDATE);
/* UPDATE sent by stack, stack sends event for it */
fail_unless(s2_check_callstate(nua_callstate_calling));
process_offer(update);
respond_with_sdp(
update, dialog, SIP_200_OK,
TAG_END());
s2_free_message(update), update = NULL;
fail_unless(s2_check_event(nua_r_update, 200));
fail_unless(s2_check_callstate(nua_callstate_ready));
bye_to_nua(nh, TAG_END());
nua_handle_destroy(nh);
}
END_TEST
START_TEST(call_2_5_3)
{
nua_handle_t *nh;
struct message *invite, *prack, *update;
sip_rseq_t rs[1];
sip_rack_t rack[1];
s2_case("2.5.3", "Call with preconditions - send 200 w/ ongoing UPDATE ",
"NUA sends INVITE, "
"receives 183, sends PRACK, receives 200 to PRACK, "
"sends UPDATE, "
"receives 200 to INVITE, "
"receives 200 to UPDATE, "
"sends ACK.");
nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local), TAG_END());
invite = invite_sent_by_nua(
nh,
SOATAG_USER_SDP_STR("m=audio 5004 RTP/AVP 0 8"),
SIPTAG_REQUIRE_STR("precondition"),
NUTAG_APPL_METHOD("PRACK"),
TAG_END());
process_offer(invite);
sip_rseq_init(rs)->rs_response = ++s2_rseq;
respond_with_sdp(
invite, dialog, SIP_183_SESSION_PROGRESS,
SIPTAG_REQUIRE_STR("100rel"),
SIPTAG_RSEQ(rs),
TAG_END());
fail_unless(s2_check_event(nua_r_invite, 183));
fail_unless(s2_check_callstate(nua_callstate_proceeding));
sip_rack_init(rack)->ra_response = s2_rseq;
rack->ra_cseq = invite->sip->sip_cseq->cs_seq;
rack->ra_method = invite->sip->sip_cseq->cs_method;
rack->ra_method_name = invite->sip->sip_cseq->cs_method_name;
nua_prack(nh, SIPTAG_RACK(rack), TAG_END());
prack = s2_wait_for_request(SIP_METHOD_PRACK);
process_offer(prack);
respond_with_sdp(
prack, dialog, SIP_200_OK,
TAG_END());
s2_free_message(prack), prack = NULL;
fail_unless(s2_check_event(nua_r_prack, 200));
fail_unless(s2_check_callstate(nua_callstate_proceeding));
update = s2_wait_for_request(SIP_METHOD_UPDATE);
/* UPDATE sent by stack, stack sends event for it */
fail_unless(s2_check_callstate(nua_callstate_proceeding));
s2_respond_to(invite, dialog, SIP_200_OK, TAG_END());
s2_free_message(invite);
fail_unless(s2_check_event(nua_r_invite, 200));
fail_unless(s2_check_callstate(nua_callstate_completing));
process_offer(update);
respond_with_sdp(
update, dialog, SIP_200_OK,
TAG_END());
s2_free_message(update), update = NULL;
fail_unless(s2_check_event(nua_r_update, 200));
fail_unless(s2_check_callstate(nua_callstate_ready));
fail_unless(s2_check_request(SIP_METHOD_ACK));
bye_to_nua(nh, TAG_END());
nua_handle_destroy(nh);
}
END_TEST
TCase *invite_precondition_tcase(void)
{
TCase *tc = tcase_create("2.5 - Call with preconditions");
tcase_add_checked_fixture(tc, call_setup, call_teardown);
{
tcase_add_test(tc, call_2_5_1);
tcase_add_test(tc, call_2_5_2);
tcase_add_test(tc, call_2_5_3);
}
return tc;
}
/* ====================================================================== */
/* 2.6 - Re-INVITEs */
START_TEST(call_2_6_1)
{
nua_handle_t *nh;
struct message *invite, *ack;
int i;
s2_case("2.6.1", "Queued re-INVITEs",
"NUA receives INVITE, "
"sends re-INVITE twice, "
"sends BYE.");
nh = invite_to_nua(TAG_END());
nua_invite(nh, TAG_END());
nua_invite(nh, TAG_END());
for (i = 0; i < 2; i++) {
fail_unless(s2_check_callstate(nua_callstate_calling));
invite = s2_wait_for_request(SIP_METHOD_INVITE);
fail_if(!invite);
process_offer(invite);
respond_with_sdp(invite, dialog, SIP_200_OK, TAG_END());
s2_free_message(invite);
ack = s2_wait_for_request(SIP_METHOD_ACK);
fail_if(!ack);
s2_free_message(ack);
fail_unless(s2_check_event(nua_r_invite, 200));
fail_unless(s2_check_callstate(nua_callstate_ready));
}
bye_by_nua(nh, TAG_END());
nua_handle_destroy(nh);
}
END_TEST
START_TEST(call_2_6_2)
{
nua_handle_t *nh;
struct message *invite, *ack, *response;
s2_case("2.6.2", "Re-INVITE glare",
"NUA sends re-INVITE and then receives re-INVITE, "
"sends BYE.");
nh = invite_to_nua(TAG_END());
nua_invite(nh, TAG_END());
fail_unless(s2_check_callstate(nua_callstate_calling));
soa_generate_offer(soa, 1, NULL);
request_with_sdp(dialog, SIP_METHOD_INVITE, NULL, TAG_END());
invite = s2_wait_for_request(SIP_METHOD_INVITE);
fail_if(!invite);
respond_with_sdp(invite, dialog, SIP_500_INTERNAL_SERVER_ERROR,
SIPTAG_RETRY_AFTER_STR("8"),
TAG_END());
s2_free_message(invite);
ack = s2_wait_for_request(SIP_METHOD_ACK);
fail_if(!ack);
s2_free_message(ack);
response = s2_wait_for_response(491, SIP_METHOD_INVITE);
fail_if(!response);
fail_if(s2_request_to(dialog, SIP_METHOD_ACK, NULL,
SIPTAG_VIA(sip_object(dialog->invite)->sip_via),
TAG_END()));
s2_free_message(response);
fail_if(soa_process_reject(soa, NULL) < 0);
/* We get nua_r_invite with 100 trying (and 500 in sip->sip_status) */
fail_unless(s2_check_event(nua_r_invite, 100));
s2_fast_forward(10);
fail_unless(s2_check_callstate(nua_callstate_calling));
invite = s2_wait_for_request(SIP_METHOD_INVITE);
process_offer(invite);
respond_with_sdp(invite, dialog, SIP_200_OK, TAG_END());
fail_unless(s2_check_event(nua_r_invite, 200));
fail_unless(s2_check_callstate(nua_callstate_ready));
ack = s2_wait_for_request(SIP_METHOD_ACK);
fail_if(!ack);
s2_free_message(ack);
bye_by_nua(nh, TAG_END());
nua_handle_destroy(nh);
}
END_TEST
TCase *invite_glare_tcase(void)
{
TCase *tc = tcase_create("2.6 - INVITE glare");
tcase_add_checked_fixture(tc, call_setup, call_teardown);
{
tcase_add_test(tc, call_2_6_1);
tcase_add_test(tc, call_2_6_2);
}
return tc;
}
/* ====================================================================== */
/* 3.1 - Call error cases */
START_TEST(call_3_1_1)
{
nua_handle_t *nh;
struct message *invite;
s2_case("3.1.1", "Call failure", "Call fails with 403 response");
nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local),
TAG_END());
nua_invite(nh, SOATAG_USER_SDP_STR("m=audio 5004 RTP/AVP 0 8"),
TAG_END());
fail_unless(s2_check_callstate(nua_callstate_calling));
invite = s2_wait_for_request(SIP_METHOD_INVITE);
fail_if(!invite);
s2_respond_to(invite, NULL, SIP_403_FORBIDDEN, TAG_END());
s2_free_message(invite);
fail_unless(s2_check_request(SIP_METHOD_ACK));
fail_unless(s2_check_event(nua_r_invite, 403));
fail_unless(s2_check_callstate(nua_callstate_terminated));
nua_handle_destroy(nh);
}
END_TEST
START_TEST(call_3_1_2)
{
nua_handle_t *nh;
struct message *invite;
int i;
s2_case("3.1.2", "Call fails after too many retries",
"Call fails after 4 times 500 Retry-After");
nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local),
NUTAG_RETRY_COUNT(3),
TAG_END());
nua_invite(nh, SOATAG_USER_SDP_STR("m=audio 5004 RTP/AVP 0 8"),
TAG_END());
for (i = 0;; i++) {
fail_unless(s2_check_callstate(nua_callstate_calling));
invite = s2_wait_for_request(SIP_METHOD_INVITE);
fail_if(!invite);
s2_respond_to(invite, NULL, SIP_500_INTERNAL_SERVER_ERROR,
SIPTAG_RETRY_AFTER_STR("5"),
TAG_END());
s2_free_message(invite);
fail_unless(s2_check_request(SIP_METHOD_ACK));
if (i == 3)
break;
fail_unless(s2_check_event(nua_r_invite, 100));
s2_fast_forward(5);
}
fail_unless(s2_check_event(nua_r_invite, 500));
fail_unless(s2_check_callstate(nua_callstate_terminated));
nua_handle_destroy(nh);
}
END_TEST
START_TEST(call_3_2_1)
{
nua_handle_t *nh;
struct message *invite;
s2_case("3.2.1", "Re-INVITE failure", "Re-INVITE fails with 403 response");
nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local),
TAG_END());
invite_by_nua(nh, TAG_END());
nua_invite(nh, TAG_END());
fail_unless(s2_check_callstate(nua_callstate_calling));
invite = s2_wait_for_request(SIP_METHOD_INVITE);
fail_if(!invite);
s2_respond_to(invite, NULL, SIP_403_FORBIDDEN, TAG_END());
s2_free_message(invite);
fail_unless(s2_check_request(SIP_METHOD_ACK));
fail_unless(s2_check_event(nua_r_invite, 403));
/* Return to previous state */
fail_unless(s2_check_callstate(nua_callstate_ready));
bye_by_nua(nh, TAG_END());
}
END_TEST
START_TEST(call_3_2_2)
{
nua_handle_t *nh;
struct message *invite, *bye;
int i;
s2_case("3.2.2", "Re-INVITE fails after too many retries",
"Call fails after 4 times 500 Retry-After");
nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local),
NUTAG_RETRY_COUNT(3),
TAG_END());
invite_by_nua(nh, TAG_END());
nua_invite(nh, SOATAG_USER_SDP_STR("m=audio 5004 RTP/AVP 0 8"),
TAG_END());
for (i = 0;; i++) {
fail_unless(s2_check_callstate(nua_callstate_calling));
invite = s2_wait_for_request(SIP_METHOD_INVITE);
fail_if(!invite);
s2_respond_to(invite, NULL, SIP_500_INTERNAL_SERVER_ERROR,
SIPTAG_RETRY_AFTER_STR("5"),
TAG_END());
s2_free_message(invite);
fail_unless(s2_check_request(SIP_METHOD_ACK));
if (i == 3)
break;
fail_unless(s2_check_event(nua_r_invite, 100));
s2_fast_forward(5);
}
fail_unless(s2_check_event(nua_r_invite, 500));
/* Graceful termination */
fail_unless(s2_check_callstate(nua_callstate_terminating));
bye = s2_wait_for_request(SIP_METHOD_BYE);
fail_if(!bye);
s2_respond_to(bye, dialog, SIP_200_OK, TAG_END());
s2_free_message(bye);
fail_unless(s2_check_event(nua_r_bye, 200));
fail_unless(s2_check_callstate(nua_callstate_terminated));
nua_handle_destroy(nh);
}
END_TEST
START_TEST(call_3_2_3)
{
nua_handle_t *nh;
struct message *invite;
s2_case("3.2.3", "Re-INVITE failure", "Re-INVITE fails with 491 response");
nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local),
TAG_END());
invite_by_nua(nh, TAG_END());
nua_invite(nh, TAG_END());
fail_unless(s2_check_callstate(nua_callstate_calling));
invite = s2_wait_for_request(SIP_METHOD_INVITE);
fail_if(!invite);
s2_respond_to(invite, NULL, SIP_491_REQUEST_PENDING, TAG_END());
s2_free_message(invite);
fail_unless(s2_check_request(SIP_METHOD_ACK));
fail_unless(s2_check_event(nua_r_invite, 491));
/* Return to previous state */
fail_unless(s2_check_callstate(nua_callstate_ready));
bye_by_nua(nh, TAG_END());
}
END_TEST
TCase *invite_error_tcase(void)
{
TCase *tc = tcase_create("3 - Call Errors");
tcase_add_checked_fixture(tc, call_setup, call_teardown);
{
tcase_add_test(tc, call_3_1_1);
tcase_add_test(tc, call_3_1_2);
tcase_add_test(tc, call_3_2_1);
tcase_add_test(tc, call_3_2_2);
tcase_add_test(tc, call_3_2_3);
tcase_set_timeout(tc, 5);
}
return tc;
}
/* ====================================================================== */
/* Weird call termination cases */
START_TEST(bye_4_1_1)
{
nua_handle_t *nh;
struct message *bye, *r481;
s2_case("4.1.1", "Re-INVITE while terminating",
"NUA sends BYE, "
"BYE is challenged, "
"and NUA is re-INVITEd at the same time.");
nh = invite_to_nua(TAG_END());
s2_flush_events();
nua_bye(nh, TAG_END());
bye = s2_wait_for_request(SIP_METHOD_BYE);
fail_if(!bye);
s2_respond_to(bye, dialog, SIP_407_PROXY_AUTH_REQUIRED,
SIPTAG_PROXY_AUTHENTICATE_STR(s2_auth_digest_str),
TAG_END());
s2_free_message(bye);
fail_unless(s2_check_event(nua_r_bye, 407));
soa_generate_offer(soa, 1, NULL);
request_with_sdp(dialog, SIP_METHOD_INVITE, NULL, TAG_END());
do {
r481 = s2_wait_for_response(0, SIP_METHOD_INVITE);
}
while (r481->sip->sip_status->st_status < 200);
s2_update_dialog(dialog, r481); /* send ACK */
fail_unless(s2_check_callstate(nua_callstate_terminated));
nua_handle_destroy(nh);
}
END_TEST
START_TEST(bye_4_1_2)
{
nua_handle_t *nh;
struct message *bye, *r481;
s2_case("4.1.2", "Re-INVITE while terminating",
"NUA sends BYE, and gets re-INVITEd at same time");
nh = invite_to_nua(TAG_END());
s2_flush_events();
nua_bye(nh, TAG_END());
bye = s2_wait_for_request(SIP_METHOD_BYE);
fail_if(!bye);
request_with_sdp(dialog, SIP_METHOD_INVITE, NULL, TAG_END());
do {
r481 = s2_wait_for_response(0, SIP_METHOD_INVITE);
}
while (r481->sip->sip_status->st_status < 200);
s2_update_dialog(dialog, r481); /* send ACK */
fail_unless(s2_check_callstate(nua_callstate_terminated));
s2_respond_to(bye, dialog, SIP_200_OK,
TAG_END());
s2_free_message(bye);
fail_unless(s2_check_event(nua_r_bye, 200));
nua_handle_destroy(nh);
}
END_TEST
START_TEST(bye_4_1_3)
{
nua_handle_t *nh;
struct message *bye;
struct event *i_bye;
s2_case("4.1.3", "BYE while terminating",
"NUA sends BYE and receives BYE");
nh = invite_to_nua(TAG_END());
mark_point();
nua_set_hparams(nh, NUTAG_APPL_METHOD("BYE"), TAG_END());
fail_unless(s2_check_event(nua_r_set_params, 200));
s2_flush_events();
nua_bye(nh, TAG_END());
bye = s2_wait_for_request(SIP_METHOD_BYE);
fail_if(!bye);
s2_request_to(dialog, SIP_METHOD_BYE, NULL, TAG_END());
i_bye = s2_wait_for_event(nua_i_bye, 100);
fail_if(!i_bye);
nua_respond(nh, 200, "OKOK", NUTAG_WITH(i_bye->data->e_msg), TAG_END());
s2_respond_to(bye, dialog, SIP_200_OK, TAG_END());
s2_free_message(bye);
fail_unless(s2_check_event(nua_r_bye, 200));
fail_unless(s2_check_callstate(nua_callstate_terminated));
fail_unless(s2_check_response(200, SIP_METHOD_BYE));
nua_handle_destroy(nh);
}
END_TEST
START_TEST(bye_4_1_4)
{
nua_handle_t *nh;
struct message *bye;
struct event *i_bye;
s2_case("4.1.4", "Send BYE after BYE has been received",
"NUA receives BYE, tries to send BYE at same time");
nh = invite_to_nua(TAG_END());
mark_point();
nua_set_hparams(nh, NUTAG_APPL_METHOD("BYE"), TAG_END());
fail_unless(s2_check_event(nua_r_set_params, 200));
s2_flush_events();
s2_request_to(dialog, SIP_METHOD_BYE, NULL, TAG_END());
i_bye = s2_wait_for_event(nua_i_bye, 100);
fail_if(!i_bye);
nua_bye(nh, TAG_END());
bye = s2_wait_for_request(SIP_METHOD_BYE);
fail_if(!bye);
s2_respond_to(bye, dialog, SIP_200_OK, TAG_END());
s2_free_message(bye);
fail_unless(s2_check_event(nua_r_bye, 200));
fail_unless(s2_check_callstate(nua_callstate_terminated));
nua_respond(nh, 200, "OKOK", NUTAG_WITH(i_bye->data->e_msg), TAG_END());
fail_unless(s2_check_response(200, SIP_METHOD_BYE));
nua_handle_destroy(nh);
}
END_TEST
START_TEST(bye_4_1_5)
{
nua_handle_t *nh;
struct message *bye;
struct event *i_bye;
s2_case("4.1.5", "Send BYE after BYE has been received",
"NUA receives BYE, tries to send BYE at same time");
nh = invite_to_nua(TAG_END());
mark_point();
nua_set_hparams(nh, NUTAG_APPL_METHOD("BYE"), TAG_END());
fail_unless(s2_check_event(nua_r_set_params, 200));
s2_flush_events();
s2_request_to(dialog, SIP_METHOD_BYE, NULL, TAG_END());
i_bye = s2_wait_for_event(nua_i_bye, 100);
fail_if(!i_bye);
nua_bye(nh, TAG_END());
bye = s2_wait_for_request(SIP_METHOD_BYE);
fail_if(!bye);
s2_respond_to(bye, dialog, SIP_200_OK, TAG_END());
s2_free_message(bye);
fail_unless(s2_check_event(nua_r_bye, 200));
fail_unless(s2_check_callstate(nua_callstate_terminated));
nua_handle_destroy(nh);
fail_unless(s2_check_response(500, SIP_METHOD_BYE));
}
END_TEST
START_TEST(bye_4_1_6)
{
nua_handle_t *nh;
struct message *bye, *r486;
s2_case("4.1.6", "Send BYE after INVITE has been received",
"NUA receives INVITE, sends BYE at same time");
nh = invite_to_nua(TAG_END());
nua_set_hparams(nh, NUTAG_AUTOANSWER(0), TAG_END());
fail_unless(s2_check_event(nua_r_set_params, 200));
s2_flush_events();
request_with_sdp(dialog, SIP_METHOD_INVITE, NULL, TAG_END());
fail_unless(s2_check_response(100, SIP_METHOD_INVITE));
nua_bye(nh, TAG_END());
fail_unless(s2_check_event(nua_i_invite, 100));
fail_unless(s2_check_callstate(nua_callstate_received));
do {
r486 = s2_wait_for_response(0, SIP_METHOD_INVITE);
}
while (r486->sip->sip_status->st_status < 200);
s2_update_dialog(dialog, r486); /* send ACK */
fail_unless(r486->sip->sip_status->st_status == 486);
bye = s2_wait_for_request(SIP_METHOD_BYE);
fail_if(!bye);
s2_respond_to(bye, dialog, SIP_200_OK, TAG_END());
s2_free_message(bye);
nua_handle_destroy(nh);
}
END_TEST
START_TEST(bye_4_1_7)
{
nua_handle_t *nh;
struct message *bye, *r486;
s2_case("4.1.7", "Send BYE after INVITE has been received",
"NUA receives INVITE, sends BYE at same time");
nh = invite_to_nua(TAG_END());
nua_set_hparams(nh, NUTAG_AUTOANSWER(0), TAG_END());
fail_unless(s2_check_event(nua_r_set_params, 200));
s2_flush_events();
request_with_sdp(dialog, SIP_METHOD_INVITE, NULL, TAG_END());
fail_unless(s2_check_response(100, SIP_METHOD_INVITE));
nua_bye(nh, TAG_END());
fail_unless(s2_check_event(nua_i_invite, 100));
fail_unless(s2_check_callstate(nua_callstate_received));
bye = s2_wait_for_request(SIP_METHOD_BYE);
fail_if(!bye);
s2_respond_to(bye, dialog, SIP_200_OK, TAG_END());
s2_free_message(bye);
do {
r486 = s2_wait_for_response(0, SIP_METHOD_INVITE);
}
while (r486->sip->sip_status->st_status < 200);
s2_update_dialog(dialog, r486); /* send ACK */
fail_unless(r486->sip->sip_status->st_status == 486);
nua_handle_destroy(nh);
}
END_TEST
START_TEST(bye_4_1_8)
{
nua_handle_t *nh;
struct message *bye, *r486;
s2_case("4.1.8", "BYE followed by response to INVITE",
"NUA receives INVITE, sends BYE at same time");
nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local), TAG_END());
invite_by_nua(nh, NUTAG_AUTOANSWER(0), TAG_END());
s2_flush_events();
request_with_sdp(dialog, SIP_METHOD_INVITE, NULL, TAG_END());
fail_unless(s2_check_response(100, SIP_METHOD_INVITE));
nua_bye(nh, TAG_END());
fail_unless(s2_check_event(nua_i_invite, 100));
fail_unless(s2_check_callstate(nua_callstate_received));
nua_respond(nh, SIP_486_BUSY_HERE, TAG_END());
do {
r486 = s2_wait_for_response(0, SIP_METHOD_INVITE);
}
while (r486->sip->sip_status->st_status < 200);
s2_update_dialog(dialog, r486); /* send ACK */
fail_unless(r486->sip->sip_status->st_status == 486);
bye = s2_wait_for_request(SIP_METHOD_BYE);
fail_if(!bye);
s2_respond_to(bye, dialog, SIP_200_OK, TAG_END());
s2_free_message(bye);
nua_handle_destroy(nh);
}
END_TEST
START_TEST(bye_4_1_9)
{
nua_handle_t *nh;
struct message *bye;
struct event *i_bye;
s2_case("4.1.6", "Send BYE, receive BYE, destroy",
"NUA sends BYE, receives BYE and handle gets destroyed");
nh = invite_to_nua(TAG_END());
mark_point();
s2_flush_events();
nua_bye(nh, TAG_END());
bye = s2_wait_for_request(SIP_METHOD_BYE);
fail_if(!bye);
s2_request_to(dialog, SIP_METHOD_BYE, NULL, TAG_END());
i_bye = s2_wait_for_event(nua_i_bye, 200);
fail_if(!i_bye);
s2_free_event(i_bye), i_bye = NULL;
fail_unless(s2_check_callstate(nua_callstate_terminated));
fail_unless(s2_check_response(200, SIP_METHOD_BYE));
nua_handle_destroy(nh);
mark_point();
su_root_step(s2->root, 10);
su_root_step(s2->root, 10);
su_root_step(s2->root, 10);
mark_point();
s2_respond_to(bye, dialog, SIP_200_OK, TAG_END());
s2_free_message(bye);
mark_point();
while (su_home_check_alloc((su_home_t *)nua, (void *)nh)) {
su_root_step(s2->root, 10);
}
}
END_TEST
START_TEST(bye_4_1_10)
{
nua_handle_t *nh;
struct message *invite, *bye;
struct event *i_bye;
s2_case("4.1.6", "Send auto-BYE upon receiving 501, receive BYE, destroy",
"NUA sends BYE, receives BYE and handle gets destroyed");
nh = invite_to_nua(TAG_END());
mark_point();
s2_flush_events();
nua_invite(nh, TAG_END());
invite = s2_wait_for_request(SIP_METHOD_INVITE);
fail_if(!invite);
s2_respond_to(invite, dialog, SIP_501_NOT_IMPLEMENTED, TAG_END());
s2_free_message(invite);
fail_unless(s2_check_request(SIP_METHOD_ACK));
bye = s2_wait_for_request(SIP_METHOD_BYE);
fail_if(!bye);
fail_unless(s2_check_callstate(nua_callstate_calling));
fail_unless(s2_check_event(nua_r_invite, 501));
fail_unless(s2_check_callstate(nua_callstate_terminating));
s2_request_to(dialog, SIP_METHOD_BYE, NULL, TAG_END());
i_bye = s2_wait_for_event(nua_i_bye, 200);
fail_if(!i_bye);
s2_free_event(i_bye), i_bye = NULL;
fail_unless(s2_check_callstate(nua_callstate_terminated));
fail_unless(s2_check_response(200, SIP_METHOD_BYE));
nua_handle_destroy(nh);
su_root_step(s2->root, 10);
su_root_step(s2->root, 10);
su_root_step(s2->root, 10);
s2_respond_to(bye, dialog, SIP_200_OK, TAG_END());
s2_free_message(bye);
while (su_home_check_alloc((su_home_t *)nua, (void *)nh)) {
su_root_step(s2->root, 10);
}
}
END_TEST
START_TEST(bye_4_1_11)
{
nua_handle_t *nh;
struct message *invite, *ack;
struct event *i_bye;
s2_case("4.1.11", "Receive BYE in completing state",
"NUA sends INVITE, receives 200, receives BYE.");
nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local), TAG_END());
invite = invite_sent_by_nua(nh, NUTAG_AUTOACK(0), TAG_END());
process_offer(invite);
s2_respond_to(invite, dialog, SIP_180_RINGING, TAG_END());
fail_unless(s2_check_event(nua_r_invite, 180));
fail_unless(s2_check_callstate(nua_callstate_proceeding));
respond_with_sdp(invite, dialog, SIP_200_OK, TAG_END());
s2_free_message(invite);
fail_unless(s2_check_event(nua_r_invite, 200));
fail_unless(s2_check_callstate(nua_callstate_completing));
s2_request_to(dialog, SIP_METHOD_BYE, NULL, TAG_END());
i_bye = s2_wait_for_event(nua_i_bye, 200);
fail_if(!i_bye);
s2_free_event(i_bye), i_bye = NULL;
fail_unless(s2_check_callstate(nua_callstate_terminated));
fail_unless(s2_check_response(200, SIP_METHOD_BYE));
ack = s2_wait_for_request(SIP_METHOD_ACK);
fail_if(!ack);
s2_free_message(ack);
nua_handle_destroy(nh);
}
END_TEST
START_TEST(bye_4_2_1)
{
nua_handle_t *nh;
struct message *bye;
s2_case("4.2.1", "BYE in progress while call timer expires",
"NUA receives INVITE, "
"activates call timers, "
"sends BYE, BYE challenged, "
"waits until session expires.");
nh = invite_to_nua(
SIPTAG_SESSION_EXPIRES_STR("300;refresher=uas"),
SIPTAG_REQUIRE_STR("timer"),
TAG_END());
s2_fast_forward(300);
invite_timer_round(nh, "300");
nua_bye(nh, TAG_END());
bye = s2_wait_for_request(SIP_METHOD_BYE);
fail_if(!bye);
s2_respond_to(bye, dialog, SIP_407_PROXY_AUTH_REQUIRED,
SIPTAG_PROXY_AUTHENTICATE_STR(s2_auth_digest_str),
TAG_END());
s2_free_message(bye);
fail_unless(s2_check_event(nua_r_bye, 407));
s2_fast_forward(300);
nua_authenticate(nh, NUTAG_AUTH("Digest:\"s2test\":abc:abc"), TAG_END());
bye = s2_wait_for_request(SIP_METHOD_BYE);
fail_if(!bye);
s2_respond_to(bye, dialog, SIP_200_OK, TAG_END());
s2_free_message(bye);
fail_unless(s2_check_event(nua_r_bye, 200));
fail_unless(s2_check_callstate(nua_callstate_terminated));
fail_if(s2->events);
nua_handle_destroy(nh);
}
END_TEST
START_TEST(bye_4_2_2)
{
nua_handle_t *nh;
struct message *bye;
s2_case("4.2.2", "BYE in progress while call timer expires",
"NUA receives INVITE, "
"activates call timers, "
"sends BYE, BYE challenged, "
"waits until session expires.");
nh = invite_to_nua(
SIPTAG_SESSION_EXPIRES_STR("300;refresher=uas"),
SIPTAG_REQUIRE_STR("timer"),
TAG_END());
s2_fast_forward(300);
invite_timer_round(nh, "300");
s2_fast_forward(300);
nua_bye(nh, TAG_END());
bye = s2_wait_for_request(SIP_METHOD_BYE);
fail_if(!bye);
s2_respond_to(bye, dialog, SIP_407_PROXY_AUTH_REQUIRED,
SIPTAG_PROXY_AUTHENTICATE_STR(s2_auth_digest_str),
TAG_END());
s2_free_message(bye);
fail_unless(s2_check_event(nua_r_bye, 407));
s2_fast_forward(300);
nua_authenticate(nh, NUTAG_AUTH(s2_auth_credentials), TAG_END());
bye = s2_wait_for_request(SIP_METHOD_BYE);
fail_if(!bye);
s2_respond_to(bye, dialog, SIP_200_OK, TAG_END());
s2_free_message(bye);
fail_unless(s2_check_event(nua_r_bye, 200));
fail_unless(s2_check_callstate(nua_callstate_terminated));
fail_if(s2->events);
nua_handle_destroy(nh);
}
END_TEST
TCase *termination_tcase(void)
{
TCase *tc = tcase_create("4 - Call Termination");
tcase_add_checked_fixture(tc, call_setup, call_teardown);
{
tcase_add_test(tc, bye_4_1_1);
tcase_add_test(tc, bye_4_1_2);
tcase_add_test(tc, bye_4_1_3);
tcase_add_test(tc, bye_4_1_4);
tcase_add_test(tc, bye_4_1_5);
tcase_add_test(tc, bye_4_1_6);
tcase_add_test(tc, bye_4_1_7);
tcase_add_test(tc, bye_4_1_8);
tcase_add_test(tc, bye_4_1_9);
tcase_add_test(tc, bye_4_1_10);
tcase_add_test(tc, bye_4_1_11);
tcase_add_test(tc, bye_4_2_1);
tcase_add_test(tc, bye_4_2_2);
tcase_set_timeout(tc, 5);
}
return tc;
}
/* ====================================================================== */
START_TEST(destroy_4_3_1)
{
nua_handle_t *nh;
struct message *invite, *cancel;
s2_case("4.3.1", "Destroy handle after INVITE sent",
"NUA sends INVITE, handle gets destroyed.");
nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local), TAG_END());
invite = invite_sent_by_nua(nh, TAG_END());
process_offer(invite);
nua_handle_destroy(nh);
s2_respond_to(invite, dialog, SIP_100_TRYING, TAG_END());
cancel = s2_wait_for_request(SIP_METHOD_CANCEL);
fail_if(!cancel);
s2_respond_to(invite, dialog, SIP_487_REQUEST_CANCELLED, TAG_END());
s2_free_message(invite);
s2_respond_to(cancel, dialog, SIP_200_OK, TAG_END());
s2_free_message(cancel);
}
END_TEST
START_TEST(destroy_4_3_2)
{
nua_handle_t *nh;
struct message *invite, *cancel;
s2_case("4.3.2", "Destroy handle in calling state",
"NUA sends INVITE, receives 180, handle gets destroyed.");
nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local), TAG_END());
invite = invite_sent_by_nua(nh, TAG_END());
process_offer(invite);
s2_respond_to(invite, dialog, SIP_180_RINGING, TAG_END());
fail_unless(s2_check_event(nua_r_invite, 180));
fail_unless(s2_check_callstate(nua_callstate_proceeding));
nua_handle_destroy(nh);
cancel = s2_wait_for_request(SIP_METHOD_CANCEL);
fail_if(!cancel);
s2_respond_to(invite, dialog, SIP_487_REQUEST_CANCELLED, TAG_END());
s2_free_message(invite);
s2_respond_to(cancel, dialog, SIP_200_OK, TAG_END());
s2_free_message(cancel);
}
END_TEST
START_TEST(destroy_4_3_3)
{
nua_handle_t *nh;
struct message *invite, *ack, *bye;
s2_case("4.3.3", "Destroy handle in completing state",
"NUA sends INVITE, receives 200, handle gets destroyed.");
nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local), TAG_END());
invite = invite_sent_by_nua(nh, NUTAG_AUTOACK(0), TAG_END());
process_offer(invite);
s2_respond_to(invite, dialog, SIP_180_RINGING, TAG_END());
fail_unless(s2_check_event(nua_r_invite, 180));
fail_unless(s2_check_callstate(nua_callstate_proceeding));
respond_with_sdp(invite, dialog, SIP_200_OK, TAG_END());
fail_unless(s2_check_event(nua_r_invite, 200));
fail_unless(s2_check_callstate(nua_callstate_completing));
nua_handle_destroy(nh);
ack = s2_wait_for_request(SIP_METHOD_ACK);
fail_if(!ack);
s2_free_message(ack);
bye = s2_wait_for_request(SIP_METHOD_BYE);
fail_if(!bye);
s2_respond_to(bye, dialog, SIP_200_OK, TAG_END());
s2_free_message(bye);
s2_free_message(invite);
}
END_TEST
START_TEST(destroy_4_3_4)
{
nua_handle_t *nh;
struct message *invite, *ack, *bye;
s2_case("4.3.3", "Destroy handle in ready state ",
"NUA sends INVITE, receives 200, handle gets destroyed.");
nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local), TAG_END());
invite = invite_sent_by_nua(nh, NUTAG_AUTOACK(0), TAG_END());
process_offer(invite);
s2_respond_to(invite, dialog, SIP_180_RINGING, TAG_END());
fail_unless(s2_check_event(nua_r_invite, 180));
fail_unless(s2_check_callstate(nua_callstate_proceeding));
respond_with_sdp(invite, dialog, SIP_200_OK, TAG_END());
fail_unless(s2_check_event(nua_r_invite, 200));
fail_unless(s2_check_callstate(nua_callstate_completing));
nua_ack(nh, TAG_END());
ack = s2_wait_for_request(SIP_METHOD_ACK);
fail_if(!ack);
s2_free_message(ack);
fail_unless(s2_check_callstate(nua_callstate_ready));
nua_handle_destroy(nh);
bye = s2_wait_for_request(SIP_METHOD_BYE);
fail_if(!bye);
s2_respond_to(bye, dialog, SIP_200_OK, TAG_END());
s2_free_message(bye);
s2_free_message(invite);
}
END_TEST
START_TEST(destroy_4_3_5)
{
nua_handle_t *nh;
struct message *invite, *cancel;
s2_case("4.3.5", "Destroy handle in re-INVITE calling state",
"NUA sends re-INVITE, handle gets destroyed.");
nh = invite_to_nua(TAG_END());
invite = invite_sent_by_nua(nh, TAG_END());
process_offer(invite);
nua_handle_destroy(nh);
s2_respond_to(invite, dialog, SIP_100_TRYING, TAG_END());
cancel = s2_wait_for_request(SIP_METHOD_CANCEL);
fail_if(!cancel);
s2_respond_to(invite, dialog, SIP_487_REQUEST_CANCELLED, TAG_END());
s2_free_message(invite);
s2_respond_to(cancel, dialog, SIP_200_OK, TAG_END());
s2_free_message(cancel);
}
END_TEST
START_TEST(destroy_4_3_6)
{
nua_handle_t *nh;
struct message *invite, *cancel;
s2_case("4.3.6", "Destroy handle in calling state of re-INVITE",
"NUA sends re-INVITE, receives 180, handle gets destroyed.");
nh = invite_to_nua(TAG_END());
invite = invite_sent_by_nua(nh, TAG_END());
process_offer(invite);
s2_respond_to(invite, dialog, SIP_180_RINGING, TAG_END());
fail_unless(s2_check_event(nua_r_invite, 180));
fail_unless(s2_check_callstate(nua_callstate_proceeding));
nua_handle_destroy(nh);
cancel = s2_wait_for_request(SIP_METHOD_CANCEL);
fail_if(!cancel);
s2_respond_to(invite, dialog, SIP_487_REQUEST_CANCELLED, TAG_END());
s2_free_message(invite);
s2_respond_to(cancel, dialog, SIP_200_OK, TAG_END());
s2_free_message(cancel);
}
END_TEST
START_TEST(destroy_4_3_7)
{
nua_handle_t *nh;
struct message *invite, *ack, *bye;
s2_case("4.3.7", "Destroy handle in completing state of re-INVITE",
"NUA sends INVITE, receives 200, handle gets destroyed.");
nh = invite_to_nua(TAG_END());
invite = invite_sent_by_nua(nh, NUTAG_AUTOACK(0), TAG_END());
process_offer(invite);
s2_respond_to(invite, dialog, SIP_180_RINGING, TAG_END());
fail_unless(s2_check_event(nua_r_invite, 180));
fail_unless(s2_check_callstate(nua_callstate_proceeding));
respond_with_sdp(invite, dialog, SIP_200_OK, TAG_END());
fail_unless(s2_check_event(nua_r_invite, 200));
fail_unless(s2_check_callstate(nua_callstate_completing));
nua_handle_destroy(nh);
ack = s2_wait_for_request(SIP_METHOD_ACK);
fail_if(!ack);
s2_free_message(ack);
bye = s2_wait_for_request(SIP_METHOD_BYE);
fail_if(!bye);
s2_respond_to(bye, dialog, SIP_200_OK, TAG_END());
s2_free_message(bye);
s2_free_message(invite);
}
END_TEST
START_TEST(destroy_4_3_8)
{
nua_handle_t *nh;
struct message *invite, *ack, *bye;
tport_set_params(s2->master, TPTAG_LOG(1), TAG_END());
s2_setup_logs(7);
s2_case("4.3.8", "Destroy handle after INVITE sent",
"NUA sends INVITE, handle gets destroyed, "
"but remote end returns 200 OK. "
"Make sure nua tries to release call properly.");
nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local), TAG_END());
invite = invite_sent_by_nua(nh, TAG_END());
process_offer(invite);
nua_handle_destroy(nh);
respond_with_sdp(invite, dialog, SIP_200_OK, TAG_END());
s2_free_message(invite);
ack = s2_wait_for_request(SIP_METHOD_ACK);
fail_if(!ack);
s2_free_message(ack);
bye = s2_wait_for_request(SIP_METHOD_BYE);
fail_if(!bye);
s2_respond_to(bye, dialog, SIP_200_OK, TAG_END());
s2_free_message(bye);
}
END_TEST
START_TEST(destroy_4_3_9)
{
nua_handle_t *nh;
struct message *invite, *cancel, *ack, *bye;
tport_set_params(s2->master, TPTAG_LOG(1), TAG_END());
s2_setup_logs(7);
s2_case("4.3.9", "Destroy handle in calling state",
"NUA sends INVITE, receives 180, handle gets destroyed, "
"but remote end returns 200 OK. "
"Make sure nua tries to release call properly.");
nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local), TAG_END());
invite = invite_sent_by_nua(nh, TAG_END());
process_offer(invite);
s2_respond_to(invite, dialog, SIP_180_RINGING, TAG_END());
fail_unless(s2_check_event(nua_r_invite, 180));
fail_unless(s2_check_callstate(nua_callstate_proceeding));
nua_handle_destroy(nh);
cancel = s2_wait_for_request(SIP_METHOD_CANCEL);
fail_if(!cancel);
s2_respond_to(cancel, dialog, SIP_481_NO_TRANSACTION, TAG_END());
s2_free_message(cancel);
respond_with_sdp(invite, dialog, SIP_200_OK, TAG_END());
s2_free_message(invite);
ack = s2_wait_for_request(SIP_METHOD_ACK);
fail_if(!ack);
s2_free_message(ack);
bye = s2_wait_for_request(SIP_METHOD_BYE);
fail_if(!bye);
s2_respond_to(bye, dialog, SIP_200_OK, TAG_END());
s2_free_message(bye);
}
END_TEST
START_TEST(destroy_4_4_1)
{
nua_handle_t *nh;
struct event *invite;
struct message *response;
s2_case("4.4.1", "Destroy handle while call is on-going",
"NUA is callee, sends 100, destroys handle");
soa_generate_offer(soa, 1, NULL);
request_with_sdp(dialog, SIP_METHOD_INVITE, NULL, TAG_END());
invite = s2_wait_for_event(nua_i_invite, 100); fail_unless(invite != NULL);
fail_unless(s2_check_callstate(nua_callstate_received));
nh = invite->nh; fail_if(!nh);
s2_free_event(invite);
response = s2_wait_for_response(100, SIP_METHOD_INVITE);
fail_if(!response);
s2_free_message(response);
nua_handle_destroy(nh);
response = s2_wait_for_response(480, SIP_METHOD_INVITE);
fail_if(!response);
fail_if(s2_request_to(dialog, SIP_METHOD_ACK, NULL,
SIPTAG_VIA(sip_object(dialog->invite)->sip_via),
TAG_END()));
s2_free_message(response);
}
END_TEST
START_TEST(destroy_4_4_2)
{
nua_handle_t *nh;
struct event *invite;
struct message *response;
s2_case("4.4.1", "Destroy handle while call is on-going",
"NUA is callee, sends 180, destroys handle");
soa_generate_offer(soa, 1, NULL);
request_with_sdp(dialog, SIP_METHOD_INVITE, NULL, TAG_END());
invite = s2_wait_for_event(nua_i_invite, 100); fail_unless(invite != NULL);
fail_unless(s2_check_callstate(nua_callstate_received));
nh = invite->nh; fail_if(!nh);
s2_free_event(invite);
response = s2_wait_for_response(100, SIP_METHOD_INVITE);
fail_if(!response);
s2_free_message(response);
nua_respond(nh, SIP_180_RINGING, TAG_END());
response = s2_wait_for_response(180, SIP_METHOD_INVITE);
fail_if(!response);
s2_free_message(response);
nua_handle_destroy(nh);
response = s2_wait_for_response(480, SIP_METHOD_INVITE);
fail_if(!response);
fail_if(s2_request_to(dialog, SIP_METHOD_ACK, NULL,
SIPTAG_VIA(sip_object(dialog->invite)->sip_via),
TAG_END()));
s2_free_message(response);
}
END_TEST
START_TEST(destroy_4_4_3_1)
{
nua_handle_t *nh;
struct event *invite;
struct message *response, *bye;
s2_case("4.4.3.1", "Destroy handle while call is on-going",
"NUA is callee, sends 200, destroys handle");
soa_generate_offer(soa, 1, NULL);
request_with_sdp(dialog, SIP_METHOD_INVITE, NULL, TAG_END());
invite = s2_wait_for_event(nua_i_invite, 100); fail_unless(invite != NULL);
fail_unless(s2_check_callstate(nua_callstate_received));
nh = invite->nh; fail_if(!nh);
s2_free_event(invite);
response = s2_wait_for_response(100, SIP_METHOD_INVITE);
fail_if(!response);
s2_free_message(response);
nua_respond(nh, SIP_180_RINGING,
SOATAG_USER_SDP_STR("m=audio 5004 RTP/AVP 0 8"),
TAG_END());
response = s2_wait_for_response(180, SIP_METHOD_INVITE);
fail_if(!response);
s2_free_message(response);
fail_unless(s2_check_callstate(nua_callstate_early));
nua_respond(nh, SIP_200_OK, TAG_END());
fail_unless(s2_check_callstate(nua_callstate_completed));
response = s2_wait_for_response(200, SIP_METHOD_INVITE);
fail_if(!response);
nua_handle_destroy(nh);
fail_if(s2_request_to(dialog, SIP_METHOD_ACK, NULL,
SIPTAG_VIA(sip_object(dialog->invite)->sip_via),
TAG_END()));
s2_free_message(response);
bye = s2_wait_for_request(SIP_METHOD_BYE);
fail_if(!bye);
s2_respond_to(bye, dialog, SIP_200_OK, TAG_END());
s2_free_message(bye);
}
END_TEST
START_TEST(destroy_4_4_3_2)
{
nua_handle_t *nh;
struct event *invite;
struct message *response, *bye;
s2_case("4.4.3.1", "Destroy handle while call is on-going",
"NUA is callee, sends 200, destroys handle");
soa_generate_offer(soa, 1, NULL);
request_with_sdp(dialog, SIP_METHOD_INVITE, NULL, TAG_END());
invite = s2_wait_for_event(nua_i_invite, 100); fail_unless(invite != NULL);
fail_unless(s2_check_callstate(nua_callstate_received));
nh = invite->nh; fail_if(!nh);
s2_free_event(invite);
response = s2_wait_for_response(100, SIP_METHOD_INVITE);
fail_if(!response);
s2_free_message(response);
nua_respond(nh, SIP_180_RINGING,
SOATAG_USER_SDP_STR("m=audio 5004 RTP/AVP 0 8"),
TAG_END());
response = s2_wait_for_response(180, SIP_METHOD_INVITE);
fail_if(!response);
s2_free_message(response);
fail_unless(s2_check_callstate(nua_callstate_early));
nua_respond(nh, SIP_200_OK, TAG_END());
fail_unless(s2_check_callstate(nua_callstate_completed));
response = s2_wait_for_response(200, SIP_METHOD_INVITE);
fail_if(!response);
nua_handle_destroy(nh);
bye = s2_wait_for_request(SIP_METHOD_BYE);
fail_if(!bye);
s2_respond_to(bye, dialog, SIP_200_OK, TAG_END());
s2_free_message(bye);
fail_if(s2_request_to(dialog, SIP_METHOD_ACK, NULL,
SIPTAG_VIA(sip_object(dialog->invite)->sip_via),
TAG_END()));
s2_free_message(response);
}
END_TEST
TCase *destroy_tcase(void)
{
TCase *tc = tcase_create("4.3 - Destroying Handle");
tcase_add_checked_fixture(tc, call_setup, call_teardown);
{
tcase_add_test(tc, destroy_4_3_1);
tcase_add_test(tc, destroy_4_3_2);
tcase_add_test(tc, destroy_4_3_3);
tcase_add_test(tc, destroy_4_3_4);
tcase_add_test(tc, destroy_4_3_5);
tcase_add_test(tc, destroy_4_3_6);
tcase_add_test(tc, destroy_4_3_7);
if (XXX) {
tcase_add_test(tc, destroy_4_3_8);
tcase_add_test(tc, destroy_4_3_9);
}
tcase_add_test(tc, destroy_4_4_1);
tcase_add_test(tc, destroy_4_4_2);
tcase_add_test(tc, destroy_4_4_3_1);
tcase_add_test(tc, destroy_4_4_3_2);
tcase_set_timeout(tc, 5);
}
return tc;
}
/* ====================================================================== */
static void options_setup(void), options_teardown(void);
START_TEST(options_5_1_1)
{
struct event *options;
nua_handle_t *nh;
struct message *response;
s2_case("5.1.1", "Test nua_respond() API",
"Test nua_respond() API with OPTIONS.");
s2_request_to(dialog, SIP_METHOD_OPTIONS, NULL, TAG_END());
options = s2_wait_for_event(nua_i_options, 200);
fail_unless(options != NULL);
nh = options->nh; fail_if(!nh);
response = s2_wait_for_response(200, SIP_METHOD_OPTIONS);
fail_if(!response);
s2_free_message(response);
nua_handle_destroy(nh);
nua_set_params(nua, NUTAG_APPL_METHOD("OPTIONS"), TAG_END());
fail_unless(s2_check_event(nua_r_set_params, 200));
s2_request_to(dialog, SIP_METHOD_OPTIONS, NULL, TAG_END());
options = s2_wait_for_event(nua_i_options, 100);
fail_unless(options != NULL);
nh = options->nh; fail_if(!nh);
nua_respond(nh, 202, "okok", NUTAG_WITH_SAVED(options->event), TAG_END());
response = s2_wait_for_response(202, SIP_METHOD_OPTIONS);
fail_if(!response);
s2_free_message(response);
nua_handle_destroy(nh);
}
END_TEST
#if HAVE_LIBPTHREAD
#include <pthread.h>
void *respond_to_options(void *arg)
{
struct event *options = (struct event *)arg;
nua_respond(options->nh, 202, "ok ok",
NUTAG_WITH_SAVED(options->event),
TAG_END());
pthread_exit(arg);
return NULL;
}
START_TEST(options_5_1_2)
{
struct event *options;
nua_handle_t *nh;
struct message *response;
pthread_t tid;
void *thread_return = NULL;
s2_case("5.1.2", "Test nua_respond() API with another thread",
"Test multithreading nua_respond() API with OPTIONS.");
nua_set_params(nua, NUTAG_APPL_METHOD("OPTIONS"), TAG_END());
fail_unless(s2_check_event(nua_r_set_params, 200));
s2_request_to(dialog, SIP_METHOD_OPTIONS, NULL, TAG_END());
options = s2_wait_for_event(nua_i_options, 100);
fail_unless(options != NULL);
nh = options->nh; fail_if(!nh);
fail_if(pthread_create(&tid, NULL, respond_to_options, (void *)options));
pthread_join(tid, &thread_return);
fail_unless(thread_return == (void *)options);
response = s2_wait_for_response(202, SIP_METHOD_OPTIONS);
fail_if(!response);
s2_free_message(response);
nua_handle_destroy(nh);
}
END_TEST
#else
START_TEST(options_5_1_2)
{
}
END_TEST
#endif
TCase *options_tcase(void)
{
TCase *tc = tcase_create("5 - OPTIONS, etc");
tcase_add_checked_fixture(tc, options_setup, options_teardown);
tcase_add_test(tc, options_5_1_1);
tcase_add_test(tc, options_5_1_2);
return tc;
}
static void options_setup(void)
{
s2_nua_thread = 1;
call_setup();
}
static void options_teardown(void)
{
call_teardown();
}
/* ====================================================================== */
/* Test case template */
START_TEST(empty)
{
s2_case("0.0.0", "Empty test case",
"Detailed explanation for empty test case.");
tport_set_params(s2->master, TPTAG_LOG(1), TAG_END());
s2_setup_logs(7);
s2_setup_logs(0);
tport_set_params(s2->master, TPTAG_LOG(0), TAG_END());
}
END_TEST
TCase *empty_tcase(void)
{
TCase *tc = tcase_create("0 - Empty");
tcase_add_checked_fixture(tc, call_setup, call_teardown);
tcase_add_test(tc, empty);
return tc;
}
/* ====================================================================== */
void check_session_cases(Suite *suite)
{
suite_add_tcase(suite, invite_tcase());
suite_add_tcase(suite, cancel_tcase());
suite_add_tcase(suite, session_timer_tcase());
suite_add_tcase(suite, invite_100rel_tcase());
suite_add_tcase(suite, invite_precondition_tcase());
suite_add_tcase(suite, invite_glare_tcase());
suite_add_tcase(suite, invite_error_tcase());
suite_add_tcase(suite, termination_tcase());
suite_add_tcase(suite, destroy_tcase());
suite_add_tcase(suite, options_tcase());
if (0) /* Template */
suite_add_tcase(suite, empty_tcase());
}