freeswitch/libs/sofia-sip/libsofia-sip-ua/nua/check_session.c

2985 lines
75 KiB
C
Raw Normal View History

/*
* 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"),
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
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);
}
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());
}