[Freeswitch-svn] [commit] r8406 - in freeswitch/trunk/libs/sofia-sip: libsofia-sip-ua/nua tests
Freeswitch SVN
mikej at freeswitch.org
Wed May 14 15:10:54 EDT 2008
Author: mikej
Date: Wed May 14 15:10:54 2008
New Revision: 8406
Added:
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/check_nua.c
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/check_nua.h
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/check_register.c
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/check_session.c
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_s2.c
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_s2.h
freeswitch/trunk/libs/sofia-sip/tests/test_100rel.c
freeswitch/trunk/libs/sofia-sip/tests/test_basic_call.c
freeswitch/trunk/libs/sofia-sip/tests/test_call_hold.c
freeswitch/trunk/libs/sofia-sip/tests/test_call_reject.c
freeswitch/trunk/libs/sofia-sip/tests/test_cancel_bye.c
freeswitch/trunk/libs/sofia-sip/tests/test_extension.c
freeswitch/trunk/libs/sofia-sip/tests/test_init.c
freeswitch/trunk/libs/sofia-sip/tests/test_nat.c
freeswitch/trunk/libs/sofia-sip/tests/test_nat.h
freeswitch/trunk/libs/sofia-sip/tests/test_nat_tags.c
freeswitch/trunk/libs/sofia-sip/tests/test_nua.c
freeswitch/trunk/libs/sofia-sip/tests/test_nua.h
freeswitch/trunk/libs/sofia-sip/tests/test_nua_api.c
freeswitch/trunk/libs/sofia-sip/tests/test_nua_params.c
freeswitch/trunk/libs/sofia-sip/tests/test_offer_answer.c
freeswitch/trunk/libs/sofia-sip/tests/test_ops.c
freeswitch/trunk/libs/sofia-sip/tests/test_proxy.c
freeswitch/trunk/libs/sofia-sip/tests/test_proxy.h
freeswitch/trunk/libs/sofia-sip/tests/test_refer.c
freeswitch/trunk/libs/sofia-sip/tests/test_register.c
freeswitch/trunk/libs/sofia-sip/tests/test_session_timer.c
freeswitch/trunk/libs/sofia-sip/tests/test_simple.c
freeswitch/trunk/libs/sofia-sip/tests/test_sip_events.c
Removed:
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_100rel.c
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_basic_call.c
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_call_hold.c
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_call_reject.c
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_cancel_bye.c
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_extension.c
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_init.c
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_nat.c
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_nat.h
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_nat_tags.c
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_nua.c
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_nua.h
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_nua_api.c
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_nua_params.c
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_offer_answer.c
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_ops.c
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_proxy.c
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_proxy.h
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_refer.c
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_register.c
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_session_timer.c
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_simple.c
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_sip_events.c
Log:
complete r8400 commit
Added: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/check_nua.c
==============================================================================
--- (empty file)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/check_nua.c Wed May 14 15:10:54 2008
@@ -0,0 +1,120 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at 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 check2_sofia.c
+ *
+ * @brief Check-driven tester for Sofia SIP User Agent library
+ *
+ * @author Pekka Pessi <Pekka.Pessi at nokia.com>
+ *
+ * @copyright (C) 2007 Nokia Corporation.
+ */
+
+#include "config.h"
+
+#include "check_nua.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#if HAVE_FNMATCH_H
+#include <fnmatch.h>
+#endif
+
+static char const * const default_patterns[] = { "*", NULL };
+static char const * const *test_patterns = default_patterns;
+
+void check_nua_tcase_add_test(TCase *tc, TFun tf, char const *name)
+{
+ char const * const *patterns;
+
+#if HAVE_FNMATCH_H
+ for (patterns = test_patterns; *patterns; patterns++) {
+ if (!fnmatch(*patterns, name, 0)) {
+ if (strcmp(*patterns, "*")) {
+ printf("%s: match with %s\n", name, *patterns);
+ }
+ _tcase_add_test(tc, tf, name, 0, 0, 1);
+ return;
+ }
+ }
+#else
+ for (patterns = test_patterns; *patterns; patterns++) {
+ if (!strcmp(*patterns, name) || !strcmp(*patterns, "*")) {
+ if (strcmp(*patterns, "*")) {
+ printf("%s: match with %s\n", name, *patterns);
+ }
+ _tcase_add_test(tc, tf, name, 0, 0, 1);
+ return;
+ }
+ }
+#endif
+ printf("%s: no match\n", name);
+}
+
+int main(int argc, char *argv[])
+{
+ int failed = 0;
+
+ Suite *suite = suite_create("Unit tests for Sofia-SIP UA Engine");
+ SRunner *runner;
+
+ if (getenv("CHECK_NUA_CASES")) {
+ size_t i;
+ char *s, **patterns;
+ char *cases = strdup(getenv("CHECK_NUA_CASES"));
+
+ /* Count commas */
+ for (i = 2, s = cases; (s = strchr(s, ',')); s++, i++);
+
+ patterns = calloc(i, sizeof *patterns);
+
+ for (i = 0, s = cases;; i++) {
+ patterns[i] = s;
+ if (s == NULL)
+ break;
+ s = strchr(s, ',');
+ if (s)
+ *s++ = '\0';
+ }
+
+ test_patterns = (char const * const *)patterns;
+ }
+
+ check_register_cases(suite);
+ check_session_cases(suite);
+
+ runner = srunner_create(suite);
+
+ if (argv[1]) {
+ srunner_set_xml(runner, argv[1]);
+ }
+ srunner_run_all(runner, CK_ENV);
+
+ failed = srunner_ntests_failed(runner);
+ srunner_free(runner);
+
+ exit(failed ? EXIT_FAILURE : EXIT_SUCCESS);
+}
Added: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/check_nua.h
==============================================================================
--- (empty file)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/check_nua.h Wed May 14 15:10:54 2008
@@ -0,0 +1,15 @@
+#ifndef CHECK_NUA_H
+
+#include <check.h>
+
+#undef tcase_add_test
+#define tcase_add_test(tc, tf) \
+ check_nua_tcase_add_test(tc, tf, "" #tf "")
+
+void check_nua_tcase_add_test(TCase *, TFun, char const *name);
+
+void check_session_cases(Suite *suite);
+void check_register_cases(Suite *suite);
+
+#endif
+
Added: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/check_register.c
==============================================================================
--- (empty file)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/check_register.c Wed May 14 15:10:54 2008
@@ -0,0 +1,741 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2008 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at 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_register.c
+ *
+ * @brief Check-driven tester for Sofia SIP User Agent library
+ *
+ * @author Pekka Pessi <Pekka.Pessi at 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 <sofia-sip/su_log.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+SOFIAPUBVAR su_log_t tport_log[];
+
+static nua_t *nua;
+
+static void register_setup(void)
+{
+ nua = s2_nua_setup(TAG_END());
+}
+
+static void register_pingpong_setup(void)
+{
+ nua = s2_nua_setup(TPTAG_PINGPONG(20000),
+ TPTAG_KEEPALIVE(10000),
+ TAG_END());
+ tport_set_params(s2->tcp.tport, TPTAG_PONG2PING(1), TAG_END());
+}
+
+
+static void register_teardown(void)
+{
+ nua_shutdown(nua);
+ fail_unless(s2_check_event(nua_r_shutdown, 200));
+ s2_nua_teardown();
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+START_TEST(register_1_0_1)
+{
+ nua_handle_t *nh = nua_handle(nua, NULL, TAG_END());
+ struct message *m;
+
+ s2_case("1.0.1", "Failed Register", "REGISTER returned 403 response");
+
+ nua_register(nh, TAG_END());
+
+ fail_unless((m = s2_wait_for_request(SIP_METHOD_REGISTER)) != NULL, NULL);
+
+ s2_respond_to(m, NULL,
+ SIP_403_FORBIDDEN,
+ TAG_END());
+ s2_free_message(m);
+
+ nua_handle_destroy(nh);
+
+} END_TEST
+
+
+START_TEST(register_1_1_1)
+{
+ s2_case("1.1.1", "Basic Register", "REGISTER returning 200 OK");
+
+ s2_register_setup();
+
+ s2_register_teardown();
+
+} END_TEST
+
+
+START_TEST(register_1_1_2)
+{
+ nua_handle_t *nh;
+ struct message *m;
+
+ s2_case("1.1.2", "Register with dual authentication",
+ "Register, authenticate");
+
+ nh = nua_handle(nua, NULL, TAG_END());
+
+ nua_register(nh, TAG_END());
+
+ m = s2_wait_for_request(SIP_METHOD_REGISTER); fail_if(!m);
+ s2_respond_to(m, NULL,
+ SIP_407_PROXY_AUTH_REQUIRED,
+ SIPTAG_PROXY_AUTHENTICATE_STR(s2_auth_digest_str),
+ TAG_END());
+ s2_free_message(m);
+ s2_check_event(nua_r_register, 407);
+
+ nua_authenticate(nh, NUTAG_AUTH(s2_auth_credentials), TAG_END());
+
+ m = s2_wait_for_request(SIP_METHOD_REGISTER); fail_if(!m);
+ s2_respond_to(m, NULL,
+ SIP_401_UNAUTHORIZED,
+ SIPTAG_WWW_AUTHENTICATE_STR(s2_auth2_digest_str),
+ SIPTAG_PROXY_AUTHENTICATE_STR(s2_auth_digest_str),
+ TAG_END());
+ s2_free_message(m);
+ s2_check_event(nua_r_register, 401);
+
+ nua_authenticate(nh, NUTAG_AUTH(s2_auth2_credentials), TAG_END());
+
+ m = s2_wait_for_request(SIP_METHOD_REGISTER);
+ fail_if(!m);
+ fail_if(!m->sip->sip_authorization);
+ fail_if(!m->sip->sip_proxy_authorization);
+ s2_save_register(m);
+
+ s2_respond_to(m, NULL,
+ SIP_200_OK,
+ SIPTAG_CONTACT(s2->registration->contact),
+ TAG_END());
+ s2_free_message(m);
+
+ assert(s2->registration->contact != NULL);
+ s2_check_event(nua_r_register, 200);
+
+ s2->registration->nh = nh;
+
+ s2_register_teardown();
+
+} END_TEST
+
+/* ---------------------------------------------------------------------- */
+
+static char const *receive_natted = "received=4.255.255.9";
+
+/* Return Via that looks very natted */
+static sip_via_t *natted_via(struct message *m)
+{
+ su_home_t *h;
+ sip_via_t *via;
+
+ h = msg_home(m->msg);
+ via = sip_via_dup(h, m->sip->sip_via);
+ msg_header_replace_param(h, via->v_common, receive_natted);
+
+ if (via->v_protocol == sip_transport_udp)
+ msg_header_replace_param(h, via->v_common, "rport=9");
+
+ if (via->v_protocol == sip_transport_tcp && via->v_rport) {
+ tp_name_t const *tpn = tport_name(m->tport);
+ char *rport = su_sprintf(h, "rport=%s", tpn->tpn_port);
+ msg_header_replace_param(h, via->v_common, rport);
+ }
+
+ return via;
+}
+
+/* ---------------------------------------------------------------------- */
+
+START_TEST(register_1_2_1) {
+ nua_handle_t *nh;
+ struct message *m;
+
+ s2_case("1.2.1", "Register behind NAT",
+ "Register through NAT, detect NAT, re-REGISTER");
+
+ nh = nua_handle(nua, NULL, TAG_END());
+
+ nua_register(nh, TAG_END());
+
+ m = s2_wait_for_request(SIP_METHOD_REGISTER);
+ fail_if(!m);
+ fail_if(!m->sip->sip_contact || m->sip->sip_contact->m_next);
+ s2_save_register(m);
+
+ s2_respond_to(m, NULL,
+ SIP_200_OK,
+ SIPTAG_CONTACT(s2->registration->contact),
+ SIPTAG_VIA(natted_via(m)),
+ TAG_END());
+ s2_free_message(m);
+
+ assert(s2->registration->contact != NULL);
+ s2_check_event(nua_r_register, 100);
+
+ m = s2_wait_for_request(SIP_METHOD_REGISTER);
+ fail_if(!m);
+ fail_if(!m->sip->sip_contact || !m->sip->sip_contact->m_next);
+ s2_save_register(m);
+
+ s2_respond_to(m, NULL,
+ SIP_200_OK,
+ SIPTAG_CONTACT(s2->registration->contact),
+ SIPTAG_VIA(natted_via(m)),
+ TAG_END());
+ s2_free_message(m);
+
+ fail_unless(s2->registration->contact != NULL);
+ fail_if(s2->registration->contact->m_next != NULL);
+ s2_check_event(nua_r_register, 200);
+
+ s2->registration->nh = nh;
+
+ s2_register_teardown();
+
+} END_TEST
+
+
+static nua_handle_t *make_auth_natted_register(
+ nua_handle_t *nh,
+ tag_type_t tag, tag_value_t value, ...)
+{
+ struct message *m;
+
+ ta_list ta;
+ ta_start(ta, tag, value);
+ nua_register(nh, ta_tags(ta));
+ ta_end(ta);
+
+ m = s2_wait_for_request(SIP_METHOD_REGISTER); fail_if(!m);
+ s2_respond_to(m, NULL,
+ SIP_401_UNAUTHORIZED,
+ SIPTAG_WWW_AUTHENTICATE_STR(s2_auth_digest_str),
+ SIPTAG_VIA(natted_via(m)),
+ TAG_END());
+ s2_free_message(m);
+
+ s2_check_event(nua_r_register, 401);
+
+ nua_authenticate(nh, NUTAG_AUTH(s2_auth_credentials), TAG_END());
+
+ m = s2_wait_for_request(SIP_METHOD_REGISTER);
+ fail_if(!m);
+ fail_if(!m->sip->sip_authorization);
+ s2_save_register(m);
+
+ s2_respond_to(m, NULL,
+ SIP_200_OK,
+ SIPTAG_CONTACT(s2->registration->contact),
+ SIPTAG_VIA(natted_via(m)),
+ TAG_END());
+ s2_free_message(m);
+
+ assert(s2->registration->contact != NULL);
+ s2_check_event(nua_r_register, 200);
+
+ return nh;
+}
+
+START_TEST(register_1_2_2_1)
+{
+ nua_handle_t *nh = nua_handle(nua, NULL, TAG_END());
+
+ s2_case("1.2.2.1", "Register behind NAT",
+ "Authenticate, outbound activated");
+
+ mark_point();
+ make_auth_natted_register(nh, TAG_END());
+ s2->registration->nh = nh;
+ s2_register_teardown();
+}
+END_TEST
+
+START_TEST(register_1_2_2_2)
+{
+ nua_handle_t *nh = nua_handle(nua, NULL, TAG_END());
+ struct message *m;
+
+ s2_case("1.2.2.2", "Register behind NAT",
+ "Authenticate, outbound activated, "
+ "authenticate OPTIONS probe, "
+ "NAT binding change");
+
+ mark_point();
+ make_auth_natted_register(nh, TAG_END());
+ s2->registration->nh = nh;
+
+ mark_point();
+
+ m = s2_wait_for_request(SIP_METHOD_OPTIONS);
+ fail_if(!m);
+ s2_respond_to(m, NULL,
+ SIP_407_PROXY_AUTH_REQUIRED,
+ SIPTAG_VIA(natted_via(m)),
+ SIPTAG_PROXY_AUTHENTICATE_STR(s2_auth_digest_str),
+ TAG_END());
+ s2_free_message(m);
+ mark_point();
+
+ m = s2_wait_for_request(SIP_METHOD_OPTIONS);
+ fail_if(!m); fail_if(!m->sip->sip_proxy_authorization);
+ s2_respond_to(m, NULL,
+ SIP_200_OK,
+ SIPTAG_VIA(natted_via(m)),
+ TAG_END());
+ s2_free_message(m);
+
+ su_root_step(s2->root, 20); su_root_step(s2->root, 20);
+ s2_fast_forward(120); /* Default keepalive interval */
+ mark_point();
+
+ m = s2_wait_for_request(SIP_METHOD_OPTIONS);
+ s2_respond_to(m, NULL,
+ SIP_200_OK,
+ SIPTAG_VIA(natted_via(m)),
+ TAG_END());
+ s2_free_message(m);
+
+ su_root_step(s2->root, 20); su_root_step(s2->root, 20);
+ s2_fast_forward(120); /* Default keepalive interval */
+ mark_point();
+
+ receive_natted = "received=4.255.255.10";
+
+ m = s2_wait_for_request(SIP_METHOD_OPTIONS);
+ s2_respond_to(m, NULL,
+ SIP_200_OK,
+ SIPTAG_VIA(natted_via(m)),
+ TAG_END());
+ s2_free_message(m);
+
+ s2_check_event(nua_i_outbound, 0);
+
+ m = s2_wait_for_request(SIP_METHOD_REGISTER);
+ fail_if(!m); fail_if(!m->sip->sip_authorization);
+ fail_if(!m->sip->sip_contact || !m->sip->sip_contact->m_next);
+ s2_save_register(m);
+
+ s2_respond_to(m, NULL,
+ SIP_200_OK,
+ SIPTAG_CONTACT(s2->registration->contact),
+ SIPTAG_VIA(natted_via(m)),
+ TAG_END());
+ s2_free_message(m);
+
+ s2_check_event(nua_r_register, 200);
+
+ fail_unless(s2->registration->contact != NULL);
+ fail_if(s2->registration->contact->m_next != NULL);
+
+ s2_register_teardown();
+
+} END_TEST
+
+
+START_TEST(register_1_2_2_3)
+{
+ nua_handle_t *nh = nua_handle(nua, NULL, TAG_END());
+ struct message *m;
+
+ s2_case("1.2.2.3", "Register behind NAT",
+ "Authenticate, outbound activated, "
+ "detect NAT binding change when re-REGISTERing");
+
+ mark_point();
+
+ make_auth_natted_register(nh,
+ NUTAG_OUTBOUND("no-options-keepalive"),
+ TAG_END());
+ s2->registration->nh = nh;
+
+ receive_natted = "received=4.255.255.10";
+
+ s2_fast_forward(3600);
+ mark_point();
+
+ m = s2_wait_for_request(SIP_METHOD_REGISTER);
+ fail_if(!m); fail_if(!m->sip->sip_authorization);
+ s2_save_register(m);
+
+ s2_respond_to(m, NULL,
+ SIP_200_OK,
+ SIPTAG_CONTACT(s2->registration->contact),
+ SIPTAG_VIA(natted_via(m)),
+ TAG_END());
+ s2_free_message(m);
+
+ s2_check_event(nua_r_register, 100);
+
+ m = s2_wait_for_request(SIP_METHOD_REGISTER);
+ fail_if(!m); fail_if(!m->sip->sip_authorization);
+ fail_if(!m->sip->sip_contact || !m->sip->sip_contact->m_next);
+ s2_save_register(m);
+
+ s2_respond_to(m, NULL,
+ SIP_200_OK,
+ SIPTAG_CONTACT(s2->registration->contact),
+ SIPTAG_VIA(natted_via(m)),
+ TAG_END());
+ s2_free_message(m);
+
+ fail_unless(s2->registration->contact != NULL);
+ fail_if(s2->registration->contact->m_next != NULL);
+
+ s2_check_event(nua_r_register, 200);
+
+ s2_register_teardown();
+
+} END_TEST
+
+
+START_TEST(register_1_2_3) {
+ nua_handle_t *nh;
+ struct message *m;
+
+ s2_case("1.2.3", "Register behind NAT",
+ "Outbound activated by error response");
+
+ nh = nua_handle(nua, NULL, TAG_END());
+ nua_register(nh, TAG_END());
+
+ mark_point();
+
+ m = s2_wait_for_request(SIP_METHOD_REGISTER);
+ fail_if(!m);
+ fail_if(!m->sip->sip_contact || m->sip->sip_contact->m_next);
+
+ s2_respond_to(m, NULL,
+ 400, "Bad Contact",
+ SIPTAG_VIA(natted_via(m)),
+ TAG_END());
+ s2_free_message(m);
+
+ s2_check_event(nua_r_register, 100);
+
+ m = s2_wait_for_request(SIP_METHOD_REGISTER);
+ fail_if(!m);
+ s2_save_register(m);
+
+ s2_respond_to(m, NULL,
+ SIP_200_OK,
+ SIPTAG_CONTACT(s2->registration->contact),
+ SIPTAG_VIA(natted_via(m)),
+ TAG_END());
+ s2_free_message(m);
+
+ fail_unless(s2->registration->contact != NULL);
+ fail_if(s2->registration->contact->m_next != NULL);
+ s2_check_event(nua_r_register, 200);
+
+ s2->registration->nh = nh;
+
+ s2_register_teardown();
+
+} END_TEST
+
+
+/* ---------------------------------------------------------------------- */
+
+START_TEST(register_1_3_1)
+{
+ nua_handle_t *nh;
+ struct message *m;
+
+ s2_case("1.3.1", "Register over TCP via NAT",
+ "REGISTER via TCP, detect NTA, re-REGISTER");
+
+ nh = nua_handle(nua, NULL, TAG_END());
+
+ nua_register(nh, NUTAG_PROXY(s2->tcp.contact->m_url), TAG_END());
+
+ m = s2_wait_for_request(SIP_METHOD_REGISTER);
+ fail_if(!m); fail_if(!m->sip->sip_contact || m->sip->sip_contact->m_next);
+ fail_if(!tport_is_tcp(m->tport));
+ s2_save_register(m);
+
+ s2_respond_to(m, NULL,
+ SIP_200_OK,
+ SIPTAG_CONTACT(s2->registration->contact),
+ SIPTAG_VIA(natted_via(m)),
+ TAG_END());
+ s2_free_message(m);
+
+ assert(s2->registration->contact != NULL);
+ s2_check_event(nua_r_register, 100);
+
+ m = s2_wait_for_request(SIP_METHOD_REGISTER);
+ fail_if(!m);
+ fail_if(!m->sip->sip_contact || !m->sip->sip_contact->m_next);
+ s2_save_register(m);
+
+ s2_respond_to(m, NULL,
+ SIP_200_OK,
+ SIPTAG_CONTACT(s2->registration->contact),
+ SIPTAG_VIA(natted_via(m)),
+ TAG_END());
+ s2_free_message(m);
+
+ fail_unless(s2->registration->contact != NULL);
+ fail_if(s2->registration->contact->m_next != NULL);
+ fail_unless(
+ url_has_param(s2->registration->contact->m_url, "transport=tcp"));
+ s2_check_event(nua_r_register, 200);
+
+ s2->registration->nh = nh;
+
+ s2_register_teardown();
+
+} END_TEST
+
+
+START_TEST(register_1_3_2_1)
+{
+ nua_handle_t *nh = nua_handle(nua, NULL, TAG_END());
+
+ s2_case("1.3.2.1", "Register behind NAT",
+ "Authenticate, outbound activated");
+
+ mark_point();
+ s2->registration->nh = nh;
+ make_auth_natted_register(nh, NUTAG_PROXY(s2->tcp.contact->m_url), TAG_END());
+ fail_if(!tport_is_tcp(s2->registration->tport));
+ s2_register_teardown();
+}
+END_TEST
+
+
+START_TEST(register_1_3_2_2)
+{
+ nua_handle_t *nh = nua_handle(nua, NULL, TAG_END());
+ struct message *m;
+
+ s2_case("1.3.2.2", "Register behind NAT with TCP",
+ "Detect NAT over TCP using rport. "
+ "Authenticate, detect NAT, "
+ "close TCP at server, wait for re-REGISTERs.");
+
+ nua_set_params(nua, NTATAG_TCP_RPORT(1), TAG_END());
+ s2_check_event(nua_r_set_params, 200);
+
+ mark_point();
+ s2->registration->nh = nh;
+ make_auth_natted_register(
+ nh, NUTAG_PROXY(s2->tcp.contact->m_url),
+ NUTAG_OUTBOUND("no-options-keepalive, no-validate"),
+ TAG_END());
+ fail_if(!tport_is_tcp(s2->registration->tport));
+ tport_shutdown(s2->registration->tport, 2);
+
+ m = s2_wait_for_request(SIP_METHOD_REGISTER);
+ fail_if(!m); fail_if(!m->sip->sip_authorization);
+ s2_save_register(m);
+
+ s2_respond_to(m, NULL,
+ SIP_200_OK,
+ SIPTAG_CONTACT(s2->registration->contact),
+ SIPTAG_VIA(natted_via(m)),
+ TAG_END());
+ s2_free_message(m);
+
+ /* The "NAT binding" changed when new TCP connection is established */
+ /* => NUA re-REGISTERs with newly detected contact */
+ s2_check_event(nua_r_register, 100);
+
+ m = s2_wait_for_request(SIP_METHOD_REGISTER);
+ fail_if(!m); fail_if(!m->sip->sip_authorization);
+ fail_if(!m->sip->sip_contact || !m->sip->sip_contact->m_next);
+ s2_save_register(m);
+
+ s2_respond_to(m, NULL,
+ SIP_200_OK,
+ SIPTAG_CONTACT(s2->registration->contact),
+ SIPTAG_VIA(natted_via(m)),
+ TAG_END());
+ s2_free_message(m);
+
+ s2_check_event(nua_r_register, 200);
+
+ fail_unless(s2->registration->contact != NULL);
+ fail_if(s2->registration->contact->m_next != NULL);
+
+ s2_register_teardown();
+}
+END_TEST
+
+START_TEST(register_1_3_3_1)
+{
+ nua_handle_t *nh = nua_handle(nua, NULL, TAG_END());
+ struct message *m;
+ tport_t *tcp;
+
+ s2_case("1.3.3.1", "Register behind NAT with UDP and TCP",
+ "Register with UDP, UDP time-outing, then w/ TCP using rport. ");
+
+ nua_set_params(nua, NTATAG_TCP_RPORT(1), TAG_END());
+ s2_check_event(nua_r_set_params, 200);
+
+ mark_point();
+ s2->registration->nh = nh;
+
+ nua_register(nh,
+ NUTAG_OUTBOUND("no-options-keepalive, no-validate"),
+ TAG_END());
+
+ /* NTA tries with UDP, we drop them */
+ for (;;) {
+ m = s2_wait_for_request(SIP_METHOD_REGISTER); fail_if(!m);
+ if (!tport_is_udp(m->tport)) /* Drop UDP */
+ break;
+ s2_free_message(m);
+ s2_fast_forward(4);
+ }
+
+ tcp = tport_ref(m->tport);
+
+ /* Respond to request over TCP */
+ s2_respond_to(m, NULL,
+ SIP_401_UNAUTHORIZED,
+ SIPTAG_WWW_AUTHENTICATE_STR(s2_auth_digest_str),
+ SIPTAG_VIA(natted_via(m)),
+ TAG_END());
+ s2_free_message(m);
+ s2_check_event(nua_r_register, 401);
+ nua_authenticate(nh, NUTAG_AUTH(s2_auth_credentials), TAG_END());
+
+ /* Turn off pong */
+ tport_set_params(tcp, TPTAG_PONG2PING(0), TAG_END());
+
+ /* Now request over UDP ... registering TCP contact! */
+ m = s2_wait_for_request(SIP_METHOD_REGISTER);
+ fail_if(!m); fail_if(!m->sip->sip_authorization);
+ s2_save_register(m);
+ fail_unless(
+ url_has_param(s2->registration->contact->m_url, "transport=tcp"));
+ s2_respond_to(m, NULL,
+ SIP_200_OK,
+ SIPTAG_CONTACT(s2->registration->contact),
+ SIPTAG_VIA(natted_via(m)),
+ TAG_END());
+ s2_free_message(m);
+
+ /* NUA detects oops... re-registers UDP */
+ s2_check_event(nua_r_register, 100);
+
+ m = s2_wait_for_request(SIP_METHOD_REGISTER);
+ fail_if(!m); fail_if(!m->sip->sip_authorization);
+ fail_if(!m->sip->sip_contact || !m->sip->sip_contact->m_next);
+ s2_save_register(m);
+
+ s2_respond_to(m, NULL,
+ SIP_200_OK,
+ SIPTAG_CONTACT(s2->registration->contact),
+ SIPTAG_VIA(natted_via(m)),
+ TAG_END());
+ s2_free_message(m);
+
+ s2_check_event(nua_r_register, 200);
+
+ fail_unless(s2->registration->contact != NULL);
+ fail_if(s2->registration->contact->m_next != NULL);
+
+ /* Wait until ping-pong failure closes the TCP connection */
+ {
+ int i;
+ for (i = 0; i < 5; i++) {
+ su_root_step(s2->root, 5);
+ su_root_step(s2->root, 5);
+ su_root_step(s2->root, 5);
+ s2_fast_forward(5);
+ }
+ }
+
+ s2_register_teardown();
+}
+END_TEST
+
+/* ---------------------------------------------------------------------- */
+
+TCase *register_tcase(void)
+{
+ TCase *tc = tcase_create("1 - REGISTER");
+ /* Each testcase is run in different process */
+ tcase_add_checked_fixture(tc, register_setup, register_teardown);
+ {
+ tcase_add_test(tc, register_1_0_1);
+ tcase_add_test(tc, register_1_1_1);
+ tcase_add_test(tc, register_1_1_2);
+ tcase_add_test(tc, register_1_2_1);
+ tcase_add_test(tc, register_1_2_2_1);
+ tcase_add_test(tc, register_1_2_2_2);
+ tcase_add_test(tc, register_1_2_2_3);
+ tcase_add_test(tc, register_1_2_3);
+ tcase_add_test(tc, register_1_3_1);
+ tcase_add_test(tc, register_1_3_2_1);
+ tcase_add_test(tc, register_1_3_2_2);
+ }
+ tcase_set_timeout(tc, 5);
+ return tc;
+}
+
+TCase *pingpong_tcase(void)
+{
+ TCase *tc = tcase_create("1 - REGISTER with PingPong");
+ /* Each testcase is run in different process */
+ tcase_add_checked_fixture(tc, register_pingpong_setup, register_teardown);
+ {
+ tcase_add_test(tc, register_1_3_3_1);
+ }
+ tcase_set_timeout(tc, 5);
+ return tc;
+}
+
+void check_register_cases(Suite *suite)
+{
+ suite_add_tcase(suite, register_tcase());
+ suite_add_tcase(suite, pingpong_tcase());
+}
+
Added: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/check_session.c
==============================================================================
--- (empty file)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/check_session.c Wed May 14 15:10:54 2008
@@ -0,0 +1,1564 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2008 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at 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 at 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>
+
+/* ====================================================================== */
+/* 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(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 struct message *
+respond_with_100rel(struct message *invite,
+ struct dialog *d,
+ int sdp,
+ int status, char const *phrase,
+ tag_type_t tag, tag_value_t value, ...)
+{
+ struct message *prack;
+ ta_list ta;
+ static uint32_t rseq;
+ sip_rseq_t rs[1];
+
+ assert(100 < status && status < 200);
+
+ sip_rseq_init(rs);
+ rs->rs_response = ++rseq;
+
+ ta_start(ta, tag, value);
+
+ if (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));
+
+ prack = s2_wait_for_request(SIP_METHOD_PRACK);
+ /* Assumes auto-prack, so there is no offer in prack */
+ s2_respond_to(prack, dialog, SIP_200_OK, TAG_END());
+
+ return 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, SOATAG_USER_SDP_STR("m=audio 5004 RTP/AVP 0 8"),
+ 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;
+ sip_cseq_t cseq[1];
+
+ 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);
+
+ sip_cseq_init(cseq);
+ cseq->cs_method = sip_method_ack;
+ cseq->cs_method_name = "ACK";
+ cseq->cs_seq = sip_object(invite->data->e_msg)->sip_cseq->cs_seq;
+
+ 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,
+ SIPTAG_CSEQ(cseq), 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(basic_call_with_bye_by_nua)
+{
+ 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(basic_call_with_bye_to_nua)
+{
+ nua_handle_t *nh;
+
+ s2_case("2.1.2", "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_to_nua_with_bye_to_nua)
+{
+ nua_handle_t *nh;
+
+ s2_case("2.1.3", "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_to_nua_with_bye_by_nua)
+{
+ 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_to_nua_with_bye_by_nua_challenged)
+{
+ 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;
+ sip_cseq_t cseq[1];
+
+ 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);
+
+ sip_cseq_init(cseq);
+ cseq->cs_method = sip_method_ack;
+ cseq->cs_method_name = "ACK";
+ cseq->cs_seq = sip_object(invite->data->e_msg)->sip_cseq->cs_seq;
+
+ 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,
+ SIPTAG_CSEQ(cseq), 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
+
+
+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, basic_call_with_bye_by_nua);
+ tcase_add_test(tc, basic_call_with_bye_to_nua);
+ tcase_add_test(tc, call_to_nua_with_bye_to_nua);
+ tcase_add_test(tc, call_to_nua_with_bye_by_nua);
+ tcase_add_test(tc, call_to_nua_with_bye_by_nua_challenged);
+ tcase_add_test(tc, call_2_1_6);
+ }
+ return tc;
+}
+
+/* ---------------------------------------------------------------------- */
+/* 2.2 - Call CANCEL cases */
+
+START_TEST(cancel_outgoing)
+{
+ nua_handle_t *nh;
+ struct message *invite, *cancel;
+
+ s2_case("2.2.1", "Cancel call",
+ "NUA is callee, 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_outgoing_after_100)
+{
+ nua_handle_t *nh;
+ struct message *invite;
+
+ s2_case("2.2.2", "Canceled call",
+ "NUA is callee, 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_outgoing_after_180)
+{
+ nua_handle_t *nh;
+ struct message *invite;
+
+ s2_case("2.2.3", "Canceled call",
+ "NUA is callee, 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_outgoing_glare)
+{
+ nua_handle_t *nh;
+ struct message *invite, *cancel;
+
+ s2_case("2.2.4", "Cancel and 200 OK glare",
+ "NUA is callee, 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
+
+
+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_outgoing);
+ tcase_add_test(tc, cancel_outgoing_after_100);
+ tcase_add_test(tc, cancel_outgoing_after_180);
+ tcase_add_test(tc, cancel_outgoing_glare);
+
+ 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_to_nua_with_timer)
+{
+ 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_to_nua_with_timer_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_to_nua_with_timer);
+ tcase_add_test(tc, call_to_nua_with_timer_2);
+ }
+ return tc;
+}
+
+/* ====================================================================== */
+/* 2.4 - 100rel */
+
+START_TEST(call_with_prack_by_nua)
+{
+ nua_handle_t *nh;
+ struct message *invite, *prack;
+
+ 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, 1,
+ SIP_183_SESSION_PROGRESS,
+ 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, 0,
+ SIP_180_RINGING,
+ 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_with_prack_sans_soa)
+{
+ nua_handle_t *nh;
+ struct message *invite, *prack;
+
+ 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,
+ 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, 0,
+ SIP_183_SESSION_PROGRESS,
+ 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, 0,
+ SIP_180_RINGING,
+ 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_with_prack_by_nua);
+ tcase_add_test(tc, call_with_prack_sans_soa);
+ }
+ return tc;
+}
+
+/* ====================================================================== */
+/* 3.1 - Call error cases */
+
+START_TEST(call_forbidden)
+{
+ 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(too_many_retrys)
+{
+ 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(reinvite_forbidden)
+{
+ 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(reinvite_too_many_retrys)
+{
+ 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
+
+
+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_forbidden);
+ tcase_add_test(tc, too_many_retrys);
+ tcase_add_test(tc, reinvite_forbidden);
+ tcase_add_test(tc, reinvite_too_many_retrys);
+ tcase_set_timeout(tc, 5);
+ }
+ return tc;
+}
+
+
+/* ====================================================================== */
+/* Weird call termination cases */
+
+START_TEST(terminating_re_invite)
+{
+ 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_invite_glare)
+{
+ 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(call_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(call_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(call_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_invite_glare2)
+{
+ 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_invite_glare3)
+{
+ 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_then_respond)
+{
+ 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_with_timer)
+{
+ 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_with_timer2)
+{
+ 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");
+
+ 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, terminating_re_invite);
+ tcase_add_test(tc, bye_invite_glare);
+ tcase_add_test(tc, call_4_1_3);
+ tcase_add_test(tc, call_4_1_4);
+ tcase_add_test(tc, call_4_1_5);
+ tcase_add_test(tc, bye_invite_glare2);
+ tcase_add_test(tc, bye_invite_glare3);
+ tcase_add_test(tc, bye_with_timer);
+ tcase_add_test(tc, bye_with_timer2);
+ tcase_add_test(tc, bye_then_respond);
+ tcase_set_timeout(tc, 5);
+ }
+ return tc;
+}
+
+/* ====================================================================== */
+
+/* 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_error_tcase());
+ suite_add_tcase(suite, termination_tcase());
+
+ if (0) /* Template */
+ suite_add_tcase(suite, empty_tcase());
+}
Added: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_s2.c
==============================================================================
--- (empty file)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_s2.c Wed May 14 15:10:54 2008
@@ -0,0 +1,1589 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2008 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at 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 s2tester.c
+ * @brief 2nd test Suite for Sofia SIP User Agent Engine
+ *
+ * @author Pekka Pessi <Pekka.Pessi at nokia.com>
+ *
+ * @date Created: Wed Apr 30 12:48:27 EEST 2008 ppessi
+ */
+
+#include "config.h"
+
+#undef NDEBUG
+
+#define TP_MAGIC_T struct tp_magic_s
+
+#include "test_s2.h"
+
+#include <sofia-sip/sip_header.h>
+#include <sofia-sip/sip_status.h>
+#include <sofia-sip/msg_addr.h>
+#include <sofia-sip/su_log.h>
+#include <sofia-sip/su_tagarg.h>
+#include <sofia-sip/su_alloc.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <limits.h>
+#include <time.h>
+
+/* -- Module types ------------------------------------------------------ */
+
+struct tp_magic_s
+{
+ sip_via_t *via;
+ sip_contact_t *contact;
+};
+
+/* -- Module prototypes ------------------------------------------------- */
+
+static msg_t *s2_msg(int flags);
+static int s2_complete_response(msg_t *response,
+ int status, char const *phrase,
+ msg_t *request);
+static char *s2_generate_tag(su_home_t *home);
+
+/* -- Module globals ---------------------------------------------------- */
+
+struct tester *s2;
+
+static char const *_s2case = "0.0";
+static unsigned s2_tag_generator = 0;
+
+/* -- Globals ----------------------------------------------------------- */
+
+unsigned s2_default_registration_duration = 3600;
+
+char const s2_auth_digest_str[] =
+ "Digest realm=\"s2test\", "
+ "nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\", "
+ "qop=\"auth\", "
+ "algorithm=\"MD5\"";
+
+char const s2_auth_credentials[] = "Digest:\"s2test\":abc:abc";
+
+char const s2_auth2_digest_str[] =
+ "Digest realm=\"s2test2\", "
+ "nonce=\"fb0c093dcd98b7102dd2f0e8b11d0f600b\", "
+ "qop=\"auth\", "
+ "algorithm=\"MD5\"";
+
+char const s2_auth2_credentials[] = "Digest:\"s2test2\":abc:abc";
+
+char const s2_auth3_digest_str[] =
+ "Digest realm=\"s2test3\", "
+ "nonce=\"e8b11d0f600bfb0c093dcd98b7102dd2f0\", "
+ "qop=\"auth-int\", "
+ "algorithm=\"MD5-sess\"";
+
+char const s2_auth3_credentials[] = "Digest:\"s2test3\":abc:abc";
+
+/* -- Delay scenarios --------------------------------------------------- */
+
+static unsigned long time_offset;
+
+extern void (*_su_time)(su_time_t *tv);
+
+static void _su_time_fast_forwarder(su_time_t *tv)
+{
+ tv->tv_sec += time_offset;
+}
+
+void s2_fast_forward(unsigned long seconds)
+{
+ if (_su_time == NULL)
+ _su_time = _su_time_fast_forwarder;
+
+ time_offset += seconds;
+}
+
+/* -- NUA events -------------------------------------------------------- */
+
+struct event *s2_remove_event(struct event *e)
+{
+ if ((*e->prev = e->next))
+ e->next->prev = e->prev;
+
+ e->prev = NULL, e->next = NULL;
+
+ return e;
+}
+
+void s2_free_event(struct event *e)
+{
+ if (e) {
+ if (e->prev) {
+ if ((*e->prev = e->next))
+ e->next->prev = e->prev;
+ }
+ nua_destroy_event(e->event);
+ nua_handle_unref(e->nh);
+ free(e);
+ }
+}
+
+void s2_flush_events(void)
+{
+ while (s2->events) {
+ s2_free_event(s2->events);
+ }
+}
+
+struct event *s2_next_event(void)
+{
+ for (;;) {
+ if (s2->events)
+ return s2_remove_event(s2->events);
+
+ su_root_step(s2->root, 100);
+ }
+}
+
+struct event *s2_wait_for_event(nua_event_t event, int status)
+{
+ struct event *e;
+
+ for (;;) {
+ for (e = s2->events; e; e = e->next) {
+ if (event != nua_i_none && event != e->data->e_event)
+ continue;
+ if (status && e->data->e_status != status)
+ continue;
+ return s2_remove_event(e);
+ }
+
+ su_root_step(s2->root, 100);
+ }
+}
+
+int s2_check_event(nua_event_t event, int status)
+{
+ struct event *e = s2_wait_for_event(event, status);
+ s2_free_event(e);
+ return e != NULL;
+}
+
+int s2_check_callstate(enum nua_callstate state)
+{
+ int retval = 0;
+ tagi_t const *tagi;
+ struct event *e;
+
+ e = s2_wait_for_event(nua_i_state, 0);
+ if (e) {
+ tagi = tl_find(e->data->e_tags, nutag_callstate);
+ if (tagi) {
+ retval = (tag_value_t)state == tagi->t_value;
+ }
+ }
+ s2_free_event(e);
+ return retval;
+}
+
+static void
+s2_nua_callback(nua_event_t event,
+ int status, char const *phrase,
+ nua_t *nua, nua_magic_t *_t,
+ nua_handle_t *nh, nua_hmagic_t *hmagic,
+ sip_t const *sip,
+ tagi_t tags[])
+{
+ struct event *e, **prev;
+
+ if (event == nua_i_active || event == nua_i_terminated)
+ return;
+
+ e = calloc(1, sizeof *e);
+ nua_save_event(nua, e->event);
+ e->nh = nua_handle_ref(nh);
+ e->data = nua_event_data(e->event);
+
+ for (prev = &s2->events; *prev; prev = &(*prev)->next)
+ ;
+
+ *prev = e, e->prev = prev;
+}
+
+
+struct message *
+s2_remove_message(struct message *m)
+{
+ if ((*m->prev = m->next))
+ m->next->prev = m->prev;
+
+ m->prev = NULL, m->next = NULL;
+
+ return m;
+}
+
+void
+s2_free_message(struct message *m)
+{
+ if (m) {
+ if (m->prev) {
+ if ((*m->prev = m->next))
+ m->next->prev = m->prev;
+ }
+ msg_destroy(m->msg);
+ tport_unref(m->tport);
+ free(m);
+ }
+}
+
+void s2_flush_messages(void)
+{
+ while (s2->received) {
+ s2_free_message(s2->received);
+ }
+}
+
+struct message *
+s2_next_response(void)
+{
+ struct message *m;
+
+ for (;;) {
+ for (m = s2->received; m; m = m->next) {
+ if (m->sip->sip_status)
+ return s2_remove_message(m);
+ }
+ su_root_step(s2->root, 100);
+ }
+}
+
+struct message *
+s2_wait_for_response(int status, sip_method_t method, char const *name)
+{
+ struct message *m;
+
+ for (;;) {
+ for (m = s2->received; m; m = m->next) {
+ if (!m->sip->sip_status)
+ continue;
+
+ if (status != 0 && m->sip->sip_status->st_status != status)
+ continue;
+
+ if (method == sip_method_unknown && name == NULL)
+ break;
+
+ if (m->sip->sip_cseq == NULL)
+ continue;
+
+ if (m->sip->sip_cseq->cs_method != method)
+ continue;
+ if (name == NULL)
+ break;
+ if (strcmp(m->sip->sip_cseq->cs_method_name, name) == 0)
+ break;
+ }
+
+ if (m)
+ return s2_remove_message(m);
+
+ su_root_step(s2->root, 100);
+ }
+}
+
+int
+s2_check_response(int status, sip_method_t method, char const *name)
+{
+ struct message *m = s2_wait_for_response(status, method, name);
+ s2_free_message(m);
+ return m != NULL;
+}
+
+
+struct message *
+s2_next_request(void)
+{
+ struct message *m;
+
+ for (;;) {
+ for (m = s2->received; m; m = m->next) {
+ if (m->sip->sip_request)
+ return s2_remove_message(m);
+ }
+
+ su_root_step(s2->root, 100);
+ }
+
+ return NULL;
+}
+
+struct message *
+s2_wait_for_request(sip_method_t method, char const *name)
+{
+ struct message *m;
+
+ for (;;) {
+ for (m = s2->received; m; m = m->next) {
+ if (m->sip->sip_request) {
+ if (method == sip_method_unknown && name == NULL)
+ return s2_remove_message(m);
+
+ if (m->sip->sip_request->rq_method == method &&
+ strcmp(m->sip->sip_request->rq_method_name, name) == 0)
+ return s2_remove_message(m);
+ }
+ }
+
+ su_root_step(s2->root, 100);
+ }
+
+ return NULL;
+}
+
+int
+s2_check_request(sip_method_t method, char const *name)
+{
+ struct message *m = s2_wait_for_request(method, name);
+ s2_free_message(m);
+ return m != NULL;
+}
+
+struct message *
+s2_respond_to(struct message *m, struct dialog *d,
+ int status, char const *phrase,
+ tag_type_t tag, tag_value_t value, ...)
+{
+ ta_list ta;
+ msg_t *reply;
+ sip_t *sip;
+ su_home_t *home;
+ tp_name_t tpn[1];
+ char *rport;
+
+ assert(m); assert(m->msg); assert(m->tport);
+ assert(100 <= status && status < 700);
+
+ ta_start(ta, tag, value);
+
+ reply = s2_msg(0); sip = sip_object(reply); home = msg_home(reply);
+
+ assert(reply && home && sip);
+
+ if (sip_add_tl(reply, sip, ta_tags(ta)) < 0) {
+ abort();
+ }
+
+ s2_complete_response(reply, status, phrase, m->msg);
+
+ if (sip->sip_status && sip->sip_status->st_status > 100 &&
+ sip->sip_to && !sip->sip_to->a_tag &&
+ sip->sip_cseq && sip->sip_cseq->cs_method != sip_method_cancel) {
+ char const *ltag = NULL;
+
+ if (d && d->local)
+ ltag = d->local->a_tag;
+
+ if (ltag == NULL)
+ ltag = s2_generate_tag(home);
+
+ if (sip_to_tag(msg_home(reply), sip->sip_to, ltag) < 0) {
+ assert(!"add To tag");
+ }
+ }
+
+ if (d && !d->local) {
+ d->local = sip_from_dup(d->home, sip->sip_to);
+ d->remote = sip_to_dup(d->home, sip->sip_from);
+ d->call_id = sip_call_id_dup(d->home, sip->sip_call_id);
+ d->rseq = sip->sip_cseq->cs_seq;
+ /* d->route = sip_route_dup(d->home, sip->sip_record_route); */
+ d->target = sip_contact_dup(d->home, m->sip->sip_contact);
+ d->contact = sip_contact_dup(d->home, sip->sip_contact);
+ }
+
+ *tpn = *tport_name(m->tport);
+
+ rport = su_sprintf(home, "rport=%u",
+ ntohs(((su_sockaddr_t *)
+ msg_addrinfo(m->msg)->ai_addr)->su_port));
+
+ if (s2->server_uses_rport &&
+ sip->sip_via->v_rport &&
+ sip->sip_via->v_rport[0] == '\0') {
+ msg_header_add_param(home, sip->sip_via->v_common, rport);
+ }
+
+ tpn->tpn_port = rport + strlen("rport=");
+
+ tport_tsend(m->tport, reply, tpn, TPTAG_MTU(INT_MAX), ta_tags(ta));
+ msg_destroy(reply);
+
+ ta_end(ta);
+
+ return m;
+}
+
+/** Add headers from the request to the response message. */
+static int
+s2_complete_response(msg_t *response,
+ int status, char const *phrase,
+ msg_t *request)
+{
+ su_home_t *home = msg_home(response);
+ sip_t *response_sip = sip_object(response);
+ sip_t const *request_sip = sip_object(request);
+
+ int incomplete = 0;
+
+ if (!response_sip || !request_sip || !request_sip->sip_request)
+ return -1;
+
+ if (!response_sip->sip_status)
+ response_sip->sip_status = sip_status_create(home, status, phrase, NULL);
+ if (!response_sip->sip_via)
+ response_sip->sip_via = sip_via_dup(home, request_sip->sip_via);
+ if (!response_sip->sip_from)
+ response_sip->sip_from = sip_from_dup(home, request_sip->sip_from);
+ if (!response_sip->sip_to)
+ response_sip->sip_to = sip_to_dup(home, request_sip->sip_to);
+ if (!response_sip->sip_call_id)
+ response_sip->sip_call_id =
+ sip_call_id_dup(home, request_sip->sip_call_id);
+ if (!response_sip->sip_cseq)
+ response_sip->sip_cseq = sip_cseq_dup(home, request_sip->sip_cseq);
+
+ if (!response_sip->sip_record_route && request_sip->sip_record_route)
+ sip_add_dup(response, response_sip, (void*)request_sip->sip_record_route);
+
+ incomplete = sip_complete_message(response) < 0;
+
+ msg_serialize(response, (msg_pub_t *)response_sip);
+
+ if (incomplete ||
+ !response_sip->sip_status ||
+ !response_sip->sip_via ||
+ !response_sip->sip_from ||
+ !response_sip->sip_to ||
+ !response_sip->sip_call_id ||
+ !response_sip->sip_cseq ||
+ !response_sip->sip_content_length ||
+ !response_sip->sip_separator ||
+ (request_sip->sip_record_route && !response_sip->sip_record_route))
+ return -1;
+
+ return 0;
+}
+
+/* Send request (updating dialog).
+ *
+ * Return zero upon success, nonzero upon failure.
+ */
+int
+s2_request_to(struct dialog *d,
+ sip_method_t method, char const *name,
+ tport_t *tport,
+ tag_type_t tag, tag_value_t value, ...)
+{
+ ta_list ta;
+ tagi_t const *tags;
+
+ msg_t *msg = s2_msg(0);
+ sip_t *sip = sip_object(msg);
+ url_string_t *target = NULL;
+ sip_cseq_t cseq[1];
+ sip_via_t via[1]; char const *v_params[8];
+ sip_content_length_t l[1];
+ tp_name_t tpn[1];
+ tp_magic_t *magic;
+
+ ta_start(ta, tag, value);
+ tags = ta_args(ta);
+
+ if (sip_add_tagis(msg, sip, &tags) < 0)
+ goto error;
+
+ if (!sip->sip_request) {
+ sip_request_t *rq;
+
+ if (d->target)
+ target = (url_string_t *)d->target->m_url;
+ else if (s2->registration->contact)
+ target = (url_string_t *)s2->registration->contact->m_url;
+ else
+ target = NULL;
+
+ if (target == NULL)
+ goto error;
+
+ rq = sip_request_create(msg_home(msg), method, name, target, NULL);
+ sip_header_insert(msg, sip, (sip_header_t *)rq);
+ }
+
+ if (!d->local && sip->sip_from)
+ d->local = sip_from_dup(d->home, sip->sip_from);
+ if (!d->contact && sip->sip_contact)
+ d->contact = sip_contact_dup(d->home, sip->sip_contact);
+ if (!d->remote && sip->sip_to)
+ d->remote = sip_to_dup(d->home, sip->sip_to);
+ if (!d->target && sip->sip_request)
+ d->target = sip_contact_create(d->home,
+ (url_string_t *)sip->sip_request->rq_url,
+ NULL);
+ if (!d->call_id && sip->sip_call_id)
+ d->call_id = sip_call_id_dup(d->home, sip->sip_call_id);
+ if (!d->lseq && sip->sip_cseq)
+ d->lseq = sip->sip_cseq->cs_seq;
+
+ if (!d->local)
+ d->local = sip_from_dup(d->home, s2->local);
+ if (!d->contact)
+ d->contact = sip_contact_dup(d->home, s2->contact);
+ if (!d->remote)
+ d->remote = sip_to_dup(d->home, s2->registration->aor);
+ if (!d->call_id)
+ d->call_id = sip_call_id_create(d->home, NULL);
+ assert(d->local && d->contact);
+ assert(d->remote && d->target);
+ assert(d->call_id);
+
+ if (tport == NULL)
+ tport = d->tport;
+
+ if (tport == NULL)
+ tport = s2->registration->tport;
+
+ if (tport == NULL && d->target->m_url->url_type == url_sips)
+ tport = s2->tls.tport;
+
+ if (tport == NULL)
+ tport = s2->udp.tport;
+ else if (tport == NULL)
+ tport = s2->tcp.tport;
+ else if (tport == NULL)
+ tport = s2->tls.tport;
+
+ assert(tport);
+
+ *tpn = *tport_name(tport);
+ tpn->tpn_host = d->target->m_url->url_host;
+ tpn->tpn_port = d->target->m_url->url_port;
+
+ magic = tport_magic(tport);
+ assert(magic != NULL);
+
+ sip_cseq_init(cseq);
+ cseq->cs_method = method;
+ cseq->cs_method_name = name;
+
+ if (d->invite && (method == sip_method_ack || method == sip_method_cancel)) {
+ cseq->cs_seq = sip_object(d->invite)->sip_cseq->cs_seq;
+ }
+ else {
+ cseq->cs_seq = ++d->lseq;
+ }
+
+ if (d->invite && method == sip_method_cancel) {
+ *via = *sip_object(d->invite)->sip_via;
+ }
+ else {
+ *via = *magic->via;
+ via->v_params = v_params;
+ v_params[0] = su_sprintf(msg_home(msg), "branch=z9hG4bK%lx", ++s2->tid);
+ v_params[1] = NULL;
+ }
+
+ sip_content_length_init(l);
+ if (sip->sip_payload)
+ l->l_length = sip->sip_payload->pl_len;
+
+ sip_add_tl(msg, sip,
+ TAG_IF(!sip->sip_from, SIPTAG_FROM(d->local)),
+ TAG_IF(!sip->sip_contact, SIPTAG_CONTACT(d->contact)),
+ TAG_IF(!sip->sip_to, SIPTAG_TO(d->remote)),
+ TAG_IF(!sip->sip_call_id, SIPTAG_CALL_ID(d->call_id)),
+ TAG_IF(!sip->sip_cseq, SIPTAG_CSEQ(cseq)),
+ SIPTAG_VIA(via),
+ TAG_IF(!sip->sip_content_length, SIPTAG_CONTENT_LENGTH(l)),
+ TAG_IF(!sip->sip_separator, SIPTAG_SEPARATOR_STR("\r\n")),
+ TAG_END());
+
+ msg_serialize(msg, NULL);
+
+ if (method == sip_method_invite) {
+ msg_destroy(d->invite);
+ d->invite = msg_ref_create(msg);
+ }
+
+ tport = tport_tsend(tport, msg, tpn, ta_tags(ta));
+ ta_end(ta);
+
+ if (d->tport != tport) {
+ tport_unref(d->tport);
+ d->tport = tport_ref(tport);
+ }
+
+ return tport ? 0 : -1;
+
+ error:
+ ta_end(ta);
+ return -1;
+}
+
+/** Save information from response.
+ *
+ * Send ACK for error messages to INVITE.
+ */
+int s2_update_dialog(struct dialog *d, struct message *m)
+{
+ int status = 0;
+
+ if (m->sip->sip_status)
+ status = m->sip->sip_status->st_status;
+
+ if (100 < status && status < 300) {
+ d->remote = sip_to_dup(d->home, m->sip->sip_to);
+ if (m->sip->sip_contact)
+ d->contact = sip_contact_dup(d->home, m->sip->sip_contact);
+ }
+
+ if (300 <= status && m->sip->sip_cseq &&
+ m->sip->sip_cseq->cs_method == sip_method_invite &&
+ d->invite) {
+ msg_t *ack = s2_msg(0);
+ sip_t *sip = sip_object(ack);
+ sip_t *invite = sip_object(d->invite);
+ sip_request_t rq[1];
+ sip_cseq_t cseq[1];
+ tp_name_t tpn[1];
+
+ *rq = *invite->sip_request;
+ rq->rq_method = sip_method_ack, rq->rq_method_name = "ACK";
+ *cseq = *invite->sip_cseq;
+ cseq->cs_method = sip_method_ack, cseq->cs_method_name = "ACK";
+
+ sip_add_tl(ack, sip,
+ SIPTAG_REQUEST(rq),
+ SIPTAG_VIA(invite->sip_via),
+ SIPTAG_FROM(invite->sip_from),
+ SIPTAG_TO(invite->sip_to),
+ SIPTAG_CALL_ID(invite->sip_call_id),
+ SIPTAG_CSEQ(cseq),
+ SIPTAG_CONTENT_LENGTH_STR("0"),
+ SIPTAG_SEPARATOR_STR("\r\n"),
+ TAG_END());
+
+ *tpn = *tport_name(d->tport);
+ if (!tport_is_secondary(d->tport) ||
+ !tport_is_clear_to_send(d->tport)) {
+ tpn->tpn_host = rq->rq_url->url_host;
+ tpn->tpn_port = rq->rq_url->url_port;
+ }
+
+ msg_serialize(ack, NULL);
+ tport_tsend(d->tport, ack, tpn, TAG_END());
+ }
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+int
+s2_save_register(struct message *rm)
+{
+ sip_contact_t *contact, *m, **m_prev;
+ sip_expires_t const *ex;
+ sip_date_t const *date;
+ sip_time_t now = rm->when.tv_sec, expires;
+
+ msg_header_free_all(s2->home, (msg_header_t *)s2->registration->aor);
+ msg_header_free_all(s2->home, (msg_header_t *)s2->registration->contact);
+ tport_unref(s2->registration->tport);
+
+ s2->registration->aor = NULL;
+ s2->registration->contact = NULL;
+ s2->registration->tport = NULL;
+
+ if (rm == NULL)
+ return 0;
+
+ assert(rm && rm->sip && rm->sip->sip_request);
+ assert(rm->sip->sip_request->rq_method == sip_method_register);
+
+ ex = rm->sip->sip_expires;
+ date = rm->sip->sip_date;
+
+ contact = sip_contact_dup(s2->home, rm->sip->sip_contact);
+
+ for (m_prev = &contact; *m_prev;) {
+ m = *m_prev;
+
+ expires = sip_contact_expires(m, ex, date,
+ s2_default_registration_duration,
+ now);
+ if (expires) {
+ char *p = su_sprintf(s2->home, "expires=%lu", (unsigned long)expires);
+ msg_header_add_param(s2->home, m->m_common, p);
+ m_prev = &m->m_next;
+ }
+ else {
+ *m_prev = m->m_next;
+ m->m_next = NULL;
+ msg_header_free(s2->home, (msg_header_t *)m);
+ }
+ }
+
+ if (contact == NULL)
+ return 0;
+
+ s2->registration->aor = sip_to_dup(s2->home, rm->sip->sip_to);
+ s2->registration->contact = contact;
+ s2->registration->tport = tport_ref(rm->tport);
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static char *
+s2_generate_tag(su_home_t *home)
+{
+ s2_tag_generator += 1;
+
+ return su_sprintf(home, "tag=N2-%s/%u", _s2case, s2_tag_generator);
+}
+
+void s2_case(char const *number,
+ char const *title,
+ char const *desciption)
+{
+ _s2case = number;
+}
+
+
+/* ---------------------------------------------------------------------- */
+/* tport interface */
+static void
+s2_stack_recv(struct tester *s2,
+ tport_t *tp,
+ msg_t *msg,
+ tp_magic_t *magic,
+ su_time_t now)
+{
+ struct message *next = calloc(1, sizeof *next), **prev;
+
+ next->msg = msg;
+ next->sip = sip_object(msg);
+ next->when = now;
+ next->tport = tport_ref(tp);
+
+#if 0
+ if (next->sip->sip_request)
+ printf("nua sent: %s\n", next->sip->sip_request->rq_method_name);
+ else
+ printf("nua sent: SIP/2.0 %u %s\n",
+ next->sip->sip_status->st_status,
+ next->sip->sip_status->st_phrase);
+#endif
+
+ for (prev = &s2->received; *prev; prev = &(*prev)->next)
+ ;
+
+ next->prev = prev, *prev = next;
+}
+
+static void
+s2_stack_error(struct tester *s2,
+ tport_t *tp,
+ int errcode,
+ char const *remote)
+{
+ fprintf(stderr, "%s(%p): error %d (%s) from %s\n",
+ "nua_tester_error",
+ (void *)tp, errcode, su_strerror(errcode),
+ remote ? remote : "<unknown destination>");
+}
+
+static msg_t *
+s2_stack_alloc(struct tester *s2, int flags,
+ char const data[], usize_t size,
+ tport_t const *tport,
+ tp_client_t *tpc)
+{
+ return msg_create(s2->mclass, flags | s2->flags);
+}
+
+static msg_t *
+s2_msg(int flags)
+{
+ return msg_create(s2->mclass, flags | s2->flags);
+}
+
+tp_stack_class_t const s2_stack[1] =
+ {{
+ /* tpac_size */ (sizeof s2_stack),
+ /* tpac_recv */ s2_stack_recv,
+ /* tpac_error */ s2_stack_error,
+ /* tpac_alloc */ s2_stack_alloc,
+ }};
+
+/** Basic setup for test cases */
+void s2_setup_base(char const *hostname)
+{
+ assert(s2 == NULL);
+
+ su_init();
+
+ s2 = su_home_new(sizeof *s2);
+
+ assert(s2 != NULL);
+
+ s2->root = su_root_create(s2);
+
+ assert(s2->root != NULL);
+
+ su_root_threading(s2->root, 0); /* disable multithreading */
+
+ s2->local = sip_from_format(s2->home, "Bob <sip:bob@%s>",
+ hostname ? hostname : "example.net");
+
+ if (hostname == NULL)
+ hostname = "127.0.0.1";
+
+ s2->hostname = hostname;
+ s2->tid = (unsigned long)time(NULL) * 510633671UL;
+}
+
+SOFIAPUBVAR su_log_t nua_log[];
+SOFIAPUBVAR su_log_t soa_log[];
+SOFIAPUBVAR su_log_t nea_log[];
+SOFIAPUBVAR su_log_t nta_log[];
+SOFIAPUBVAR su_log_t tport_log[];
+SOFIAPUBVAR su_log_t su_log_default[];
+
+void
+s2_setup_logs(int level)
+{
+ assert(s2);
+
+ su_log_soft_set_level(nua_log, level);
+ su_log_soft_set_level(soa_log, level);
+ su_log_soft_set_level(su_log_default, level);
+ su_log_soft_set_level(nea_log, level);
+ su_log_soft_set_level(nta_log, level);
+ su_log_soft_set_level(tport_log, level);
+}
+
+static char const * default_protocols[] = { "udp", "tcp", NULL };
+
+void
+s2_setup_tport(char const * const *protocols,
+ tag_type_t tag, tag_value_t value, ...)
+{
+ ta_list ta;
+ tp_name_t tpn[1];
+ int bound;
+ tport_t *tp;
+
+ assert(s2 != NULL);
+
+ ta_start(ta, tag, value);
+
+ if (s2->master == NULL) {
+ s2->master = tport_tcreate(s2, s2_stack, s2->root, ta_tags(ta));
+
+ if (s2->master == NULL) {
+ assert(s2->master);
+ }
+ s2->mclass = sip_default_mclass();
+ s2->flags = 0;
+ }
+
+ memset(tpn, 0, (sizeof tpn));
+ tpn->tpn_proto = "*";
+ tpn->tpn_host = s2->hostname;
+ tpn->tpn_port = "*";
+
+ if (protocols == NULL)
+ protocols = default_protocols;
+
+ bound = tport_tbind(s2->master, tpn, protocols,
+ TPTAG_SERVER(1),
+ ta_tags(ta));
+ assert(bound != -1);
+
+ tp = tport_primaries(s2->master);
+
+ if (protocols == default_protocols && s2->contact == NULL) {
+ *tpn = *tport_name(tp);
+ s2->contact = sip_contact_format(s2->home, "<sip:%s:%s>",
+ tpn->tpn_host,
+ tpn->tpn_port);
+ }
+
+ for (;tp; tp = tport_next(tp)) {
+ sip_via_t *v;
+ sip_contact_t *m;
+ tp_magic_t *magic;
+
+ if (tport_magic(tp))
+ continue;
+
+ *tpn = *tport_name(tp);
+
+ v = sip_via_format(s2->home, "SIP/2.0/%s %s:%s",
+ tpn->tpn_proto,
+ tpn->tpn_host,
+ tpn->tpn_port);
+ assert(v != NULL);
+ if (strncasecmp(tpn->tpn_proto, "tls", 3)) {
+ m = sip_contact_format(s2->home, "<sip:%s:%s;transport=%s>",
+ tpn->tpn_host,
+ tpn->tpn_port,
+ tpn->tpn_proto);
+ if (s2->udp.contact == NULL && strcasecmp(tpn->tpn_proto, "udp") == 0) {
+ s2->udp.tport = tport_ref(tp);
+ s2->udp.contact = m;
+ }
+ if (s2->tcp.contact == NULL && strcasecmp(tpn->tpn_proto, "tcp") == 0) {
+ s2->tcp.tport = tport_ref(tp);
+ s2->tcp.contact = m;
+ }
+ }
+ else if (strcasecmp(tpn->tpn_proto, "tls")) {
+ m = sip_contact_format(s2->home, "<sips:%s:%s;transport=%s>",
+ tpn->tpn_host,
+ tpn->tpn_port,
+ tpn->tpn_proto);
+ }
+ else {
+ m = sip_contact_format(s2->home, "<sips:%s:%s>",
+ tpn->tpn_host,
+ tpn->tpn_port);
+ if (s2->tls.contact == NULL) {
+ s2->tls.tport = tport_ref(tp);
+ s2->tls.contact = m;
+ }
+ }
+ assert(m != NULL);
+
+ magic = su_zalloc(s2->home, (sizeof *magic));
+ magic->via = v, magic->contact = m;
+
+ if (s2->contact == NULL)
+ s2->contact = m;
+
+ tport_set_magic(tp, magic);
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+/* S2 DNS server */
+
+#include <sofia-resolv/sres_record.h>
+
+extern uint16_t _sres_default_port;
+
+static int s2_dns_query(struct tester *s2,
+ su_wait_t *w,
+ su_wakeup_arg_t *arg);
+
+void s2_setup_dns(void)
+{
+ int n;
+ su_socket_t socket;
+ su_wait_t *wait;
+ su_sockaddr_t su[1];
+ socklen_t sulen = sizeof su->su_sin;
+
+ assert(s2->nua == NULL); assert(s2->root != NULL);
+
+ memset(su, 0, sulen);
+ su->su_len = sulen;
+ su->su_family = AF_INET;
+
+ /* su->su_port = htons(1053); */
+
+ socket = su_socket(su->su_family, SOCK_DGRAM, 0);
+
+ n = bind(socket, &su->su_sa, sulen); assert(n == 0);
+ n = getsockname(socket, &su->su_sa, &sulen); assert(n == 0);
+
+ _sres_default_port = ntohs(su->su_port);
+
+ wait = s2->dns.wait;
+ n = su_wait_create(wait, socket, SU_WAIT_IN); assert(n == 0);
+ s2->dns.reg = su_root_register(s2->root, wait, s2_dns_query, NULL, 0);
+ assert(s2->dns.reg > 0);
+ s2->dns.socket = socket;
+}
+
+static
+struct s2_dns_response {
+ struct s2_dns_response *next;
+ uint16_t qlen, dlen;
+ struct m_header {
+ /* Header defined in RFC 1035 section 4.1.1 (page 26) */
+ uint16_t mh_id; /* Query ID */
+ uint16_t mh_flags; /* Flags */
+ uint16_t mh_qdcount; /* Question record count */
+ uint16_t mh_ancount; /* Answer record count */
+ uint16_t mh_nscount; /* Authority records count */
+ uint16_t mh_arcount; /* Additional records count */
+ } header[1];
+ uint8_t data[1500];
+} *zonedata;
+
+enum {
+ FLAGS_QR = (1 << 15),
+ FLAGS_QUERY = (0 << 11),
+ FLAGS_IQUERY = (1 << 11),
+ FLAGS_STATUS = (2 << 11),
+ FLAGS_OPCODE = (15 << 11), /* mask */
+ FLAGS_AA = (1 << 10), /* */
+ FLAGS_TC = (1 << 9),
+ FLAGS_RD = (1 << 8),
+ FLAGS_RA = (1 << 7),
+
+ FLAGS_RCODE = (15 << 0), /* mask of return code */
+
+ FLAGS_OK = 0, /* No error condition. */
+ FLAGS_FORMAT_ERR = 1, /* Server could not interpret query. */
+ FLAGS_SERVER_ERR = 2, /* Server error. */
+ FLAGS_NAME_ERR = 3, /* No domain name. */
+ FLAGS_UNIMPL_ERR = 4, /* Not implemented. */
+ FLAGS_AUTH_ERR = 5, /* Refused */
+};
+
+uint32_t s2_dns_ttl = 3600;
+
+static int
+s2_dns_query(struct tester *s2,
+ su_wait_t *w,
+ su_wakeup_arg_t *arg)
+{
+ union {
+ struct m_header header[1];
+ uint8_t buffer[1500];
+ } request;
+ ssize_t len;
+
+ su_socket_t socket;
+ su_sockaddr_t su[1];
+ socklen_t sulen = sizeof su;
+ uint16_t flags;
+ struct s2_dns_response *r;
+ size_t const hlen = sizeof r->header;
+
+ (void)arg;
+
+ socket = s2->dns.socket;
+
+ len = su_recvfrom(socket, request.buffer, sizeof request.buffer, 0,
+ &su->su_sa, &sulen);
+
+ flags = ntohs(request.header->mh_flags);
+
+ if (len < (ssize_t)hlen)
+ return 0;
+ if ((flags & FLAGS_QR) == FLAGS_QR)
+ return 0;
+ if ((flags & FLAGS_RCODE) != FLAGS_OK)
+ return 0;
+
+ if ((flags & FLAGS_OPCODE) != FLAGS_QUERY
+ || ntohs(request.header->mh_qdcount) != 1) {
+ flags |= FLAGS_QR | FLAGS_UNIMPL_ERR;
+ request.header->mh_flags = htons(flags);
+ su_sendto(socket, request.buffer, len, 0, &su->su_sa, sulen);
+ return 0;
+ }
+
+ for (r = zonedata; r; r = r->next) {
+ if (memcmp(r->data, request.buffer + hlen, r->qlen) == 0)
+ break;
+ }
+
+ if (r) {
+ flags |= FLAGS_QR | FLAGS_AA | FLAGS_OK;
+ request.header->mh_flags = htons(flags);
+ request.header->mh_ancount = htons(r->header->mh_ancount);
+ request.header->mh_nscount = htons(r->header->mh_nscount);
+ request.header->mh_arcount = htons(r->header->mh_arcount);
+ memcpy(request.buffer + hlen + r->qlen,
+ r->data + r->qlen,
+ r->dlen - r->qlen);
+ len = hlen + r->dlen;
+ }
+ else {
+ flags |= FLAGS_QR | FLAGS_AA | FLAGS_NAME_ERR;
+ }
+
+ request.header->mh_flags = htons(flags);
+ su_sendto(socket, request.buffer, len, 0, &su->su_sa, sulen);
+ return 0;
+}
+
+static void put_uint16(struct s2_dns_response *m, uint16_t h)
+{
+ uint8_t *p = m->data + m->dlen;
+
+ assert(m->dlen + (sizeof h) < sizeof m->data);
+ p[0] = h >> 8; p[1] = h;
+ m->dlen += (sizeof h);
+}
+
+static void put_uint32(struct s2_dns_response *m, uint32_t w)
+{
+ uint8_t *p = m->data + m->dlen;
+
+ assert(m->dlen + (sizeof w) < sizeof m->data);
+ p[0] = w >> 24; p[1] = w >> 16; p[2] = w >> 8; p[3] = w;
+ m->dlen += (sizeof w);
+}
+
+static void put_domain(struct s2_dns_response *m, char const *domain)
+{
+ char const *label;
+ size_t llen;
+
+ /* Copy domain into query label at a time */
+ for (label = domain; label && label[0]; label += llen) {
+ assert(!(label[0] == '.' && label[1] != '\0'));
+ llen = strcspn(label, ".");
+ assert(llen < 64);
+ assert(m->dlen + llen + 1 < sizeof m->data);
+ m->data[m->dlen++] = (uint8_t)llen;
+ if (llen == 0)
+ return;
+
+ memcpy(m->data + m->dlen, label, llen);
+ m->dlen += (uint16_t)llen;
+
+ if (label[llen] == '\0')
+ break;
+ if (label[llen + 1])
+ llen++;
+ }
+
+ assert(m->dlen < sizeof m->data);
+ m->data[m->dlen++] = '\0';
+}
+
+static void put_string(struct s2_dns_response *m, char const *string)
+{
+ uint8_t *p = m->data + m->dlen;
+ size_t len = strlen(string);
+
+ assert(len <= 255);
+ assert(m->dlen + len + 1 < sizeof m->data);
+
+ *p++ = (uint8_t)len;
+ memcpy(p, string, len);
+ m->dlen += len + 1;
+}
+
+static uint16_t put_len_at(struct s2_dns_response *m)
+{
+ uint16_t at = m->dlen;
+ assert(m->dlen + sizeof(at) < sizeof m->data);
+ memset(m->data + m->dlen, 0, sizeof(at));
+ m->dlen += sizeof(at);
+ return at;
+}
+
+static void put_len(struct s2_dns_response *m, uint16_t start)
+{
+ uint8_t *p = m->data + start;
+ uint16_t len = m->dlen - (start + 2);
+ p[0] = len >> 8; p[1] = len;
+}
+
+static void put_data(struct s2_dns_response *m, void *data, uint16_t len)
+{
+ assert(m->dlen + len < sizeof m->data);
+ memcpy(m->data + m->dlen, data, len);
+ m->dlen += len;
+}
+
+static void put_query(struct s2_dns_response *m, char const *domain,
+ uint16_t qtype)
+{
+ assert(m->header->mh_qdcount == 0);
+ put_domain(m, domain), put_uint16(m, qtype), put_uint16(m, sres_class_in);
+ m->header->mh_qdcount++;
+ m->qlen = m->dlen;
+}
+
+static void put_a_record(struct s2_dns_response *m,
+ char const *domain,
+ struct in_addr addr)
+{
+ uint16_t start;
+
+ put_domain(m, domain);
+ put_uint16(m, sres_type_a);
+ put_uint16(m, sres_class_in);
+ put_uint32(m, s2_dns_ttl);
+ start = put_len_at(m);
+
+ put_data(m, &addr, sizeof addr);
+ put_len(m, start);
+}
+
+static void put_srv_record(struct s2_dns_response *m,
+ char const *domain,
+ uint16_t prio, uint16_t weight,
+ uint16_t port, char const *target)
+{
+ uint16_t start;
+ put_domain(m, domain);
+ put_uint16(m, sres_type_srv);
+ put_uint16(m, sres_class_in);
+ put_uint32(m, s2_dns_ttl);
+ start = put_len_at(m);
+
+ put_uint16(m, prio);
+ put_uint16(m, weight);
+ put_uint16(m, port);
+ put_domain(m, target);
+ put_len(m, start);
+}
+
+static void put_naptr_record(struct s2_dns_response *m,
+ char const *domain,
+ uint16_t order, uint16_t preference,
+ char const *flags,
+ char const *services,
+ char const *regexp,
+ char const *replace)
+{
+ uint16_t start;
+ put_domain(m, domain);
+ put_uint16(m, sres_type_naptr);
+ put_uint16(m, sres_class_in);
+ put_uint32(m, s2_dns_ttl);
+ start = put_len_at(m);
+
+ put_uint16(m, order);
+ put_uint16(m, preference);
+ put_string(m, flags);
+ put_string(m, services);
+ put_string(m, regexp);
+ put_domain(m, replace);
+ put_len(m, start);
+}
+
+static void put_srv_record_from_uri(struct s2_dns_response *m,
+ char const *base,
+ uint16_t prio, uint16_t weight,
+ url_t const *uri, char const *server)
+{
+ char domain[1024] = "none";
+ char const *service = url_port(uri);
+ uint16_t port;
+
+ if (uri->url_type == url_sips) {
+ strcpy(domain, "_sips._tcp.");
+ }
+ else if (uri->url_type == url_sip) {
+ if (url_has_param(uri, "transport=udp")) {
+ strcpy(domain, "_sip._udp.");
+ }
+ else if (url_has_param(uri, "transport=tcp")) {
+ strcpy(domain, "_sip._tcp.");
+ }
+ }
+
+ assert(strcmp(domain, "none"));
+
+ strcat(domain, base);
+
+ if (m->header->mh_qdcount == 0)
+ put_query(m, domain, sres_type_srv);
+
+ port = (uint16_t)strtoul(service, NULL, 10);
+
+ put_srv_record(m, domain, prio, weight, port, server);
+}
+
+static
+void s2_add_to_zone(struct s2_dns_response *_r)
+{
+ size_t size = offsetof(struct s2_dns_response, data[_r->dlen]);
+ struct s2_dns_response *r = malloc(size); assert(r);
+
+ memcpy(r, _r, size);
+ r->next = zonedata;
+ zonedata = r;
+}
+
+
+static void make_server(char *server, char const *prefix, char const *domain)
+{
+ strcpy(server, prefix);
+
+ if (strlen(server) == 0 || server[strlen(server) - 1] != '.') {
+ strcat(server, ".");
+ strcat(server, domain);
+ }
+}
+
+/** Set up DNS domain */
+void s2_dns_domain(char const *domain, int use_naptr,
+ /* char *prefix, int priority, url_t const *uri, */
+ ...)
+{
+ struct s2_dns_response m[1];
+
+ char server[1024], target[1024];
+
+ va_list va0, va;
+ char const *prefix; int priority; url_t const *uri;
+ struct in_addr localhost;
+
+ assert(s2->dns.reg != 0);
+
+ inet_pton(AF_INET, "127.0.0.1", &localhost);
+
+ va_start(va0, use_naptr);
+
+ if (use_naptr) {
+ memset(m, 0, sizeof m);
+ put_query(m, domain, sres_type_naptr);
+
+ va_copy(va, va0);
+
+ for (;(prefix = va_arg(va, char *));) {
+ char *services = NULL;
+
+ priority = va_arg(va, int);
+ uri = va_arg(va, url_t *); assert(uri);
+
+ if (uri->url_type == url_sips) {
+ services = "SIPS+D2T";
+ strcpy(target, "_sips._tcp.");
+ }
+ else if (uri->url_type == url_sip) {
+ if (url_has_param(uri, "transport=udp")) {
+ services = "SIP+D2U";
+ strcpy(target, "_sip._udp.");
+ }
+ else if (url_has_param(uri, "transport=tcp")) {
+ services = "SIP+D2T";
+ strcpy(target, "_sip._tcp.");
+ }
+ }
+
+ strcat(target, domain);
+ assert(services);
+ put_naptr_record(m, domain, 1, priority, "s", services, "", target);
+ m->header->mh_ancount++;
+ }
+
+ va_end(va);
+ va_copy(va, va0);
+
+ for (;(prefix = va_arg(va, char *));) {
+ priority = va_arg(va, int);
+ uri = va_arg(va, url_t *); assert(uri);
+
+ make_server(server, prefix, domain);
+
+ put_srv_record_from_uri(m, domain, priority, 10, uri, server);
+ m->header->mh_arcount++;
+
+ put_a_record(m, server, localhost);
+ m->header->mh_arcount++;
+ }
+ va_end(va);
+
+ s2_add_to_zone(m);
+ }
+
+ /* Add SRV records */
+ va_copy(va, va0);
+ for (;(prefix = va_arg(va, char *));) {
+ priority = va_arg(va, int);
+ uri = va_arg(va, url_t *); assert(uri);
+
+ make_server(server, prefix, domain);
+
+ memset(m, 0, sizeof m);
+ put_srv_record_from_uri(m, domain, priority, 10, uri, server);
+ m->header->mh_ancount++;
+
+ strcpy(server, prefix); strcat(server, domain);
+
+ put_a_record(m, server, localhost);
+ m->header->mh_arcount++;
+
+ s2_add_to_zone(m);
+ }
+ va_end(va);
+
+ /* Add A records */
+ va_copy(va, va0);
+ for (;(prefix = va_arg(va, char *));) {
+ (void)va_arg(va, int);
+ (void)va_arg(va, url_t *);
+
+ memset(m, 0, sizeof m);
+ make_server(server, prefix, domain);
+
+ put_query(m, server, sres_type_a);
+ put_a_record(m, server, localhost);
+ m->header->mh_ancount++;
+
+ s2_add_to_zone(m);
+ }
+ va_end(va);
+
+ va_end(va0);
+}
+
+void
+s2_teardown(void)
+{
+ s2 = NULL;
+ su_deinit();
+}
+
+/* ====================================================================== */
+
+#include <sofia-sip/sresolv.h>
+
+nua_t *s2_nua_setup(tag_type_t tag, tag_value_t value, ...)
+{
+ ta_list ta;
+
+ s2_setup_base(NULL);
+ s2_setup_dns();
+
+ s2_setup_logs(0);
+ s2_setup_tport(NULL, TPTAG_LOG(0), TAG_END());
+ assert(s2->contact);
+
+ s2_dns_domain("example.org", 1,
+ "s2", 1, s2->udp.contact->m_url,
+ "s2", 1, s2->tcp.contact->m_url,
+ NULL);
+
+ ta_start(ta, tag, value);
+ s2->nua =
+ nua_create(s2->root,
+ s2_nua_callback,
+ s2,
+ SIPTAG_FROM_STR("Alice <sip:alice at example.org>"),
+ /* NUTAG_PROXY((url_string_t *)s2->contact->m_url), */
+ /* Use internal DNS server */
+ NUTAG_PROXY("sip:example.org"),
+#if HAVE_WIN32
+ SRESTAG_RESOLV_CONF("NUL"),
+#else
+ SRESTAG_RESOLV_CONF("/dev/null"),
+#endif
+ ta_tags(ta));
+ ta_end(ta);
+
+ return s2->nua;
+}
+
+void s2_nua_teardown(void)
+{
+ nua_destroy(s2->nua);
+ s2->nua = NULL;
+ s2_teardown();
+}
+
+/* ====================================================================== */
+
+/** Register NUA user.
+ *
+ * <pre>
+ * A B
+ * |-----REGISTER---->|
+ * |<-----200 OK------|
+ * | |
+ * </pre>
+ */
+void s2_register_setup(void)
+{
+ nua_handle_t *nh;
+ struct message *m;
+
+ assert(s2 && s2->nua);
+ assert(!s2->registration->nh);
+
+ nh = nua_handle(s2->nua, NULL, TAG_END());
+
+ nua_register(nh, TAG_END());
+
+ m = s2_wait_for_request(SIP_METHOD_REGISTER);
+ assert(m);
+ s2_save_register(m);
+
+ s2_respond_to(m, NULL,
+ SIP_200_OK,
+ SIPTAG_CONTACT(s2->registration->contact),
+ TAG_END());
+ s2_free_message(m);
+
+ assert(s2->registration->contact != NULL);
+ s2_check_event(nua_r_register, 200);
+
+ s2->registration->nh = nh;
+}
+
+/** Un-register NUA user.
+ *
+ * <pre>
+ * A B
+ * |-----REGISTER---->|
+ * |<-----200 OK------|
+ * | |
+ * </pre>
+ */
+void s2_register_teardown(void)
+{
+ if (s2 && s2->registration->nh) {
+ nua_handle_t *nh = s2->registration->nh;
+ struct message *m;
+
+ nua_unregister(nh, TAG_END());
+
+ m = s2_wait_for_request(SIP_METHOD_REGISTER); assert(m);
+ s2_save_register(m);
+ s2_respond_to(m, NULL,
+ SIP_200_OK,
+ SIPTAG_CONTACT(s2->registration->contact),
+ TAG_END());
+ assert(s2->registration->contact == NULL);
+
+ s2_free_message(m);
+
+ s2_check_event(nua_r_unregister, 200);
+
+ nua_handle_destroy(nh);
+ s2->registration->nh = NULL;
+ }
+}
+
Added: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_s2.h
==============================================================================
--- (empty file)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_s2.h Wed May 14 15:10:54 2008
@@ -0,0 +1,174 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2008 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at 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
+ */
+
+#ifndef S2TESTER_H
+#define S2TESTER_H
+
+#define TP_STACK_T struct tester
+#define SU_ROOT_MAGIC_T struct tester
+
+#include <sofia-sip/su_wait.h>
+#include <sofia-sip/sip.h>
+#include <sofia-sip/tport.h>
+#include <sofia-sip/nua.h>
+
+struct tester
+{
+ su_home_t home[1];
+
+ su_root_t *root;
+ msg_mclass_t const *mclass;
+ int flags;
+
+ char const *hostname;
+ tport_t *master;
+
+ sip_to_t *local;
+ sip_contact_t *contact;
+ struct {
+ sip_contact_t *contact;
+ tport_t *tport;
+ } udp, tcp, tls;
+
+ struct message {
+ struct message *next, **prev;
+ msg_t *msg;
+ sip_t *sip;
+ tport_t *tport;
+ su_time_t when;
+ } *received;
+
+ struct {
+ su_socket_t socket;
+ su_wait_t wait[1];
+ int reg;
+ } dns;
+
+ nua_t *nua;
+
+ struct event {
+ struct event *next, **prev;
+ nua_saved_event_t event[1];
+ nua_handle_t *nh;
+ nua_event_data_t const *data;
+ su_time_t when;
+ } *events;
+
+ struct {
+ nua_handle_t *nh;
+ sip_to_t *aor;
+ sip_contact_t *contact;
+ tport_t *tport;
+ } registration[1];
+
+ unsigned long tid;
+
+ /* Settings */
+ int server_uses_rport;
+};
+
+struct dialog
+{
+ su_home_t home[1];
+ sip_from_t *local;
+ sip_to_t *remote;
+ sip_call_id_t *call_id;
+ uint32_t lseq, rseq;
+ sip_contact_t *target;
+ sip_route_t *route;
+ sip_contact_t *contact;
+
+ tport_t *tport;
+ msg_t *invite; /* latest invite sent */
+};
+
+extern struct tester *s2;
+extern tp_stack_class_t const s2_stack[1];
+
+extern unsigned s2_default_registration_duration;
+extern char const s2_auth_digest_str[];
+extern char const s2_auth_credentials[];
+
+extern char const s2_auth2_digest_str[];
+extern char const s2_auth2_credentials[];
+
+extern char const s2_auth3_digest_str[];
+extern char const s2_auth3_credentials[];
+
+void s2_fast_forward(unsigned long seconds);
+
+void s2_case(char const *tag,
+ char const *title,
+ char const *description);
+
+struct event *s2_remove_event(struct event *);
+void s2_free_event(struct event *);
+void s2_flush_events(void);
+
+struct event *s2_next_event(void);
+struct event *s2_wait_for_event(nua_event_t event, int status);
+int s2_check_event(nua_event_t event, int status);
+int s2_check_callstate(enum nua_callstate state);
+
+struct message *s2_remove_message(struct message *m);
+void s2_free_message(struct message *m);
+void s2_flush_messages(void);
+
+struct message *s2_next_response(void);
+struct message *s2_wait_for_response(int status, sip_method_t , char const *);
+int s2_check_response(int status, sip_method_t method, char const *name);
+
+struct message *s2_next_request(void);
+struct message *s2_wait_for_request(sip_method_t method, char const *name);
+int s2_check_request(sip_method_t method, char const *name);
+
+#define SIP_METHOD_UNKNOWN sip_method_unknown, NULL
+
+struct message *s2_respond_to(struct message *m, struct dialog *d,
+ int status, char const *phrase,
+ tag_type_t tag, tag_value_t value, ...);
+
+int s2_request_to(struct dialog *d,
+ sip_method_t method, char const *name,
+ tport_t *tport,
+ tag_type_t tag, tag_value_t value, ...);
+
+int s2_update_dialog(struct dialog *d, struct message *response);
+
+int s2_save_register(struct message *m);
+
+void s2_flush_all(void);
+
+void s2_setup_base(char const *hostname);
+void s2_setup_logs(int level);
+void s2_setup_tport(char const * const *protocols,
+ tag_type_t tag, tag_value_t value, ...);
+void s2_teardown(void);
+
+nua_t *s2_nua_setup(tag_type_t tag, tag_value_t value, ...);
+void s2_nua_teardown(void);
+
+void s2_register_setup(void);
+void s2_register_teardown(void);
+
+#endif
Added: freeswitch/trunk/libs/sofia-sip/tests/test_100rel.c
==============================================================================
--- (empty file)
+++ freeswitch/trunk/libs/sofia-sip/tests/test_100rel.c Wed May 14 15:10:54 2008
@@ -0,0 +1,2624 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2005 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at 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 test_nua_100rel.c
+ * @brief NUA-10 tests: early session, PRACK, UPDATE, precondition.
+ *
+ * @author Pekka Pessi <Pekka.Pessi at nokia.com>
+ * @author Martti Mela <Martti.Mela at nokia.com>
+ *
+ * @date Created: Wed Aug 17 12:12:12 EEST 2005 ppessi
+ */
+
+#include "config.h"
+
+#include "test_nua.h"
+#include <sofia-sip/auth_common.h>
+#include <sofia-sip/su_tag_class.h>
+
+#if HAVE_FUNC
+#elif HAVE_FUNCTION
+#define __func__ __FUNCTION__
+#else
+#define __func__ "test_call_hold"
+#endif
+
+/* ======================================================================== */
+
+/*
+ X accept_pracked ep
+ |-------INVITE------>|
+ | (sdp) |
+ | |
+ |<----100 Trying-----|
+ | |
+ |<-------180---------|
+ | (sdp) |
+ |-------PRACK------->|
+ |<-------200---------|
+ | |
+ |<------200 OK-------|
+ |--------ACK-------->|
+ | |
+*/
+int accept_pracked(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (event) {
+ case nua_i_prack:
+ if (200 <= status && status < 300) {
+ RESPOND(ep, call, nh, SIP_200_OK, TAG_END());
+ ep->next_condition = until_ready;
+ }
+ default:
+ break;
+ }
+
+ switch (callstate(tags)) {
+ case nua_callstate_received:
+ RESPOND(ep, call, nh, SIP_180_RINGING,
+ TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
+ TAG_END());
+ return 0;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int accept_pracked2(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_received:
+ RESPOND(ep, call, nh, SIP_180_RINGING,
+ TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
+ TAG_END());
+ RESPOND(ep, call, nh, SIP_200_OK,
+ NUTAG_INCLUDE_EXTRA_SDP(1),
+ TAG_END());
+ return 0;
+ case nua_callstate_ready:
+ return 1;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+
+int test_180rel(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e, *ep, *ei;
+ sip_t *sip;
+
+ if (print_headings)
+ printf("TEST NUA-10.1.1: Call with 100rel and 180\n");
+
+/* Test for 100rel:
+
+ A B
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<-------180---------|
+ |-------PRACK------->|
+ |<-------200---------|
+ | |
+ |<------200 OK-------|
+ |--------ACK-------->|
+ | |
+ |<-------BYE---------|
+ |-------200 OK-------|
+ | |
+
+*/
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+ nua_set_params(ctx->a.nua,
+ NUTAG_EARLY_MEDIA(1),
+ TAG_END());
+ run_a_until(ctx, nua_r_set_params, until_final_response);
+
+ nua_set_params(ctx->b.nua,
+ NUTAG_EARLY_MEDIA(1),
+ NUTAG_SESSION_TIMER(180),
+ TAG_END());
+ run_b_until(ctx, nua_r_set_params, until_final_response);
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ TAG_END());
+
+ run_ab_until(ctx, -1, until_ready, -1, accept_pracked2);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state, nua_r_prack
+ PROCEEDING -(C3+C4)-> READY: nua_r_invite, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(!is_offer_sent(e->data->e_tags));
+
+ ei = event_by_type(e->next, nua_r_invite);
+ ep = event_by_type(e->next, nua_r_prack);
+ if (!ep) {
+ run_a_until(ctx, -1, save_until_final_response);
+ ep = event_by_type(e->next, nua_r_prack);
+ }
+
+ TEST_1(e = ep); TEST_E(e->data->e_event, nua_r_prack);
+ TEST(e->data->e_status, 200);
+
+ TEST_1(e = ei); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_content_type);
+ TEST_S(sip->sip_content_type->c_type, "application/sdp");
+ TEST_1(sip->sip_payload);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+
+ TEST_1(!e->next || !ep->next);
+ free_events_in_list(ctx, a->events);
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+ EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+
+ /* Responded with 180 Ringing */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(is_answer_sent(e->data->e_tags));
+
+ /* 180 is PRACKed */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_prack);
+ /* Does not have effect on call state */
+
+ /* Respond with 200 OK */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-10.1.1: PASSED\n");
+
+ if (print_headings)
+ printf("TEST NUA-10.1.2: terminate call\n");
+
+ BYE(b, b_call, b_call->nh, TAG_END());
+ run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
+
+ /* B transitions:
+ READY --(T2)--> TERMINATING: nua_bye()
+ TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_r_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ /* A: READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_bye);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-10.1.2: PASSED\n");
+
+ END();
+}
+
+/*
+ X INVITE
+ | |
+ |-------INVITE------>|
+ |<--------200--------|
+ |---------ACK------->|
+*/
+int authenticate_until_ready(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ if (status == 401 || status == 407) {
+ AUTHENTICATE(ep, call, nh,
+ NUTAG_AUTH("Digest:\"test-proxy\":charlie:secret"),
+ TAG_END());
+ }
+
+ switch (callstate(tags)) {
+ case nua_callstate_ready:
+ return 1;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/** Test authentication for PRACK */
+int test_prack_auth(struct context *ctx)
+{
+ if (!ctx->proxy_tests)
+ return 0;
+
+ BEGIN();
+
+ struct endpoint *c = &ctx->c, *b = &ctx->b;
+ struct call *c_call = c->call, *b_call = b->call;
+ struct event *e, *ep, *ei;
+ sip_t *sip;
+ sip_proxy_authenticate_t *au;
+
+ if (print_headings)
+ printf("TEST NUA-10.1.3: Call with 100rel, PRACK is challenged\n");
+
+/* Test for authentication during 100rel
+
+ C B
+ |-------INVITE--\ |
+ |<-------407----/ |
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<-------180---------|
+ |-------PRACK---\ |
+ |<-------407----/ |
+ |-------PRACK------->|
+ |<-------200---------|
+ | |
+ |<------200 OK-------|
+ |--------ACK-------->|
+ | |
+ |<-------BYE---------|
+ |-------200 OK-------|
+ | |
+
+*/
+
+ c_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+ nua_set_params(ctx->c.nua,
+ NUTAG_EARLY_MEDIA(1),
+ TAG_END());
+ run_c_until(ctx, nua_r_set_params, until_final_response);
+
+ nua_set_params(ctx->b.nua,
+ NUTAG_EARLY_MEDIA(1),
+ TAG_END());
+ run_b_until(ctx, nua_r_set_params, until_final_response);
+
+ TEST_1(c_call->nh = nua_handle(c->nua, c_call, SIPTAG_TO(b->to), TAG_END()));
+
+ INVITE(c, c_call, c_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SIPTAG_FROM(c->to),
+ SOATAG_USER_SDP_STR(c_call->sdp),
+ TAG_END());
+
+ run_bc_until(ctx, -1, accept_pracked, -1, authenticate_until_ready);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state, nua_r_prack
+ PROCEEDING -(C3+C4)-> READY: nua_r_invite, nua_i_state
+ */
+ TEST_1(e = c->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 407);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(au = sip->sip_proxy_authenticate);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(!is_offer_sent(e->data->e_tags));
+
+ ei = event_by_type(e->next, nua_r_invite);
+ ep = event_by_type(e->next, nua_r_prack);
+
+ TEST_1(e = ep); TEST_E(e->data->e_event, nua_r_prack);
+ if (e->data->e_status == 100 || e->data->e_status == 407) {
+ /* The final response to PRACK may be received after ACK is sent */
+ if (!event_by_type(e->next, nua_r_prack))
+ run_bc_until(ctx, -1, save_events, -1, save_until_final_response);
+ TEST_1(e = ep = event_by_type(e->next, nua_r_prack));
+ }
+ TEST_E(e->data->e_event, nua_r_prack);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_from->a_url->url_user);
+
+ TEST_1(e = ei); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 200);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+ TEST_1(!e->next || !ep->next);
+ free_events_in_list(ctx, c->events);
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+ EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+
+ /* Responded with 180 Ringing */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(is_answer_sent(e->data->e_tags));
+
+ /* 180 is PRACKed */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_prack);
+ /* Does not have effect on call state */
+
+ /* Respond with 200 OK */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-10.1.3: PASSED\n");
+
+ if (print_headings)
+ printf("TEST NUA-10.1.4: terminate call\n");
+
+ BYE(b, b_call, b_call->nh, TAG_END());
+ run_bc_until(ctx, -1, until_terminated, -1, until_terminated);
+
+ /* B transitions:
+ READY --(T2)--> TERMINATING: nua_bye()
+ TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_r_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ /* C: READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state */
+ TEST_1(e = c->events->head); TEST_E(e->data->e_event, nua_i_bye);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, c->events);
+
+ nua_handle_destroy(c_call->nh), c_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-10.1.4: PASSED\n");
+
+ END();
+}
+
+/*
+ X ringing_pracked ep
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<-------183---------|
+ |-------PRACK------->|
+ |<-------200---------|
+ | |
+ |<-------180---------|
+ |-------PRACK------->|
+ |<-------200---------|
+ | |
+ |<------200 OK-------|
+ |--------ACK-------->|
+ | |
+*/
+int ringing_pracked(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (event) {
+ case nua_i_prack:
+ if (200 <= status && status < 300) {
+ RESPOND(ep, call, nh, SIP_180_RINGING, TAG_END());
+ ep->next_condition = accept_pracked;
+ }
+ default:
+ break;
+ }
+
+ switch (callstate(tags)) {
+ case nua_callstate_received:
+ RESPOND(ep, call, nh, SIP_183_SESSION_PROGRESS,
+ TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
+ TAG_END());
+ return 0;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int respond_483_to_prack(CONDITION_PARAMS);
+static int prack_100rel(CONDITION_PARAMS);
+
+int test_183rel(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e, *ei, *ep;
+
+ if (print_headings)
+ printf("TEST NUA-10.2.1: Call with 100rel, 183 and 180\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ TAG_END());
+
+ run_ab_until(ctx, -1, until_ready, -1, ringing_pracked);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state, nua_r_prack
+ PROCEEDING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state, nua_r_prack
+ PROCEEDING -(C3+C4)-> READY: nua_r_invite, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 183);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(!is_offer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next);
+
+ ep = e->data->e_event == nua_r_prack ? e : NULL;
+
+ if (ep) {
+ TEST(ep->data->e_status, 200); TEST_1(e = e->next);
+ }
+
+ TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+
+ if (!ep) {
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_prack);
+ TEST(e->data->e_status, 200);
+ }
+
+ ei = event_by_type(e->next, nua_r_invite);
+ ep = event_by_type(e->next, nua_r_prack);
+ if (!ep) {
+ run_a_until(ctx, -1, save_until_final_response);
+ ep = event_by_type(e->next, nua_r_prack);
+ }
+
+ TEST_1(e = ep); TEST_E(e->data->e_event, nua_r_prack);
+ TEST(e->data->e_status, 200);
+
+ TEST_1(e = ei); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 200);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+ TEST_1(!e->next || !ep->next);
+ free_events_in_list(ctx, a->events);
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+ EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+
+ /* Responded with 183 Session Progress */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(e->data->e_status, 183);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(is_answer_sent(e->data->e_tags));
+
+ /* 183 is PRACKed */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_prack);
+ /* Does not have effect on call state */
+
+ /* Responded with 180 Ringing */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(e->data->e_status, 180);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+
+ /* 180 is PRACKed */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_prack);
+ /* Does not have effect on call state */
+
+ /* Respond with 200 OK */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(e->data->e_status, 200);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-10.2.1: PASSED\n");
+
+ /* Test for graceful termination by client because 483 sent to PRACK */
+ if (print_headings)
+ printf("TEST NUA-10.2.2: graceful termination because PRACK fails\n");
+
+ nua_set_hparams(a_call->nh, NUTAG_APPL_METHOD("PRACK"), TAG_END());
+ nua_set_hparams(b_call->nh, NUTAG_APPL_METHOD("PRACK"),
+ NUTAG_AUTOANSWER(0), TAG_END());
+ run_ab_until(ctx, nua_r_set_params, NULL, nua_r_set_params, NULL);
+
+ INVITE(a, a_call, a_call->nh, TAG_END());
+
+ run_ab_until(ctx, -1, prack_100rel, -1, respond_483_to_prack);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C2)-> TERMINATING: nua_r_invite, nua_i_state,
+ nua_r_prack, nua_i_state
+ TERMINATING -(T1)-> TERMINATED: nua_r_invite, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 183);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(!is_offer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_prack);
+ TEST(e->data->e_status, 483);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminating);
+
+ {
+ int bye = 1, cancel = 1, invite = 1;
+
+ while (bye || cancel || invite) {
+ TEST_1(e = e->next);
+ if (e->data->e_event == nua_r_bye) {
+ TEST_E(e->data->e_event, nua_r_bye);
+ TEST(e->data->e_status, 200);
+ bye = 0;
+ break;
+ }
+ else if (e->data->e_event == nua_r_invite) {
+ TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 487);
+ invite = 0;
+ }
+ else if (e->data->e_event == nua_r_cancel) {
+ TEST_E(e->data->e_event, nua_r_cancel);
+ TEST_1(e->data->e_status == 200 || e->data->e_status == 481);
+ cancel = 0;
+ }
+ }
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated);
+ }
+
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state, nua_respond(to PRACK)
+ EARLY -(S3b)-> TERMINATED: nua_i_bye, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+
+ /* Responded with 183 Session Progress */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(e->data->e_status, 183);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(is_answer_sent(e->data->e_tags));
+
+ /* 183 is PRACKed, PRACK is responded with 483 */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_prack);
+
+ /* Client terminates the call
+ - we may (it is received before BYE) or may not (received after BYE)
+ get CANCEL request
+ */
+ TEST_1(e = e->next);
+ if (e->data->e_event == nua_i_cancel) {
+ TEST_E(e->data->e_event, nua_i_cancel);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready);
+ TEST_1(e = e->next);
+ }
+
+ TEST_E(e->data->e_event, nua_i_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, b->events);
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-10.2.2: PASSED\n");
+
+ END();
+}
+
+static int prack_100rel(CONDITION_PARAMS)
+{
+ if (!check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ if (event == nua_r_invite && 100 < status && status < 200 &&
+ sip_has_feature(sip->sip_require, "100rel")) {
+ sip_rack_t rack[1];
+
+ sip_rack_init(rack);
+ rack->ra_response = sip->sip_rseq->rs_response;
+ rack->ra_cseq = sip->sip_cseq->cs_seq;
+ rack->ra_method = sip->sip_cseq->cs_method;
+ rack->ra_method_name = sip->sip_cseq->cs_method_name;
+
+ nua_prack(nh, SIPTAG_RACK(rack), TAG_END());
+ }
+
+ return event == nua_i_state && callstate(tags) == nua_callstate_terminated;
+}
+
+int respond_483_to_prack(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ switch (event) {
+ case nua_i_prack:
+ if (status <= 200) {
+ RESPOND(ep, call, nh, 483, "Foo",
+ NUTAG_WITH_THIS(nua),
+ TAG_END());
+ }
+ default:
+ break;
+ }
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_received:
+ RESPOND(ep, call, nh, SIP_183_SESSION_PROGRESS,
+ TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
+ TAG_END());
+ return 0;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/*
+ X ringing_updated ep
+ |-------INVITE------>|
+ | (sdp) |
+ |<----100 Trying-----|
+ | |
+ |<-------183---------|
+ | (sdp) |
+ |-------PRACK------->|
+ | (sdp) |
+ |<-------200---------|
+ | (sdp) |
+ | |
+ |-------UPDATE------>|
+ | (sdp) |
+ |<-------200---------|
+ | (sdp) |
+ | |
+<using acccept_pracked>
+ | |
+ |<-------180---------|
+ |-------PRACK------->|
+ |<-------200---------|
+ | |
+ |<------200 OK-------|
+ |--------ACK-------->|
+ | |
+*/
+int ringing_updated(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (event) {
+ case nua_i_update:
+ if (200 <= status && status < 300) {
+ RESPOND(ep, call, nh, SIP_180_RINGING, TAG_END());
+ ep->next_condition = accept_pracked;
+ }
+ return 0;
+ default:
+ break;
+ }
+
+ switch (callstate(tags)) {
+ case nua_callstate_received:
+ RESPOND(ep, call, nh, SIP_183_SESSION_PROGRESS,
+ SIPTAG_REQUIRE_STR("100rel"),
+ TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
+ TAG_END());
+ return 0;
+ case nua_callstate_early:
+ return 0;
+ case nua_callstate_ready:
+ return 1;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int test_preconditions(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e, *ep, *ei;
+ sip_t *sip;
+
+ if (print_headings)
+ printf("TEST NUA-10.3.1: Call with 100rel and preconditions\n");
+
+/* Test for precondition:
+
+ A B
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<-------183---------|
+ |-------PRACK------->|
+ |<-------200---------|
+ | |
+ |-------UPDATE------>|
+ |<-------200---------|
+ | |
+ |<-------180---------|
+ |-------PRACK------->|
+ |<-------200---------|
+ | |
+ |<------200 OK-------|
+ |--------ACK-------->|
+ | |
+ |<-------BYE---------|
+ |-------200 OK-------|
+ | |
+
+*/
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+ nua_set_params(ctx->a.nua,
+ NUTAG_EARLY_MEDIA(1),
+ SIPTAG_SUPPORTED_STR("100rel, precondition"),
+ TAG_END());
+ run_a_until(ctx, nua_r_set_params, until_final_response);
+
+ nua_set_params(ctx->b.nua,
+ NUTAG_EARLY_MEDIA(0),
+ SIPTAG_SUPPORTED_STR("100rel, precondition, timer"),
+ TAG_END());
+ run_b_until(ctx, nua_r_set_params, until_final_response);
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ SIPTAG_SUPPORTED_STR("100rel"),
+ SIPTAG_REQUIRE_STR("precondition"),
+ TAG_END());
+
+ run_ab_until(ctx, -1, until_ready, -1, ringing_updated);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state
+ PROCEEDING --> PROCEEDING: nua_r_prack, nua_i_state
+ PROCEEDING --> PROCEEDING: nua_r_update, nua_i_state
+ PROCEEDING -(C3+C4)-> READY: nua_r_invite, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 183);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(is_offer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_prack);
+ TEST(e->data->e_status, 200);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(!is_offer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
+ TEST_1(!is_answer_recv(e->data->e_tags));
+ TEST_1(is_offer_sent(e->data->e_tags));
+
+ ei = event_by_type(e->next, nua_r_invite);
+ ep = event_by_type(e->next, nua_r_update);
+
+ TEST_1(e = ep); TEST_E(e->data->e_event, nua_r_update);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_session_expires);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(!is_offer_sent(e->data->e_tags));
+
+ TEST_1(e = ei); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+
+ if (e == ep) /* invite was responded before update */
+ e = ep->next->next;
+
+ ei = event_by_type(e->next, nua_r_invite);
+ ep = event_by_type(e->next, nua_r_prack);
+ if (!ep) {
+ run_a_until(ctx, -1, save_until_final_response);
+ ep = event_by_type(e->next, nua_r_prack);
+ }
+
+ TEST_1(e = ep); TEST_E(e->data->e_event, nua_r_prack);
+ TEST(e->data->e_status, 200);
+ /* Does not have effect on call state */
+
+ TEST_1(e = ei); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ if (ctx->proxy_tests) {
+ TEST_1(sip->sip_session_expires);
+ TEST_S(sip->sip_session_expires->x_refresher, "uas");
+ TEST_1(!sip_has_supported(sip->sip_require, "timer"));
+ }
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+ TEST_1(!e->next || !ep->next);
+ free_events_in_list(ctx, a->events);
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+ EARLY --> EARLY: nua_i_prack, nua_i_state
+ EARLY --> EARLY: nua_i_update, nua_i_state
+ EARLY --> EARLY: nua_r_update, nua_i_state
+ EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+
+ /* Responded with 183 Session Progress */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(e->data->e_status, 183);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(is_answer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_prack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(is_answer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_update);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(!is_offer_sent(e->data->e_tags));
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST_1(!is_answer_recv(e->data->e_tags));
+
+ /* Responded with 180 Ringing */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(e->data->e_status, 180);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+
+ /* 180 PRACKed */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_prack);
+ /* Does not have effect on call state */
+
+ /* Responded with 200 OK */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(e->data->e_status, 200);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-10.3.1: PASSED\n");
+
+ if (print_headings)
+ printf("TEST NUA-10.3.2: terminate call\n");
+
+ BYE(b, b_call, b_call->nh, TAG_END());
+ run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
+
+ /* B transitions:
+ READY --(T2)--> TERMINATING: nua_bye()
+ TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_r_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ /* A: READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_bye);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-10.3.2: PASSED\n");
+
+ END();
+}
+
+/*
+ X accept_updated ep
+ |-------INVITE------>|
+ | (sdp) |
+ |<----100 Trying-----|
+ | |
+ |<-------183---------|
+ | (sdp) |
+ |-------PRACK------->|
+ | (sdp) |
+ |<-------200---------|
+ | (sdp) |
+ | |
+ |-------UPDATE------>|
+ | (sdp) |
+ |<-------200---------|
+ | (sdp) |
+ | |
+ | |
+ |<-------180---------|
+ | |
+ |<------200 OK-------|
+ |--------ACK-------->|
+ | |
+*/
+int accept_updated(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (event) {
+ case nua_i_update:
+ if (200 <= status && status < 300) {
+ RESPOND(ep, call, nh, SIP_180_RINGING, TAG_END());
+ }
+ return 0;
+ default:
+ break;
+ }
+
+ switch (callstate(tags)) {
+ case nua_callstate_received:
+ RESPOND(ep, call, nh, SIP_183_SESSION_PROGRESS,
+ SIPTAG_REQUIRE_STR("100rel"),
+ TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
+ TAG_END());
+ return 0;
+ case nua_callstate_early:
+ if (status == 180)
+ RESPOND(ep, call, nh, SIP_200_OK, TAG_END());
+ return 0;
+ case nua_callstate_ready:
+ return 1;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+
+int test_preconditions2(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e, *eu, *ei;
+ enum nua_callstate ustate, istate;
+
+ if (print_headings)
+ printf("TEST NUA-10.4.1: Call with preconditions and non-100rel 180\n");
+
+/* Test 100rel and preconditions with NUTAG_ONLY183_100REL(1):
+
+ A B
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<-------183---------|
+ |-------PRACK------->|
+ |<-------200---------|
+ | |
+ |-------UPDATE------>|
+ +------------------------+
+ | |<-------200---------| |
+ | | | |
+ | |<-------180---------| |
+ | | | |
+ | |<------200 OK-------| |
+ +------------------------+
+ |--------ACK-------->|
+ | |
+ |<-------BYE---------|
+ |-------200 OK-------|
+ | |
+
+ Note that the boxed responses above can be re-ordered
+ (180 or 200 OK to INVITE is received before 200 OK to UPDATE).
+ ACK, however, is sent only after 200 OK to both UPDATE and INVITE.
+*/
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+ nua_set_params(ctx->a.nua,
+ NUTAG_EARLY_MEDIA(1),
+ SIPTAG_SUPPORTED_STR("100rel, precondition, timer"),
+ TAG_END());
+ run_a_until(ctx, nua_r_set_params, until_final_response);
+
+ nua_set_params(ctx->b.nua,
+ NUTAG_EARLY_MEDIA(1),
+ NUTAG_ONLY183_100REL(1),
+ SIPTAG_SUPPORTED_STR("100rel, precondition, timer"),
+ TAG_END());
+ run_b_until(ctx, nua_r_set_params, until_final_response);
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ SIPTAG_SUPPORTED_STR("100rel"),
+ SIPTAG_REQUIRE_STR("precondition"),
+ TAG_END());
+
+ run_ab_until(ctx, -1, until_ready, -1, accept_updated);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state
+ PROCEEDING -(C3+C4)-> READY: nua_r_invite, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 183);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
+ TEST_1(is_answer_recv(e->data->e_tags));
+ /* Offer is sent in PRACK */
+ TEST_1(is_offer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_prack);
+ TEST(e->data->e_status, 200);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(!is_offer_sent(e->data->e_tags));
+
+ /* Send UPDATE */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
+ TEST_1(!is_answer_recv(e->data->e_tags));
+ TEST_1(is_offer_sent(e->data->e_tags));
+
+ /* The final response to the UPDATE and INVITE can be received in any order */
+ eu = event_by_type(e->next, nua_r_update);
+ ei = event_by_type(e->next, nua_r_invite);
+
+ TEST_1(e = eu); TEST_E(e->data->e_event, nua_r_update);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ ustate = callstate(e->data->e_tags);
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(!is_offer_sent(e->data->e_tags));
+
+ TEST_1(e = ei); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+
+ /* Final response to INVITE */
+ TEST_1(ei = event_by_type(ei->next, nua_r_invite));
+
+ TEST_E(ei->data->e_event, nua_r_invite); TEST(ei->data->e_status, 200);
+ TEST_1(e = ei->next); TEST_E(e->data->e_event, nua_i_state);
+ istate = callstate(e->data->e_tags);
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+
+ if (eu == e->next) {
+ /* 200 OK to UPDATE is received after 200 OK to INVITE */
+ TEST(ustate, nua_callstate_ready);
+ TEST(istate, nua_callstate_completing);
+ }
+ else {
+ /* 200 OK to UPDATE is received before 200 OK to INVITE */
+ TEST(ustate, nua_callstate_proceeding);
+ TEST(istate, nua_callstate_ready);
+ }
+
+ free_events_in_list(ctx, a->events);
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+ EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+
+ /* Responded with 183 Session Progress */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(is_answer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_prack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(is_answer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_update);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(is_answer_sent(e->data->e_tags));
+
+ /* Responded with 180 Ringing */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+
+ /* Responded with 200 OK */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-10.4.1: PASSED\n");
+
+ if (print_headings)
+ printf("TEST NUA-10.4.2: terminate call\n");
+
+ BYE(b, b_call, b_call->nh, TAG_END());
+ run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
+
+ /* B transitions:
+ READY --(T2)--> TERMINATING: nua_bye()
+ TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_r_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ /* A: READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_bye);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-10.4.2: PASSED\n");
+
+ END();
+}
+
+/*
+ X ringing_updated2 ep
+ |-------INVITE------>|
+ | (sdp) |
+ |<----100 Trying-----|
+ | |
+ |<-------183---------|
+ | (sdp) |
+ |-------PRACK------->|
+ | (sdp) |
+ |<-------200---------|
+ | (sdp) |
+ | |
+ |-------UPDATE------>|
+ | (sdp) |
+ |<-------200---------|
+ | (sdp) |
+ | |
+ |<------UPDATE-------|
+ | (sdp) |
+ |--------200-------->|
+ | (sdp) |
+ | |
+<using acccept_pracked>
+ | |
+ |<-------180---------|
+ |-------PRACK------->|
+ |<-------200---------|
+ | |
+ |<------200 OK-------|
+ |--------ACK-------->|
+ | |
+*/
+int ringing_updated2(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (event) {
+ case nua_i_update:
+ if (200 <= status && status < 300) {
+ UPDATE(ep, call, nh, TAG_END());
+ }
+ return 0;
+ case nua_r_update:
+ if (200 <= status && status < 300) {
+ RESPOND(ep, call, nh, SIP_180_RINGING,
+ SIPTAG_REQUIRE_STR("100rel"),
+ TAG_END());
+ ep->next_condition = accept_pracked;
+ }
+ else if (300 <= status) {
+ RESPOND(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR, TAG_END());
+ }
+ return 0;
+ default:
+ break;
+ }
+
+ switch (callstate(tags)) {
+ case nua_callstate_received:
+ RESPOND(ep, call, nh, SIP_183_SESSION_PROGRESS,
+ SIPTAG_REQUIRE_STR("100rel"),
+ TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
+ TAG_END());
+ return 0;
+ case nua_callstate_early:
+ return 0;
+ case nua_callstate_ready:
+ return 1;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int test_update_by_uas(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e, *ep, *ei;
+ sip_t *sip;
+
+ /* -------------------------------------------------------------------- */
+
+ if (print_headings)
+ printf("TEST NUA-10.5.1: Call with dual UPDATE\n");
+
+/* Test for update by UAS.
+
+ A B
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<-------183---------|
+ |-------PRACK------->|
+ |<-------200---------|
+ | |
+ |-------UPDATE------>|
+ +------------------------+
+ | |<-------200---------| |
+ | | | |
+ | |<------UPDATE-------| |
+ +------------------------+
+ |--------200-------->|
+ | |
+ |<-------180---------|
+ |-------PRACK------->|
+ |<-------200---------|
+ | |
+ |<------200 OK-------|
+ |--------ACK-------->|
+ | |
+ |<-------BYE---------|
+ |-------200 OK-------|
+ | |
+
+ Note that the 200 OK to UPDATE from A and UPDATE from B may be re-ordered
+ In that case, A will respond with 500/Retry-After and B will retry UPDATE.
+ See do {} while () loop below.
+*/
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+ nua_set_params(ctx->a.nua,
+ NUTAG_EARLY_MEDIA(1),
+ SIPTAG_SUPPORTED_STR("100rel, precondition"),
+ TAG_END());
+ run_a_until(ctx, nua_r_set_params, until_final_response);
+
+ nua_set_params(ctx->b.nua,
+ NUTAG_EARLY_MEDIA(0),
+ SIPTAG_SUPPORTED_STR("100rel, precondition, timer"),
+ TAG_END());
+ run_b_until(ctx, nua_r_set_params, until_final_response);
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ SIPTAG_SUPPORTED_STR("100rel"),
+ SIPTAG_REQUIRE_STR("precondition"),
+ TAG_END());
+
+ run_ab_until(ctx, -1, until_ready, -1, ringing_updated2);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state
+ PROCEEDING: nua_r_prack, nua_i_state
+ PROCEEDING: nua_r_update, nua_i_state
+ PROCEEDING: nua_i_update, nua_i_state
+ PROCEEDING -(C3+C4)-> READY: nua_r_invite, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 183);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(is_offer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_prack);
+ TEST(e->data->e_status, 200);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(!is_offer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
+ TEST_1(!is_answer_recv(e->data->e_tags));
+ TEST_1(is_offer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_update);
+ TEST(e->data->e_status, 200);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(!is_offer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_update);
+ TEST(e->data->e_status, 200);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(is_answer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+
+ ei = event_by_type(e->next, nua_r_invite);
+ ep = event_by_type(e->next, nua_r_prack);
+ if (!ep) {
+ run_a_until(ctx, -1, save_until_final_response);
+ ep = event_by_type(e->next, nua_r_prack);
+ }
+
+ TEST_1(e = ep); TEST_E(e->data->e_event, nua_r_prack);
+ TEST(e->data->e_status, 200);
+ /* Does not have effect on call state */
+
+ TEST_1(e = ei); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 200);
+ if (ctx->proxy_tests) {
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_session_expires);
+ TEST_S(sip->sip_session_expires->x_refresher, "uas");
+ TEST_1(!sip_has_supported(sip->sip_require, "timer"));
+ }
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+ TEST_1(!e->next || !ep->next);
+ free_events_in_list(ctx, a->events);
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(183), nua_i_state
+ EARLY --> EARLY: nua_i_prack, nua_i_state
+ EARLY --> EARLY: nua_i_update, nua_i_state
+ EARLY --> EARLY: nua_update(), nua_i_state
+ EARLY --> EARLY: nua_r_update, nua_i_state
+ EARLY --> EARLY: nua_respond(180), nua_i_state
+ EARLY --> EARLY: nua_i_prack, nua_i_state
+ EARLY -(S3b)-> COMPLETED: nua_respond(200), nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+
+ /* Responded with 183 Session Progress */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(e->data->e_status, 183);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(is_answer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_prack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(is_answer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_update);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(is_answer_sent(e->data->e_tags));
+
+ /* sent UPDATE */
+ do {
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(!is_offer_recv(e->data->e_tags));
+ TEST_1(!is_answer_sent(e->data->e_tags));
+ TEST_1(!is_answer_recv(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_update);
+ if (e->data->e_status == 100) {
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST(sip->sip_status->st_status, 500); TEST_1(sip->sip_retry_after);
+ }
+ } while (e->data->e_status == 100);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(!is_offer_sent(e->data->e_tags)); /* XXX */
+ TEST_1(!is_offer_recv(e->data->e_tags));
+ TEST_1(!is_answer_sent(e->data->e_tags));
+ TEST_1(is_answer_recv(e->data->e_tags));
+
+ /* Responded with 180 Ringing */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(e->data->e_status, 180);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+
+ /* 180 PRACKed */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_prack);
+ /* Does not have effect on call state */
+
+ /* Responded with 200 OK */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(e->data->e_status, 200);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-10.5.1: PASSED\n");
+
+ if (print_headings)
+ printf("TEST NUA-10.5.2: terminate call\n");
+
+ BYE(b, b_call, b_call->nh, TAG_END());
+ run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
+
+ /* B transitions:
+ READY --(T2)--> TERMINATING: nua_bye()
+ TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_r_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ /* A: READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_bye);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-10.5.2: PASSED\n");
+
+ END();
+}
+
+int test_update_failure(struct context *ctx)
+{
+ BEGIN();
+#if 0
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e, *eu, *ei;
+ enum nua_callstate ustate, istate;
+
+ if (print_headings)
+ printf("TEST NUA-10.4.1: UPDATE failure terminating session\n");
+
+/* Test UPDATE failing:
+
+ A B
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<-------183---------|
+ |-------PRACK------->|
+ |<-------200---------|
+ | |
+ |<-------180---------|
+ | |
+ +------------------------+
+ |<------200 OK-------|
+ |-------UPDATE------>|
+ | |
+ |<-------481---------|
+ | |
+ |--------ACK-------->|
+ | |
+ |<-------BYE---------|
+ |-------200 OK-------|
+ | |
+
+*/
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+ nua_set_params(ctx->a.nua,
+ NUTAG_EARLY_MEDIA(1),
+ SIPTAG_SUPPORTED_STR("100rel, precondition, timer"),
+ TAG_END());
+ run_a_until(ctx, nua_r_set_params, until_final_response);
+
+ nua_set_params(ctx->b.nua,
+ NUTAG_EARLY_MEDIA(1),
+ NUTAG_ONLY183_100REL(1),
+ SIPTAG_SUPPORTED_STR("100rel, precondition, timer"),
+ NUTAG_APPL_METHOD("UPDATE"),
+ TAG_END());
+ run_b_until(ctx, nua_r_set_params, until_final_response);
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ SIPTAG_SUPPORTED_STR("100rel"),
+ TAG_END());
+
+ run_ab_until(ctx, -1, save_until_final_response, -1, until_pracked);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state
+ PROCEEDING -(C3+C4)-> READY: nua_r_invite, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 183);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
+ TEST_1(is_answer_recv(e->data->e_tags));
+ /* Offer is sent in PRACK */
+ TEST_1(is_offer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_prack);
+ TEST(e->data->e_status, 200);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(!is_offer_sent(e->data->e_tags));
+
+ /* Send UPDATE */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
+ TEST_1(!is_answer_recv(e->data->e_tags));
+ TEST_1(is_offer_sent(e->data->e_tags));
+
+ /* The final response to the UPDATE and INVITE can be received in any order */
+ eu = event_by_type(e->next, nua_r_update);
+ ei = event_by_type(e->next, nua_r_invite);
+
+ TEST_1(e = eu); TEST_E(e->data->e_event, nua_r_update);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ ustate = callstate(e->data->e_tags);
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(!is_offer_sent(e->data->e_tags));
+
+ TEST_1(e = ei); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+
+ /* Final response to INVITE */
+ TEST_1(ei = event_by_type(ei->next, nua_r_invite));
+
+ TEST_E(ei->data->e_event, nua_r_invite); TEST(ei->data->e_status, 200);
+ TEST_1(e = ei->next); TEST_E(e->data->e_event, nua_i_state);
+ istate = callstate(e->data->e_tags);
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+
+ if (eu == e->next) {
+ /* 200 OK to UPDATE is received after 200 OK to INVITE */
+ TEST(ustate, nua_callstate_ready);
+ TEST(istate, nua_callstate_completing);
+ }
+ else {
+ /* 200 OK to UPDATE is received before 200 OK to INVITE */
+ TEST(ustate, nua_callstate_proceeding);
+ TEST(istate, nua_callstate_ready);
+ }
+
+ free_events_in_list(ctx, a->events);
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+ EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+
+ /* Responded with 183 Session Progress */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(is_answer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_prack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(is_answer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_update);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(is_answer_sent(e->data->e_tags));
+
+ /* Responded with 180 Ringing */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+
+ /* Responded with 200 OK */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-10.4.1: PASSED\n");
+
+ if (print_headings)
+ printf("TEST NUA-10.4.2: terminate call\n");
+
+ BYE(b, b_call, b_call->nh, TAG_END());
+ run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
+
+ /* B transitions:
+ READY --(T2)--> TERMINATING: nua_bye()
+ TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_r_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ /* A: READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_bye);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-10.4.2: PASSED\n");
+#endif
+ END();
+}
+
+int cancel_when_pracked(CONDITION_PARAMS);
+int alert_call(CONDITION_PARAMS);
+
+int test_180rel_cancel1(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+
+ if (print_headings)
+ printf("TEST NUA-10.6: CANCEL after PRACK\n");
+
+/* Test for 100rel:
+
+ A B
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<-------180---------|
+ |-------PRACK------->|
+ |<-------200---------|
+ | |
+ |------CANCEL------->|
+ |<------200 OK-------|
+ | |
+ |<-------487---------|
+ |--------ACK-------->|
+ | |
+
+*/
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+ nua_set_params(ctx->a.nua,
+ NUTAG_EARLY_MEDIA(1),
+ NUTAG_ONLY183_100REL(0),
+ TAG_END());
+ run_a_until(ctx, nua_r_set_params, until_final_response);
+
+ nua_set_params(ctx->b.nua,
+ NUTAG_EARLY_MEDIA(1),
+ NUTAG_ONLY183_100REL(0),
+ TAG_END());
+ run_b_until(ctx, nua_r_set_params, until_final_response);
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ TAG_END());
+
+ run_ab_until(ctx, -1, cancel_when_pracked, -1, alert_call);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state, nua_r_prack
+ PROCEEDING -(C3+C4)-> TERMINATED: nua_r_invite, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(!is_offer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_prack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_cancel);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 487);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+ Option A:
+ EARLY -(S10)-> TERMINATED: nua_i_cancel, nua_i_state
+ Option B:
+ EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+
+ /* Responded with 180 Ringing */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(is_answer_sent(e->data->e_tags));
+
+ /* 180 is PRACKed */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_prack);
+ /* Does not have effect on call state */
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_cancel);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+
+ free_events_in_list(ctx, b->events);
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-10.6: PASSED\n");
+
+ END();
+}
+
+int cancel_when_pracked(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ if (event == nua_r_prack)
+ CANCEL(ep, call, nh, TAG_END());
+
+ switch (callstate(tags)) {
+ case nua_callstate_proceeding:
+ return 0;
+ case nua_callstate_ready:
+ return 1;
+ case nua_callstate_terminated:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int test_180rel_cancel2(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e, *ep, *ec;
+
+ if (print_headings)
+ printf("TEST NUA-10.7: CANCEL after 100rel 180\n");
+
+/* Test for 100rel:
+
+ A B
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<-------180---------|
+ |-------PRACK------->|
+ |<-------200---------|
+ | |
+ |------CANCEL------->|
+ |<------200 OK-------|
+ | |
+ |<-------487---------|
+ |--------ACK-------->|
+ | |
+
+*/
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+ nua_set_params(ctx->a.nua,
+ NUTAG_EARLY_MEDIA(1),
+ NUTAG_ONLY183_100REL(0),
+ TAG_END());
+ run_a_until(ctx, nua_r_set_params, until_final_response);
+
+ nua_set_params(ctx->b.nua,
+ NUTAG_EARLY_MEDIA(1),
+ NUTAG_ONLY183_100REL(0),
+ TAG_END());
+ run_b_until(ctx, nua_r_set_params, until_final_response);
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ TAG_END());
+
+ run_ab_until(ctx, -1, cancel_when_ringing, -1, accept_pracked2);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state, nua_r_prack
+ PROCEEDING -(C3+C4)-> TERMINATED: nua_r_invite, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(!is_offer_sent(e->data->e_tags));
+
+#define NEXT_SKIP(x) \
+ do { TEST_1(e = e->next); } \
+ while (x);
+
+ NEXT_SKIP(e->data->e_event == nua_r_prack ||
+ e->data->e_event == nua_r_cancel ||
+ e->data->e_event == nua_i_state);
+
+ TEST_E(e->data->e_event, nua_r_invite);
+ if (e->data->e_status == 487) {
+ TEST(e->data->e_status, 487);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated);
+ if (e->next)
+ NEXT_SKIP(e->data->e_event == nua_r_prack || e->data->e_event == nua_r_cancel);
+ TEST_1(!e->next);
+ }
+ else {
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready);
+
+ BYE(a, a_call, a_call->nh, TAG_END());
+ run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
+
+ NEXT_SKIP(e->data->e_event == nua_r_prack || e->data->e_event == nua_r_cancel);
+ TEST_E(e->data->e_event, nua_r_bye);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ }
+
+ free_events_in_list(ctx, a->events);
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+ Option A:
+ EARLY -(S10)-> TERMINATED: nua_i_cancel, nua_i_state
+ Option B:
+ EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+
+ /* Responded with 180 Ringing */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(is_answer_sent(e->data->e_tags));
+
+ ec = event_by_type(e->next, nua_i_cancel);
+
+ if (ec) {
+ TEST_1(e = ec); TEST_E(e->data->e_event, nua_i_cancel);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ }
+ else {
+ /* 180 is PRACKed, PRACK does not have effect on call state */
+ ep = event_by_type(e->next, nua_i_prack);
+ if (ep) e = ep;
+ /* Responded with 200 OK */
+ TEST_1(e = event_by_type(e->next, nua_i_state));
+ TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ }
+
+ free_events_in_list(ctx, b->events);
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-10.7: PASSED\n");
+
+ END();
+}
+
+int redirect_pracked(CONDITION_PARAMS);
+
+int test_180rel_redirected(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e, *ep, *ei;
+ sip_t *sip;
+
+ if (print_headings)
+ printf("TEST NUA-10.8.1: Call with 100rel and 180\n");
+
+/* Test for 100rel:
+
+ A B
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<-------180---------|
+ |-------PRACK------->|
+ |<-------200---------|
+ | |
+ |<----302 Moved------|
+ |--------ACK-------->|
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<-------180---------|
+ |-------PRACK------->|
+ |<-------200---------|
+ | |
+ |<------200 OK-------|
+ |--------ACK-------->|
+ | |
+ |<-------BYE---------|
+ |-------200 OK-------|
+ | |
+
+*/
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+ nua_set_params(ctx->a.nua,
+ NUTAG_EARLY_MEDIA(1),
+ TAG_END());
+ run_a_until(ctx, nua_r_set_params, until_final_response);
+
+ nua_set_params(ctx->b.nua,
+ NUTAG_EARLY_MEDIA(1),
+ NUTAG_SESSION_TIMER(180),
+ TAG_END());
+ run_b_until(ctx, nua_r_set_params, until_final_response);
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ TAG_END());
+
+ run_ab_until(ctx, -1, until_ready, -1, redirect_pracked);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state, nua_r_prack
+ PROCEEDING->(redirected)->CALLING: nua_r_invite, nua_i_state
+ CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state, nua_r_prack
+ PROCEEDING -(C3+C4)-> READY: nua_r_invite, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(!is_offer_sent(e->data->e_tags));
+
+ ei = event_by_type(e->next, nua_r_invite); /* 302 */
+ ep = event_by_type(e->next, nua_r_prack); /* 200 */
+ if (!ep) {
+ run_a_until(ctx, -1, save_until_final_response);
+ ep = event_by_type(e->next, nua_r_prack);
+ }
+
+ TEST_1(e = ep); TEST_E(e->data->e_event, nua_r_prack);
+ TEST(e->data->e_status, 200);
+
+ TEST_1(e = ei); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST(sip->sip_status->st_status, 302);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(!is_offer_sent(e->data->e_tags));
+
+ ei = event_by_type(e->next, nua_r_invite);
+ ep = event_by_type(e->next, nua_r_prack);
+ if (!ep) {
+ run_a_until(ctx, -1, save_until_final_response);
+ ep = event_by_type(e->next, nua_r_prack);
+ }
+
+ TEST_1(e = ep); TEST_E(e->data->e_event, nua_r_prack);
+ TEST(e->data->e_status, 200);
+
+ TEST_1(e = ei); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 200);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+
+ TEST_1(!e->next || !ep->next);
+ free_events_in_list(ctx, a->events);
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+ EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+
+ /* Responded with 180 Ringing */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(is_answer_sent(e->data->e_tags));
+
+ /* 180 is PRACKed */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_prack);
+ /* 302 terminates call */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* Terminated */
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+
+ /* Responded with 180 Ringing */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(is_answer_sent(e->data->e_tags));
+
+ /* 180 is PRACKed */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_prack);
+ /* Does not have effect on call state */
+
+ /* Respond with 200 OK */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!is_offer_answer_done(e->data->e_tags));
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-10.8.1: PASSED\n");
+
+ if (print_headings)
+ printf("TEST NUA-10.8.2: terminate call\n");
+
+ BYE(b, b_call, b_call->nh, TAG_END());
+ run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
+
+ /* B transitions:
+ READY --(T2)--> TERMINATING: nua_bye()
+ TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_r_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ /* A: READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_bye);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-10.8.2: PASSED\n");
+
+ END();
+}
+
+int redirect_pracked(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ if (event == nua_i_prack) {
+ if (!ep->flags.bit0) {
+ sip_contact_t m[1];
+
+ ep->flags.bit0 = 1;
+
+ *m = *ep->contact;
+ m->m_url->url_user = "302";
+ RESPOND(ep, call, nh, SIP_302_MOVED_TEMPORARILY, SIPTAG_CONTACT(m), TAG_END());
+ return 0;
+ }
+ else {
+ RESPOND(ep, call, nh, SIP_200_OK,
+ TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
+ TAG_END());
+ }
+ }
+
+
+ switch (callstate(tags)) {
+ case nua_callstate_received:
+ RESPOND(ep, call, nh, SIP_180_RINGING,
+ TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
+ TAG_END());
+ return 0;
+ case nua_callstate_ready:
+ return 1;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+int test_100rel(struct context *ctx)
+{
+ int retval = 0;
+
+ retval = test_180rel(ctx); RETURN_ON_SINGLE_FAILURE(retval);
+ retval = test_prack_auth(ctx); RETURN_ON_SINGLE_FAILURE(retval);
+ retval = test_183rel(ctx); RETURN_ON_SINGLE_FAILURE(retval);
+ retval = test_preconditions(ctx); RETURN_ON_SINGLE_FAILURE(retval);
+ retval = test_preconditions2(ctx); RETURN_ON_SINGLE_FAILURE(retval);
+ retval = test_update_by_uas(ctx); RETURN_ON_SINGLE_FAILURE(retval);
+ retval = test_update_failure(ctx); RETURN_ON_SINGLE_FAILURE(retval);
+ retval = test_180rel_cancel1(ctx); RETURN_ON_SINGLE_FAILURE(retval);
+ retval = test_180rel_cancel2(ctx); RETURN_ON_SINGLE_FAILURE(retval);
+ retval = test_180rel_redirected(ctx); RETURN_ON_SINGLE_FAILURE(retval);
+
+ nua_set_params(ctx->a.nua,
+ NUTAG_EARLY_MEDIA(0),
+ SIPTAG_SUPPORTED(ctx->a.supported),
+ TAG_END());
+ run_a_until(ctx, nua_r_set_params, until_final_response);
+
+ nua_set_params(ctx->b.nua,
+ NUTAG_EARLY_MEDIA(0),
+ NUTAG_ONLY183_100REL(0),
+ SIPTAG_SUPPORTED(ctx->b.supported),
+ TAG_END());
+ run_b_until(ctx, nua_r_set_params, until_final_response);
+
+ nua_set_params(ctx->c.nua,
+ NUTAG_EARLY_MEDIA(0),
+ NUTAG_ONLY183_100REL(0),
+ SIPTAG_SUPPORTED(ctx->c.supported),
+ TAG_END());
+ run_c_until(ctx, nua_r_set_params, until_final_response);
+
+ return retval;
+}
Added: freeswitch/trunk/libs/sofia-sip/tests/test_basic_call.c
==============================================================================
--- (empty file)
+++ freeswitch/trunk/libs/sofia-sip/tests/test_basic_call.c Wed May 14 15:10:54 2008
@@ -0,0 +1,1999 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2005 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at 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 test_nua_basic_call.c
+ * @brief Test basic call.
+ *
+ * @author Pekka Pessi <Pekka.Pessi at nokia.com>
+ * @author Martti Mela <Martti Mela at nokia.com>
+ *
+ * @date Created: Wed Aug 17 12:12:12 EEST 2005 ppessi
+ */
+
+#include "config.h"
+
+#include "test_nua.h"
+#include <sofia-sip/su_tag_class.h>
+
+#if HAVE_FUNC
+#elif HAVE_FUNCTION
+#define __func__ __FUNCTION__
+#else
+#define __func__ "test_basic_call"
+#endif
+
+/* ======================================================================== */
+
+int until_terminated(CONDITION_PARAMS)
+{
+ if (!check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ return event == nua_i_state && callstate(tags) == nua_callstate_terminated;
+}
+
+/*
+ X accept_call ep
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<----180 Ringing----|
+ | |
+ |<--------200--------|
+ |---------ACK------->|
+*/
+int accept_call(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_received:
+ RESPOND(ep, call, nh, SIP_180_RINGING,
+ TAG_END());
+ return 0;
+ case nua_callstate_early:
+ RESPOND(ep, call, nh, SIP_200_OK,
+ TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
+ TAG_END());
+ return 0;
+ case nua_callstate_ready:
+ return 1;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+
+/*
+ X accept_call ep
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<----180 Ringing----|
+ | |
+ |<--------200--------|
+ |---------ACK------->|
+*/
+int accept_call_with_early_sdp(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_received:
+ RESPOND(ep, call, nh, SIP_180_RINGING,
+ TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
+ NUTAG_M_DISPLAY("Bob"),
+ NUTAG_M_USERNAME("b+b"),
+ TAG_END());
+ return 0;
+ case nua_callstate_early:
+ RESPOND(ep, call, nh, SIP_200_OK,
+ TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
+ TAG_END());
+ return 0;
+ case nua_callstate_ready:
+ return 1;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+
+/*
+ X INVITE
+ | |
+ |-------INVITE------>|
+ |<--------200--------|
+ |---------ACK------->|
+*/
+int until_ready(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_ready:
+ return 1;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/* ======================================================================== */
+
+/* Basic call:
+
+ A B
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<----180 Ringing----|
+ | |
+ |<------200 OK-------|
+ |--------ACK-------->|
+ | |
+ |<-------BYE---------|
+ |-------200 OK------>|
+ | |
+
+ Client transitions:
+ INIT -(C1)-> CALLING -(C2a)-> PROCEEDING -(C3+C4)-> READY
+ Server transitions:
+ INIT -(S1)-> RECEIVED -(S2a)-> EARLY -(S3b)-> COMPLETED -(S4)-> READY
+
+ B sends BYE:
+ READY -(T2)-> TERMINATING -(T3)-> TERMINATED
+ A receives BYE:
+ READY -(T1)-> TERMINATED
+
+ See @page nua_call_model in nua.docs for more information
+*/
+
+int test_basic_call_1(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+ sip_t *sip;
+ sip_replaces_t *repa, *repb;
+ nua_handle_t *nh;
+
+ static int once = 0;
+ sip_time_t se, min_se;
+
+ if (print_headings)
+ printf("TEST NUA-3.1: Basic call\n");
+
+ /* Disable session timer from proxy */
+ test_proxy_get_session_timer(ctx->p, &se, &min_se);
+ test_proxy_set_session_timer(ctx->p, 0, 0);
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ TEST_1(!nua_handle_has_active_call(a_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ NUTAG_M_USERNAME("a+a"),
+ NUTAG_M_DISPLAY("Alice"),
+ TAG_END());
+
+ run_ab_until(ctx, -1, until_ready, -1, accept_call_with_early_sdp);
+
+ TEST_1(nua_handle_has_active_call(a_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+ TEST_1(nua_handle_has_active_call(b_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(b_call->nh));
+
+ TEST_1(repa = nua_handle_make_replaces(a_call->nh, nua_handle_home(a_call->nh), 0));
+ TEST_1(repb = nua_handle_make_replaces(b_call->nh, nua_handle_home(b_call->nh), 0));
+
+ TEST_S(repa->rp_call_id, repb->rp_call_id);
+
+ TEST_1(!nua_handle_by_replaces(a->nua, repa));
+ TEST_1(!nua_handle_by_replaces(b->nua, repb));
+
+ TEST_1(nh = nua_handle_by_replaces(a->nua, repb));
+ TEST_P(nh, a_call->nh);
+ nua_handle_unref(nh);
+
+ TEST_1(nh = nua_handle_by_replaces(b->nua, repa));
+ TEST_P(nh, b_call->nh);
+ nua_handle_unref(nh);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state
+ PROCEEDING -(C3+C4)-> READY: nua_r_invite, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_S(sip->sip_call_id->i_id, repb->rp_call_id);
+ TEST_S(sip->sip_from->a_tag, repb->rp_to_tag);
+ TEST_S(sip->sip_to->a_tag, repb->rp_from_tag);
+ TEST_1(sip->sip_payload);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_payload);
+ TEST_1(sip->sip_contact);
+ TEST_S(sip->sip_contact->m_display, "Bob");
+ TEST_S(sip->sip_contact->m_url->url_user, "b+b");
+ if (!once) {
+ /* The session expiration is not used by default. */
+ TEST_1(sip->sip_session_expires == NULL);
+ }
+ /* Test that B uses application-specific contact */
+ if (ctx->proxy_tests)
+ TEST_1(sip->sip_contact->m_url->url_user);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+ EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_contact);
+ TEST_S(sip->sip_contact->m_display, "Alice");
+ TEST_S(sip->sip_contact->m_url->url_user, "a+a");
+ if (!once++) {
+ /* The Session-Expires header is not used by default. */
+ TEST_1(sip->sip_session_expires == NULL);
+ }
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_S(sip->sip_call_id->i_id, repa->rp_call_id);
+ TEST_S(sip->sip_from->a_tag, repa->rp_from_tag);
+ TEST_S(sip->sip_to->a_tag, repa->rp_to_tag);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ BYE(b, b_call, b_call->nh, TAG_END());
+ run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
+
+ /* B transitions:
+ READY --(T2)--> TERMINATING: nua_bye()
+ TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_r_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ TEST_1(!nua_handle_has_active_call(b_call->nh));
+
+ /* A transitions:
+ READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_bye);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ TEST_1(!nua_handle_has_active_call(a_call->nh));
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ test_proxy_set_session_timer(ctx->p, se, min_se);
+
+ if (print_headings)
+ printf("TEST NUA-3.1: PASSED\n");
+
+ END();
+}
+
+/*
+ accept_early_answer
+ X ep
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<----180 Ringing----|
+ | |
+ |<--------200--------|
+ |---------ACK------->|
+*/
+int accept_early_answer(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_received:
+ RESPOND(ep, call, nh, SIP_180_RINGING,
+ NUTAG_EARLY_ANSWER(1),
+ TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
+ TAG_END());
+ return 0;
+ case nua_callstate_early:
+ RESPOND(ep, call, nh, SIP_200_OK,
+ TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
+ TAG_END());
+ return 0;
+ case nua_callstate_ready:
+ return 1;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int test_basic_call_2(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+ sip_t const *sip;
+
+ if (print_headings)
+ printf("TEST NUA-3.2: Basic call with SDP in 180\n");
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call,
+ SIPTAG_TO_STR("<sip:b at x.org>"),
+ TAG_END()));
+
+ TEST_1(!nua_handle_has_active_call(a_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+ INVITE(a, a_call, a_call->nh,
+ NUTAG_URL(b->contact->m_url),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ NUTAG_ALLOW("INFO"),
+ TAG_END());
+
+ run_ab_until(ctx, -1, until_ready, -1, accept_early_answer);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state
+ PROCEEDING -(C3+C4)-> READY: nua_r_invite, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_content_type);
+ TEST_S(sip->sip_content_type->c_type, "application/sdp");
+ TEST_1(sip->sip_payload); /* there is sdp in 200 OK */
+ TEST_1(sip->sip_contact);
+#if nomore
+ /* Test that B does not use application-specific contact */
+ TEST_1(!sip->sip_contact->m_url->url_user);
+#else
+ /* sf.net bug #1816647: Outbound contact does not make it to dialogs */
+ /* Now we use first registered contact if aor does not match */
+ if (ctx->proxy_tests)
+ TEST_S(sip->sip_contact->m_url->url_user, "b");
+#endif
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!is_answer_recv(e->data->e_tags)); /* but it is ignored */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ TEST_1(nua_handle_has_active_call(a_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+ EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ TEST_1(nua_handle_has_active_call(b_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(b_call->nh));
+
+ /* Send a NOTIFY from B to A */
+ if (print_headings)
+ printf("TEST NUA-3.2.2: send a NOTIFY within a dialog\n");
+
+ /* Make A to accept NOTIFY */
+ nua_set_params(a->nua, NUTAG_APPL_METHOD("NOTIFY"), TAG_END());
+ run_a_until(ctx, nua_r_set_params, until_final_response);
+
+ NOTIFY(b, b_call, b_call->nh,
+ NUTAG_NEWSUB(1),
+ SIPTAG_SUBJECT_STR("NUA-3.2.2"),
+ SIPTAG_EVENT_STR("message-summary"),
+ SIPTAG_CONTENT_TYPE_STR("application/simple-message-summary"),
+ SIPTAG_PAYLOAD_STR("Messages-Waiting: no"),
+ TAG_END());
+
+ run_ab_until(ctx, -1, accept_notify, -1, save_until_final_response);
+
+ /* Notifier events: nua_r_notify */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_r_notify);
+ TEST(e->data->e_status, 200);
+ TEST_1(tl_find(e->data->e_tags, nutag_substate));
+ TEST(tl_find(e->data->e_tags, nutag_substate)->t_value,
+ nua_substate_terminated);
+
+ /* watcher events: nua_i_notify */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_notify);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "terminated");
+ TEST_1(tl_find(e->data->e_tags, nutag_substate));
+ TEST(tl_find(e->data->e_tags, nutag_substate)->t_value,
+ nua_substate_terminated);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-3.2.2: PASSED\n");
+
+ INFO(b, b_call, b_call->nh, TAG_END());
+ BYE(b, b_call, b_call->nh, TAG_END());
+ INFO(b, b_call, b_call->nh, TAG_END());
+
+ run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
+
+ while (!b->events->head || /* r_info */
+ !b->events->head->next || /* r_bye */
+ !b->events->head->next->next || /* i_state */
+ !b->events->head->next->next->next) /* r_info */
+ run_ab_until(ctx, -1, save_events, -1, save_until_final_response);
+
+ /* B transitions:
+ nua_info()
+ READY --(T2)--> TERMINATING: nua_bye()
+ nua_r_info with 200
+ TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
+ nua_r_info with 481/900
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_r_info);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_info);
+ TEST_1(e->data->e_status >= 900 || e->data->e_status == 481);
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ TEST_1(!nua_handle_has_active_call(b_call->nh));
+
+ /* A transitions:
+ nua_i_info
+ READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_info);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_bye);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ BYE(a, a_call, a_call->nh, TAG_END());
+ run_a_until(ctx, -1, save_until_final_response);
+
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_bye);
+ TEST_1(e->data->e_status >= 900);
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-3.2: PASSED\n");
+
+ END();
+}
+
+/* ====================================================================== */
+
+/* Basic calls with soa disabled */
+
+int accept_call_no_media(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_received:
+ RESPOND(ep, call, nh, SIP_180_RINGING,
+ NUTAG_MEDIA_ENABLE(0),
+ TAG_END());
+ return 0;
+ case nua_callstate_early:
+ RESPOND(ep, call, nh, SIP_200_OK,
+ TAG_IF(call->sdp, SIPTAG_CONTENT_TYPE_STR("application/sdp")),
+ TAG_IF(call->sdp, SIPTAG_PAYLOAD_STR(call->sdp)),
+ TAG_END());
+ return 0;
+ case nua_callstate_ready:
+ return 1;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int test_basic_call_3(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+ sip_t const *sip;
+
+ if (print_headings)
+ printf("TEST NUA-3.3: Basic call with media disabled\n");
+
+ a_call->sdp = "v=0\r\n"
+ "o=- 1 1 IN IP4 127.0.0.1\r\n"
+ "s=-\r\n"
+ "c=IN IP4 127.0.0.1\r\n"
+ "t=0 0\r\n"
+ "m=audio 5008 RTP/AVP 8\r\n";
+
+ b_call->sdp =
+ "v=0\r\n"
+ "o=- 2 1 IN IP4 127.0.0.1\r\n"
+ "s=-\r\n"
+ "c=IN IP4 127.0.0.1\r\n"
+ "t=0 0\r\n"
+ "m=audio 5010 RTP/AVP 0 8\r\n";
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call,
+ SIPTAG_TO_STR("<sip:b at x.org>"),
+ TAG_END()));
+
+ TEST_1(!nua_handle_has_active_call(a_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+ INVITE(a, a_call, a_call->nh,
+ NUTAG_MEDIA_ENABLE(0),
+ NUTAG_URL(b->contact->m_url),
+ SIPTAG_CONTENT_TYPE_STR("application/sdp"),
+ SIPTAG_PAYLOAD_STR(a_call->sdp),
+ TAG_END());
+
+ run_ab_until(ctx, -1, until_ready, -1, accept_call_no_media);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state
+ PROCEEDING -(C3+C4)-> READY: nua_r_invite, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
+ TEST_1(!is_answer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_content_type);
+ TEST_S(sip->sip_content_type->c_type, "application/sdp");
+ TEST_1(sip->sip_payload); /* there is sdp in 200 OK */
+ TEST_1(sip->sip_contact);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ TEST_1(nua_handle_has_active_call(a_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+ EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(!is_answer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ TEST_1(nua_handle_has_active_call(b_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(b_call->nh));
+
+ BYE(b, b_call, b_call->nh, TAG_END());
+
+ run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
+
+ /* B transitions:
+ READY --(T2)--> TERMINATING: nua_bye()
+ TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_r_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ TEST_1(!nua_handle_has_active_call(b_call->nh));
+
+ /* A transitions:
+ nua_i_info
+ READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_bye);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-3.3: PASSED\n");
+
+ END();
+}
+
+int ack_when_completing_no_media(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_completing:
+ ACK(ep, call, nh,
+ TAG_IF(call->sdp, SIPTAG_CONTENT_TYPE_STR("application/sdp")),
+ TAG_IF(call->sdp, SIPTAG_PAYLOAD_STR(call->sdp)),
+ TAG_END());
+ return 0;
+ case nua_callstate_ready:
+ return 1;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int accept_call_no_media2(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_received:
+ RESPOND(ep, call, nh, SIP_180_RINGING,
+ NUTAG_MEDIA_ENABLE(0),
+ TAG_IF(call->sdp, SIPTAG_CONTENT_TYPE_STR("application/sdp")),
+ TAG_IF(call->sdp, SIPTAG_PAYLOAD_STR(call->sdp)),
+ TAG_END());
+ return 0;
+ case nua_callstate_early:
+ RESPOND(ep, call, nh, SIP_200_OK,
+ TAG_IF(call->sdp, SIPTAG_CONTENT_TYPE_STR("application/sdp")),
+ TAG_IF(call->sdp, SIPTAG_PAYLOAD_STR(call->sdp)),
+ TAG_END());
+ return 0;
+ case nua_callstate_ready:
+ return 1;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/* Media disabled, offer/answer in 200 OK/ACK */
+int test_basic_call_4(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+ sip_t const *sip;
+
+ if (print_headings)
+ printf("TEST NUA-3.4: 3pcc call with media disabled\n");
+
+ a_call->sdp = "v=0\r\n"
+ "o=- 1 1 IN IP4 127.0.0.1\r\n"
+ "s=-\r\n"
+ "c=IN IP4 127.0.0.1\r\n"
+ "t=0 0\r\n"
+ "m=audio 5008 RTP/AVP 8\r\n";
+
+ b_call->sdp =
+ "v=0\r\n"
+ "o=- 2 1 IN IP4 127.0.0.1\r\n"
+ "s=-\r\n"
+ "c=IN IP4 127.0.0.1\r\n"
+ "t=0 0\r\n"
+ "m=audio 5010 RTP/AVP 0 8\r\n";
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call,
+ SIPTAG_TO_STR("<sip:b at x.org>"),
+ TAG_END()));
+
+ TEST_1(!nua_handle_has_active_call(a_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+ INVITE(a, a_call, a_call->nh,
+ NUTAG_MEDIA_ENABLE(0),
+ NUTAG_URL(b->contact->m_url),
+ NUTAG_AUTOACK(0),
+ TAG_END());
+
+ run_ab_until(ctx, -1, ack_when_completing_no_media,
+ -1, accept_call_no_media2);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state
+ PROCEEDING -(C3+C4)-> READY: nua_r_invite, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(!is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
+ TEST_1(!is_answer_recv(e->data->e_tags));
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_content_type);
+ TEST_S(sip->sip_content_type->c_type, "application/sdp");
+ TEST_1(sip->sip_payload); /* there is sdp in 200 OK */
+ TEST_1(sip->sip_contact);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completing); /* COMPLETING */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ TEST_1(nua_handle_has_active_call(a_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+ EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(!is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(!is_answer_sent(e->data->e_tags));
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ TEST_1(nua_handle_has_active_call(b_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(b_call->nh));
+
+ BYE(b, b_call, b_call->nh, TAG_END());
+
+ run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
+
+ /* B transitions:
+ READY --(T2)--> TERMINATING: nua_bye()
+ TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_r_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ TEST_1(!nua_handle_has_active_call(b_call->nh));
+
+ /* A transitions:
+ nua_i_info
+ READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_bye);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-3.4: PASSED\n");
+
+ END();
+}
+
+int change_uri_in_ack(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_completing:
+ ACK(ep, call, nh,
+ SIPTAG_FROM_STR("sip:anonymous at org.invalid"),
+ SIPTAG_TO_STR("sip:anonymous at net.invalid"),
+ TAG_END());
+ return 0;
+ case nua_callstate_ready:
+ return 1;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/* Test changing from/to within dialog */
+/* Test that a proper Contact gets selected in response
+ * regardless of the To URI.
+ */
+int test_basic_call_5(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+ sip_t const *sip;
+
+ if (print_headings)
+ printf("TEST NUA-3.5: test changing From/To URL in ACK\n");
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call,
+ SIPTAG_TO_STR("<sips:b at x.org>"),
+ TAG_END()));
+
+ TEST_1(!nua_handle_has_active_call(a_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+ INVITE(a, a_call, a_call->nh,
+ NUTAG_URL(b->contact->m_url),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ NUTAG_AUTOACK(0),
+ TAG_END());
+
+ run_ab_until(ctx, -1, change_uri_in_ack, -1, accept_call);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state
+ PROCEEDING -(C3+C4)-> READY: nua_r_invite, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_contact);
+ if (ctx->proxy_tests) /* Use Contact from registration? */
+ TEST_S(sip->sip_contact->m_url->url_user, "b");
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completing); /* COMPLETING */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ TEST_1(nua_handle_has_active_call(a_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+ EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_S(sip->sip_to->a_url->url_user, "anonymous");
+ TEST_S(sip->sip_from->a_url->url_user, "anonymous");
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ TEST_1(nua_handle_has_active_call(b_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(b_call->nh));
+
+ BYE(b, b_call, b_call->nh, TAG_END());
+
+ run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
+
+ /* B transitions:
+ READY --(T2)--> TERMINATING: nua_bye()
+ TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_r_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ TEST_1(!nua_handle_has_active_call(b_call->nh));
+
+ /* A transitions:
+ nua_i_info
+ READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_bye);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-3.5: PASSED\n");
+
+ END();
+}
+
+/* ======================================================================== */
+
+/* Call with media upgrade:
+
+ A B
+ |-------INVITE------>|
+ | with audio only |
+ |<----100 Trying-----|
+ | |
+ |<----180 Ringing----|
+ | |
+ |<------200 OK-------|
+ |--------ACK-------->|
+ | |
+ |<------INVITE-------|
+ | with video |
+ |-----100 Trying---->|
+ | |
+ |-------200 OK------>|
+ | with video |
+ |<-------ACK---------|
+ | |
+ |<-------BYE---------|
+ |-------200 OK------>|
+ | |
+
+ Client transitions:
+ INIT -(C1)-> CALLING -(C2a)-> PROCEEDING -(C3+C4)-> READY
+ Server transitions:
+ INIT -(S1)-> RECEIVED -(S2a)-> EARLY -(S3b)-> COMPLETED -(S4)-> READY
+
+ B sends BYE:
+ READY -(T2)-> TERMINATING -(T3)-> TERMINATED
+ A receives BYE:
+ READY -(T1)-> TERMINATED
+
+ See @page nua_call_model in nua.docs for more information
+*/
+int accept_upgrade(CONDITION_PARAMS);
+
+int test_video_call_1(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+ sip_t *sip;
+ sdp_session_t *b_sdp;
+ sdp_media_t *m, b_video[1];
+ sdp_rtpmap_t *rm, b_h261[1];
+
+ sip_time_t se, min_se;
+
+ if (print_headings)
+ printf("TEST NUA-3.6: Basic call\n");
+
+ /* Disable session timer from proxy */
+ test_proxy_get_session_timer(ctx->p, &se, &min_se);
+ test_proxy_set_session_timer(ctx->p, 0, 0);
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ TEST_1(!nua_handle_has_active_call(a_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ NUTAG_AUTOANSWER(0),
+ TAG_END());
+
+ run_ab_until(ctx, -1, until_ready, -1, accept_call_with_early_sdp);
+
+ TEST_1(nua_handle_has_active_call(a_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+ TEST_1(nua_handle_has_active_call(b_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(b_call->nh));
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state
+ PROCEEDING -(C3+C4)-> READY: nua_r_invite, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_payload);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+ EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST_1(tl_find(e->data->e_tags, soatag_local_sdp));
+ TEST_1(b_sdp = sdp_session_dup(nua_handle_home(a_call->nh),
+ (sdp_session_t *)
+ tl_find(e->data->e_tags, soatag_local_sdp)
+ ->t_value));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ a_call->sdp =
+ "m=audio 5008 RTP/AVP 8\n"
+ "m=video 5014 RTP/AVP 34 31\n";
+
+ m = memset(b_video, 0, sizeof b_video);
+ m->m_size = sizeof *m;
+ m->m_session = b_sdp;
+ m->m_type = sdp_media_video, m->m_type_name = "video";
+ m->m_port = 5016;
+ m->m_proto = sdp_proto_rtp; m->m_proto_name = "RTP/AVP";
+ m->m_rtpmaps = memset(rm = b_h261, 0, sizeof b_h261);
+ rm->rm_size = sizeof *rm;
+ rm->rm_pt = 31; rm->rm_encoding = "h261"; rm->rm_rate = 90000;
+
+ b_sdp->sdp_media->m_next = m;
+
+ INVITE(b, b_call, b_call->nh,
+ SOATAG_USER_SDP(b_sdp),
+ TAG_END());
+ run_ab_until(ctx, -1, accept_upgrade, -1, until_ready);
+
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(tl_find(e->data->e_tags, soatag_local_sdp_str));
+ TEST_1(strstr((char *)
+ tl_find(e->data->e_tags, soatag_local_sdp_str)->t_value,
+ "m=video"));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST_1(tl_find(e->data->e_tags, soatag_local_sdp_str));
+ TEST_1(strstr((char *)
+ tl_find(e->data->e_tags, soatag_local_sdp_str)->t_value,
+ "m=video"));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ BYE(b, b_call, b_call->nh, TAG_END());
+ run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
+
+ /* B transitions:
+ READY --(T2)--> TERMINATING: nua_bye()
+ TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_r_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ TEST_1(!nua_handle_has_active_call(b_call->nh));
+
+ /* A transitions:
+ READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_bye);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ TEST_1(!nua_handle_has_active_call(a_call->nh));
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ test_proxy_set_session_timer(ctx->p, se, min_se);
+
+ if (print_headings)
+ printf("TEST NUA-3.6: PASSED\n");
+
+ END();
+}
+
+int accept_upgrade(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ if (event == nua_i_invite && status < 200) {
+ RESPOND(ep, call, nh, SIP_200_OK,
+ TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
+ TAG_END());
+ return 0;
+ }
+
+ switch (callstate(tags)) {
+ case nua_callstate_ready:
+ return 1;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/* Basic call and re-INVITE with user-specified Contact:
+
+ A B
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<----180 Ringing----|
+ | |
+ |<------200 OK-------|
+ |--------ACK-------->|
+ | |
+ |-----re-INVITE----->|
+ |<------200 OK-------|
+ |--------ACK-------->|
+ | |
+ |<-------BYE---------|
+ |-------200 OK------>|
+ | |
+
+ Client transitions:
+ INIT -(C1)-> CALLING -(C2a)-> PROCEEDING -(C3+C4)-> READY
+ Server transitions:
+ INIT -(S1)-> RECEIVED -(S2a)-> EARLY -(S3b)-> COMPLETED -(S4)-> READY
+
+ Both client and server save Contact from nua_invite() and nua_respond(),
+ respectively.
+
+ INIT -(C1)-> CALLING -(C3a+C4)-> READY
+ INIT -(S3c)-> COMPLETED -(S4)-> READY
+
+ Both client and server use saved Contact.
+
+ B sends BYE:
+ READY -(T2)-> TERMINATING -(T3)-> TERMINATED
+ A receives BYE:
+ READY -(T1)-> TERMINATED
+
+ See @page nua_call_model in nua.docs for more information
+*/
+
+static sip_contact_t *contact_for_b;
+
+int accept_call_with_contact(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_received:
+ RESPOND(ep, call, nh, SIP_180_RINGING,
+ TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
+ SIPTAG_CONTACT(contact_for_b),
+ TAG_END());
+ return 0;
+ case nua_callstate_early:
+ RESPOND(ep, call, nh, SIP_200_OK,
+ TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
+ SIPTAG_CONTACT(contact_for_b),
+ TAG_END());
+ return 0;
+ case nua_callstate_ready:
+ return 1;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int test_basic_call_6(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+ sip_route_t *r, rb[1];
+ sip_t *sip;
+
+ sip_contact_t ma[1], mb[1];
+
+ if (print_headings)
+ printf("TEST NUA-3.6: Basic call\n");
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ TEST_1(!nua_handle_has_active_call(a_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+ *ma = *a->contact;
+ ma->m_display = "Alice B.";
+ ma->m_url->url_user = "a++a";
+
+ *mb = *b->contact;
+ mb->m_display = "Bob A.";
+ mb->m_url->url_user = "b++b";
+
+ contact_for_b = mb;
+
+ sip_route_init(rb)->r_url[0] = b->contact->m_url[0];
+ rb->r_url->url_user = "bob+0";
+ url_param_add(nua_handle_home(a_call->nh), rb->r_url, "lr");
+
+ INVITE(a, a_call, a_call->nh,
+ NUTAG_URL("sip:bob at example.org"), /* Expanded by proxy */
+ SIPTAG_ROUTE_STR("B2 <sip:bob+2 at example.org>;bar=foo"), /* Last in list */
+ NUTAG_INITIAL_ROUTE(ctx->lr), /* Removed by proxy (if any) */
+ NUTAG_INITIAL_ROUTE(rb), /* Used to route request to b (not removed) */
+ NUTAG_INITIAL_ROUTE_STR("B1 <sip:bob+1 at example.org;lr>;foo=bar"), /* Next in list */
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ SIPTAG_CONTACT(ma),
+ TAG_END());
+
+ run_ab_until(ctx, -1, until_ready, -1, accept_call_with_contact);
+
+ TEST_1(nua_handle_has_active_call(a_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+ TEST_1(nua_handle_has_active_call(b_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(b_call->nh));
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state
+ PROCEEDING -(C3+C4)-> READY: nua_r_invite, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_payload);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_payload);
+ TEST_1(sip->sip_contact);
+ TEST_S(sip->sip_contact->m_display, "Bob A.");
+ TEST_S(sip->sip_contact->m_url->url_user, "b++b");
+ /* Test that B uses application-specific contact */
+ TEST_1(sip->sip_contact->m_url->url_user);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+ EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_contact);
+ TEST_S(sip->sip_contact->m_display, "Alice B.");
+ TEST_S(sip->sip_contact->m_url->url_user, "a++a");
+ TEST_1(r = sip->sip_route);
+ TEST_S(r->r_url->url_user, "bob+0");
+ TEST_1(r = r->r_next);
+ TEST_S(r->r_url->url_user, "bob+1");
+ TEST_1(r = r->r_next);
+ TEST_S(r->r_url->url_user, "bob+2");
+ TEST_1(!r->r_next);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ /* re-INVITE */
+ INVITE(a, a_call, a_call->nh, TAG_END());
+ run_ab_until(ctx, -1, until_ready, -1, until_ready);
+
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_contact);
+ TEST_S(sip->sip_contact->m_display, "Bob A.");
+ TEST_S(sip->sip_contact->m_url->url_user, "b++b");
+ /* Test that B uses application-specific contact */
+ TEST_1(sip->sip_contact->m_url->url_user);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_contact);
+ TEST_S(sip->sip_contact->m_display, "Alice B.");
+ TEST_S(sip->sip_contact->m_url->url_user, "a++a");
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ BYE(b, b_call, b_call->nh, TAG_END());
+ run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
+
+ /* B transitions:
+ READY --(T2)--> TERMINATING: nua_bye()
+ TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_r_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ TEST_1(!nua_handle_has_active_call(b_call->nh));
+
+ /* A transitions:
+ READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_bye);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ TEST_1(!nua_handle_has_active_call(a_call->nh));
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-3.6: PASSED\n");
+
+ END();
+}
+
+/* Terminate call with 408:
+
+ A B
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<----180 Ringing----|
+ | |
+ |<------200 OK-------|
+ |--------ACK-------->|
+ | |
+ |--------INFO------->|
+ |<--------408--------|
+ | |
+ |<-------BYE---------|
+ |--------487-------->|
+
+ Client transitions:
+ INIT -(C1)-> CALLING -(C2a)-> PROCEEDING -(C3+C4)-> READY
+ Server transitions:
+ INIT -(S1)-> RECEIVED -(S2a)-> EARLY -(S3b)-> COMPLETED -(S4)-> READY
+
+ A send INFO:
+ READY -(T1)-> TERMINATED
+
+ B sends BYE:
+ READY -(T2)-> TERMINATING -(T3)-> TERMINATED
+
+ See @page nua_call_model in nua.docs for more information
+*/
+int reject_method(CONDITION_PARAMS);
+int reject_info(CONDITION_PARAMS);
+
+int test_basic_call_7(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+ sip_t *sip;
+ sip_replaces_t *repa, *repb;
+ nua_handle_t *nh;
+
+ if (print_headings)
+ printf("TEST NUA-3.7.1: Release dialog with error response (RFC 5057)\n");
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ TEST_1(!nua_handle_has_active_call(a_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ TAG_END());
+
+ run_ab_until(ctx, -1, until_ready, -1, accept_call_with_early_sdp);
+
+ TEST_1(nua_handle_has_active_call(a_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+ TEST_1(nua_handle_has_active_call(b_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(b_call->nh));
+
+ TEST_1(repa = nua_handle_make_replaces(a_call->nh, nua_handle_home(a_call->nh), 0));
+ TEST_1(repb = nua_handle_make_replaces(b_call->nh, nua_handle_home(b_call->nh), 0));
+
+ TEST_S(repa->rp_call_id, repb->rp_call_id);
+
+ TEST_1(!nua_handle_by_replaces(a->nua, repa));
+ TEST_1(!nua_handle_by_replaces(b->nua, repb));
+
+ TEST_1(nh = nua_handle_by_replaces(a->nua, repb));
+ TEST_P(nh, a_call->nh);
+ nua_handle_unref(nh);
+
+ TEST_1(nh = nua_handle_by_replaces(b->nua, repa));
+ TEST_P(nh, b_call->nh);
+ nua_handle_unref(nh);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state
+ PROCEEDING -(C3+C4)-> READY: nua_r_invite, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_S(sip->sip_call_id->i_id, repb->rp_call_id);
+ TEST_S(sip->sip_from->a_tag, repb->rp_to_tag);
+ TEST_S(sip->sip_to->a_tag, repb->rp_from_tag);
+ TEST_1(sip->sip_payload);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_payload);
+ TEST_1(sip->sip_contact);
+ TEST_S(sip->sip_contact->m_display, "Bob");
+ TEST_S(sip->sip_contact->m_url->url_user, "b+b");
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+ EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ /* Make B to process HUMPPA at application level */
+ nua_set_hparams(b_call->nh, NUTAG_APPL_METHOD("HUMPPA"),
+ NUTAG_ALLOW("HUMPPA"),
+ TAG_END());
+ run_b_until(ctx, nua_r_set_params, until_final_response);
+
+ METHOD(a, a_call, a_call->nh, NUTAG_METHOD("HUMPPA"), TAG_END());
+
+ run_ab_until(ctx, -1, until_terminated, -1, reject_method);
+
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_method);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ TEST_1(!nua_handle_has_active_call(a_call->nh));
+
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_method);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ TEST_1(!nua_handle_has_active_call(b_call->nh));
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-3.7.1: PASSED\n");
+
+ if (print_headings)
+ printf("TEST NUA-3.7.2: Release dialog usage with error response (RFC 5057)\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ TEST_1(!nua_handle_has_active_call(a_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ TAG_END());
+
+ run_ab_until(ctx, -1, until_ready, -1, accept_call_with_early_sdp);
+
+ TEST_1(nua_handle_has_active_call(a_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+ TEST_1(nua_handle_has_active_call(b_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(b_call->nh));
+
+ TEST_1(repa = nua_handle_make_replaces(a_call->nh, nua_handle_home(a_call->nh), 0));
+ TEST_1(repb = nua_handle_make_replaces(b_call->nh, nua_handle_home(b_call->nh), 0));
+
+ TEST_S(repa->rp_call_id, repb->rp_call_id);
+
+ TEST_1(!nua_handle_by_replaces(a->nua, repa));
+ TEST_1(!nua_handle_by_replaces(b->nua, repb));
+
+ TEST_1(nh = nua_handle_by_replaces(a->nua, repb));
+ TEST_P(nh, a_call->nh);
+ nua_handle_unref(nh);
+
+ TEST_1(nh = nua_handle_by_replaces(b->nua, repa));
+ TEST_P(nh, b_call->nh);
+ nua_handle_unref(nh);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state
+ PROCEEDING -(C3+C4)-> READY: nua_r_invite, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_S(sip->sip_call_id->i_id, repb->rp_call_id);
+ TEST_S(sip->sip_from->a_tag, repb->rp_to_tag);
+ TEST_S(sip->sip_to->a_tag, repb->rp_from_tag);
+ TEST_1(sip->sip_payload);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_payload);
+ TEST_1(sip->sip_contact);
+ TEST_S(sip->sip_contact->m_display, "Bob");
+ TEST_S(sip->sip_contact->m_url->url_user, "b+b");
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+ EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ /* Let A allow INFO */
+ nua_set_params(a->nua, NUTAG_ALLOW("INFO"), TAG_END());
+ /* Make B to process INFO at application level */
+ nua_set_hparams(b_call->nh, NUTAG_APPL_METHOD("INFO"),
+ NUTAG_ALLOW("INFO"),
+ TAG_END());
+ run_ab_until(ctx, nua_r_set_params, NULL, nua_r_set_params, NULL);
+
+ INFO(a, a_call, a_call->nh, TAG_END());
+
+ run_ab_until(ctx, -1, until_terminated, -1, reject_info);
+
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_info);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ TEST_1(!nua_handle_has_active_call(a_call->nh));
+
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_info);
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ TEST_1(nua_handle_has_active_call(b_call->nh));
+
+ INFO(b, b_call, b_call->nh, TAG_END());
+ run_b_until(ctx, -1, until_terminated);
+
+ /* B transitions:
+ READY --(T2)--> TERMINATING: nua_bye()
+ TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_r_info);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ TEST_1(!nua_handle_has_active_call(b_call->nh));
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-3.7.2: PASSED\n");
+
+ END();
+}
+
+int reject_method(CONDITION_PARAMS)
+{
+ msg_t *current = nua_current_request(nua);
+
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ if (event == nua_i_method) {
+ RESPOND(ep, call, nh,
+ SIP_604_DOES_NOT_EXIST_ANYWHERE,
+ NUTAG_WITH(current),
+ TAG_END());
+ return 1;
+ }
+ return 0;
+}
+
+int reject_info(CONDITION_PARAMS)
+{
+ msg_t *current = nua_current_request(nua);
+
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ if (event == nua_i_info) {
+ RESPOND(ep, call, nh,
+ SIP_480_TEMPORARILY_UNAVAILABLE,
+ NUTAG_WITH(current),
+ TAG_END());
+ return 1;
+ }
+ return 0;
+}
+
+
+int test_basic_call(struct context *ctx)
+{
+ return 0
+ || test_basic_call_1(ctx)
+ || test_basic_call_2(ctx)
+ || test_basic_call_3(ctx)
+ || test_basic_call_4(ctx)
+ || test_basic_call_5(ctx)
+ || test_basic_call_6(ctx)
+ || test_basic_call_7(ctx)
+ || test_video_call_1(ctx)
+ ;
+}
Added: freeswitch/trunk/libs/sofia-sip/tests/test_call_hold.c
==============================================================================
--- (empty file)
+++ freeswitch/trunk/libs/sofia-sip/tests/test_call_hold.c Wed May 14 15:10:54 2008
@@ -0,0 +1,1170 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2005 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at 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 test_call_hold.c
+ * @brief Test re-INVITE, call hold, un-hold.
+ *
+ * @author Pekka Pessi <Pekka.Pessi at nokia.com>
+ * @author Martti Mela <Martti Mela at nokia.com>
+ *
+ * @date Created: Wed Aug 17 12:12:12 EEST 2005 ppessi
+ */
+
+#include "config.h"
+
+#include "test_nua.h"
+#include <sofia-sip/su_tag_class.h>
+
+#if HAVE_FUNC
+#elif HAVE_FUNCTION
+#define __func__ __FUNCTION__
+#else
+#define __func__ "test_call_hold"
+#endif
+
+int complete_call(CONDITION_PARAMS);
+int until_complete(CONDITION_PARAMS);
+int invite_responded(CONDITION_PARAMS);
+static size_t remove_first_ack(void *_once, void *message, size_t len);
+
+/* ======================================================================== */
+/* test_call_hold message sequence looks like this:
+
+ A B
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<----180 Ringing----|
+ | |
+ |<--------200--------|
+ |---------ACK------->|
+ : :
+ |--INVITE(sendonly)->|
+ |<---200(recvonly)---|
+ |---------ACK------->|
+ : :
+ |<-INVITE(inactive)--|
+ |----200(inactive)-->|
+ |<--------ACK--------|
+ : :
+ |--INVITE(recvonly)->|
+ |<---200(sendonly)---|
+ |---------ACK------->|
+ : :
+ |<-INVITE(sendrecv)--|
+ |----200(sendrecv)-->|
+ |<--------ACK--------|
+ : :
+ |--------INFO------->|
+ |<--------200--------|
+ : :
+ |---------BYE------->|
+ |<--------200--------|
+*/
+
+int test_call_hold(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+ sip_t *sip;
+ int zero = 0;
+ struct nat_filter *f;
+
+ a_call->sdp =
+ "m=audio 5008 RTP/AVP 0 8\n"
+ "m=video 6008 RTP/AVP 30\n";
+ b_call->sdp =
+ "m=audio 5010 RTP/AVP 8\n"
+ "a=rtcp:5011\n"
+ "m=video 6010 RTP/AVP 30\n"
+ "a=rtcp:6011\n";
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ TAG_END());
+
+ run_ab_until(ctx, -1, until_ready, -1, accept_call);
+
+ /*
+ Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state
+ PROCEEDING -(C3+C4)-> READY: nua_r_invite, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST(audio_activity(e->data->e_tags), SOA_ACTIVE_SENDRECV);
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ TEST_1(nua_handle_has_active_call(a_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+ EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST(audio_activity(e->data->e_tags), SOA_ACTIVE_SENDRECV);
+ TEST_1(!e->next);
+
+ TEST_1(nua_handle_has_active_call(b_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(b_call->nh));
+
+ free_events_in_list(ctx, b->events);
+
+ /*
+ : :
+ |--INVITE(sendonly)->|
+ |<---200(recvonly)---|
+ |---------ACK------->|
+ : :
+ */
+
+ if (print_headings)
+ printf("TEST NUA-7.1: put B on hold\n");
+
+ /* Put B on hold */
+ INVITE(a, a_call, a_call->nh, SOATAG_HOLD("audio"),
+ SIPTAG_SUBJECT_STR("hold b"),
+ TAG_END());
+ run_ab_until(ctx, -1, until_ready, -1, until_ready);
+
+ /* Client transitions:
+ READY -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C3a+C4)-> READY: nua_r_invite, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST(audio_activity(e->data->e_tags), SOA_ACTIVE_SENDONLY);
+ TEST_1(!e->next);
+
+ TEST_1(nua_handle_has_active_call(a_call->nh));
+ TEST_1(nua_handle_has_call_on_hold(a_call->nh));
+
+ free_events_in_list(ctx, a->events);
+
+ /*
+ Server transitions:
+ READY -(S3a)-> COMPLETED: nua_i_invite, <auto-answer>, nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST(audio_activity(e->data->e_tags), SOA_ACTIVE_RECVONLY);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST(audio_activity(e->data->e_tags), SOA_ACTIVE_RECVONLY);
+ TEST_1(!e->next);
+
+ TEST_1(nua_handle_has_active_call(b_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(b_call->nh));
+
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-7.1: PASSED\n");
+
+ /* ------------------------------------------------------------------------ */
+ /*
+ : :
+ |<-INVITE(inactive)--|
+ |----200(inactive)-->|
+ |<--------ACK--------|
+ : :
+ */
+
+ if (print_headings)
+ printf("TEST NUA-7.2: put A on hold\n");
+
+ /* Put A on hold, too. */
+ INVITE(b, b_call, b_call->nh, SOATAG_HOLD("audio"),
+ SIPTAG_SUBJECT_STR("hold a"),
+ TAG_END());
+ run_ab_until(ctx, -1, until_ready, -1, until_ready);
+
+ /* Client transitions:
+ READY -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C3a+C4)-> READY: nua_r_invite, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST(audio_activity(e->data->e_tags), SOA_ACTIVE_INACTIVE);
+ TEST(video_activity(e->data->e_tags), SOA_ACTIVE_SENDRECV);
+ TEST_1(!e->next);
+
+ TEST_1(nua_handle_has_active_call(b_call->nh));
+ TEST_1(nua_handle_has_call_on_hold(b_call->nh));
+
+ free_events_in_list(ctx, b->events);
+
+ /*
+ Server transitions:
+ READY -(S3a)-> COMPLETED: nua_i_invite, <auto-answer>, nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST(audio_activity(e->data->e_tags), SOA_ACTIVE_INACTIVE);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST(audio_activity(e->data->e_tags), SOA_ACTIVE_INACTIVE);
+ TEST(video_activity(e->data->e_tags), SOA_ACTIVE_SENDRECV);
+ TEST_1(!e->next);
+
+ TEST_1(nua_handle_has_active_call(a_call->nh));
+ TEST_1(nua_handle_has_call_on_hold(a_call->nh));
+
+ free_events_in_list(ctx, a->events);
+
+ if (print_headings)
+ printf("TEST NUA-7.2: PASSED\n");
+
+ /* ------------------------------------------------------------------------ */
+ /*
+ : :
+ |--INVITE(recvonly)->|
+ |<---200(sendonly)---|
+ |---------ACK------->|
+ : :
+ */
+
+ if (print_headings)
+ printf("TEST NUA-7.3: resume B\n");
+
+ /* Resume B from hold */
+ INVITE(a, a_call, a_call->nh, SOATAG_HOLD(NULL),
+ SIPTAG_SUBJECT_STR("resume b"),
+ TAG_END());
+ run_ab_until(ctx, -1, until_ready, -1, until_ready);
+
+ /* Client transitions:
+ READY -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C3a+C4)-> READY: nua_r_invite, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST(audio_activity(e->data->e_tags), SOA_ACTIVE_RECVONLY);
+ TEST(video_activity(e->data->e_tags), SOA_ACTIVE_SENDRECV);
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ TEST_1(nua_handle_has_active_call(a_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+ /*
+ Server transitions:
+ READY -(S3a)-> COMPLETED: nua_i_invite, <auto-answer>, nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST(audio_activity(e->data->e_tags), SOA_ACTIVE_SENDONLY);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST(audio_activity(e->data->e_tags), SOA_ACTIVE_SENDONLY);
+ TEST(video_activity(e->data->e_tags), SOA_ACTIVE_SENDRECV);
+ TEST_1(!e->next);
+
+ TEST_1(nua_handle_has_active_call(b_call->nh));
+ TEST_1(nua_handle_has_call_on_hold(b_call->nh));
+
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-7.3: PASSED\n");
+
+ /* ------------------------------------------------------------------------ */
+ /*
+ : :
+ |<-INVITE(sendrecv)--|
+ |----200(sendrecv)-->|
+ |<--------ACK--------|
+ : :
+ */
+
+ if (print_headings)
+ printf("TEST NUA-7.4: resume A\n");
+
+ /* Resume A on hold, too. */
+ INVITE(b, b_call, b_call->nh, SOATAG_HOLD(""),
+ SIPTAG_SUBJECT_STR("TEST NUA-7.4: resume A"),
+ TAG_END());
+ run_ab_until(ctx, -1, until_ready, -1, until_ready);
+
+ /* Client transitions:
+ READY -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C3a+C4)-> READY: nua_r_invite, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST(audio_activity(e->data->e_tags), SOA_ACTIVE_SENDRECV);
+ TEST(video_activity(e->data->e_tags), SOA_ACTIVE_SENDRECV);
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ TEST_1(nua_handle_has_active_call(a_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+ /*
+ Server transitions:
+ READY -(S3a)-> COMPLETED: nua_i_invite, <auto-answer>, nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST(audio_activity(e->data->e_tags), SOA_ACTIVE_SENDRECV);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST(audio_activity(e->data->e_tags), SOA_ACTIVE_SENDRECV);
+ TEST(video_activity(e->data->e_tags), SOA_ACTIVE_SENDRECV);
+ TEST_1(!e->next);
+
+ TEST_1(nua_handle_has_active_call(b_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(b_call->nh));
+
+ free_events_in_list(ctx, a->events);
+
+ if (print_headings)
+ printf("TEST NUA-7.4: PASSED\n");
+
+ /* ---------------------------------------------------------------------- */
+ /*
+ A B
+ |--------INFO------->|
+ |<--------200--------|
+ */
+ if (print_headings)
+ printf("TEST NUA-7.5: send INFO\n");
+
+ INFO(a, a_call, a_call->nh, TAG_END());
+ run_a_until(ctx, -1, save_until_final_response);
+ /* XXX - B should get a nua_i_info event with 405 */
+
+ /* A sent INFO, receives 405 */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_info);
+ TEST(e->data->e_status, 405);
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+#if 0 /* XXX */
+ /* B received INFO */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_info);
+ TEST(e->data->e_status, 405);
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+#endif
+
+ /* Add INFO to allowed methods */
+ nua_set_hparams(b_call->nh, NUTAG_ALLOW("INFO, PUBLISH"), TAG_END());
+ run_b_until(ctx, nua_r_set_params, until_final_response);
+
+ INFO(a, a_call, a_call->nh, TAG_END());
+ run_ab_until(ctx, -1, save_until_final_response, -1, save_until_received);
+
+ /* A sent INFO, receives 200 */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_info);
+ TEST(e->data->e_status, 200);
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ /* B received INFO */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_info);
+ TEST(e->data->e_status, 200);
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-7.5: PASSED\n");
+
+ /* ------------------------------------------------------------------------ */
+ /*
+ : :
+ |<------INVITE-------|
+ |--------200-------->|
+ |<--------ACK--------|
+ : :
+ */
+
+ if (print_headings)
+ printf("TEST NUA-7.6.1: re-INVITE without auto-ack\n");
+
+ /* Turn off auto-ack */
+ nua_set_hparams(b_call->nh, NUTAG_AUTOACK(0), TAG_END());
+ run_b_until(ctx, nua_r_set_params, until_final_response);
+
+ INVITE(b, b_call, b_call->nh, SOATAG_HOLD(""),
+ SIPTAG_SUBJECT_STR("TEST NUA-7.6: re-INVITE without auto-ack"),
+ TAG_END());
+ run_ab_until(ctx, -1, until_complete, -1, complete_call);
+
+ /* Client transitions:
+ READY -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C3a)-> COMPLETING: nua_r_invite, nua_i_state
+ COMPLETING -(C4)-> READY: nua_ack(), nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completing); /* COMPLETING */
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST(audio_activity(e->data->e_tags), SOA_ACTIVE_SENDRECV);
+ TEST(video_activity(e->data->e_tags), SOA_ACTIVE_SENDRECV);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, b->events);
+
+ /*
+ Server transitions:
+ READY -(S3a)-> COMPLETED: nua_i_invite, <auto-answer>, nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST(audio_activity(e->data->e_tags), SOA_ACTIVE_SENDRECV);
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ ACK(b, b_call, b_call->nh, TAG_END());
+
+ run_ab_until(ctx, -1, until_ready, -1, until_ready);
+
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST(audio_activity(e->data->e_tags), SOA_ACTIVE_SENDRECV);
+ TEST(video_activity(e->data->e_tags), SOA_ACTIVE_SENDRECV);
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ if (print_headings)
+ printf("TEST NUA-7.6.1: PASSED\n");
+
+ /* ------------------------------------------------------------------------ */
+ /*
+ : :
+ |<------INVITE-------|
+ |--------200-------->|
+ |<--------ACK--------|
+ : :
+ */
+
+ if (ctx->proxy_tests && ctx->nat) {
+ if (print_headings)
+ printf("TEST NUA-7.6.2: almost overlapping re-INVITE\n");
+
+ /* Turn off auto-ack */
+ nua_set_hparams(b_call->nh, NUTAG_AUTOACK(0), TAG_END());
+ run_b_until(ctx, nua_r_set_params, until_final_response);
+
+ INVITE(b, b_call, b_call->nh, SOATAG_HOLD("#"),
+ SIPTAG_SUBJECT_STR("TEST NUA-7.6.2: re-INVITE"),
+ TAG_END());
+ run_ab_until(ctx, -1, until_complete, -1, complete_call);
+
+ /* Client transitions:
+ READY -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C3a)-> COMPLETING: nua_r_invite, nua_i_state
+ COMPLETING -(C4)-> READY: nua_ack(), nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completing); /* COMPLETING */
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST(audio_activity(e->data->e_tags), SOA_ACTIVE_INACTIVE);
+ TEST(video_activity(e->data->e_tags), SOA_ACTIVE_INACTIVE);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, b->events);
+
+ /*
+ Server transitions:
+ READY -(S3a)-> COMPLETED: nua_i_invite, <auto-answer>, nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST(audio_activity(e->data->e_tags), SOA_ACTIVE_INACTIVE);
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ f = test_nat_add_filter(ctx->nat, remove_first_ack, &zero, nat_inbound);
+
+ ACK(b, b_call, b_call->nh, TAG_END());
+ INVITE(b, b_call, b_call->nh, SOATAG_HOLD("*"),
+ SIPTAG_SUBJECT_STR("TEST NUA-7.6.2: almost overlapping re-INVITE"),
+ NUTAG_AUTOACK(1),
+ TAG_END());
+
+ run_ab_until(ctx, -1, until_ready, -1, invite_responded);
+
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_retry_after);
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST(audio_activity(e->data->e_tags), SOA_ACTIVE_INACTIVE);
+ TEST(video_activity(e->data->e_tags), SOA_ACTIVE_INACTIVE);
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ test_nat_remove_filter(ctx->nat, f);
+
+ if (print_headings)
+ printf("TEST NUA-7.6.2: PASSED\n");
+ }
+
+
+ /* ---------------------------------------------------------------------- */
+ /*
+ A B
+ |---------BYE------->|
+ |<--------200--------|
+ */
+
+ if (print_headings)
+ printf("TEST NUA-7.6.3: terminate call\n");
+
+ BYE(a, a_call, a_call->nh, TAG_END());
+ run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
+
+ /*
+ Transitions of A:
+ READY --(T2)--> TERMINATING: nua_bye()
+ TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ /* Transitions of B:
+ READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_bye);
+ TEST(e->data->e_status, 200);
+ if (ctx->proxy_tests && ctx->nat) {
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 481);
+ }
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-7.6.3: PASSED\n");
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ END();
+}
+
+/*
+ INVITE without auto-ack
+ X
+ | |
+ |-------INVITE------>|
+ |<--------200--------|
+ | |
+ |---------ACK------->|
+*/
+int complete_call(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_completing:
+ return 1;
+ case nua_callstate_ready:
+ return 1;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/*
+ X INVITE
+ | |
+ |-------INVITE------>|
+ |<--------200--------|
+*/
+int until_complete(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_completed:
+ case nua_callstate_ready:
+ return 1;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static size_t remove_first_ack(void *_once, void *message, size_t len)
+{
+ int *once = _once;
+
+ if (*once)
+ return len;
+
+ if (strncmp("ACK ", message, 4) == 0) {
+ printf("FILTERING %.*s\n", strcspn(message, "\r\n"), (char *)message);
+ *once = 1;
+ return 0;
+ }
+
+ return len;
+}
+
+/* ======================================================================== */
+/* test_reinvite message sequence looks like this:
+
+ A B
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<----180 Ringing----|
+ | |
+ |<--------200--------|
+ |---------ACK------->|
+ : :
+ |<----re-INVITE------|
+ |<-------BYE---------|
+ |--------200-------->|
+ |-----487-INVITE---->|
+ |<--------ACK--------|
+*/
+
+int accept_no_save(CONDITION_PARAMS);
+int ringing_until_terminated(CONDITION_PARAMS);
+int bye_when_ringing(CONDITION_PARAMS);
+
+int test_reinvite(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+
+ if (print_headings)
+ printf("TEST NUA-7.7: Test re-INVITE and BYE\n");
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 0 8\n";
+ b_call->sdp = "m=audio 5010 RTP/AVP 8\n";
+
+ TEST_1(a_call->nh =
+ nua_handle(a->nua, a_call,
+ SIPTAG_FROM_STR("Alice <sip:alice at example.com>"),
+ SIPTAG_TO(b->to),
+ NUTAG_AUTOANSWER(0),
+ TAG_END()));
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ TAG_END());
+
+ run_ab_until(ctx, -1, accept_no_save, -1, accept_no_save);
+
+ TEST_1(nua_handle_has_active_call(a_call->nh));
+ TEST_1(nua_handle_has_active_call(b_call->nh));
+
+ free_events_in_list(ctx, a->events);
+ free_events_in_list(ctx, b->events);
+
+/*
+ A B
+ |<----re-INVITE------|
+ |<------CANCEL-------|
+ |<-------BYE---------|
+ |-----200-CANCEL---->|
+ |------200-BYE------>|
+ |-----487-INVITE---->|
+ |<--------ACK--------|
+*/
+
+ /* re-INVITE A, send BYE after receiving 180 */
+ INVITE(b, b_call, b_call->nh,
+ SIPTAG_SUBJECT_STR("re-INVITE"),
+ TAG_END());
+ /* Run until both a and b has terminated their call */
+ run_ab_until(ctx, -1, ringing_until_terminated, -1, bye_when_ringing);
+
+#if notyet
+ struct event *e;
+
+ /* XXX - check events later - now we are happy that calls get terminated */
+ /* Client events:
+ READY -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING --(C2)--> PROCEEDING: nua_r_invite, nua_i_state, nua_bye()
+ PROCEEDING--((C3a+C4)-> READY: nua_r_invite, nua_i_state
+ READY --(T2)--> TERMINATING: nua_bye()
+ TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminating); /* READY */
+ /* Now we can receive events, in any possible order */
+ /* XXX */
+ TEST_1(e = e->next);
+
+ TEST_1(!nua_handle_has_active_call(a_call->nh));
+
+ /*
+ Server transitions:
+ READY --(T2)--> CTERMINATING: nua_bye()
+ TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
+ READY -(S3a)-> COMPLETED: nua_i_invite, <auto-answer>, nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST(audio_activity(e->data->e_tags), SOA_ACTIVE_RECVONLY);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST(audio_activity(e->data->e_tags), SOA_ACTIVE_RECVONLY);
+ TEST_1(!e->next);
+
+ TEST_1(nua_handle_has_active_call(b_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(b_call->nh));
+
+#endif
+
+ if (print_headings)
+ printf("TEST NUA-7.7: PASSED\n");
+
+ free_events_in_list(ctx, a->events);
+ free_events_in_list(ctx, b->events);
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ END();
+}
+
+/*
+ Accept INVITE
+ X
+ | |
+ |-------INVITE------>|
+ |<--------200--------|
+ | |
+ |---------ACK------->|
+*/
+int accept_no_save(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ switch (callstate(tags)) {
+ case nua_callstate_received:
+ RESPOND(ep, call, nh, SIP_200_OK,
+ TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
+ TAG_END());
+ return 0;
+ case nua_callstate_ready:
+ return 1;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int ringing_until_terminated(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_received:
+ RESPOND(ep, call, nh, SIP_180_RINGING, TAG_END());
+ return 0;
+ case nua_callstate_ready:
+ return 0;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/* ======================================================================== */
+
+int accept_and_attempt_reinvite(CONDITION_PARAMS);
+int until_ready2(CONDITION_PARAMS);
+
+/* test_reinvite2 message sequence looks like this:
+
+ A B
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<----180 Ringing----|
+ | |
+ | /-INVITE-|
+ | \---900->|
+ | |
+ |<--------200--------|
+ |---------ACK------->|
+ : :
+ : queue INVITE :
+ : :
+ |-----re-INVITE----->|
+ |<--------200--------|
+ |---------ACK------->|
+ |-----re-INVITE----->|
+ |<--------200--------|
+ |---------ACK------->|
+ : :
+ : glare :
+ : :
+ |-----re-INVITE----->|
+ |<----re-INVITE------|
+ |<--------491--------|
+ |---------491------->|
+ |---------ACK------->|
+ |<--------ACK--------|
+ : :
+ |---------BYE------->|
+ |<--------200--------|
+*/
+
+int test_reinvite2(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+
+ if (print_headings)
+ printf("TEST NUA-7.8.1: Test re-INVITE glare\n");
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 0 8\n";
+ b_call->sdp = "m=audio 5010 RTP/AVP 8\n";
+
+ TEST_1(a_call->nh =
+ nua_handle(a->nua, a_call,
+ SIPTAG_FROM_STR("Alice <sip:alice at example.com>"),
+ SIPTAG_TO(b->to),
+ TAG_END()));
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ TAG_END());
+
+ run_ab_until(ctx, -1, until_ready, -1, accept_and_attempt_reinvite);
+
+ TEST_1(nua_handle_has_active_call(a_call->nh));
+ TEST_1(nua_handle_has_active_call(b_call->nh));
+
+ free_events_in_list(ctx, a->events);
+ free_events_in_list(ctx, b->events);
+
+ /* Check that we can queue INVITEs */
+ INVITE(a, a_call, a_call->nh, TAG_END());
+ INVITE(a, a_call, a_call->nh, TAG_END());
+
+ run_ab_until(ctx, -1, until_ready2, -1, until_ready2);
+
+ free_events_in_list(ctx, a->events);
+ free_events_in_list(ctx, b->events);
+
+ /* Check that INVITE glare works */
+ INVITE(a, a_call, a_call->nh, TAG_END());
+ INVITE(b, b_call, b_call->nh, TAG_END());
+
+ a->flags.n = 0, b->flags.n = 0;
+ run_ab_until(ctx, -1, until_ready2, -1, until_ready2);
+
+ free_events_in_list(ctx, a->events);
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-7.8.1: PASSED\n");
+
+
+
+ /* ---------------------------------------------------------------------- */
+ /*
+ A B
+ |---------BYE------->|
+ |<--------200--------|
+ */
+
+ if (print_headings)
+ printf("TEST NUA-7.8.2: terminate call\n");
+
+ BYE(a, a_call, a_call->nh, TAG_END());
+ run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
+
+ /*
+ Transitions of A:
+ READY --(T2)--> TERMINATING: nua_bye()
+ TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ /* Transitions of B:
+ READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_bye);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-7.8.2: PASSED\n");
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ END();
+}
+
+int accept_and_attempt_reinvite(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ if (event == nua_i_prack) {
+ INVITE(ep, call, nh, TAG_END());
+ RESPOND(ep, call, nh, SIP_200_OK,
+ TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
+ TAG_END());
+ }
+ else switch (callstate(tags)) {
+ case nua_callstate_received:
+ RESPOND(ep, call, nh, SIP_180_RINGING,
+ SIPTAG_REQUIRE_STR("100rel"),
+ TAG_END());
+ return 0;
+ case nua_callstate_early:
+ return 0;
+ case nua_callstate_ready:
+ return 1;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+/*
+ X INVITE
+ | |
+ |-------INVITE------>|
+ |<--------200--------|
+ |---------ACK------->|
+*/
+int until_ready2(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ if (event == nua_r_invite && status == 491) {
+ if (ep == &ctx->a && ++ctx->b.flags.n >= 2) ctx->b.running = 0;
+ if (ep == &ctx->b && ++ctx->a.flags.n >= 2) ctx->a.running = 0;
+ }
+
+ switch (callstate(tags)) {
+ case nua_callstate_ready:
+ return ++ep->flags.n >= 2;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int invite_responded(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ return event == nua_r_invite && status >= 100;
+}
+
+
+int test_reinvites(struct context *ctx)
+{
+ int retval = 0;
+
+ if (print_headings)
+ printf("TEST NUA-7: Test call hold and re-INVITEs\n");
+
+ retval = test_call_hold(ctx);
+
+ if (retval == 0)
+ retval = test_reinvite(ctx);
+
+ if (retval == 0)
+ retval = test_reinvite2(ctx);
+
+ if (print_headings && retval == 0)
+ printf("TEST NUA-7: PASSED\n");
+
+ return retval;
+}
Added: freeswitch/trunk/libs/sofia-sip/tests/test_call_reject.c
==============================================================================
--- (empty file)
+++ freeswitch/trunk/libs/sofia-sip/tests/test_call_reject.c Wed May 14 15:10:54 2008
@@ -0,0 +1,1689 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2005 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at 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 test_call_reject.c
+ * @brief NUA-4 tests: call reject cases
+ *
+ * @author Pekka Pessi <Pekka.Pessi at nokia.com>
+ * @author Martti Mela <Martti Mela at nokia.com>
+ *
+ * @date Created: Wed Aug 17 12:12:12 EEST 2005 ppessi
+ */
+
+#include "config.h"
+
+#include "test_nua.h"
+#include <sofia-sip/su_tag_class.h>
+
+#if HAVE_FUNC
+#elif HAVE_FUNCTION
+#define __func__ __FUNCTION__
+#else
+#define __func__ "test_reject"
+#endif
+
+/* ======================================================================== */
+/*
+ A reject-1 B
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<--------486--------|
+ |---------ACK------->|
+*/
+int reject_1(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_received:
+ RESPOND(ep, call, nh, SIP_486_BUSY_HERE, TAG_END());
+ return 0;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+
+int test_reject_a(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+ sip_t const *sip;
+
+ if (print_headings)
+ printf("TEST NUA-4.1: reject before ringing\n");
+
+ /*
+ A reject-1 B
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<--------486--------|
+ |---------ACK------->|
+ */
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SIPTAG_SUBJECT_STR("reject-1"),
+ SIPTAG_CONTACT_STR("sip:a at 127.0.0.1"),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ TAG_END());
+ run_ab_until(ctx, -1, until_terminated, -1, reject_1);
+
+ /*
+ Client transitions in reject-1:
+ INIT -(C1)-> CALLING -(C6a)-> TERMINATED
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 486);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+
+ /*
+ Server transitions in reject-1:
+ INIT -(S1)-> RECEIVED -(S6a)-> TERMINATED
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_contact); TEST_1(!sip->sip_contact->m_next);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-4.1: PASSED\n");
+
+ END();
+}
+
+/*
+ A reject-2 B
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<----180 Ringing----|
+ | |
+ |<--------602--------|
+ |---------ACK------->|
+*/
+int reject_2(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_received:
+ RESPOND(ep, call, nh, SIP_180_RINGING, TAG_END());
+ return 0;
+ case nua_callstate_early:
+ RESPOND(ep, call, nh, 602, "Rejected 2", TAG_END());
+ return 0;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int test_reject_b(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+
+ /* ------------------------------------------------------------------------ */
+ /*
+ A reject-2 B
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<----180 Ringing----|
+ | |
+ |<--------602--------|
+ |---------ACK------->|
+ */
+
+ if (print_headings)
+ printf("TEST NUA-4.2: reject after ringing\n");
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+ /* Make call reject-2 */
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SIPTAG_SUBJECT_STR("reject-2"),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ TAG_END());
+ run_ab_until(ctx, -1, until_terminated, -1, reject_2);
+
+ /*
+ Client transitions in reject-2:
+ INIT -(C1)-> CALLING -(C2)-> PROCEEDING -(C6b)-> TERMINATED
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 602);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+
+ /*
+ Server transitions in reject-2:
+ INIT -(S1)-> RECEIVED -(S2)-> EARLY -(S6a)-> TERMINATED
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-4.2: PASSED\n");
+
+ END();
+}
+
+/* ------------------------------------------------------------------------ */
+
+int reject_302(CONDITION_PARAMS), reject_305(CONDITION_PARAMS);
+int reject_500_retry_after(CONDITION_PARAMS);
+int redirect_always(CONDITION_PARAMS);
+int reject_604(CONDITION_PARAMS);
+
+/*
+ A reject-302 B
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ |<-----302 Other-----|
+ |--------ACK-------->|
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ |<--305 Use Proxy----|
+ |--------ACK-------->|
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ |<----500 Retry------|
+ |--------ACK-------->|
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<----180 Ringing----|
+ | |
+ |<---604 Nowhere-----|
+ |--------ACK-------->|
+*/
+
+int reject_302(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_received:
+ {
+ sip_contact_t m[1];
+ *m = *ep->contact;
+ m->m_url->url_user = "302";
+ RESPOND(ep, call, nh, SIP_302_MOVED_TEMPORARILY,
+ SIPTAG_CONTACT(m), TAG_END());
+ }
+ return 0;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ ep->next_condition = reject_305;
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+int reject_305(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_received:
+ {
+ sip_contact_t m[1];
+ *m = *ep->contact;
+ m->m_url->url_user = "305";
+ m->m_url->url_params = "lr=1";
+ RESPOND(ep, call, nh, SIP_305_USE_PROXY, SIPTAG_CONTACT(m), TAG_END());
+ }
+ return 0;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ ep->next_condition = reject_500_retry_after;
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+int reject_500_retry_after(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ if (event == nua_i_invite) {
+ sip_retry_after_t af[1];
+ sip_retry_after_init(af)->af_delta = 1;
+ RESPOND(ep, call, nh, 500, "Retry After", SIPTAG_RETRY_AFTER(af), TAG_END());
+ }
+ else if (event == nua_i_state) switch (callstate(tags)) {
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ ep->next_condition = reject_604;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+int reject_604(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_received:
+ RESPOND(ep, call, nh, SIP_180_RINGING, TAG_END());
+ return 0;
+ case nua_callstate_early:
+ RESPOND(ep, call, nh, SIP_604_DOES_NOT_EXIST_ANYWHERE, TAG_END());
+ return 0;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int redirect_always(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ if (event == nua_i_invite) {
+ char user[30];
+ sip_contact_t m[1];
+ *m = *ep->contact;
+ snprintf(user, sizeof user, "user-%u", ep->flags.n++);
+ m->m_url->url_user = user;
+ RESPOND(ep, call, nh, SIP_302_MOVED_TEMPORARILY,
+ SIPTAG_CONTACT(m), TAG_END());
+ nua_handle_destroy(nh);
+ call->nh = NULL;
+ return 1;
+ }
+
+ return 0;
+}
+
+
+int test_reject_302(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+ sip_t const *sip;
+
+ /* Make call reject-3 */
+ if (print_headings)
+ printf("TEST NUA-4.3: redirect then reject\n");
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SIPTAG_SUBJECT_STR("reject-3"),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ TAG_END());
+ run_ab_until(ctx, -1, until_terminated, -1, reject_302);
+
+ /*
+ A reject-3 B
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<-----302 Other-----|
+ |--------ACK-------->|
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ |<---305 Use Proxy---|
+ |--------ACK-------->|
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ |<-----500 Retry-----|
+ |--------ACK-------->|
+ | |
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<----180 Ringing----|
+ | |
+ |<---604 Nowhere-----|
+ |--------ACK-------->|
+ */
+
+ /*
+ Client transitions in reject-3:
+ INIT -(C1)-> PROCEEDING -(C6a)-> TERMINATED/INIT
+ INIT -(C1)-> CALLING -(C2)-> PROCEEDING -(C6b)-> TERMINATED
+ */
+
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 100);
+ TEST(sip_object(e->data->e_msg)->sip_status->st_status, 302);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 100);
+ TEST(sip_object(e->data->e_msg)->sip_status->st_status, 305);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 100);
+ TEST(sip_object(e->data->e_msg)->sip_status->st_status, 500);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 604);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED -(S6a)-> TERMINATED/INIT
+ INIT -(S1)-> RECEIVED -(S2)-> EARLY -(S6b)-> TERMINATED
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_request);
+ TEST_S(sip->sip_request->rq_url->url_user, "302");
+ TEST_1(sip->sip_route);
+ TEST_S(sip->sip_route->r_url->url_user, "305");
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-4.3: PASSED\n");
+
+ if (print_headings)
+ printf("TEST NUA-4.3.1: redirect until retry count is exceeded\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SIPTAG_SUBJECT_STR("redirect always"),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ TAG_END());
+ run_ab_until(ctx, -1, until_terminated, -1, redirect_always);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-4.3: PASSED\n");
+
+ END();
+}
+
+/* ------------------------------------------------------------------------ */
+
+/* Reject call with 407, then 401 */
+
+int reject_407(CONDITION_PARAMS);
+int reject_401(CONDITION_PARAMS);
+int authenticate_call(CONDITION_PARAMS);
+int reject_403(CONDITION_PARAMS);
+
+/*
+ A reject-401 B
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ |<--------407--------|
+ |---------ACK------->|
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<----180 Ringing----|
+ | |
+ |<--------401--------|
+ |---------ACK------->|
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ |<-------403---------|
+ |--------ACK-------->|
+*/
+
+int reject_407(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_received:
+ RESPOND(ep, call, nh, SIP_407_PROXY_AUTH_REQUIRED,
+ SIPTAG_PROXY_AUTHENTICATE_STR("Digest realm=\"test_nua\", "
+ "nonce=\"nsdhfuds\", algorithm=MD5, "
+ "qop=\"auth-int\""),
+ TAG_END());
+ return 0;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ ep->next_condition = reject_401;
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+int reject_401(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_received:
+ RESPOND(ep, call, nh, SIP_180_RINGING, TAG_END());
+ return 0;
+ case nua_callstate_early:
+ RESPOND(ep, call, nh, SIP_401_UNAUTHORIZED,
+ SIPTAG_WWW_AUTHENTICATE_STR("Digest realm=\"test_nua\", "
+ "nonce=\"nsdhfuds\", algorithm=MD5, "
+ "qop=\"auth\""),
+ TAG_END());
+ return 0;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ ep->next_condition = reject_403;
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+int reject_403(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_received:
+ RESPOND(ep, call, nh, SIP_403_FORBIDDEN, TAG_END());
+ return 0;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ ep->next_condition = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int authenticate_call(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ if (event == nua_r_invite && status == 401) {
+ AUTHENTICATE(ep, call, nh, NUTAG_AUTH("Digest:\"test_nua\":jaska:secret"),
+ SIPTAG_SUBJECT_STR("Got 401"),
+ TAG_END());
+ return 0;
+ }
+
+ if (event == nua_r_invite && status == 407) {
+ AUTHENTICATE(ep, call, nh, NUTAG_AUTH("Digest:\"test_nua\":erkki:secret"),
+ SIPTAG_SUBJECT_STR("Got 407"),
+ TAG_END());
+ return 0;
+ }
+
+ switch (callstate(tags)) {
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int test_reject_401(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event const *e;
+ sip_t const *sip;
+
+ if (print_headings)
+ printf("TEST NUA-4.4: challenge then reject\n");
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SIPTAG_SUBJECT_STR("reject-401"),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ TAG_END());
+ run_ab_until(ctx, -1, authenticate_call, -1, reject_407);
+
+ /*
+ Client transitions in reject-3:
+ INIT -(C1)-> CALLING -(C2)-> PROCEEDING -(C6b)-> TERMINATED/INIT
+ INIT -(C1)-> CALLING -(C6a)-> TERMINATED
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(sip_object(e->data->e_msg)->sip_status->st_status, 407);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 401);
+ TEST(sip_object(e->data->e_msg)->sip_status->st_status, 401);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 403);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED -(S6a)-> TERMINATED/INIT
+ INIT -(S1)-> RECEIVED -(S2)-> EARLY -(S6b)-> TERMINATED/INIT
+ INIT -(S1)-> RECEIVED -(S6a)-> TERMINATED
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_subject);
+ TEST_S(sip->sip_subject->g_value, "reject-401");
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_proxy_authorization);
+ /* Ensure that nua_authenticate() tags get added to the request */
+ TEST_1(sip->sip_subject);
+ TEST_S(sip->sip_subject->g_value, "Got 407");
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_invite);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_subject);
+ TEST_S(sip->sip_subject->g_value, "Got 401");
+ TEST_1(sip->sip_authorization);
+ TEST_1(sip->sip_proxy_authorization);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, b->events);
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-4.4: PASSED\n");
+
+ END();
+}
+
+/* ------------------------------------------------------------------------ */
+
+/* Reject call with 401 and bad challenge */
+
+/*
+ A reject-401-aka B
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ |<--------401--------|
+ |---------ACK------->|
+*/
+
+int reject_401_aka(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_received:
+ RESPOND(ep, call, nh, SIP_401_UNAUTHORIZED,
+ /* Send a challenge that we do not grok */
+ SIPTAG_WWW_AUTHENTICATE_STR("Digest realm=\"test_nua\", "
+ "nonce=\"nsdhfuds\", algorithm=SHA0-AKAv6, "
+ "qop=\"auth\""),
+ TAG_END());
+ return 0;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int test_reject_401_aka(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event const *e;
+ sip_t const *sip;
+
+ if (print_headings)
+ printf("TEST NUA-4.6.1: invalid challenge \n");
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SIPTAG_SUBJECT_STR("reject-401-aka"),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ TAG_END());
+
+ run_ab_until(ctx, -1, until_terminated, -1, reject_401_aka);
+
+ /*
+ Client transitions
+ INIT -(C1)-> CALLING -(C6a)-> TERMINATED/INIT
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(sip_object(e->data->e_msg)->sip_status->st_status, 401);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED -(S6a)-> TERMINATED
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, b->events);
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-4.6.1: PASSED\n");
+
+ END();
+}
+
+
+/* ------------------------------------------------------------------------ */
+
+/* Reject call with 401, twice */
+
+/*
+ A reject-401-bad B
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ |<--------401--------|
+ |---------ACK------->|
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ |<--------401--------|
+ |---------ACK------->|
+*/
+
+int reject_401_bad(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_received:
+ RESPOND(ep, call, nh, SIP_401_UNAUTHORIZED,
+ /* Send a challenge that we do not grok */
+ SIPTAG_WWW_AUTHENTICATE_STR("Digest realm=\"No hope\", "
+ "nonce=\"goO541ftNrw327aWpu2\", "
+ "algorithm=MD5, "
+ "qop=\"auth\""),
+ TAG_END());
+ return 0;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ if (ep->flags.bit0) /* Terminate 2 calls */
+ return 1;
+ ep->flags.bit0 = 1;
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+int authenticate_bad(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ if (event == nua_r_invite && status == 401) {
+ AUTHENTICATE(ep, call, nh, NUTAG_AUTH("Digest:\"No hope\":jaska:secret"),
+ SIPTAG_SUBJECT_STR("Bad password"),
+ TAG_END());
+ return 0;
+ }
+
+ switch (callstate(tags)) {
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+
+int test_reject_401_bad(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event const *e;
+ sip_t const *sip;
+
+ if (print_headings)
+ printf("TEST NUA-4.6.2: bad username/password\n");
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SIPTAG_SUBJECT_STR("reject-401-bad"),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ TAG_END());
+
+
+ run_ab_until(ctx, -1, authenticate_bad, -1, reject_401_bad);
+
+ /*
+ Client transitions
+ INIT -(C1)-> CALLING -(C6a)-> TERMINATED/INIT
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(sip_object(e->data->e_msg)->sip_status->st_status, 401);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(sip_object(e->data->e_msg)->sip_status->st_status, 401);
+ /* nua_authenticate() fails and INVITE returns an internal error response */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 904);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED -(S6a)-> TERMINATED
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_invite);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, b->events);
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-4.6.2: PASSED\n");
+
+ END();
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+int test_mime_negotiation(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+ sip_t const *sip;
+
+ /* Make call reject-3 */
+ if (print_headings)
+ printf("TEST NUA-4.5: check for rejections of invalid requests\n");
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ if (print_headings)
+ printf("TEST NUA-4.5.1: invalid Content-Type\n");
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SIPTAG_SUBJECT_STR("reject-3"),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ SIPTAG_CONTENT_TYPE_STR("application/xyzzy+xml"),
+ SIPTAG_CONTENT_DISPOSITION_STR("session;required"),
+ SIPTAG_PAYLOAD_STR("m=audio 5008 RTP/AVP 8\n"),
+ TAG_END());
+ run_ab_until(ctx, -1, until_terminated, -1, NULL);
+
+ /*
+ A reject-5.1 B
+ | |
+ |-------INVITE------>|
+ |<-------415---------|
+ |--------ACK-------->|
+ */
+
+ /*
+ Client transitions in reject-3:
+ INIT -(C1)-> CALLING -(C6a)-> TERMINATED
+ */
+
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 415);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST(sip->sip_status->st_status, 415);
+ TEST_1(sip->sip_accept);
+ TEST_S(sip->sip_accept->ac_type, "application/sdp");
+ TEST_1(sip->sip_accept_encoding);
+ /* No content-encoding is supported */
+ TEST_S(sip->sip_accept_encoding->aa_value, "");
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* CALLING */
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+
+ if (print_headings)
+ printf("TEST NUA-4.5.1: PASSED\n");
+
+ if (print_headings)
+ printf("TEST NUA-4.5.2: invalid Content-Encoding\n");
+
+ /*
+ A reject-5.2 B
+ | |
+ |-------INVITE------>|
+ |<-------415---------|
+ |--------ACK-------->|
+ */
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SIPTAG_SUBJECT_STR("reject-5"),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ SIPTAG_CONTENT_ENCODING_STR("zyxxy"),
+ TAG_END());
+ run_ab_until(ctx, -1, until_terminated, -1, NULL);
+
+ /*
+ Client transitions in reject-3:
+ INIT -(C1)-> CALLING -(C6a)-> TERMINATED
+ */
+
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 415);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST(sip->sip_status->st_status, 415);
+ TEST_1(sip->sip_accept);
+ TEST_S(sip->sip_accept->ac_type, "application/sdp");
+ TEST_1(sip->sip_accept_encoding);
+ /* No content-encoding is supported */
+ TEST_S(sip->sip_accept_encoding->aa_value, "");
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+
+ if (print_headings)
+ printf("TEST NUA-4.5.2: PASSED\n");
+
+ if (print_headings)
+ printf("TEST NUA-4.5.3: invalid Accept\n");
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SIPTAG_SUBJECT_STR("reject-3"),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ SIPTAG_ACCEPT_STR("application/xyzzy+xml"),
+ TAG_END());
+ run_ab_until(ctx, -1, until_terminated, -1, NULL);
+
+
+ /*
+ A reject-5.3 B
+ | |
+ |-------INVITE------>|
+ |<-------406---------|
+ |--------ACK-------->|
+ */
+
+ /*
+ Client transitions in reject-3:
+ INIT -(C1)-> CALLING -(C6a)-> TERMINATED
+ */
+
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 406);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST(sip->sip_status->st_status, 406);
+ TEST_1(sip->sip_accept);
+ TEST_S(sip->sip_accept->ac_type, "application/sdp");
+ TEST_1(sip->sip_accept_encoding);
+ /* No content-encoding is supported */
+ TEST_S(sip->sip_accept_encoding->aa_value, "");
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+
+ if (print_headings)
+ printf("TEST NUA-4.5.3: PASSED\n");
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-4.5: PASSED\n");
+
+ END();
+}
+
+/* ---------------------------------------------------------------------- */
+
+size_t filter_200_OK(void *arg, void *message, size_t len)
+{
+ (void)arg;
+
+ if (len >= 11 && strncasecmp(message, "SIP/2.0 200", 11) == 0)
+ return 0;
+ return len;
+}
+
+size_t filter_ACK(void *arg, void *message, size_t len)
+{
+ (void)arg;
+
+ if (len >= 7 && strncasecmp(message, "ACK sip", 7) == 0)
+ return 0;
+ return len;
+}
+
+int call_with_bad_ack(CONDITION_PARAMS);
+int accept_call_with_bad_contact(CONDITION_PARAMS);
+
+int test_call_timeouts(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+ struct nat_filter *f, *f2;
+
+ if (print_headings)
+ printf("TEST NUA-4.7: check for error and timeout handling\n");
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+ if (!ctx->nat)
+ goto completed_4_7_1;
+
+ if (print_headings)
+ printf("TEST NUA-4.7.1: ACK timeout (200 OK filtered)\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ TEST_1(f = test_nat_add_filter(ctx->nat, filter_200_OK, NULL, nat_inbound));
+ TEST_1(f2 = test_nat_add_filter(ctx->nat, filter_200_OK,
+ NULL, nat_outbound));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SIPTAG_SUBJECT_STR("NUA-4.7.1"),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ TAG_END());
+ run_ab_until(ctx, -1, until_terminated, -1, accept_call);
+
+ /*
+ A accept_call B
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<----180 Ringing----|
+ | |
+ | X-----200--------|
+ | X-----200--------|
+ | X-----200--------|
+ | |
+ |<--------BYE--------|
+ |--------200 OK---X |
+
+ */
+
+ /*
+ Client transitions:
+ */
+
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+
+ /*
+ Server transitions:
+ -(S1)-> RECEIVED -(S2a)-> EARLY -(S3b)-> COMPLETED -(S5)-> TERMINATING
+ -(S10)-> TERMINATED -X
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_error);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminating); /* TERMINATING */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_bye);
+ TEST(e->data->e_status, 408);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ TEST_1(test_nat_remove_filter(ctx->nat, f) == 0);
+ TEST_1(test_nat_remove_filter(ctx->nat, f2) == 0);
+
+ if (print_headings)
+ printf("TEST NUA-4.7.1: PASSED\n");
+
+ completed_4_7_1:
+
+ if (!ctx->nat)
+ goto completed_4_7_2;
+
+ if (print_headings)
+ printf("TEST NUA-4.7.2: ACK timeout (ACK filtered)\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ TEST_1(f = test_nat_add_filter(ctx->nat, filter_ACK, NULL, nat_outbound));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SIPTAG_SUBJECT_STR("NUA-4.7.2"),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ TAG_END());
+ run_ab_until(ctx, -1, until_terminated, -1, accept_call);
+
+ /*
+ A accept_call B
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<----180 Ringing----|
+ | |
+ |<--------200--------|
+ |--------ACK-----X |
+ | |
+ |<--------200--------|
+ |--------ACK-----X |
+ | |
+ |<--------200--------|
+ |--------ACK-----X |
+ | |
+ |<--------BYE--------|
+ |--------200 OK----->|
+
+ */
+
+ /*
+ */
+
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated);
+ TEST_1(!e->next);
+
+ /*
+ Server transitions:
+ -(S1)-> RECEIVED -(S2a)-> EARLY -(S3b)-> COMPLETED -(S5)-> TERMINATING
+ -(S10)-> TERMINATED -X
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_error);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminating); /* TERMINATING */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ TEST_1(test_nat_remove_filter(ctx->nat, f) == 0);
+
+ if (print_headings)
+ printf("TEST NUA-4.7.2: PASSED\n");
+
+ completed_4_7_2:
+
+ if (print_headings)
+ printf("TEST NUA-4.7.3: sending ACK fails\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SIPTAG_SUBJECT_STR("NUA-4.7.3"),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ NUTAG_AUTOACK(0),
+ TAG_END());
+ run_ab_until(ctx, -1, call_with_bad_ack, -1, accept_call);
+
+ /*
+ A accept_call B
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<----180 Ringing----|
+ | |
+ |<--------200--------|
+ |--ACK-X |
+ | |
+ |<--------BYE--------|
+ |--------200 OK----->|
+
+ */
+
+ /*
+ */
+
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completing); /* COMPLETING */
+ /* try to send ACK */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminating);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated);
+ TEST_1(!e->next);
+
+ /*
+ Server transitions:
+ -(S1)-> RECEIVED -(S2a)-> EARLY -(S3b)-> COMPLETED -(S5)-> TERMINATING
+ -(S10)-> TERMINATED -X
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-4.7.3: PASSED\n");
+
+ if (!ctx->nat)
+ goto completed_4_7_4;
+
+ if (print_headings)
+ printf("TEST NUA-4.7.4: 200 OK timeout after client has timed out\n");
+
+ if (ctx->expensive)
+ nua_set_params(b->nua, NTATAG_SIP_T1X64(34000), TAG_END());
+ else
+ nua_set_params(b->nua, NTATAG_SIP_T1X64(4000), TAG_END());
+ run_b_until(ctx, nua_r_set_params, until_final_response);
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ TEST_1(f = test_nat_add_filter(ctx->nat, filter_ACK, NULL, nat_outbound));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SIPTAG_SUBJECT_STR("NUA-4.7.4"),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ TAG_END());
+ run_ab_until(ctx, -1, until_terminated, -1, accept_call);
+
+ /*
+ A accept_call B
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<----180 Ringing----|
+ | |
+ |<--------200--------| Timer H'
+ |--------ACK-----X X--+
+ | | |
+ |<--------200--------| |
+ |--------ACK-----X | |
+ | | |
+ |<--------200--------| |
+ | | |
+ |<--------200--------| |
+ | | |
+ | |<-+
+ |<--------BYE--------|
+ |--------200 OK----->|
+
+ */
+
+ /*
+ */
+
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated);
+ TEST_1(!e->next);
+
+ /*
+ Server transitions:
+ -(S1)-> RECEIVED -(S2a)-> EARLY -(S3b)-> COMPLETED -(S5)-> TERMINATING
+ -(S10)-> TERMINATED -X
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_error);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminating);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ TEST_1(test_nat_remove_filter(ctx->nat, f) == 0);
+
+ if (print_headings)
+ printf("TEST NUA-4.7.4: PASSED\n");
+
+ nua_set_params(b->nua, NTATAG_SIP_T1X64(2000), TAG_END());
+ run_b_until(ctx, nua_r_set_params, until_final_response);
+
+ completed_4_7_4:
+
+ /* XXX - PRACK timeout, PRACK failing, media failing, re-INVITEs */
+
+ if (print_headings)
+ printf("TEST NUA-4.7: PASSED\n");
+
+ END();
+}
+
+int call_with_bad_ack(CONDITION_PARAMS)
+{
+ if (!check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ if (event == nua_r_invite && 200 <= status && status < 300) {
+ ACK(ep, call, nh,
+ /* Syntax error - sending ACK fails, we send BYE */
+ SIPTAG_MAX_FORWARDS_STR("blue"),
+ TAG_END());
+ }
+
+ return event == nua_i_state && callstate(tags) == nua_callstate_terminated;
+}
+
+/* ---------------------------------------------------------------------- */
+
+int test_rejects(struct context *ctx)
+{
+ return
+ test_reject_401_bad(ctx) ||
+ test_reject_a(ctx) ||
+ test_reject_b(ctx) ||
+ test_reject_302(ctx) ||
+ test_reject_401(ctx) ||
+ test_mime_negotiation(ctx) ||
+ test_reject_401_aka(ctx) ||
+ test_call_timeouts(ctx) ||
+ 0;
+}
Added: freeswitch/trunk/libs/sofia-sip/tests/test_cancel_bye.c
==============================================================================
--- (empty file)
+++ freeswitch/trunk/libs/sofia-sip/tests/test_cancel_bye.c Wed May 14 15:10:54 2008
@@ -0,0 +1,1663 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2005 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at 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 test_cancel_bye.c
+ * @brief Test CANCEL, weird BYE and handle destroy
+ *
+ * @author Pekka Pessi <Pekka.Pessi at nokia.com>
+ * @author Martti Mela <Martti Mela at nokia.com>
+ *
+ * @date Created: Wed Aug 17 12:12:12 EEST 2005 ppessi
+ */
+
+#include "config.h"
+
+#include "test_nua.h"
+#include <sofia-sip/su_tag_class.h>
+
+#if HAVE_FUNC
+#elif HAVE_FUNCTION
+#define __func__ __FUNCTION__
+#else
+#define __func__ "test_cancel_bye"
+#endif
+
+/* ======================================================================== */
+
+/* Cancel cases:
+
+
+ A B
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |------CANCEL------->|
+ |<------200 OK-------|
+ | |
+ |<-------487---------|
+ |--------ACK-------->|
+ | |
+ | |
+
+ Client transitions:
+ INIT -(C1)-> CALLING -(C6a)-> TERMINATED
+
+ Server transitions:
+ INIT -(S1)-> RECEIVED -(S6a)-> TERMINATED
+
+ A B
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<----180 Ringing----|
+ | |
+ |------CANCEL------->|
+ |<------200 OK-------|
+ | |
+ |<-------487---------|
+ |--------ACK-------->|
+ | |
+ | |
+
+ Client transitions:
+ INIT -(C1)-> CALLING -(C2)-> PROCEEDING -(C6b)-> TERMINATED
+
+ Server transitions:
+ INIT -(S1)-> RECEIVED -(S2a)-> EARLY -(S6b)-> TERMINATED
+
+*/
+
+int cancel_when_calling(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_calling:
+ CANCEL(ep, call, nh,
+ /* sf.net bug #173323 */
+ SIPTAG_CALL_ID_STR("non-existing-call-id"),
+ TAG_END());
+ return 0;
+ case nua_callstate_terminated:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int bye_when_calling(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_calling:
+ BYE(ep, call, nh, TAG_END());
+ return 0;
+ case nua_callstate_terminated:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+
+int cancel_when_ringing(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_proceeding:
+ CANCEL(ep, call, nh, TAG_END());
+ return 0;
+ case nua_callstate_ready:
+ return 1;
+ case nua_callstate_terminated:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+
+int alert_call(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_received:
+ RESPOND(ep, call, nh, SIP_180_RINGING, TAG_END());
+ return 0;
+ case nua_callstate_terminated:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int accept_after_183(CONDITION_PARAMS);
+
+int test_call_cancel(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+ if (print_headings)
+ printf("TEST NUA-5.1.1: cancel call\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ TAG_END());
+
+ run_ab_until(ctx, -1, cancel_when_calling, -1, until_terminated);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state, nua_cancel()
+ CALLING -(C6a)-> TERMINATED: nua_r_invite(487), nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_cancel);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 487);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S6a)--> TERMINATED: nua_i_cancel, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_cancel);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-5.1.1: PASSED\n");
+
+ /* ------------------------------------------------------------------------ */
+
+ if (print_headings)
+ printf("TEST NUA-5.1.2: cancel call (with nua_bye())\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SIPTAG_SUBJECT_STR("TEST NUA-5.1.2"),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ TAG_END());
+
+ run_ab_until(ctx, -1, bye_when_calling, -1, until_terminated);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state, nua_cancel()
+ CALLING -(C6a)-> TERMINATED: nua_r_invite(487), nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_bye);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 487);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S6a)--> TERMINATED: nua_i_cancel, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_cancel);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-5.1.2: PASSED\n");
+
+ /* ----------------------------------------------------------------------- */
+
+ if (print_headings)
+ printf("TEST NUA-5.2.1: cancel call when ringing\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ /*SIPTAG_REJECT_CONTACT_STR("*;audio=FALSE"),*/
+ TAG_END());
+
+ run_ab_until(ctx, -1, cancel_when_ringing, -1, alert_call);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C2)-> PROCEEDING: nua_r_invite(180, nua_i_state, nua_cancel()
+ PROCEEDING -(C6b)-> TERMINATED: nua_r_invite(487), nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_cancel);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 487);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(180), nua_i_state
+ EARLY -(S6b)--> TERMINATED: nua_i_cancel, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_cancel);
+ TEST(e->data->e_status, 200);
+ /* Check for bug #1326727 */
+ TEST_1(e->data->e_msg);
+#if 0
+ TEST_1(sip_object(e->data->e_msg)->sip_reject_contact);
+ TEST_1(sip_object(e->data->e_msg)->sip_reject_contact->cp_params &&
+ sip_object(e->data->e_msg)->sip_reject_contact->cp_params[0]);
+ TEST_S(sip_object(e->data->e_msg)->sip_reject_contact->cp_params[0],
+ "audio=FALSE");
+#endif
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-5.2.1: PASSED\n");
+
+ /* ------------------------------------------------------------------------ */
+ if (print_headings)
+ printf("TEST NUA-5.2.2: CANCEL call when server waits for PRACK\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ NUTAG_APPL_METHOD("PRACK"),
+ /*SIPTAG_REJECT_CONTACT_STR("*;audio=FALSE"),*/
+ TAG_END());
+
+ run_ab_until(ctx, -1, cancel_when_ringing, -1, accept_after_183);
+
+ free_events_in_list(ctx, a->events);
+ free_events_in_list(ctx, b->events);
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-5.2.2: PASSED\n");
+
+
+ END();
+}
+
+int accept_after_183(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_received:
+ RESPOND(ep, call, nh, SIP_183_SESSION_PROGRESS, TAG_END());
+ RESPOND(ep, call, nh, SIP_200_OK, TAG_END());
+ return 0;
+ case nua_callstate_terminated:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+
+/* ======================================================================== */
+/* Destroy call handle */
+
+int destroy_when_calling(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_calling:
+ DESTROY(ep, call, nh);
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int destroy_when_completing(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_completing:
+ DESTROY(ep, call, nh);
+ return 1;
+ case nua_callstate_ready:
+ return 1;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int test_call_destroy_1(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+
+ if (print_headings)
+ printf("TEST NUA-5.3: destroy when calling\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ TAG_END());
+
+ run_ab_until(ctx, -1, destroy_when_calling, -1, until_terminated);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), ...
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S6a)--> TERMINATED: nua_i_cancel, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_cancel);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-5.3: PASSED\n");
+
+ END();
+}
+
+int accept_until_terminated(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_received:
+ RESPOND(ep, call, nh, SIP_180_RINGING, TAG_END());
+ return 0;
+ case nua_callstate_early:
+ RESPOND(ep, call, nh, SIP_200_OK,
+ TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
+ TAG_END());
+ return 0;
+ case nua_callstate_completed:
+ case nua_callstate_ready:
+ return 0;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int test_call_destroy_2(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+
+ if (print_headings)
+ printf("TEST NUA-5.4: destroy when completing\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ NUTAG_AUTOACK(0),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ TAG_END());
+
+ run_ab_until(ctx, -1, destroy_when_completing, -1, accept_until_terminated);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), ...
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completing); /* COMPLETING */
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+ EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-5.4: PASSED\n");
+
+ END();
+}
+
+int destroy_when_early(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_received:
+ RESPOND(ep, call, nh, SIP_180_RINGING, TAG_END());
+ return 0;
+ case nua_callstate_early:
+ if (call)
+ DESTROY(ep, call, nh), call->nh = NULL;
+ return 1;
+ case nua_callstate_completed:
+ case nua_callstate_ready:
+ case nua_callstate_terminated:
+ if (call)
+ DESTROY(ep, call, nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int test_call_destroy_3(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+
+ if (print_headings)
+ printf("TEST NUA-5.5: destroy when early\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ NUTAG_AUTOACK(0),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ TAG_END());
+
+ run_ab_until(ctx, -1, until_terminated, -1, destroy_when_early);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), ...
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 480);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+ EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state ... DESTROY
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-5.5: PASSED\n");
+
+ END();
+}
+
+int destroy_when_completed(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_received:
+ RESPOND(ep, call, nh, SIP_180_RINGING, TAG_END());
+ return 0;
+ case nua_callstate_early:
+ RESPOND(ep, call, nh, SIP_200_OK,
+ TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
+ TAG_END());
+ return 0;
+ case nua_callstate_completed:
+ case nua_callstate_ready:
+ case nua_callstate_terminated:
+ if (call)
+ DESTROY(ep, call, nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int test_call_destroy_4(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+
+ if (print_headings)
+ printf("TEST NUA-5.6: destroy when completed\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ NUTAG_AUTOACK(0),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ TAG_END());
+
+ run_ab_until(ctx, -1, until_terminated, -1, destroy_when_completed);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), ...
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); if (e->data->e_event == nua_r_invite) {
+ TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
+ TEST_1(e = e->next);
+ }
+ if (e->data->e_event == nua_r_invite) {
+ TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completing); /* COMPLETING */
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(e = e->next);
+ }
+ TEST_E(e->data->e_event, nua_i_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+ EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state ... DESTROY
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-5.6: PASSED\n");
+
+ END();
+}
+
+/* Destroy when one INVITE is queued. */
+int test_call_destroy_5(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+
+ if (print_headings)
+ printf("TEST NUA-5.7: destroy when re-INVITE is queued\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ NUTAG_AUTOACK(0),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ TAG_END());
+
+ INVITE(a, a_call, a_call->nh, TAG_END());
+
+ run_ab_until(ctx, -1, until_terminated, -1, destroy_when_completed);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), ...
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completing); /* COMPLETING */
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 481);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+ EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state ... DESTROY
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-5.7: PASSED\n");
+
+ END();
+}
+
+int test_call_destroy(struct context *ctx)
+{
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+ return
+ test_call_destroy_1(ctx) ||
+ test_call_destroy_2(ctx) ||
+ test_call_destroy_3(ctx) ||
+ test_call_destroy_4(ctx) ||
+ test_call_destroy_5(ctx);
+}
+
+/* ======================================================================== */
+
+/* Early BYE
+
+ A B
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<----180 Ringing----|
+ | |
+ |--------BYE-------->|
+ |<------200 OK-------|
+ | |
+ |<-------487---------|
+ |--------ACK-------->|
+ | |
+ | |
+
+ Client transitions:
+ INIT -(C1)-> CALLING -(C2)-> PROCEEDING -(8)-> TERMINATING -> TERMINATED
+
+ Server transitions:
+ INIT -(S1)-> RECEIVED -(S2a)-> EARLY -(S8)-> TERMINATED
+
+*/
+
+int bye_when_ringing(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_proceeding:
+ BYE(ep, call, nh, TAG_END());
+ return 0;
+ case nua_callstate_terminated:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int bye_when_completing(CONDITION_PARAMS);
+
+static int ack_sent = 0;
+
+size_t count_acks(void *arg, void *message, size_t len)
+{
+ (void)arg;
+
+ if (strncasecmp(message, "ACK sip:", 8) == 0)
+ ack_sent++;
+
+ return len;
+}
+
+int test_bye_before_200(struct context *ctx)
+{
+ BEGIN();
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+ if (print_headings)
+ printf("TEST NUA-6.1: BYE call when ringing\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ TAG_END());
+
+ run_ab_until(ctx, -1, bye_when_ringing, -1, alert_call);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C2)-> PROCEEDING: nua_r_invite(180, nua_i_state, nua_cancel()
+ PROCEEDING -(C6b)-> TERMINATED: nua_r_invite(487), nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
+ TEST_1(e = e->next);
+ if (e->data->e_event == nua_r_bye) {
+ /* We might receive this before or after response to INVITE */
+ /* If afterwards, it will come after nua_i_state and we just ignore it */
+ TEST_E(e->data->e_event, nua_r_bye); TEST(e->data->e_status, 200);
+ TEST_1(e->data->e_msg);
+ /* Forking has not been enabled, so this should be actually a CANCEL */
+ TEST(sip_object(e->data->e_msg)->sip_cseq->cs_method, sip_method_cancel);
+ TEST_1(e = e->next);
+ }
+ TEST_E(e->data->e_event, nua_r_invite); TEST(e->data->e_status, 487);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(180), nua_i_state
+ EARLY -(S6b)--> TERMINATED: nua_i_cancel, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ /* Forking has not been enabled, so this should be actually a CANCEL */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_cancel);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-6.1: PASSED\n");
+
+ END();
+}
+
+
+int bye_when_completing(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_completing:
+ ack_sent = 0;
+ BYE(ep, call, nh, TAG_END());
+ return 0;
+ case nua_callstate_terminated:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+
+int test_bye_before_ack(struct context *ctx)
+{
+ BEGIN();
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+ struct nat_filter *f = NULL;
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+/* Early BYE 2
+
+ A B
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<----180 Ringing----|
+ |<-------200---------|
+ | |
+ |--------BYE-------->|
+ |<------200 OK-------|
+ |--------ACK-------->|
+ | |
+ | |
+*/
+ if (print_headings)
+ printf("TEST NUA-6.2: BYE call when completing\n");
+
+ if (ctx->nat)
+ TEST_1(f = test_nat_add_filter(ctx->nat, count_acks, NULL, nat_outbound));
+ ack_sent = 0;
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ NUTAG_AUTOACK(0),
+ TAG_END());
+
+ run_ab_until(ctx, -1, bye_when_completing, -1, accept_until_terminated);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C2)-> PROCEEDING: nua_r_invite(180, nua_i_state, nua_cancel()
+ PROCEEDING -(C6b)-> TERMINATED: nua_r_invite(487), nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling);
+ TEST_1(is_offer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completing);
+ TEST_1(e = e->next);
+
+ TEST_E(e->data->e_event, nua_r_bye); TEST(e->data->e_status, 200);
+ TEST_1(e->data->e_msg);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+
+ if (ctx->nat) {
+ while (ack_sent == 0)
+ su_root_step(ctx->root, 100);
+ TEST_1(ack_sent > 0);
+ TEST_1(test_nat_remove_filter(ctx->nat, f) == 0);
+ }
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(180), nua_i_state
+ EARLY -(S6b)--> TERMINATED: nua_i_bye, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_bye);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-6.2: PASSED\n");
+
+ END();
+}
+
+int reject_reinvite_401(CONDITION_PARAMS);
+
+int test_bye_after_receiving_401(struct context *ctx)
+{
+ BEGIN();
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+/* BYE after receiving 401
+
+ A B
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<----180 Ringing----|
+ |<-------200---------|
+ |--------ACK-------->|
+ | |
+ |<------INVITE-------|
+ |--------401-------->|
+ |<--------ACK--------|
+ | |
+ |<--------BYE--------|
+ |------200 OK------->|
+ | |
+*/
+ if (print_headings)
+ printf("TEST NUA-6.3: BYE after receiving 401\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SIPTAG_SUBJECT_STR("NUA-6.3"),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ NUTAG_AUTOANSWER(0),
+ TAG_END());
+
+ run_ab_until(ctx, -1, until_ready, -1, accept_call);
+
+ free_events_in_list(ctx, a->events);
+
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ free_events_in_list(ctx, b->events);
+
+ /* re-INVITE A. */
+ INVITE(b, b_call, b_call->nh,
+ SIPTAG_SUBJECT_STR("NUA-6.3 re-INVITE"),
+ TAG_END());
+ run_ab_until(ctx, -1, reject_reinvite_401, -1, save_until_final_response);
+
+ TEST_1(nua_handle_has_active_call(a_call->nh));
+ TEST_1(nua_handle_has_active_call(b_call->nh));
+
+ free_events_in_list(ctx, a->events);
+ free_events_in_list(ctx, b->events);
+
+ BYE(b, b_call, b_call->nh, TAG_END());
+
+ run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-6.3: PASSED\n");
+
+ END();
+}
+
+int test_bye_after_sending_401(struct context *ctx)
+{
+ BEGIN();
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+/* BYE after sending 401
+
+ A B
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<----180 Ringing----|
+ |<-------200---------|
+ |--------ACK-------->|
+ | |
+ |<------INVITE-------|
+ |--------401-------->|
+ |<--------ACK--------|
+ | |
+ |--------BYE-------->|
+ |<------200 OK-------|
+ | |
+*/
+ if (print_headings)
+ printf("TEST NUA-6.4.1: BYE after sending 401\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SIPTAG_SUBJECT_STR("NUA-6.4.1"),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ NUTAG_AUTOANSWER(0),
+ TAG_END());
+
+ run_ab_until(ctx, -1, until_ready, -1, accept_call);
+
+ free_events_in_list(ctx, a->events);
+
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ free_events_in_list(ctx, b->events);
+
+ /* re-INVITE A. */
+ INVITE(b, b_call, b_call->nh,
+ SIPTAG_SUBJECT_STR("NUA-6.4.1 re-INVITE"),
+ TAG_END());
+ run_ab_until(ctx, -1, reject_reinvite_401, -1, save_until_final_response);
+
+ TEST_1(nua_handle_has_active_call(a_call->nh));
+ TEST_1(nua_handle_has_active_call(b_call->nh));
+
+ free_events_in_list(ctx, a->events);
+ free_events_in_list(ctx, b->events);
+
+ BYE(a, a_call, a_call->nh, TAG_END());
+
+ run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-6.4.1: PASSED\n");
+
+ END();
+}
+
+int test_bye_after_receiving_401_to_update(struct context *ctx)
+{
+ BEGIN();
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+/* BYE after receiving 401
+
+ A B
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<----180 Ringing----|
+ |<-------200---------|
+ |--------ACK-------->|
+ | |
+ |<------UPDATE-------|
+ |--------401-------->|
+ | |
+ |<--------BYE--------|
+ |------200 OK------->|
+ | |
+*/
+ if (print_headings)
+ printf("TEST NUA-6.4.2: BYE after receiving 401 to UPDATE\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SIPTAG_SUBJECT_STR("NUA-6.4.2"),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ NUTAG_AUTOANSWER(0),
+ NUTAG_APPL_METHOD("UPDATE"),
+ TAG_END());
+
+ run_ab_until(ctx, -1, until_ready, -1, accept_call);
+
+ free_events_in_list(ctx, a->events);
+
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ free_events_in_list(ctx, b->events);
+
+ /* UPDATE A. */
+ UPDATE(b, b_call, b_call->nh,
+ SIPTAG_SUBJECT_STR("NUA-6.4.2 UPDATE"),
+ TAG_END());
+ BYE(b, b_call, b_call->nh, TAG_END()); /* Queued until nua_authenticate */
+ run_ab_until(ctx, -1, reject_reinvite_401, -1, save_until_final_response);
+
+ TEST_1(nua_handle_has_active_call(a_call->nh));
+ TEST_1(nua_handle_has_active_call(b_call->nh));
+
+ free_events_in_list(ctx, a->events);
+ free_events_in_list(ctx, b->events);
+
+ AUTHENTICATE(b, b_call, b_call->nh, TAG_END());
+
+ run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-6.4.2: PASSED\n");
+
+ END();
+}
+
+int reject_reinvite_401(CONDITION_PARAMS)
+{
+ void *request = nua_current_request(nua);
+
+ save_event_in_list(ctx, event, ep, call);
+
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ if (status < 200 && (event == nua_i_invite || event == nua_i_update)) {
+ RESPOND(ep, call, nh, SIP_401_UNAUTHORIZED,
+ NUTAG_WITH(request),
+ SIPTAG_WWW_AUTHENTICATE_STR("Digest realm=\"test_nua\", "
+ "nonce=\"nsdhfuds\", algorithm=MD5, "
+ "qop=\"auth\""),
+ TAG_END());
+ return 0;
+ }
+
+ if (event == nua_i_state) switch (callstate(tags)) {
+ case nua_callstate_ready:
+ return 1;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+int test_bye_with_407(struct context *ctx)
+{
+ BEGIN();
+ struct endpoint *a = &ctx->a, *c = &ctx->c;
+ struct call *a_call = a->call, *c_call = c->call;
+ struct event *e;
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ c_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+ if (!ctx->proxy_tests)
+ return 0;
+
+/* BYE after receiving 401
+
+ A C
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<----180 Ringing----|
+ |<-------200---------|
+ |--------ACK-------->|
+ | |
+ | |<----BYE----|
+ | |-----407--->|
+ |<-------BYE---------|
+ |--------200-------->|
+ | |
+*/
+ if (print_headings)
+ printf("TEST NUA-6.4.5: BYE with 407\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(c->to), TAG_END()));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(c->contact->m_url)),
+ SIPTAG_SUBJECT_STR("NUA-6.4.2"),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ NUTAG_AUTOANSWER(0),
+ NUTAG_APPL_METHOD("UPDATE"),
+ TAG_END());
+
+ run_abc_until(ctx, -1, until_ready, -1, NULL, -1, accept_call);
+
+ free_events_in_list(ctx, a->events);
+
+ TEST_1(e = c->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ free_events_in_list(ctx, c->events);
+
+ BYE(c, c_call, c_call->nh,
+ TAG_END());
+ run_c_until(ctx, -1, save_until_final_response);
+
+ TEST_1(nua_handle_has_active_call(a_call->nh));
+ TEST_1(nua_handle_has_active_call(c_call->nh));
+
+ free_events_in_list(ctx, a->events);
+ free_events_in_list(ctx, c->events);
+
+ AUTHENTICATE(c, c_call, c_call->nh,
+ NUTAG_AUTH("Digest:\"test-proxy\":charlie:secret"), TAG_END());
+
+ run_abc_until(ctx, -1, until_terminated, -1, NULL, -1, until_terminated);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ free_events_in_list(ctx, c->events);
+ nua_handle_destroy(c_call->nh), c_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-6.4.5: PASSED\n");
+
+ END();
+}
+
+int test_bye_to_invalid_contact(struct context *ctx)
+{
+ BEGIN();
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+ sip_t *sip = NULL;
+
+ int seen_401;
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+/* Bad Contact URI
+
+ A B
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<----180 Ringing----|
+ |<-------200---------|
+ | |
+ |--------ACK-------->|
+ | |
+ |<-------BYE---------|
+ |--------400-------->|
+ | |
+ |--------BYE-------->|
+ |<------200 OK-------|
+ | |
+*/
+ if (print_headings)
+ printf("TEST NUA-6.4.3: Test dialog with bad Contact info\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ SIPTAG_CONTACT(NULL),
+ SIPTAG_HEADER_STR("Contact: <<sip:xyzzy at com.invalid>"),
+ TAG_END());
+
+ run_ab_until(ctx, -1, until_ready, -1, accept_call);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C2)-> PROCEEDING: nua_r_invite(180, nua_i_state, nua_cancel()
+ PROCEEDING -(C6b)-> TERMINATED: nua_r_invite(487), nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling);
+ TEST_1(is_offer_sent(e->data->e_tags));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready);
+ TEST_1(!e->next);
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(180), nua_i_state
+ EARLY -(S6b)--> TERMINATED: nua_i_cancel, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ free_events_in_list(ctx, b->events);
+
+ BYE(b, b_call, b_call->nh, TAG_END());
+
+ run_b_until(ctx, -1, until_terminated);
+
+ /* B transitions:
+ READY --(T2)--> TERMINATING: nua_bye()
+ TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_r_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+
+ TEST_1(!nua_handle_has_active_call(b_call->nh));
+ TEST_1(nua_handle_has_active_call(a_call->nh));
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-6.4.3: PASSED\n");
+
+ if (!ctx->p) {
+ free_events_in_list(ctx, b->events);
+ return 0;
+ }
+
+ if (print_headings)
+ printf("TEST NUA-6.4.4: Wait for re-REGISTER after connection has been closed\n");
+
+ if (!e->next || (!e->next->next || !e->next->data->e_status != 200))
+ /* B is supposed to re-register pretty soon, wait for re-registration */
+ run_b_until(ctx, -1, save_until_final_response);
+
+ seen_401 = 0;
+
+ for (e = e->next; e; e = e->next) {
+ TEST_E(e->data->e_event, nua_r_register);
+ TEST_1(sip = sip_object(e->data->e_msg));
+
+ if (e->data->e_status == 200) {
+ TEST(e->data->e_status, 200);
+ TEST_1(seen_401);
+ TEST_1(sip->sip_contact);
+ }
+ else if (sip->sip_status && sip->sip_status->st_status == 401) {
+ seen_401 = 1;
+ }
+
+ if (!e->next)
+ break;
+ }
+ TEST_1(e);
+ TEST_S(sip->sip_contact->m_expires, "3600");
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-6.4.4: PASSED\n");
+
+ END();
+}
+
+int test_early_bye(struct context *ctx)
+{
+ return
+ test_bye_with_407(ctx) ||
+ test_bye_before_200(ctx) ||
+ test_bye_before_ack(ctx) ||
+ test_bye_after_receiving_401(ctx) ||
+ test_bye_after_sending_401(ctx) ||
+ test_bye_after_receiving_401_to_update(ctx) ||
+ test_bye_to_invalid_contact(ctx) ||
+ 0;
+}
Added: freeswitch/trunk/libs/sofia-sip/tests/test_extension.c
==============================================================================
--- (empty file)
+++ freeswitch/trunk/libs/sofia-sip/tests/test_extension.c Wed May 14 15:10:54 2008
@@ -0,0 +1,180 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2005 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at 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 test_extension.c
+ * @brief NUA-12: Test extension methods.
+ *
+ * @author Pekka Pessi <Pekka.Pessi at nokia.com>
+ *
+ * @date Created: Mon Nov 13 15:37:05 EET 2006
+ */
+
+#include "config.h"
+
+#include "test_nua.h"
+
+#include <sofia-sip/su_tag_class.h>
+
+#if HAVE_FUNC
+#elif HAVE_FUNCTION
+#define __func__ __FUNCTION__
+#else
+#define __func__ "test_extension"
+#endif
+
+
+int respond_to_extension(CONDITION_PARAMS)
+{
+ msg_t *with = nua_current_request(nua);
+
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (event) {
+ case nua_i_method:
+ RESPOND(ep, call, nh, SIP_200_OK,
+ NUTAG_WITH(with),
+ SIPTAG_SUBJECT_STR("extended"),
+ TAG_END());
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int test_extension(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+ sip_t const *sip;
+
+
+/* Test for EXTENSION
+
+ A B
+ |------EXTENSION---->|
+ |<--------501--------| (method not recognized)
+ | |
+ |------EXTENSION---->|
+ |<-------200---------| (method allowed, responded)
+ | |
+*/
+
+ if (print_headings)
+ printf("TEST NUA-13.1: EXTENSION\n");
+
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ /* Test first without NUTAG_METHOD() */
+ METHOD(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ TAG_END());
+
+ run_ab_until(ctx, -1, save_until_final_response, -1, NULL);
+
+ /* Client events:
+ nua_method(), nua_r_method
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_method);
+ TEST(e->data->e_status, 900); /* Internal error */
+ TEST_1(!e->data->e_msg);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ METHOD(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ NUTAG_METHOD("EXTENSION"),
+ TAG_END());
+
+ run_ab_until(ctx, -1, save_until_final_response, -1, NULL);
+
+ /* Client events:
+ nua_method(), nua_r_method
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_method);
+ TEST(e->data->e_status, 501);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ nua_set_params(b->nua, NUTAG_ALLOW("EXTENSION"), TAG_END());
+
+ run_b_until(ctx, nua_r_set_params, until_final_response);
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ METHOD(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ NUTAG_METHOD("EXTENSION"),
+ TAG_END());
+
+ run_ab_until(ctx, -1, save_until_final_response, -1, respond_to_extension);
+
+ /* Client events:
+ nua_method(), nua_r_method
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_method);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(!e->next);
+
+ /*
+ Server events:
+ nua_i_method
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_method);
+ TEST(e->data->e_status, 100);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ nua_set_params(b->nua,
+ SIPTAG_ALLOW(b->allow),
+ NUTAG_APPL_METHOD(NULL),
+ NUTAG_APPL_METHOD(b->appl_method),
+ TAG_END());
+ run_b_until(ctx, nua_r_set_params, until_final_response);
+
+ if (print_headings)
+ printf("TEST NUA-13.1: PASSED\n");
+ END();
+}
Added: freeswitch/trunk/libs/sofia-sip/tests/test_init.c
==============================================================================
--- (empty file)
+++ freeswitch/trunk/libs/sofia-sip/tests/test_init.c Wed May 14 15:10:54 2008
@@ -0,0 +1,448 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2005 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at 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 test_init.c
+ * @brief Init nua test context
+ *
+ * @author Pekka Pessi <Pekka.Pessi at nokia.com>
+ * @author Martti Mela <Martti Mela at nokia.com>
+ *
+ * @date Created: Wed Aug 17 12:12:12 EEST 2005 ppessi
+ */
+
+#include "config.h"
+
+#include "test_nua.h"
+
+#include <sofia-sip/tport_tag.h>
+#include <sofia-sip/auth_module.h>
+
+#if HAVE_FUNC
+#elif HAVE_FUNCTION
+#define __func__ __FUNCTION__
+#else
+#define __func__ name
+#endif
+
+static char passwd_name[] = "tmp_sippasswd.XXXXXX";
+
+static void remove_tmp(void)
+{
+ if (passwd_name[0])
+ unlink(passwd_name);
+}
+
+static char const passwd[] =
+ "alice:secret:\n"
+ "bob:secret:\n"
+ "charlie:secret:\n";
+
+int test_nua_init(struct context *ctx,
+ int start_proxy,
+ url_t const *o_proxy,
+ int start_nat,
+ tag_type_t tag, tag_value_t value, ...)
+{
+ BEGIN();
+ struct event *e;
+ sip_contact_t const *m = NULL;
+ sip_from_t const *sipaddress = NULL;
+ sip_allow_t const *allow = NULL;
+ sip_supported_t const *supported = NULL;
+ char const *appl_method = NULL;
+ url_t const *p_uri, *a_uri, *b_uri; /* Proxy URI */
+ char const *initial_route = NULL; /* Initial route towards internal proxy */
+ char const *a_bind, *a_bind2;
+ url_t *e_proxy = NULL;
+ int err = -1;
+ url_t b_proxy[1];
+
+ a_bind = a_bind2 = "sip:0.0.0.0:*";
+
+#if SU_HAVE_OSX_CF_API
+ if (ctx->osx_runloop)
+ ctx->root = su_root_osx_runloop_create(NULL);
+ else
+#endif
+ ctx->root = su_root_create(NULL);
+ TEST_1(ctx->root);
+
+ /* Disable threading by command line switch -s */
+ su_root_threading(ctx->root, ctx->threading);
+
+ if (start_proxy && !o_proxy) {
+ int temp;
+
+ if (print_headings)
+ printf("TEST NUA-2.1.1: init proxy P\n");
+
+#ifndef _WIN32
+ temp = mkstemp(passwd_name);
+#else
+ temp = open(passwd_name, O_WRONLY|O_CREAT|O_TRUNC, 666);
+#endif
+ TEST_1(temp != -1);
+ atexit(remove_tmp); /* Make sure temp file is unlinked */
+
+ TEST_SIZE(write(temp, passwd, strlen(passwd)), strlen(passwd));
+
+ TEST_1(close(temp) == 0);
+
+ ctx->p = test_proxy_create(ctx->root,
+ TAG_IF(ctx->proxy_logging, TPTAG_LOG(1)),
+ TAG_END());
+
+ if (ctx->p) {
+ ctx->a.domain =
+ test_proxy_add_domain(ctx->p,
+ URL_STRING_MAKE("sip:example.com")->us_url,
+ AUTHTAG_METHOD("Digest"),
+ AUTHTAG_REALM("test-proxy"),
+ AUTHTAG_OPAQUE("kuik"),
+ AUTHTAG_DB(passwd_name),
+ AUTHTAG_QOP("auth-int"),
+ AUTHTAG_ALGORITHM("md5"),
+ AUTHTAG_NEXT_EXPIRES(60),
+ TAG_END());
+
+ ctx->b.domain =
+ test_proxy_add_domain(ctx->p,
+ URL_STRING_MAKE("sip:example.org")->us_url,
+ AUTHTAG_METHOD("Digest"),
+ AUTHTAG_REALM("test-proxy"),
+ AUTHTAG_OPAQUE("kuik"),
+ AUTHTAG_DB(passwd_name),
+ AUTHTAG_QOP("auth-int"),
+ AUTHTAG_ALGORITHM("md5"),
+ AUTHTAG_NEXT_EXPIRES(60),
+ TAG_END());
+
+ test_proxy_domain_set_outbound(ctx->b.domain, 1);
+
+ ctx->c.domain =
+ test_proxy_add_domain(ctx->p,
+ URL_STRING_MAKE("sip:example.net")->us_url,
+ AUTHTAG_METHOD("Digest"),
+ AUTHTAG_REALM("test-proxy"),
+ AUTHTAG_OPAQUE("kuik"),
+ AUTHTAG_DB(passwd_name),
+ AUTHTAG_QOP("auth-int"),
+ AUTHTAG_ALGORITHM("md5"),
+ AUTHTAG_NEXT_EXPIRES(60),
+ AUTHTAG_MAX_NCOUNT(1),
+ AUTHTAG_ALLOW("ACK, CANCEL"),
+ TAG_END());
+
+ test_proxy_domain_set_record_route(ctx->c.domain, 1);
+
+ ctx->proxy_tests = 1;
+ }
+
+
+ if (print_headings)
+ printf("TEST NUA-2.1.1: PASSED\n");
+ }
+
+ p_uri = a_uri = b_uri = test_proxy_uri(ctx->p);
+
+ if (o_proxy) {
+ TEST_1(e_proxy = url_hdup(ctx->home, (void *)o_proxy));
+ ctx->external_proxy = e_proxy;
+ }
+
+ if (start_nat && p_uri == NULL)
+ p_uri = e_proxy;
+
+ if (ctx->p)
+ initial_route = test_proxy_route_uri(ctx->p, &ctx->lr);
+
+ if (start_nat && p_uri != NULL) {
+ int family = 0;
+ su_sockaddr_t su[1];
+ socklen_t sulen = sizeof su;
+ char b[64];
+ size_t len;
+ ta_list ta;
+
+ if (print_headings)
+ printf("TEST NUA-2.1.2: creating test NAT\n");
+
+ /* Try to use different family than proxy. */
+ if (p_uri->url_host[0] == '[')
+ family = AF_INET;
+#if defined(SU_HAVE_IN6)
+ else
+ family = AF_INET6;
+#endif
+
+ ta_start(ta, tag, value);
+ ctx->nat = test_nat_create(ctx->root, family, ta_tags(ta));
+ ta_end(ta);
+
+ /*
+ * NAT thingy works so that we set the outgoing proxy URI to point
+ * towards its "private" address and give the real address of the proxy
+ * as its "public" address. If we use different IP families here, we may
+ * even manage to test real connectivity problems as proxy and endpoint
+ * can not talk to each other.
+ */
+
+ if (test_nat_private(ctx->nat, su, &sulen) < 0) {
+ printf("%s:%u: NUA-2.1.2: failed to get private NAT address\n",
+ __FILE__, __LINE__);
+ }
+
+#if defined(SU_HAVE_IN6)
+ else if (su->su_family == AF_INET6) {
+ a_uri = (void *)
+ su_sprintf(ctx->home, "sip:[%s]:%u",
+ inet_ntop(su->su_family, SU_ADDR(su), b, sizeof b),
+ ntohs(su->su_port));
+ a_bind = "sip:[::]:*";
+ }
+#endif
+ else if (su->su_family == AF_INET) {
+ a_uri = (void *)
+ su_sprintf(ctx->home, "sip:%s:%u",
+ inet_ntop(su->su_family, SU_ADDR(su), b, sizeof b),
+ ntohs(su->su_port));
+ }
+
+#if defined(SU_HAVE_IN6)
+ if (p_uri->url_host[0] == '[') {
+ su->su_len = sulen = (sizeof su->su_sin6), su->su_family = AF_INET6;
+ len = strcspn(p_uri->url_host + 1, "]"); assert(len < sizeof b);
+ memcpy(b, p_uri->url_host + 1, len); b[len] = '\0';
+ inet_pton(su->su_family, b, SU_ADDR(su));
+ }
+ else {
+ su->su_len = sulen = (sizeof su->su_sin), su->su_family = AF_INET;
+ inet_pton(su->su_family, p_uri->url_host, SU_ADDR(su));
+ }
+#else
+ su->su_len = sulen = (sizeof su->su_sin), su->su_family = AF_INET;
+ inet_pton(su->su_family, p_uri->url_host, SU_ADDR(su));
+#endif
+
+ su->su_port = htons(strtoul(url_port(p_uri), NULL, 10));
+
+ if (test_nat_public(ctx->nat, su, sulen) < 0) {
+ printf("%s:%u: NUA-2.1.2: failed to set public address\n",
+ __FILE__, __LINE__);
+ a_uri = NULL;
+ }
+
+ if (print_headings) {
+ if (ctx->nat && a_uri) {
+ printf("TEST NUA-2.1.2: PASSED\n");
+ } else {
+ printf("TEST NUA-2.1.2: FAILED\n");
+ }
+ }
+ }
+
+ if (print_headings)
+ printf("TEST NUA-2.2.1: init endpoint A\n");
+
+ if (a_uri == NULL)
+ a_uri = p_uri;
+
+ ctx->a.instance = nua_generate_instance_identifier(ctx->home);
+
+ ctx->a.nua = nua_create(ctx->root, a_callback, ctx,
+ NUTAG_PROXY(a_uri ? a_uri : e_proxy),
+ NUTAG_INITIAL_ROUTE_STR(initial_route),
+ SIPTAG_FROM_STR("sip:alice at example.com"),
+ NUTAG_URL(a_bind),
+ TAG_IF(a_bind != a_bind2, NUTAG_SIPS_URL(a_bind2)),
+ SOATAG_USER_SDP_STR("m=audio 5004 RTP/AVP 0 8"),
+ NTATAG_SIP_T1X64(2000),
+ NUTAG_INSTANCE(ctx->a.instance),
+ TAG_IF(ctx->a.logging, TPTAG_LOG(1)),
+ TAG_END());
+ TEST_1(ctx->a.nua);
+
+ nua_get_params(ctx->a.nua, TAG_ANY(), TAG_END());
+ run_a_until(ctx, nua_r_get_params, save_until_final_response);
+ TEST_1(e = ctx->a.specials->head);
+ err = tl_gets(e->data->e_tags,
+ NTATAG_CONTACT_REF(m),
+ SIPTAG_FROM_REF(sipaddress),
+ SIPTAG_ALLOW_REF(allow),
+ NUTAG_APPL_METHOD_REF(appl_method),
+ SIPTAG_SUPPORTED_REF(supported),
+ TAG_END());
+ TEST(err, 5);
+ TEST_1(m);
+ TEST_1(ctx->a.contact = sip_contact_dup(ctx->home, m));
+ TEST_1(ctx->a.to = sip_to_dup(ctx->home, sipaddress));
+ TEST_1(ctx->a.allow = sip_allow_dup(ctx->home, allow));
+ TEST_1(ctx->a.appl_method = su_strdup(ctx->home, appl_method));
+ TEST_1(ctx->a.supported = sip_supported_dup(ctx->home, supported));
+
+ free_events_in_list(ctx, ctx->a.specials);
+
+ if (print_headings)
+ printf("TEST NUA-2.2.1: PASSED\n");
+
+ if (print_headings)
+ printf("TEST NUA-2.2.2: init endpoint B\n");
+
+ ctx->b.instance = nua_generate_instance_identifier(ctx->home);
+
+ if (ctx->p) {
+ /* B uses TCP when talking with proxy */
+ *b_proxy = *b_uri;
+ b_uri = b_proxy;
+ b_proxy->url_params = "transport=tcp";
+ }
+
+ ctx->b.nua = nua_create(ctx->root, b_callback, ctx,
+ NUTAG_PROXY(b_uri ? b_uri : e_proxy),
+ SIPTAG_FROM_STR("sip:bob at example.org"),
+ NUTAG_URL("sip:0.0.0.0:*"),
+ SOATAG_USER_SDP_STR("m=audio 5006 RTP/AVP 8 0"),
+ NUTAG_INSTANCE(ctx->b.instance),
+ /* Quicker timeout */
+ NTATAG_SIP_T1X64(2000),
+ TPTAG_KEEPALIVE(100),
+ TAG_IF(ctx->b.logging, TPTAG_LOG(1)),
+ TAG_END());
+ TEST_1(ctx->b.nua);
+
+ nua_get_params(ctx->b.nua, TAG_ANY(), TAG_END());
+ run_b_until(ctx, nua_r_get_params, save_until_final_response);
+ TEST_1(e = ctx->b.specials->head);
+ err = tl_gets(e->data->e_tags,
+ NTATAG_CONTACT_REF(m),
+ SIPTAG_FROM_REF(sipaddress),
+ SIPTAG_ALLOW_REF(allow),
+ NUTAG_APPL_METHOD_REF(appl_method),
+ SIPTAG_SUPPORTED_REF(supported),
+ TAG_END());
+ TEST(err, 5); TEST_1(m);
+
+ TEST_1(ctx->b.contact = sip_contact_dup(ctx->home, m));
+ TEST_1(ctx->b.to = sip_to_dup(ctx->home, sipaddress));
+ TEST_1(ctx->b.allow = sip_allow_dup(ctx->home, allow));
+ TEST_1(ctx->b.appl_method = su_strdup(ctx->home, appl_method));
+ TEST_1(ctx->b.supported = sip_supported_dup(ctx->home, supported));
+
+ free_events_in_list(ctx, ctx->b.specials);
+
+ if (print_headings)
+ printf("TEST NUA-2.2.2: PASSED\n");
+
+ if (print_headings)
+ printf("TEST NUA-2.2.3: init endpoint C\n");
+
+ /* ctx->c.instance = nua_generate_instance_identifier(ctx->home); */
+
+ ctx->c.to = sip_from_make(ctx->home, "Charlie <sip:charlie at example.net>");
+
+ ctx->c.nua = nua_create(ctx->root, c_callback, ctx,
+ NUTAG_PROXY(p_uri ? p_uri : e_proxy),
+ NUTAG_URL("sip:0.0.0.0:*"),
+ SOATAG_USER_SDP_STR("m=audio 5400 RTP/AVP 8 0"),
+ NUTAG_INSTANCE(ctx->c.instance),
+ TAG_IF(ctx->c.logging, TPTAG_LOG(1)),
+ TAG_END());
+ TEST_1(ctx->c.nua);
+
+ nua_get_params(ctx->c.nua, TAG_ANY(), TAG_END());
+ run_c_until(ctx, nua_r_get_params, save_until_final_response);
+ TEST_1(e = ctx->c.specials->head);
+ err = tl_gets(e->data->e_tags,
+ NTATAG_CONTACT_REF(m),
+ SIPTAG_ALLOW_REF(allow),
+ NUTAG_APPL_METHOD_REF(appl_method),
+ SIPTAG_SUPPORTED_REF(supported),
+ TAG_END());
+
+ TEST(err, 4); TEST_1(m);
+ TEST_1(ctx->c.contact = sip_contact_dup(ctx->home, m));
+ TEST_1(ctx->c.allow = sip_allow_dup(ctx->home, allow));
+ TEST_1(ctx->c.appl_method = su_strdup(ctx->home, appl_method));
+ TEST_1(ctx->c.supported = sip_supported_dup(ctx->home, supported));
+ free_events_in_list(ctx, ctx->c.specials);
+
+ if (print_headings)
+ printf("TEST NUA-2.2.3: PASSED\n");
+
+ END();
+}
+
+
+/* ====================================================================== */
+
+int test_deinit(struct context *ctx)
+{
+ BEGIN();
+
+ struct call *call;
+
+ if (!ctx->threading)
+ su_root_step(ctx->root, 100);
+
+ if (ctx->a.nua) {
+ for (call = ctx->a.call; call; call = call->next)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+
+ nua_shutdown(ctx->a.nua);
+ run_a_until(ctx, nua_r_shutdown, until_final_response);
+ free_events_in_list(ctx, ctx->a.events);
+ free_events_in_list(ctx, ctx->a.specials);
+ nua_destroy(ctx->a.nua), ctx->a.nua = NULL;
+ }
+
+ if (ctx->b.nua) {
+ for (call = ctx->b.call; call; call = call->next)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+
+ nua_shutdown(ctx->b.nua);
+ run_b_until(ctx, nua_r_shutdown, until_final_response);
+ free_events_in_list(ctx, ctx->b.events);
+ free_events_in_list(ctx, ctx->b.specials);
+ nua_destroy(ctx->b.nua), ctx->b.nua = NULL;
+ }
+
+ if (ctx->c.nua) {
+ for (call = ctx->c.call; call; call = call->next)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+
+ nua_shutdown(ctx->c.nua);
+ run_c_until(ctx, nua_r_shutdown, until_final_response);
+ free_events_in_list(ctx, ctx->c.events);
+ free_events_in_list(ctx, ctx->c.specials);
+ nua_destroy(ctx->c.nua), ctx->c.nua = NULL;
+ }
+
+ test_proxy_destroy(ctx->p), ctx->p = NULL;
+
+ test_nat_destroy(ctx->nat), ctx->nat = NULL;
+
+ su_root_destroy(ctx->root), ctx->root = NULL;
+
+ END();
+}
Added: freeswitch/trunk/libs/sofia-sip/tests/test_nat.c
==============================================================================
--- (empty file)
+++ freeswitch/trunk/libs/sofia-sip/tests/test_nat.c Wed May 14 15:10:54 2008
@@ -0,0 +1,999 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2005 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at 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 test_nat.c
+ * @brief Simulated NAT for testing
+ *
+ * NAT thing works so that we set the outgoing proxy URI to point
+ * towards its "private" address and give the real address of the proxy
+ * as its "public" address. If we use different IP families here, we may
+ * even manage to test real connectivity problems as proxy and endpoint
+ * can not talk to each other.
+ *
+ * @author Pekka Pessi <Pekka.Pessi at nokia.com>
+ *
+ * @date Created: Wed Mar 8 19:54:28 EET 2006
+ */
+
+#include "config.h"
+
+struct nat;
+struct binding;
+
+#define SU_ROOT_MAGIC_T struct nat
+#define SU_WAKEUP_ARG_T struct binding
+
+#include <sofia-sip/su.h>
+#include <sofia-sip/su_errno.h>
+#include <sofia-sip/su_wait.h>
+#include <sofia-sip/su_tagarg.h>
+#include <sofia-sip/su_localinfo.h>
+#include <sofia-sip/su_log.h>
+
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#define LIST_PROTOS(STORAGE, PREFIX, T) \
+STORAGE void PREFIX ##_insert(T **list, T *node), \
+ PREFIX ##_remove(T *node)
+
+#define LIST_BODIES(STORAGE, PREFIX, T, NEXT, PREV) \
+STORAGE void PREFIX ##_insert(T **list, T *node) \
+{ \
+ if ((node->NEXT = *list)) { \
+ node->PREV = node->NEXT->PREV; \
+ node->NEXT->PREV = &node->NEXT; \
+ } \
+ else \
+ node->PREV = list; \
+ *list = node; \
+} \
+STORAGE void PREFIX ##_remove(T *node) \
+{ \
+ if (node->PREV) \
+ if ((*node->PREV = node->NEXT)) \
+ node->NEXT->PREV = node->PREV; \
+ node->PREV = NULL; \
+} \
+extern int LIST_DUMMY_VARIABLE
+
+#include "test_nat.h"
+
+struct nat {
+ su_home_t home[1];
+ su_root_t *parent;
+ su_clone_r clone;
+ tagi_t *tags;
+
+ su_root_t *root;
+
+ struct binding *bindings;
+
+ struct nat_filter *in_filters, *out_filters;
+
+ /* True if we act in symmetric way */
+ int symmetric;
+ /* True if we do logging */
+ int logging;
+
+ /* Everything sent to in_address will be forwarded to out_address */
+ su_sockaddr_t in_address[1], out_address[1];
+ socklen_t in_addrlen, out_addrlen;
+
+ int family; /* Preferred private family */
+
+ /* ...but source address will be "fake" */
+ su_localinfo_t *localinfo, *private, *fake;
+
+ su_socket_t udp_socket, tcp_socket;
+ int udp_register, tcp_register;
+
+ char buffer[65536];
+};
+
+LIST_PROTOS(static, nat_binding, struct binding);
+
+struct binding
+{
+ struct binding *next, **prev;
+ struct nat *nat; /* backpointer */
+ int socktype, protocol;
+ su_socket_t in_socket, out_socket;
+ int in_register, out_register;
+ int in_closed, out_closed;
+ char in_name[64], out_name[64];
+};
+
+static struct binding *nat_binding_new(struct nat *nat,
+ char const *protoname,
+ int socktype, int protocol,
+ int connected,
+ su_socket_t in_socket,
+ su_sockaddr_t *from,
+ socklen_t fromlen);
+static void nat_binding_destroy(struct binding *);
+
+static int binding_init(struct binding *b,
+ char const *protoname,
+ int connected,
+ su_localinfo_t *li,
+ su_sockaddr_t *from,
+ socklen_t fromlen);
+
+static void flush_bindings(struct nat *nat);
+static int invalidate_bindings(void *nat);
+
+static int new_udp(struct nat *, su_wait_t *wait, struct binding *dummy);
+static int udp_in_to_out(struct nat *, su_wait_t *wait, struct binding *);
+static int udp_out_to_in(struct nat *, su_wait_t *wait, struct binding *);
+
+static int new_tcp(struct nat *, su_wait_t *wait, struct binding *dummy);
+static int tcp_in_to_out(struct nat *, su_wait_t *wait, struct binding *);
+static int tcp_out_to_in(struct nat *, su_wait_t *wait, struct binding *);
+
+static int invalidate_binding(struct binding *b);
+
+LIST_PROTOS(static, nat_filter, struct nat_filter);
+
+struct nat_filter
+{
+ struct nat_filter *next, **prev;
+ size_t (*condition)(void *arg, void *message, size_t len);
+ void *arg;
+};
+
+/* nat entry point */
+static int
+test_nat_init(su_root_t *root, struct nat *nat)
+{
+ su_localinfo_t *li, hints[1] = {{ 0 }};
+ int error;
+ unsigned port = 0, port0 = 0;
+ su_sockaddr_t su[1];
+ socklen_t sulen;
+ su_wait_t wait[1];
+
+ nat->root = root;
+ nat->udp_socket = INVALID_SOCKET, nat->tcp_socket = INVALID_SOCKET;
+
+ tl_gets(nat->tags,
+ TESTNATTAG_SYMMETRIC_REF(nat->symmetric),
+ TESTNATTAG_LOGGING_REF(nat->logging),
+ TAG_END());
+
+ hints->li_scope = LI_SCOPE_HOST | LI_SCOPE_SITE | LI_SCOPE_GLOBAL;
+
+ error = su_getlocalinfo(hints, &nat->localinfo);
+ if (error) {
+ fprintf(stderr, "test_nat: su_getlocalinfo: %s\n", su_gli_strerror(error));
+ return -1;
+ }
+
+ /* We must have two different IP addresses. */
+ if (!nat->localinfo || !nat->localinfo->li_next) {
+ fprintf(stderr, "test_nat: only one IP address available\n");
+ return -1;
+ }
+
+ for (li = nat->localinfo; li; li = li->li_next) {
+ if (nat->family == 0 || nat->family == li->li_family)
+ break;
+ }
+ if (li == NULL)
+ li = nat->localinfo;
+
+ memcpy(su, li->li_addr, sulen = li->li_addrlen);
+ memset(SU_ADDR(su), 0, SU_ADDRLEN(su));
+
+ nat->private = li;
+
+ /* Bind TCP and UDP to same port */
+ for (;;) {
+ nat->udp_socket = su_socket(li->li_family, SOCK_DGRAM, IPPROTO_UDP);
+ if (nat->udp_socket == INVALID_SOCKET)
+ return -1;
+
+ if (bind(nat->udp_socket, (void *)su, sulen) < 0) {
+ if (port0 == 0) {
+ su_perror("nat: bind(udp_socket)");
+ return -1;
+ }
+
+ fprintf(stderr, "test_nat: port %u: %s\n",
+ port, su_strerror(su_errno()));
+ su_close(nat->udp_socket);
+
+ nat->udp_socket = INVALID_SOCKET;
+
+ if (++port > 65535)
+ port = 1024;
+ if (port == port0) {
+ fprintf(stderr, "test_nat: could not find free port pairt\n");
+ return -1;
+ }
+
+ continue;
+ }
+
+ if (getsockname(nat->udp_socket, (void *)su, &sulen) < 0) {
+ su_perror("nat: getsockname(udp_socket)");
+ return -1;
+ }
+
+ if (port0 == 0) {
+ port = port0 = ntohs(su->su_port);
+ if (port0 == 0) {
+ fprintf(stderr, "test_nat: bind did not return port\n");
+ return -1;
+ }
+ }
+
+ nat->tcp_socket = su_socket(li->li_family, SOCK_STREAM, IPPROTO_TCP);
+ if (nat->tcp_socket == INVALID_SOCKET)
+ return -1;
+
+ if (bind(nat->tcp_socket, (void *)su, sulen) < 0) {
+ su_close(nat->tcp_socket);
+ nat->tcp_socket = INVALID_SOCKET;
+
+ fprintf(stderr, "test_nat: port %u: %s\n",
+ port, su_strerror(su_errno()));
+
+ if (++port > 65535)
+ port = 1024;
+ if (port == port0) {
+ fprintf(stderr, "test_nat: could not find free port pair\n");
+ return -1;
+ }
+
+ continue;
+ }
+
+ break;
+ }
+
+ memcpy(nat->in_address, li->li_addr, nat->in_addrlen = li->li_addrlen);
+ nat->in_address->su_port = su->su_port;
+
+ if (su_setreuseaddr(nat->udp_socket, 1) < 0) {
+ su_perror("nat: su_setreuseaddr(udp_socket)");
+ return -1;
+ }
+
+ if (listen(nat->tcp_socket, 5) < 0) {
+ su_perror("nat: listen(tcp_socket)");
+ return -1;
+ }
+
+ if (su_wait_create(wait, nat->udp_socket, SU_WAIT_IN) < 0) {
+ su_perror("nat: su_wait_create");
+ return -1;
+ }
+
+ nat->udp_register = su_root_register(root, wait, new_udp, NULL, 0);
+ if (nat->udp_register < 0) {
+ su_perror("nat: su_root_register");
+ return -1;
+ }
+
+ if (su_wait_create(wait, nat->tcp_socket, SU_WAIT_IN) < 0) {
+ su_perror("nat: su_wait_create");
+ return -1;
+ }
+
+ nat->tcp_register = su_root_register(root, wait, new_tcp, NULL, 0);
+ if (nat->tcp_register < 0) {
+ su_perror("nat: su_root_register");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+test_nat_deinit(su_root_t *root, struct nat *nat)
+{
+ flush_bindings(nat);
+
+ if (nat->tcp_register)
+ su_root_deregister(root, nat->tcp_register);
+ if (nat->udp_register)
+ su_root_deregister(root, nat->udp_register);
+
+ if (nat->udp_socket != INVALID_SOCKET)
+ su_close(nat->udp_socket);
+ if (nat->tcp_socket != INVALID_SOCKET)
+ su_close(nat->tcp_socket);
+
+ su_freelocalinfo(nat->localinfo);
+
+ free(nat->tags);
+}
+
+struct nat *test_nat_create(su_root_t *root,
+ int family,
+ tag_type_t tag, tag_value_t value, ...)
+{
+ struct nat *nat = su_home_new(sizeof *nat);
+
+ if (nat) {
+ ta_list ta;
+
+ nat->parent = root;
+ nat->family = family;
+
+ ta_start(ta, tag, value);
+ nat->tags = tl_llist(ta_tags(ta));
+ ta_end(ta);
+
+ if (su_clone_start(root,
+ nat->clone,
+ nat,
+ test_nat_init,
+ test_nat_deinit) == -1)
+ su_home_unref(nat->home), nat = NULL;
+ }
+
+ return nat;
+}
+
+void test_nat_destroy(struct nat *nat)
+{
+ if (nat) {
+ su_clone_wait(nat->parent, nat->clone);
+ su_home_unref(nat->home);
+ }
+}
+
+/** Get "private" address. */
+int test_nat_private(struct nat *nat, void *address, socklen_t *return_addrlen)
+{
+ if (nat == NULL || address == NULL || return_addrlen == NULL)
+ return su_seterrno(EFAULT);
+
+ if (*return_addrlen < nat->in_addrlen)
+ return su_seterrno(EINVAL);
+
+ memcpy(address, nat->in_address, *return_addrlen = nat->in_addrlen);
+
+ return 0;
+}
+
+/** Set "public" address. */
+int test_nat_public(struct nat *nat, void const *address, int addrlen)
+{
+ su_sockaddr_t const *su = address;
+ su_localinfo_t *li;
+
+ if (nat == NULL)
+ return su_seterrno(EFAULT);
+
+ if (address == NULL) {
+ nat->fake = NULL;
+ return 0;
+ }
+
+ if ((size_t)addrlen > sizeof nat->out_address)
+ return su_seterrno(EINVAL);
+
+ for (li = nat->localinfo; li; li = li->li_next) {
+ if (li != nat->private &&
+ li->li_scope == LI_SCOPE_HOST &&
+ li->li_family == su->su_family)
+ break;
+ }
+
+ if (li == NULL)
+ for (li = nat->localinfo; li; li = li->li_next) {
+ if (li != nat->private && li->li_family == su->su_family)
+ break;
+ }
+
+ if (li == NULL)
+ return su_seterrno(EADDRNOTAVAIL);
+
+ su_clone_pause(nat->clone);
+ memcpy(nat->out_address, address, nat->out_addrlen = addrlen);
+ nat->fake = li;
+ su_clone_resume(nat->clone);
+
+ return 0;
+}
+
+int test_nat_flush(struct nat *nat)
+{
+ if (nat == NULL)
+ return su_seterrno(EFAULT);
+
+ return su_task_execute(su_clone_task(nat->clone),
+ invalidate_bindings, nat, NULL);
+}
+
+/* ====================================================================== */
+
+struct binding *nat_binding_new(struct nat *nat,
+ char const *protoname,
+ int socktype,
+ int protocol,
+ int connected,
+ su_socket_t in_socket,
+ su_sockaddr_t *from,
+ socklen_t fromlen)
+{
+ struct binding *b;
+
+ if (nat->fake == NULL) { /* Xyzzy */
+ fprintf(stderr, "test_nat: fake address missing\n");
+ su_close(in_socket);
+ return NULL;
+ }
+
+ b = su_zalloc(nat->home, sizeof *b);
+ if (b == NULL) {
+ su_perror("nat_binding_new: su_zalloc");
+ su_close(in_socket);
+ return 0;
+ }
+
+ b->nat = nat;
+ b->socktype = socktype;
+ b->protocol = protocol;
+ b->in_socket = in_socket, b->out_socket = INVALID_SOCKET;
+ b->in_register = -1, b->out_register = -1;
+
+ if (binding_init(b, protoname, connected, nat->fake, from, fromlen) < 0)
+ nat_binding_destroy(b), b = NULL;
+
+ return b;
+}
+
+static int binding_init(struct binding *b,
+ char const *protoname,
+ int connected,
+ su_localinfo_t *li,
+ su_sockaddr_t *from,
+ socklen_t fromlen)
+{
+ struct nat *nat = b->nat;
+ su_socket_t out_socket;
+ su_sockaddr_t addr[1];
+ socklen_t addrlen = (sizeof addr);
+ char ipname[64];
+ su_wait_t wait[1];
+ su_wakeup_f in_to_out, out_to_in;
+
+ if (b->socktype == SOCK_STREAM)
+ in_to_out = tcp_in_to_out, out_to_in = tcp_out_to_in;
+ else
+ in_to_out = udp_in_to_out, out_to_in = udp_out_to_in;
+
+ if (b->in_socket == INVALID_SOCKET) {
+ int in_socket;
+
+ in_socket = su_socket(from->su_family, b->socktype, b->protocol);
+ if (in_socket == INVALID_SOCKET) {
+ su_perror("nat_binding_new: socket");
+ return -1;
+ }
+ b->in_socket = in_socket;
+ if (su_setreuseaddr(in_socket, 1) < 0) {
+ su_perror("nat_binding_new: su_setreuseaddr(in_socket)");
+ return -1;
+ }
+ if (bind(in_socket, (void *)nat->in_address, nat->in_addrlen) < 0) {
+ su_perror("nat_binding_new: bind(in_socket)");
+ return -1;
+ }
+ if (connect(in_socket, (void *)from, fromlen) < 0) {
+ su_perror("nat_binding_new: connect(in_socket)");
+ return -1;
+ }
+ }
+
+ out_socket = su_socket(li->li_family, b->socktype, b->protocol);
+ if (out_socket == INVALID_SOCKET) {
+ su_perror("nat_binding_new: socket");
+ return -1;
+ }
+ b->out_socket = out_socket;
+
+ if (bind(out_socket, (void *)li->li_addr, li->li_addrlen) < 0) {
+ su_perror("nat_binding_new: bind(to)");
+ return -1;
+ }
+
+ if (connected)
+ if (connect(out_socket, (void *)nat->out_address, nat->out_addrlen) < 0) {
+ su_perror("nat_binding_new: connect(to)");
+ return -1;
+ }
+
+ getpeername(b->in_socket, (void *)addr, &addrlen);
+ inet_ntop(addr->su_family, SU_ADDR(addr), ipname, sizeof ipname);
+ snprintf(b->in_name, sizeof b->in_name,
+ addr->su_family == AF_INET6 ? "[%s]:%u" : "%s:%u",
+ ipname, ntohs(addr->su_port));
+
+ getsockname(out_socket, (void *)addr, &addrlen);
+ inet_ntop(addr->su_family, SU_ADDR(addr), ipname, sizeof ipname);
+ snprintf(b->out_name, sizeof b->out_name,
+ addr->su_family == AF_INET6 ? "[%s]:%u" : "%s:%u",
+ ipname, ntohs(addr->su_port));
+
+ if (su_wait_create(wait, b->in_socket, SU_WAIT_IN) < 0) {
+ su_perror("nat_binding_new: su_wait_create");
+ return -1;
+ }
+ b->in_register = su_root_register(nat->root, wait, in_to_out, b, 0);
+ if (b->in_register < 0) {
+ su_perror("nat_binding_new: su_root_register");
+ su_wait_destroy(wait);
+ return -1;
+ }
+
+ if (su_wait_create(wait, out_socket, SU_WAIT_IN) < 0) {
+ su_perror("nat_binding_new: su_wait_create");
+ return -1;
+ }
+ b->out_register = su_root_register(nat->root, wait, out_to_in, b, 0);
+ if (b->out_register < 0) {
+ su_perror("nat_binding_new: su_root_register");
+ su_wait_destroy(wait);
+ return -1;
+ }
+
+ nat_binding_insert(&nat->bindings, b);
+
+ if (nat->logging)
+ printf("nat: new %s binding %s <=> %s\n",
+ protoname, b->in_name, b->out_name);
+
+ return 0;
+}
+
+static void nat_binding_destroy(struct binding *b)
+{
+ nat_binding_remove(b);
+ if (b->in_register != -1)
+ su_root_deregister(b->nat->root, b->in_register);
+ if (b->out_register != -1)
+ su_root_deregister(b->nat->root, b->out_register);
+ su_close(b->in_socket), su_close(b->out_socket);
+}
+
+static void flush_bindings(struct nat *nat)
+{
+ struct binding *b;
+
+ for (b = nat->bindings; b; b = b->next) {
+ if (b->in_register)
+ su_root_deregister(nat->root, b->in_register);
+ su_close(b->in_socket);
+ if (b->out_register)
+ su_root_deregister(nat->root, b->out_register);
+ su_close(b->out_socket);
+ }
+}
+
+static int invalidate_bindings(void *arg)
+{
+ struct nat *nat = arg;
+ struct binding *b;
+
+ for (b = nat->bindings; b; b = b->next) {
+ invalidate_binding(b);
+ }
+ return 0;
+}
+
+#if 0
+static struct binding *nat_binding_find(struct nat *nat,
+ su_sockaddr_t *from,
+ int fromlen)
+{
+ char name[64], ipname[64];
+ size_t namelen;
+ struct binding *b;
+
+ inet_ntop(from->su_family, SU_ADDR(from), ipname, sizeof ipname);
+ snprintf(name, sizeof name,
+ from->su_family == AF_INET6 ? "[%s]:%u" : "%s:%u",
+ ipname, ntohs(from->su_port));
+ namelen = strlen(name) + 1;
+
+ for (b = nat->bindings; b; b = b->next) {
+ if (memcmp(name, b->in_name, namelen) == 0)
+ return b;
+ }
+
+ if (b == NULL)
+ b = nat_binding_new(nat, "UDP", SOCK_DGRAM, IPPROTO_UDP, nat->symmetric,
+ INVALID_SOCKET, from, fromlen);
+
+ return b;
+}
+#endif
+
+/* ====================================================================== */
+
+LIST_BODIES(static, nat_binding, struct binding, next, prev);
+
+/* ====================================================================== */
+
+static int new_udp(struct nat *nat, su_wait_t *wait, struct binding *dummy)
+{
+ int events;
+ su_sockaddr_t from[1];
+ socklen_t fromlen = (sizeof from);
+ struct binding *b;
+ ssize_t n, m;
+
+ events = su_wait_events(wait, nat->udp_socket);
+
+ n = su_recvfrom(nat->udp_socket, nat->buffer, sizeof nat->buffer, 0,
+ from, &fromlen);
+ if (n < 0) {
+ su_perror("new_udp: recvfrom");
+ return 0;
+ }
+
+ b = nat_binding_new(nat, "UDP", SOCK_DGRAM, IPPROTO_UDP, nat->symmetric,
+ INVALID_SOCKET, from, fromlen);
+ if (b == NULL)
+ return 0;
+
+ if (nat->symmetric)
+ m = su_send(b->out_socket, nat->buffer, n, 0);
+ else
+ m = su_sendto(b->out_socket, nat->buffer, n, 0,
+ nat->out_address, nat->out_addrlen);
+
+ if (nat->logging)
+ printf("nat: udp out %d/%d %s => %s\n",
+ (int)m, (int)n, b->in_name, b->out_name);
+
+ return 0;
+}
+
+static int udp_in_to_out(struct nat *nat, su_wait_t *wait, struct binding *b)
+{
+ int events;
+ ssize_t n, m;
+ size_t len, filtered;
+ struct nat_filter *f;
+
+ events = su_wait_events(wait, b->in_socket);
+
+ n = su_recv(b->in_socket, nat->buffer, sizeof nat->buffer, 0);
+ if (n == -1) {
+ su_perror("udp_in_to_out: recv");
+ return 0;
+ }
+
+ len = (size_t)n;
+
+ for (f = nat->out_filters; f; f = f->next) {
+ filtered = f->condition(f->arg, nat->buffer, len);
+ if (filtered != len) {
+ if (nat->logging)
+ printf("nat: udp filtered "MOD_ZU" from %s => "MOD_ZU" to %s\n",
+ len, b->in_name, filtered, b->out_name);
+ if (filtered == 0)
+ return 0;
+ len = filtered;
+ }
+ }
+
+ if (nat->symmetric)
+ m = su_send(b->out_socket, nat->buffer, len, 0);
+ else
+ m = su_sendto(b->out_socket, nat->buffer, len, 0,
+ nat->out_address, nat->out_addrlen);
+
+ if (nat->logging)
+ printf("nat: udp out %d/%d %s => %s\n",
+ (int)m, (int)n, b->in_name, b->out_name);
+
+ return 0;
+}
+
+static int udp_out_to_in(struct nat *nat, su_wait_t *wait, struct binding *b)
+{
+ int events;
+ ssize_t n, m;
+ size_t len, filtered;
+ struct nat_filter *f;
+
+ events = su_wait_events(wait, b->out_socket);
+
+ n = su_recv(b->out_socket, nat->buffer, sizeof nat->buffer, 0);
+ if (n < 0) {
+ su_perror("udp_out_to_out: recv");
+ return 0;
+ }
+
+ len = (size_t)n;
+
+ for (f = nat->in_filters; f; f = f->next) {
+ filtered = f->condition(f->arg, nat->buffer, len);
+ if (filtered != len) {
+ if (nat->logging)
+ printf("nat: udp filtered "MOD_ZU" from %s => "MOD_ZU" to %s\n",
+ len, b->out_name, filtered, b->in_name);
+ if (filtered == 0)
+ return 0;
+ len = filtered;
+ }
+ }
+
+ m = su_send(b->in_socket, nat->buffer, n, 0);
+
+ if (nat->logging)
+ printf("nat: udp in %d/%d %s => %s\n",
+ (int)m, (int)n, b->out_name, b->in_name);
+
+ return 0;
+}
+
+/* ====================================================================== */
+
+static int new_tcp(struct nat *nat, su_wait_t *wait, struct binding *dummy)
+{
+ int events;
+ su_socket_t in_socket;
+ su_sockaddr_t from[1];
+ socklen_t fromlen = (sizeof from);
+ struct binding *b;
+
+ events = su_wait_events(wait, nat->tcp_socket);
+
+ in_socket = accept(nat->tcp_socket, (void *)from, &fromlen);
+ if (in_socket == INVALID_SOCKET) {
+ su_perror("new_tcp: accept");
+ return 0;
+ }
+
+ b = nat_binding_new(nat, "TCP", SOCK_STREAM, IPPROTO_TCP, 1,
+ in_socket, from, fromlen);
+ if (b == NULL)
+ return 0;
+
+ return 0;
+}
+
+static int tcp_in_to_out(struct nat *nat, su_wait_t *wait, struct binding *b)
+{
+ int events;
+ ssize_t n, m, o;
+
+ events = su_wait_events(wait, b->in_socket);
+
+ n = su_recv(b->in_socket, nat->buffer, sizeof nat->buffer, 0);
+ if (n < 0) {
+ su_perror("tcp_in_to_out: recv");
+ return 0;
+ }
+
+ if (n == 0) {
+ if (nat->logging)
+ printf("nat: tcp out FIN %s => %s\n", b->in_name, b->out_name);
+ shutdown(b->out_socket, 1);
+ su_root_eventmask(nat->root, b->in_register, b->in_socket, 0);
+ b->in_closed = 1;
+ if (b->out_closed && b->in_closed)
+ nat_binding_destroy(b);
+ return 0;
+ }
+
+ for (m = 0; m < n; m += o) {
+ o = su_send(b->out_socket, nat->buffer + m, n - m, 0);
+ if (o < 0) {
+ su_perror("tcp_in_to_out: send");
+ break;
+ }
+ }
+
+ if (nat->logging)
+ printf("nat: tcp out %d/%d %s => %s\n",
+ (int)m, (int)n, b->in_name, b->out_name);
+
+ return 0;
+}
+
+static int tcp_out_to_in(struct nat *nat, su_wait_t *wait, struct binding *b)
+{
+ int events;
+ ssize_t n, m, o;
+
+ events = su_wait_events(wait, b->out_socket);
+
+ n = su_recv(b->out_socket, nat->buffer, sizeof nat->buffer, 0);
+ if (n < 0) {
+ su_perror("tcp_out_to_in: recv");
+ return 0;
+ }
+
+ if (n == 0) {
+ if (nat->logging)
+ printf("nat: tcp out FIN %s => %s\n", b->out_name, b->in_name);
+ shutdown(b->in_socket, 1);
+ su_root_eventmask(nat->root, b->in_register, b->out_socket, 0);
+ b->out_closed = 1;
+ if (b->out_closed && b->in_closed)
+ nat_binding_destroy(b);
+ return 0;
+ }
+
+ for (m = 0; m < n; m += o) {
+ o = su_send(b->in_socket, nat->buffer + m, n - m, 0);
+ if (o < 0) {
+ if (su_errno() != EPIPE)
+ su_perror("tcp_in_to_out: send");
+ break;
+ }
+ }
+
+ if (nat->logging)
+ printf("nat: tcp in %d/%d %s => %s\n",
+ (int)m, (int)n, b->out_name, b->in_name);
+
+ return 0;
+}
+
+static int invalidate_binding(struct binding *b)
+{
+ struct nat *nat = b->nat;
+ su_sockaddr_t addr[1];
+ socklen_t addrlen = (sizeof addr);
+ su_socket_t out;
+ int out_register;
+ su_wait_t wout[1];
+ char name[64];
+
+ out = su_socket(nat->fake->li_family, b->socktype, 0);
+ if (out == INVALID_SOCKET) {
+ su_perror("new_udp: socket");
+ return -1;
+ }
+ if (bind(out, (void *)nat->fake->li_addr, nat->fake->li_addrlen) < 0) {
+ su_perror("new_udp: bind(to)");
+ su_close(out);
+ return -1;
+ }
+
+ if (nat->symmetric)
+ if (connect(out, (void *)nat->out_address, nat->out_addrlen) < 0) {
+ su_perror("new_udp: connect(to)");
+ su_close(out);
+ return -1;
+ }
+
+ if (su_wait_create(wout, out, SU_WAIT_IN) < 0) {
+ su_perror("new_udp: su_wait_create");
+ su_close(out);
+ return -1;
+ }
+
+ if (b->socktype == SOCK_DGRAM)
+ out_register = su_root_register(nat->root, wout, udp_out_to_in, b, 0);
+ else
+ out_register = su_root_register(nat->root, wout, tcp_out_to_in, b, 0);
+
+ if (out_register < 0) {
+ su_perror("new_udp: su_root_register");
+ su_wait_destroy(wout);
+ su_close(out);
+ return -1;
+ }
+
+ su_root_deregister(nat->root, b->out_register);
+ su_close(b->out_socket);
+
+ b->out_socket = out;
+ b->out_register = out_register;
+
+ getsockname(out, (void *)addr, &addrlen);
+ inet_ntop(addr->su_family, SU_ADDR(addr), name, sizeof name);
+ snprintf(b->out_name, sizeof b->out_name,
+ addr->su_family == AF_INET6 ? "[%s]:%u" : "%s:%u",
+ name, ntohs(addr->su_port));
+
+ if (nat->logging)
+ printf("nat: flushed binding %s <=> %s\n", b->in_name, b->out_name);
+
+ return 0;
+}
+
+LIST_BODIES(static, nat_filter, struct nat_filter, next, prev);
+
+struct args {
+ struct nat *nat;
+ struct nat_filter *f;
+ int outbound;
+};
+
+int execute_nat_filter_insert(void *_args)
+{
+ struct args *a = (struct args *)_args;
+ if (a->outbound)
+ nat_filter_insert(&a->nat->out_filters, a->f);
+ else
+ nat_filter_insert(&a->nat->in_filters, a->f);
+ return 0;
+}
+
+int execute_nat_filter_remove(void *_args)
+{
+ struct args *a = (struct args *)_args;
+ nat_filter_remove(a->f);
+ return 0;
+}
+
+struct nat_filter *test_nat_add_filter(struct nat *nat,
+ size_t (*condition)(void *arg,
+ void *message,
+ size_t len),
+ void *arg,
+ int outbound)
+{
+ struct args a[1];
+
+ if (nat == NULL)
+ return su_seterrno(EFAULT), NULL;
+
+ a->nat = nat;
+ a->f = su_zalloc(nat->home, sizeof *a->f);
+ a->outbound = outbound;
+
+ if (a->f) {
+ a->f->condition = condition;
+ a->f->arg = arg;
+ if (su_task_execute(su_clone_task(nat->clone),
+ execute_nat_filter_insert, a, NULL) < 0)
+ su_free(nat->home, a->f), a->f = NULL;
+ }
+
+ return a->f;
+}
+
+
+int test_nat_remove_filter(struct nat *nat,
+ struct nat_filter *filter)
+{
+ struct args a[1];
+
+ if (nat == NULL)
+ return su_seterrno(EFAULT);
+
+ a->nat = nat;
+ a->f = filter;
+
+ if (su_task_execute(su_clone_task(nat->clone),
+ execute_nat_filter_remove, a, NULL) < 0)
+ return -1;
+
+ su_free(nat->home, filter);
+ return 0;
+}
Added: freeswitch/trunk/libs/sofia-sip/tests/test_nat.h
==============================================================================
--- (empty file)
+++ freeswitch/trunk/libs/sofia-sip/tests/test_nat.h Wed May 14 15:10:54 2008
@@ -0,0 +1,74 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2005 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at 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
+ *
+ */
+
+#ifndef TEST_NAT_H
+#define TEST_NAT_H
+
+#include <sofia-sip/su_wait.h>
+#include <sofia-sip/nta.h>
+
+SOFIA_BEGIN_DECLS
+
+struct nat;
+struct nat_filter;
+
+struct nat *test_nat_create(su_root_t *, int family,
+ tag_type_t, tag_value_t, ...);
+
+void test_nat_destroy(struct nat *);
+
+int test_nat_private(struct nat *nat, void *address, socklen_t *return_addrlen);
+int test_nat_public(struct nat *nat, void const *address, int addrlen);
+
+int test_nat_flush(struct nat *nat);
+
+struct nat_filter *test_nat_add_filter(struct nat *nat,
+ size_t (*condition)(void *arg,
+ void *message,
+ size_t len),
+ void *arg,
+ int outbound);
+
+enum { nat_inbound, nat_outbound };
+
+int test_nat_remove_filter(struct nat *nat,
+ struct nat_filter *filter);
+
+/* Tags */
+
+/** If true, act as symmetric nat. */
+#define TESTNATTAG_SYMMETRIC(x) testnattag_symmetric, tag_bool_v((x))
+#define TESTNATTAG_SYMMETRIC_REF(x) testnattag_symmetric_ref, tag_bool_vr(&(x))
+extern tag_typedef_t testnattag_symmetric;
+extern tag_typedef_t testnattag_symmetric_ref;
+
+/** If true, print information about connections. */
+#define TESTNATTAG_LOGGING(x) testnattag_logging, tag_bool_v((x))
+#define TESTNATTAG_LOGGING_REF(x) testnattag_logging_ref, tag_bool_vr(&(x))
+extern tag_typedef_t testnattag_logging;
+extern tag_typedef_t testnattag_logging_ref;
+
+SOFIA_END_DECLS
+
+#endif
Added: freeswitch/trunk/libs/sofia-sip/tests/test_nat_tags.c
==============================================================================
--- (empty file)
+++ freeswitch/trunk/libs/sofia-sip/tests/test_nat_tags.c Wed May 14 15:10:54 2008
@@ -0,0 +1,40 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2005 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at 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 test_nat_tags.c
+ * @brief Tags for simulated NAT
+ *
+ * @author Pekka Pessi <Pekka.Pessi at nokia.com>
+ *
+ * @date Created: Wed Mar 8 19:54:28 EET 2006
+ */
+
+#include "config.h"
+
+#include "test_nat.h"
+
+tag_typedef_t testnattag_symmetric = BOOLTAG_TYPEDEF(symmetric);
+tag_typedef_t testnattag_symmetric_ref = REFTAG_TYPEDEF(testnattag_symmetric);
+tag_typedef_t testnattag_logging = BOOLTAG_TYPEDEF(symmetric);
+tag_typedef_t testnattag_logging_ref = REFTAG_TYPEDEF(testnattag_logging);
Added: freeswitch/trunk/libs/sofia-sip/tests/test_nua.c
==============================================================================
--- (empty file)
+++ freeswitch/trunk/libs/sofia-sip/tests/test_nua.c Wed May 14 15:10:54 2008
@@ -0,0 +1,397 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2005 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at 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 test_nua.c
+ * @brief High-level tester for Sofia SIP User Agent Engine
+ *
+ * @author Pekka Pessi <Pekka.Pessi at nokia.com>
+ * @author Martti Mela <Martti Mela at nokia.com>
+ *
+ * @date Created: Wed Aug 17 12:12:12 EEST 2005 ppessi
+ */
+
+#include "config.h"
+
+#include "test_nua.h"
+
+#if HAVE_ALARM
+#include <signal.h>
+#endif
+
+#if defined(_WIN32)
+#include <fcntl.h>
+#endif
+
+SOFIAPUBVAR su_log_t nua_log[];
+SOFIAPUBVAR su_log_t soa_log[];
+SOFIAPUBVAR su_log_t nea_log[];
+SOFIAPUBVAR su_log_t nta_log[];
+SOFIAPUBVAR su_log_t tport_log[];
+SOFIAPUBVAR su_log_t su_log_default[];
+
+char const name[] = "test_nua";
+int print_headings = 1;
+int tstflags = 0;
+
+#if HAVE_FUNC
+#elif HAVE_FUNCTION
+#define __func__ __FUNCTION__
+#else
+#define __func__ name
+#endif
+
+#if HAVE_ALARM
+static RETSIGTYPE sig_alarm(int s)
+{
+ fprintf(stderr, "%s: FAIL! test timeout!\n", name);
+ if (tstflags & tst_abort)
+ abort();
+ exit(1);
+}
+#endif
+
+static char const options_usage[] =
+ " -v | --verbose be verbose\n"
+ " -q | --quiet be quiet\n"
+ " -a | --abort abort on error\n"
+ " -s use only single thread\n"
+ " -l level set logging level (0 by default)\n"
+ " -e | --events print nua events\n"
+ " -A print nua events for A\n"
+ " -B print nua events for B\n"
+ " -C print nua events for C\n"
+ " --log=a log messages for A\n"
+ " --log=b log messages for B\n"
+ " --log=c log messages for C\n"
+ " --log=proxy log messages for proxy\n"
+ " --attach print pid, wait for a debugger to be attached\n"
+ " --no-proxy do not use internal proxy\n"
+ " --no-nat do not use internal \"nat\"\n"
+ " --symmetric run internal \"nat\" in symmetric mode\n"
+ " -N print events from internal \"nat\"\n"
+ " --loop loop main tests for ever\n"
+ " --no-alarm don't ask for guard ALARM\n"
+ " -p uri specify uri of outbound proxy (implies --no-proxy)\n"
+ " --proxy-tests run tests involving proxy, too\n"
+#if SU_HAVE_OSX_CF_API /* If compiled with CoreFoundation events */
+ " --osx-runloop use OSX CoreFoundation runloop instead of poll() loop\n"
+#endif
+ " -k do not exit after first error\n"
+ ;
+
+void usage(int exitcode)
+{
+ fprintf(stderr, "usage: %s OPTIONS\n where OPTIONS are\n%s",
+ name, options_usage);
+ exit(exitcode);
+}
+
+int main(int argc, char *argv[])
+{
+ int retval = 0;
+ int i, o_quiet = 0, o_attach = 0, o_alarm = 1, o_loop = 0;
+ int o_events_init = 0, o_events_a = 0, o_events_b = 0, o_events_c = 0;
+ int o_iproxy = 1, o_inat = 1;
+ int o_inat_symmetric = 0, o_inat_logging = 0, o_expensive = 0;
+ url_t const *o_proxy = NULL;
+ int level = 0;
+
+ struct context ctx[1] = {{{ SU_HOME_INIT(ctx) }}};
+
+#if HAVE_OPEN_C
+ dup2(1, 2);
+#endif
+
+ if (getenv("EXPENSIVE_CHECKS"))
+ o_expensive = 1;
+
+ ctx->threading = 1;
+ ctx->quit_on_single_failure = 1;
+
+ endpoint_init(ctx, &ctx->a, 'a');
+ endpoint_init(ctx, &ctx->b, 'b');
+ endpoint_init(ctx, &ctx->c, 'c');
+
+ for (i = 1; argv[i]; i++) {
+ if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0)
+ tstflags |= tst_verbatim;
+ else if (strcmp(argv[i], "-a") == 0 || strcmp(argv[i], "--abort") == 0)
+ tstflags |= tst_abort;
+ else if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--quiet") == 0)
+ tstflags &= ~tst_verbatim, o_quiet = 1;
+ else if (strcmp(argv[i], "-k") == 0)
+ ctx->quit_on_single_failure = 0;
+ else if (strncmp(argv[i], "-l", 2) == 0) {
+ char *rest = NULL;
+
+ if (argv[i][2])
+ level = strtol(argv[i] + 2, &rest, 10);
+ else if (argv[i + 1])
+ level = strtol(argv[i + 1], &rest, 10), i++;
+ else
+ level = 3, rest = "";
+
+ if (rest == NULL || *rest)
+ usage(1);
+
+ su_log_set_level(nua_log, level);
+ su_log_soft_set_level(soa_log, level);
+ su_log_soft_set_level(nea_log, level);
+ su_log_soft_set_level(nta_log, level);
+ su_log_soft_set_level(tport_log, level);
+ }
+ else if (strcmp(argv[i], "-e") == 0 || strcmp(argv[i], "--events") == 0) {
+ o_events_init = o_events_a = o_events_b = o_events_c = 1;
+ }
+ else if (strcmp(argv[i], "-I") == 0) {
+ o_events_init = 1;
+ }
+ else if (strcmp(argv[i], "-A") == 0) {
+ o_events_a = 1;
+ }
+ else if (strcmp(argv[i], "-B") == 0) {
+ o_events_b = 1;
+ }
+ else if (strcmp(argv[i], "-C") == 0) {
+ o_events_c = 1;
+ }
+ else if (strcmp(argv[i], "-s") == 0) {
+ ctx->threading = 0;
+ }
+ else if (strcmp(argv[i], "--attach") == 0) {
+ o_attach = 1;
+ }
+ else if (strncmp(argv[i], "-p", 2) == 0) {
+ if (argv[i][2])
+ o_proxy = URL_STRING_MAKE(argv[i] + 2)->us_url;
+ else if (!argv[++i] || argv[i][0] == '-')
+ usage(1);
+ else
+ o_proxy = URL_STRING_MAKE(argv[i])->us_url;
+ }
+ else if (strcmp(argv[i], "--proxy-tests") == 0) {
+ ctx->proxy_tests = 1;
+ }
+ else if (strcmp(argv[i], "--no-proxy") == 0) {
+ o_iproxy = 0;
+ }
+ else if (strcmp(argv[i], "--no-nat") == 0) {
+ o_inat = 0;
+ }
+ else if (strcmp(argv[i], "--nat") == 0) {
+ o_inat = 1;
+ }
+ else if (strcmp(argv[i], "--symmetric") == 0) {
+ o_inat_symmetric = 1;
+ }
+ else if (strcmp(argv[i], "-N") == 0) {
+ o_inat_logging = 1;
+ }
+ else if (strcmp(argv[i], "--expensive") == 0) {
+ o_expensive = 1;
+ }
+ else if (strcmp(argv[i], "--no-alarm") == 0) {
+ o_alarm = 0;
+ }
+ else if (strcmp(argv[i], "--loop") == 0) {
+ o_alarm = 0, o_loop = 1;
+ }
+ else if (strcmp(argv[i], "--print-tags") == 0) {
+ ctx->print_tags = 1;
+ }
+ else if (strcmp(argv[i], "--tags=a") == 0) {
+ ctx->a.print_tags = 1;
+ }
+ else if (strcmp(argv[i], "--tags=b") == 0) {
+ ctx->b.print_tags = 1;
+ }
+ else if (strcmp(argv[i], "--tags=c") == 0) {
+ ctx->c.print_tags = 1;
+ }
+ else if (strcmp(argv[i], "--log=a") == 0) {
+ ctx->a.logging = 1;
+ }
+ else if (strcmp(argv[i], "--log=b") == 0) {
+ ctx->b.logging = 1;
+ }
+ else if (strcmp(argv[i], "--log=c") == 0) {
+ ctx->c.logging = 1;
+ }
+ else if (strcmp(argv[i], "--log=proxy") == 0) {
+ ctx->proxy_logging = 1;
+ }
+#if SU_HAVE_OSX_CF_API /* If compiled with CoreFoundation events */
+ else if (strcmp(argv[i], "--osx-runloop") == 0) {
+ ctx->osx_runloop = 1;
+ }
+#endif
+ else if (strcmp(argv[i], "-") == 0) {
+ i++; break;
+ }
+ else if (argv[i][0] != '-') {
+ break;
+ }
+ else {
+ fprintf(stderr, "test_nua: unknown argument \"%s\"\n\n", argv[i]);
+ usage(1);
+ }
+ }
+
+ if (o_attach) {
+ char line[10], *l;
+ printf("%s: pid %lu\n", name, (unsigned long)getpid());
+ printf("<Press RETURN to continue>\n");
+ l = fgets(line, sizeof line, stdin);
+ }
+#if HAVE_ALARM
+ else if (o_alarm) {
+ signal(SIGALRM, sig_alarm);
+ if (o_expensive) {
+ printf("%s: extending timeout to %u because expensive tests\n",
+ name, 240);
+ alarm(240);
+ }
+ else {
+ alarm(120);
+ }
+ }
+#endif
+
+#if HAVE_OPEN_C
+ tstflags |= tst_verbatim;
+ level = 9;
+ o_inat = 1; /* No NATs */
+ ctx->threading = 1;
+ ctx->quit_on_single_failure = 1;
+ su_log_soft_set_level(nua_log, level);
+ su_log_soft_set_level(soa_log, level);
+ su_log_soft_set_level(su_log_default, level);
+ su_log_soft_set_level(nea_log, level);
+ su_log_soft_set_level(nta_log, level);
+ su_log_soft_set_level(tport_log, level);
+ setenv("SU_DEBUG", "9", 1);
+ setenv("NUA_DEBUG", "9", 1);
+ setenv("NTA_DEBUG", "9", 1);
+ setenv("TPORT_DEBUG", "9", 1);
+ o_events_a = o_events_b = 1;
+#endif
+
+ su_init();
+
+ if (!(TSTFLAGS & tst_verbatim)) {
+ if (level == 0 && !o_quiet)
+ level = 1;
+ su_log_soft_set_level(nua_log, level);
+ su_log_soft_set_level(soa_log, level);
+ su_log_soft_set_level(su_log_default, level);
+ su_log_soft_set_level(nea_log, level);
+ su_log_soft_set_level(nta_log, level);
+ su_log_soft_set_level(tport_log, level);
+ }
+
+ if (!o_quiet || (TSTFLAGS & tst_verbatim)
+ || o_events_a || o_events_b || o_events_c)
+ print_headings = 1;
+
+#if !HAVE_OPEN_C
+#define SINGLE_FAILURE_CHECK() \
+ do { fflush(stdout); \
+ if (retval && ctx->quit_on_single_failure) { \
+ su_deinit(); return retval; } \
+ } while(0)
+#else
+#define SINGLE_FAILURE_CHECK() \
+ do { fflush(stdout); \
+ if (retval && ctx->quit_on_single_failure) { \
+ su_deinit(); sleep(10); return retval; } \
+ } while(0)
+#endif
+
+ ctx->a.printer = o_events_init ? print_event : NULL;
+
+ retval |= test_nua_api_errors(ctx); SINGLE_FAILURE_CHECK();
+
+ retval |= test_tag_filter(); SINGLE_FAILURE_CHECK();
+
+ retval |= test_nua_params(ctx); SINGLE_FAILURE_CHECK();
+
+ retval |= test_nua_destroy(ctx); SINGLE_FAILURE_CHECK();
+
+ retval |= test_stack_errors(ctx); SINGLE_FAILURE_CHECK();
+
+ retval |= test_nua_init(ctx, o_iproxy, o_proxy, o_inat,
+ TESTNATTAG_SYMMETRIC(o_inat_symmetric),
+ TESTNATTAG_LOGGING(o_inat_logging),
+ TAG_END());
+
+ ctx->expensive = o_expensive;
+
+ if (retval == 0) {
+ ctx->a.printer = o_events_a ? print_event : NULL;
+ if (o_events_b)
+ ctx->b.printer = print_event;
+ if (o_events_c)
+ ctx->c.printer = print_event;
+
+ retval |= test_register(ctx);
+
+ if (retval == 0)
+ retval |= test_connectivity(ctx);
+
+ if (retval == 0 && o_inat)
+ retval |= test_nat_timeout(ctx);
+
+ while (retval == 0) {
+ retval |= test_basic_call(ctx); SINGLE_FAILURE_CHECK();
+ retval |= test_rejects(ctx); SINGLE_FAILURE_CHECK();
+ retval |= test_call_cancel(ctx); SINGLE_FAILURE_CHECK();
+ retval |= test_call_destroy(ctx); SINGLE_FAILURE_CHECK();
+ retval |= test_early_bye(ctx); SINGLE_FAILURE_CHECK();
+ retval |= test_offer_answer(ctx); SINGLE_FAILURE_CHECK();
+ retval |= test_reinvites(ctx); SINGLE_FAILURE_CHECK();
+ retval |= test_session_timer(ctx); SINGLE_FAILURE_CHECK();
+ retval |= test_refer(ctx); SINGLE_FAILURE_CHECK();
+ retval |= test_100rel(ctx); SINGLE_FAILURE_CHECK();
+ retval |= test_simple(ctx); SINGLE_FAILURE_CHECK();
+ retval |= test_events(ctx); SINGLE_FAILURE_CHECK();
+ retval |= test_extension(ctx); SINGLE_FAILURE_CHECK();
+ if (!o_loop)
+ break;
+ }
+
+ if (ctx->proxy_tests && (retval == 0 || !ctx->p))
+ retval |= test_unregister(ctx); SINGLE_FAILURE_CHECK();
+ }
+ retval |= test_deinit(ctx);
+
+ su_home_deinit(ctx->home);
+
+ su_deinit();
+
+#if HAVE_OPEN_C
+ sleep(7);
+#endif
+
+ return retval;
+}
Added: freeswitch/trunk/libs/sofia-sip/tests/test_nua.h
==============================================================================
--- (empty file)
+++ freeswitch/trunk/libs/sofia-sip/tests/test_nua.h Wed May 14 15:10:54 2008
@@ -0,0 +1,364 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2005 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at 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
+ *
+ */
+
+/**@@internal
+ * @file test_nua.h
+ * @brief High-level tester framework for Sofia SIP User Agent Engine
+ *
+ * @author Pekka Pessi <Pekka.Pessi at nokia.com>
+ * @author Martti Mela <Martti Mela at nokia.com>
+ *
+ * @date Created: Wed Aug 17 12:12:12 EEST 2005 ppessi
+ */
+
+#ifndef TEST_NUA_H
+#define TEST_NUA_H
+
+struct context;
+#define NUA_MAGIC_T struct context
+
+struct call;
+#define NUA_HMAGIC_T struct call
+
+#include "sofia-sip/nua.h"
+#include "sofia-sip/sip_status.h"
+
+#include <sofia-sip/sdp.h>
+#include <sofia-sip/sip_header.h>
+
+#include <sofia-sip/su_log.h>
+#include <sofia-sip/su_tagarg.h>
+#include <sofia-sip/su_tag_io.h>
+#include <sofia-sip/nua_tag.h>
+
+#if __APPLE_CC__
+#include <sofia-sip/su_osx_runloop.h>
+#endif
+
+#include "test_proxy.h"
+#include "test_nat.h"
+#include <sofia-sip/auth_module.h>
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <assert.h>
+#include <unistd.h>
+
+extern char const name[];
+
+extern int print_headings;
+extern int tstflags;
+#define TSTFLAGS tstflags
+
+#include <sofia-sip/tstdef.h>
+
+#define TEST_E(a, b) TEST_S(nua_event_name(a), nua_event_name(b))
+
+#define NONE ((void*)-1)
+
+struct endpoint;
+
+typedef
+int condition_function(nua_event_t event,
+ int status, char const *phrase,
+ nua_t *nua, struct context *ctx,
+ struct endpoint *ep,
+ nua_handle_t *nh, struct call *call,
+ sip_t const *sip,
+ tagi_t tags[]);
+
+typedef
+void printer_function(nua_event_t event,
+ char const *operation,
+ int status, char const *phrase,
+ nua_t *nua, struct context *ctx,
+ struct endpoint *ep,
+ nua_handle_t *nh, struct call *call,
+ sip_t const *sip,
+ tagi_t tags[]);
+
+struct proxy_transaction;
+struct registration_entry;
+
+enum { event_is_extra, event_is_normal, event_is_special };
+
+struct eventlist
+{
+ nua_event_t kind;
+ struct event *head, **tail;
+};
+
+struct event
+{
+ struct event *next, **prev;
+ struct call *call;
+ nua_saved_event_t saved_event[1];
+ nua_event_data_t const *data;
+};
+
+
+struct context
+{
+ su_home_t home[1];
+ su_root_t *root;
+
+ int threading, proxy_tests, expensive, quit_on_single_failure, osx_runloop;
+ int print_tags;
+
+ url_t *external_proxy;
+
+ int proxy_logging;
+
+ struct endpoint {
+ char name[4];
+ struct context *ctx; /* Backpointer */
+
+ int logging;
+ int print_tags;
+
+ int running;
+
+ struct domain *domain;
+ condition_function *next_condition;
+ nua_event_t next_event, last_event;
+ nua_t *nua;
+ sip_contact_t *contact;
+ sip_from_t *to;
+
+ sip_allow_t *allow;
+ char const *appl_method;
+ sip_supported_t *supported;
+
+ printer_function *printer;
+
+ char const *instance;
+
+ /* Per-call stuff */
+ struct call {
+ struct call *next;
+ nua_handle_t *nh;
+ char const *sdp;
+ struct eventlist *events;
+ } call[1], reg[1];
+
+ int (*is_special)(nua_event_t e);
+
+ /* Normal events are saved here */
+ struct eventlist events[1];
+ /* Special events are saved here */
+ struct eventlist specials[1];
+
+ /* State flags for complex scenarios */
+ struct {
+ unsigned n;
+ unsigned bit0:1, bit1:1, bit2:1, bit3:1;
+ unsigned bit4:1, bit5:1, bit6:1, bit7:1;
+ unsigned :0;
+ } flags;
+ /* Accross-run state information */
+ struct {
+ unsigned n;
+ } state;
+ } a, b, c;
+
+ struct proxy *p;
+ sip_route_t const *lr;
+ struct nat *nat;
+};
+
+#define RETURN_ON_SINGLE_FAILURE(retval) \
+ do { \
+ fflush(stdout); \
+ if (retval && ctx->quit_on_single_failure) { return retval; } \
+ } while(0)
+
+
+int save_event_in_list(struct context *,
+ nua_event_t nevent,
+ struct endpoint *,
+ struct call *);
+void free_events_in_list(struct context *,
+ struct eventlist *);
+void free_event_in_list(struct context *ctx,
+ struct eventlist *list,
+ struct event *e);
+
+struct event *event_by_type(struct event *e, nua_event_t);
+size_t count_events(struct event const *e);
+
+#define CONDITION_PARAMS \
+ nua_event_t event, \
+ int status, char const *phrase, \
+ nua_t *nua, struct context *ctx, \
+ struct endpoint *ep, \
+ nua_handle_t *nh, struct call *call, \
+ sip_t const *sip, \
+ tagi_t tags[]
+
+int save_events(CONDITION_PARAMS);
+int until_final_response(CONDITION_PARAMS);
+int save_until_final_response(CONDITION_PARAMS);
+int save_until_received(CONDITION_PARAMS);
+int save_until_special(CONDITION_PARAMS);
+
+int until_terminated(CONDITION_PARAMS);
+int until_ready(CONDITION_PARAMS);
+int accept_call(CONDITION_PARAMS);
+int cancel_when_ringing(CONDITION_PARAMS);
+
+int accept_notify(CONDITION_PARAMS);
+
+void a_callback(nua_event_t event,
+ int status, char const *phrase,
+ nua_t *nua, struct context *ctx,
+ nua_handle_t *nh, struct call *call,
+ sip_t const *sip,
+ tagi_t tags[]);
+void b_callback(nua_event_t event,
+ int status, char const *phrase,
+ nua_t *nua, struct context *ctx,
+ nua_handle_t *nh, struct call *call,
+ sip_t const *sip,
+ tagi_t tags[]);
+void c_callback(nua_event_t event,
+ int status, char const *phrase,
+ nua_t *nua, struct context *ctx,
+ nua_handle_t *nh, struct call *call,
+ sip_t const *sip,
+ tagi_t tags[]);
+
+void run_abc_until(struct context *ctx,
+ nua_event_t a_event, condition_function *a_condition,
+ nua_event_t b_event, condition_function *b_condition,
+ nua_event_t c_event, condition_function *c_condition);
+
+void run_ab_until(struct context *ctx,
+ nua_event_t a_event, condition_function *a_condition,
+ nua_event_t b_event, condition_function *b_condition);
+
+void run_bc_until(struct context *ctx,
+ nua_event_t b_event, condition_function *b_condition,
+ nua_event_t c_event, condition_function *c_condition);
+
+int run_a_until(struct context *, nua_event_t, condition_function *);
+int run_b_until(struct context *, nua_event_t, condition_function *);
+int run_c_until(struct context *, nua_event_t, condition_function *);
+
+typedef int operation_f(struct endpoint *ep, struct call *call,
+ nua_handle_t *nh, tag_type_t tag, tag_value_t value,
+ ...);
+
+operation_f INVITE, ACK, BYE, CANCEL, AUTHENTICATE, UPDATE, INFO, PRACK,
+ REFER, MESSAGE, METHOD, OPTIONS, PUBLISH, UNPUBLISH, REGISTER, UNREGISTER,
+ SUBSCRIBE, UNSUBSCRIBE, NOTIFY, NOTIFIER, TERMINATE, AUTHORIZE;
+
+int RESPOND(struct endpoint *ep,
+ struct call *call,
+ nua_handle_t *nh,
+ int status, char const *phrase,
+ tag_type_t tag, tag_value_t value,
+ ...);
+
+int DESTROY(struct endpoint *ep,
+ struct call *call,
+ nua_handle_t *nh);
+
+struct call *check_handle(struct endpoint *ep,
+ struct call *call,
+ nua_handle_t *nh,
+ int status, char const *phrase);
+
+int is_special(nua_event_t e);
+int callstate(tagi_t const *tags);
+int is_offer_sent(tagi_t const *tags);
+int is_answer_sent(tagi_t const *tags);
+int is_offer_recv(tagi_t const *tags);
+int is_answer_recv(tagi_t const *tags);
+int is_offer_answer_done(tagi_t const *tags);
+int audio_activity(tagi_t const *tags);
+int video_activity(tagi_t const *tags);
+
+void print_event(nua_event_t event,
+ char const *operation,
+ int status, char const *phrase,
+ nua_t *nua, struct context *ctx,
+ struct endpoint *ep,
+ nua_handle_t *nh, struct call *call,
+ sip_t const *sip,
+ tagi_t tags[]);
+
+su_inline
+void eventlist_init(struct eventlist *list)
+{
+ list->tail = &list->head;
+}
+
+su_inline
+void call_init(struct call *call)
+{
+}
+
+void endpoint_init(struct context *ctx, struct endpoint *e, char id);
+
+int test_nua_init(struct context *ctx,
+ int start_proxy,
+ url_t const *o_proxy,
+ int start_nat,
+ tag_type_t tag, tag_value_t value, ...);
+
+int test_deinit(struct context *ctx);
+
+int test_nua_api_errors(struct context *ctx);
+int test_nua_destroy(struct context *ctx);
+int test_stack_errors(struct context *ctx);
+int test_tag_filter(void);
+int test_nua_params(struct context *ctx);
+
+int test_register(struct context *ctx);
+int test_connectivity(struct context *ctx);
+int test_nat_timeout(struct context *ctx);
+int test_unregister(struct context *ctx);
+
+int test_basic_call(struct context *ctx);
+int test_offer_answer(struct context *ctx);
+int test_rejects(struct context *ctx);
+int test_mime_negotiation(struct context *ctx);
+int test_call_timeouts(struct context *ctx);
+int test_reject_401_aka(struct context *ctx);
+int test_call_cancel(struct context *ctx);
+int test_call_destroy(struct context *ctx);
+int test_early_bye(struct context *ctx);
+int test_call_hold(struct context *ctx);
+int test_reinvites(struct context *ctx);
+int test_session_timer(struct context *ctx);
+int test_refer(struct context *ctx);
+int test_100rel(struct context *ctx);
+int test_simple(struct context *ctx);
+int test_events(struct context *ctx);
+
+int test_extension(struct context *ctx);
+
+#endif
Added: freeswitch/trunk/libs/sofia-sip/tests/test_nua_api.c
==============================================================================
--- (empty file)
+++ freeswitch/trunk/libs/sofia-sip/tests/test_nua_api.c Wed May 14 15:10:54 2008
@@ -0,0 +1,460 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2005 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at 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 test_nua_api.c
+ * @brief NUA API tester.
+ *
+ * @author Pekka Pessi <Pekka.Pessi at nokia.com>
+ * @author Martti Mela <Martti Mela at nokia.com>
+ *
+ * @date Created: Wed Aug 17 12:12:12 EEST 2005 ppessi
+ */
+
+#include "config.h"
+
+#include "test_nua.h"
+#include "sofia-sip/tport_tag.h"
+
+#if HAVE_FUNC
+#elif HAVE_FUNCTION
+#define __func__ __FUNCTION__
+#else
+#define __func__ "test_nua_api_errors"
+#endif
+
+/* ------------------------------------------------------------------------ */
+/* API tests */
+
+SOFIAPUBVAR su_log_t nua_log[];
+
+int check_set_status(int status, char const *phrase)
+{
+ return status == 200 && strcmp(phrase, sip_200_OK) == 0;
+}
+
+int test_nua_api_errors(struct context *ctx)
+{
+ BEGIN();
+
+ /* Invoke every API function with invalid arguments */
+
+ int level;
+
+ int status; char const *phrase;
+
+ if (print_headings)
+ printf("TEST NUA-1.0: test API\n");
+
+ /* This is a nasty macro. Test it. */
+#define SET_STATUS1(x) ((status = x), status), (phrase = ((void)x))
+ TEST_1(check_set_status(SET_STATUS1(SIP_200_OK)));
+ TEST(status, 200); TEST_S(phrase, sip_200_OK);
+
+ su_log_init(nua_log);
+ level = nua_log->log_level;
+ if (!(tstflags & tst_verbatim))
+ su_log_set_level(nua_log, 0); /* Log at level 0 by default */
+
+ TEST_1(!nua_create(NULL, NULL, NULL, TAG_END()));
+ TEST_VOID(nua_shutdown(NULL));
+ TEST_VOID(nua_destroy(NULL));
+ TEST_VOID(nua_set_params(NULL, TAG_END()));
+ TEST_VOID(nua_get_params(NULL, TAG_END()));
+ TEST_1(!nua_default(NULL));
+ TEST_1(!nua_handle(NULL, NULL, TAG_END()));
+ TEST_VOID(nua_handle_destroy(NULL));
+ TEST_VOID(nua_handle_bind(NULL, NULL));
+ TEST_1(!nua_handle_has_invite(NULL));
+ TEST_1(!nua_handle_has_subscribe(NULL));
+ TEST_1(!nua_handle_has_register(NULL));
+ TEST_1(!nua_handle_has_active_call(NULL));
+ TEST_1(!nua_handle_has_call_on_hold(NULL));
+ TEST_1(!nua_handle_has_events(NULL));
+ TEST_1(!nua_handle_has_registrations(NULL));
+ TEST_1(!nua_handle_remote(NULL));
+ TEST_1(!nua_handle_local(NULL));
+ TEST_S(nua_event_name(-1), "NUA_UNKNOWN");
+ TEST_VOID(nua_register(NULL, TAG_END()));
+ TEST_VOID(nua_unregister(NULL, TAG_END()));
+ TEST_VOID(nua_invite(NULL, TAG_END()));
+ TEST_VOID(nua_ack(NULL, TAG_END()));
+ TEST_VOID(nua_prack(NULL, TAG_END()));
+ TEST_VOID(nua_options(NULL, TAG_END()));
+ TEST_VOID(nua_publish(NULL, TAG_END()));
+ TEST_VOID(nua_message(NULL, TAG_END()));
+ TEST_VOID(nua_chat(NULL, TAG_END()));
+ TEST_VOID(nua_info(NULL, TAG_END()));
+ TEST_VOID(nua_subscribe(NULL, TAG_END()));
+ TEST_VOID(nua_unsubscribe(NULL, TAG_END()));
+ TEST_VOID(nua_notify(NULL, TAG_END()));
+ TEST_VOID(nua_notifier(NULL, TAG_END()));
+ TEST_VOID(nua_terminate(NULL, TAG_END()));
+ TEST_VOID(nua_refer(NULL, TAG_END()));
+ TEST_VOID(nua_update(NULL, TAG_END()));
+ TEST_VOID(nua_bye(NULL, TAG_END()));
+ TEST_VOID(nua_cancel(NULL, TAG_END()));
+ TEST_VOID(nua_authenticate(NULL, TAG_END()));
+ TEST_VOID(nua_redirect(NULL, TAG_END()));
+ TEST_VOID(nua_respond(NULL, 0, "", TAG_END()));
+
+ TEST_1(!nua_handle_home(NULL));
+ TEST_1(!nua_save_event(NULL, NULL));
+ TEST_1(!nua_event_data(NULL));
+ TEST_VOID(nua_destroy_event(NULL));
+
+ {
+ nua_saved_event_t event[1];
+
+ memset(event, 0, sizeof event);
+
+ TEST_1(!nua_save_event(NULL, event));
+ TEST_1(!nua_event_data(event));
+ TEST_VOID(nua_destroy_event(event));
+ }
+
+ su_log_set_level(nua_log, level);
+
+ if (print_headings)
+ printf("TEST NUA-1.0: PASSED\n");
+
+ END();
+}
+
+/* ======================================================================== */
+
+void destroy_callback(nua_event_t event,
+ int status, char const *phrase,
+ nua_t *nua, struct context *ctx,
+ nua_handle_t *nh, struct call *call,
+ sip_t const *sip,
+ tagi_t tags[])
+{
+ if (status >= 200) {
+ nua_destroy(ctx->a.nua), ctx->a.nua = NULL;
+ su_root_break(ctx->root);
+ }
+}
+
+/* Test different nua_destroy() corner cases */
+int test_nua_destroy(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a;
+
+ TEST_1(ctx->root = su_root_create(NULL));
+
+#if 0
+ a->nua = nua_create(ctx->root, destroy_callback, ctx,
+ NUTAG_URL("sip:0.0.0.0:*"),
+ TAG_IF(ctx->a.logging, TPTAG_LOG(1)),
+ TAG_END());
+ TEST_1(a->nua);
+
+ nua_get_params(a->nua, TAG_ANY(), TAG_END());
+
+ su_root_run(ctx->root);
+
+ TEST_1(a->nua == NULL);
+#endif
+
+ a->nua = nua_create(ctx->root, destroy_callback, ctx,
+ NUTAG_URL("sip:0.0.0.0:*"),
+ TAG_IF(ctx->a.logging, TPTAG_LOG(1)),
+ TAG_END());
+ TEST_1(a->nua);
+
+ nua_shutdown(a->nua);
+
+ su_root_run(ctx->root);
+
+ TEST_1(a->nua == NULL);
+
+ su_root_destroy(ctx->root), ctx->root = NULL;
+
+ END();
+}
+
+/* ======================================================================== */
+
+int test_byecancel_without_invite(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call;
+ struct event *e;
+
+ int internal_error = 900;
+
+ if (print_headings)
+ printf("TEST NUA-1.2.1: CANCEL without INVITE\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ CANCEL(a, a_call, a_call->nh, TAG_END());
+
+ run_a_until(ctx, -1, save_until_final_response);
+
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_cancel);
+ TEST(e->data->e_status, 481);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-1.2.1: PASSED\n");
+
+ /* -BYE without INVITE--------------------------------------------------- */
+
+ if (print_headings)
+ printf("TEST NUA-1.2.2: BYE without INVITE\n");
+
+ a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END());
+ TEST_1(a_call->nh);
+
+ BYE(a, a_call, a_call->nh, TAG_END());
+
+ run_a_until(ctx, -1, save_until_final_response);
+
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_bye);
+ TEST(e->data->e_status, internal_error);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-1.2.2: PASSED\n");
+
+ END();
+}
+
+
+int test_unregister_without_register(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call;
+ struct event *e;
+
+ /* -Un-register without REGISTER--------------------------------------- */
+
+ if (print_headings)
+ printf("TEST NUA-1.2.3: unregister without register\n");
+
+ a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(a->to), TAG_END());
+ TEST_1(a_call->nh);
+
+ UNREGISTER(a, a_call, a_call->nh, TAG_END());
+
+ run_a_until(ctx, -1, save_until_final_response);
+
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_unregister);
+ if (e->data->e_status == 401)
+ TEST(e->data->e_status, 401);
+ else
+ TEST(e->data->e_status, 406);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-1.2.3: PASSED\n");
+
+ /* -Un-publish without publish--------------------------------------- */
+
+ if (print_headings)
+ printf("TEST NUA-1.2.4: unpublish without publish\n");
+
+ a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END());
+ TEST_1(a_call->nh);
+
+ UNPUBLISH(a, a_call, a_call->nh, TAG_END());
+
+ run_a_until(ctx, -1, save_until_final_response);
+
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_unpublish);
+ TEST(e->data->e_status, 404);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-1.2.4: PASSED\n");
+
+ END();
+}
+
+/* -terminate without notifier--------------------------------------- */
+
+int test_terminate_without_notifier(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call;
+ struct event *e;
+
+ int internal_error = 900;
+
+ if (print_headings)
+ printf("TEST NUA-1.2.5: terminate without notifier\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ TERMINATE(a, a_call, a_call->nh, TAG_END());
+
+ run_a_until(ctx, -1, save_until_final_response);
+
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_terminate);
+ TEST(e->data->e_status, internal_error);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+
+ AUTHORIZE(a, a_call, a_call->nh, TAG_END());
+
+ run_a_until(ctx, -1, save_until_final_response);
+
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_authorize);
+ TEST(e->data->e_status, internal_error);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-1.2.5: PASSED\n");
+
+ END();
+}
+
+int destroy_on_503(CONDITION_PARAMS)
+{
+ save_event_in_list(ctx, event, ep, call);
+
+ if (status == 503) {
+ assert(nh == call->nh);
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ }
+
+ return
+ nua_r_set_params <= event && event < nua_i_network_changed
+ && status >= 200;
+}
+
+
+int test_register_503(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a;
+ struct call *a_reg = a->reg;
+ struct event *e;
+
+/* REGISTER test
+
+ A
+ |------REGISTER--\
+ |<-------503-----/
+ |
+
+*/
+
+ if (print_headings)
+ printf("TEST NUA-1.2.6: REGISTER with bad domain\n");
+
+ TEST_1(a_reg->nh = nua_handle(a->nua, a_reg, TAG_END()));
+
+ REGISTER(a, a_reg, a_reg->nh,
+ NUTAG_REGISTRAR(URL_STRING_MAKE("sip:bad.domain")),
+ SIPTAG_TO_STR("sip:lissu at bad.domain"),
+ TAG_END());
+ run_a_until(ctx, -1, destroy_on_503);
+
+ TEST_1(e = a->events->head);
+ TEST_E(e->data->e_event, nua_r_register);
+ TEST(e->data->e_status, 503);
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ TEST_1(a_reg->nh = nua_handle(a->nua, a_reg, TAG_END()));
+
+ REGISTER(a, a_reg, a_reg->nh,
+ NUTAG_REGISTRAR(URL_STRING_MAKE("sip:bad.domain")),
+ SIPTAG_TO_STR("sip:lissu at bad.domain"),
+ TAG_END());
+ nua_handle_destroy(a_reg->nh), a_reg->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-1.2.6: PASSED\n");
+
+ END();
+}
+
+int test_stack_errors(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a;
+
+ if (print_headings)
+ printf("TEST NUA-1.2: Stack error handling\n");
+
+ TEST_1(ctx->root == NULL);
+ TEST_1(ctx->root = su_root_create(NULL));
+
+ a->nua = nua_create(ctx->root, a_callback, ctx,
+ NUTAG_URL("sip:0.0.0.0:*"),
+ TAG_IF(ctx->a.logging, TPTAG_LOG(1)),
+ TAG_END());
+ TEST_1(a->nua);
+
+ TEST(test_byecancel_without_invite(ctx), 0);
+ if (ctx->proxy_tests)
+ TEST(test_unregister_without_register(ctx), 0);
+ TEST(test_terminate_without_notifier(ctx), 0);
+ TEST(test_register_503(ctx), 0);
+ TEST(test_register_503(ctx), 0);
+
+ nua_shutdown(a->nua);
+
+ run_a_until(ctx, -1, until_final_response);
+
+ TEST_VOID(nua_destroy(a->nua));
+
+ su_root_destroy(ctx->root), ctx->root = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-1.2: PASSED\n");
+
+ END();
+}
+
Added: freeswitch/trunk/libs/sofia-sip/tests/test_nua_params.c
==============================================================================
--- (empty file)
+++ freeswitch/trunk/libs/sofia-sip/tests/test_nua_params.c Wed May 14 15:10:54 2008
@@ -0,0 +1,656 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2005 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at 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 test_nua_params.c
+ * @brief Test NUA parameter handling.
+ *
+ * @author Pekka Pessi <Pekka.Pessi at nokia.com>
+ * @author Martti Mela <Martti Mela at nokia.com>
+ *
+ * @date Created: Wed Aug 17 12:12:12 EEST 2005 ppessi
+ */
+
+#include "config.h"
+
+#include "test_nua.h"
+#include <sofia-sip/su_tag_class.h>
+
+#if HAVE_FUNC
+#elif HAVE_FUNCTION
+#define __func__ __FUNCTION__
+#else
+#define __func__ "test_nua_params"
+#endif
+
+sip_route_t *GLOBAL_ROUTE;
+
+int test_tag_filter(void)
+{
+ BEGIN();
+
+#undef TAG_NAMESPACE
+#define TAG_NAMESPACE "test"
+ tag_typedef_t tag_a = STRTAG_TYPEDEF(a);
+#define TAG_A(s) tag_a, tag_str_v((s))
+ tag_typedef_t tag_b = STRTAG_TYPEDEF(b);
+#define TAG_B(s) tag_b, tag_str_v((s))
+
+ tagi_t filter[2] = {{ NUTAG_ANY() }, { TAG_END() }};
+
+ tagi_t *lst, *result;
+
+ lst = tl_list(TAG_A("X"),
+ TAG_SKIP(2),
+ NUTAG_URL((void *)"urn:foo"),
+ TAG_B("Y"),
+ NUTAG_URL((void *)"urn:bar"),
+ TAG_NULL());
+
+ TEST_1(lst);
+
+ result = tl_afilter(NULL, filter, lst);
+
+ TEST_1(result);
+ TEST_P(result[0].t_tag, nutag_url);
+ TEST_P(result[1].t_tag, nutag_url);
+
+ tl_vfree(lst);
+ free(result);
+
+ END();
+}
+
+int test_nua_params(struct context *ctx)
+{
+ BEGIN();
+
+ char const Alice[] = "Alice <sip:a at wonderland.org>";
+ sip_from_t const *from;
+ su_home_t tmphome[SU_HOME_AUTO_SIZE(16384)];
+ nua_handle_t *nh;
+ struct event *e;
+ tagi_t const *t;
+ int n;
+
+ su_home_auto(tmphome, sizeof(tmphome));
+
+ if (print_headings)
+ printf("TEST NUA-1.1: PARAMETERS\n");
+
+#if SU_HAVE_OSX_CF_API
+ if (ctx->osx_runloop)
+ ctx->root = su_root_osx_runloop_create(NULL);
+ else
+#endif
+ ctx->root = su_root_create(NULL);
+ TEST_1(ctx->root);
+
+ su_root_threading(ctx->root, ctx->threading);
+
+ ctx->a.nua = nua_create(ctx->root, a_callback, ctx,
+ SIPTAG_FROM_STR("sip:alice at example.com"),
+ NUTAG_URL("sip:0.0.0.0:*;transport=udp"),
+ TAG_END());
+
+ TEST_1(ctx->a.nua);
+
+ nua_get_params(ctx->a.nua, TAG_ANY(), TAG_END());
+ run_a_until(ctx, nua_r_get_params, save_until_final_response);
+
+ TEST_1(e = ctx->a.specials->head);
+ TEST_E(e->data->e_event, nua_r_get_params);
+ for (n = 0, t = e->data->e_tags; t; n++, t = tl_next(t))
+ ;
+ TEST_1(n > 32);
+ free_events_in_list(ctx, ctx->a.specials);
+
+ nh = nua_handle(ctx->a.nua, NULL, TAG_END()); TEST_1(nh);
+ nua_handle_unref(nh);
+
+ nh = nua_handle(ctx->a.nua, NULL, TAG_END()); TEST_1(nh);
+ nua_handle_destroy(nh);
+
+ from = sip_from_make(tmphome, Alice);
+
+ nh = nua_handle(ctx->a.nua, NULL, TAG_END());
+
+ nua_set_hparams(nh, NUTAG_INVITE_TIMER(90), TAG_END());
+ run_a_until(ctx, nua_r_set_params, until_final_response);
+
+ /* Modify all pointer values */
+ nua_set_params(ctx->a.nua,
+ SIPTAG_FROM_STR(Alice),
+
+ NUTAG_MEDIA_ENABLE(0),
+ NUTAG_SOA_NAME("test"),
+
+ NUTAG_REGISTRAR("sip:openlaboratory.net"),
+
+ SIPTAG_SUPPORTED_STR("test"),
+ SIPTAG_ALLOW_STR("DWIM, OPTIONS, INFO"),
+ NUTAG_APPL_METHOD(NULL),
+ NUTAG_APPL_METHOD("INVITE, REGISTER, PUBLISH, SUBSCRIBE"),
+ SIPTAG_ALLOW_EVENTS_STR("reg"),
+ SIPTAG_USER_AGENT_STR("test_nua/1.0"),
+
+ SIPTAG_ORGANIZATION_STR("Open Laboratory"),
+
+ NUTAG_M_DISPLAY("XXX"),
+ NUTAG_M_USERNAME("xxx"),
+ NUTAG_M_PARAMS("user=ip"),
+ NUTAG_M_FEATURES("language=\"fi\""),
+ NUTAG_INSTANCE("urn:uuid:3eb007b1-6d7f-472e-8b64-29e482795da8"),
+ NUTAG_OUTBOUND("bar"),
+
+ NUTAG_INITIAL_ROUTE(NULL),
+ NUTAG_INITIAL_ROUTE(sip_route_make(tmphome, "<sip:tst at example.net;lr>")),
+ NUTAG_INITIAL_ROUTE_STR("<sip:str1 at example.net;lr>"),
+ NUTAG_INITIAL_ROUTE_STR("sip:str2 at example.net;lr=foo"),
+ NUTAG_INITIAL_ROUTE_STR(NULL),
+
+ TAG_END());
+
+ run_a_until(ctx, nua_r_set_params, until_final_response);
+
+ /* Modify everything from their default value */
+ nua_set_params(ctx->a.nua,
+ SIPTAG_FROM(from),
+ NUTAG_RETRY_COUNT(9),
+ NUTAG_MAX_SUBSCRIPTIONS(6),
+
+ NUTAG_ENABLEINVITE(0),
+ NUTAG_AUTOALERT(1),
+ NUTAG_EARLY_MEDIA(1),
+ NUTAG_AUTOANSWER(1),
+ NUTAG_AUTOACK(0),
+ NUTAG_INVITE_TIMER(60),
+
+ NUTAG_SESSION_TIMER(600),
+ NUTAG_MIN_SE(35),
+ NUTAG_SESSION_REFRESHER(nua_remote_refresher),
+ NUTAG_UPDATE_REFRESH(1),
+
+ NUTAG_ENABLEMESSAGE(0),
+ NUTAG_ENABLEMESSENGER(1),
+ /* NUTAG_MESSAGE_AUTOANSWER(0), */
+
+ NUTAG_CALLEE_CAPS(1),
+ NUTAG_MEDIA_FEATURES(1),
+ NUTAG_SERVICE_ROUTE_ENABLE(0),
+ NUTAG_PATH_ENABLE(0),
+ NUTAG_AUTH_CACHE(nua_auth_cache_challenged),
+ NUTAG_REFER_EXPIRES(333),
+ NUTAG_REFER_WITH_ID(0),
+ NUTAG_SUBSTATE(nua_substate_pending),
+ NUTAG_SUB_EXPIRES(3700),
+
+ NUTAG_KEEPALIVE(66),
+ NUTAG_KEEPALIVE_STREAM(33),
+
+ NUTAG_INSTANCE("urn:uuid:97701ad9-39df-1229-1083-dbc0a85f029c"),
+ NUTAG_M_DISPLAY("Joe"),
+ NUTAG_M_USERNAME("joe"),
+ NUTAG_M_PARAMS("user=phone"),
+ NUTAG_M_FEATURES("language=\"en\""),
+ NUTAG_OUTBOUND("foo"),
+ SIPTAG_SUPPORTED(sip_supported_make(tmphome, "foo")),
+ NUTAG_SUPPORTED("foo, bar"),
+ SIPTAG_SUPPORTED_STR(",baz,"),
+
+ SIPTAG_ALLOW_STR("OPTIONS"),
+ SIPTAG_ALLOW(sip_allow_make(tmphome, "INFO")),
+ NUTAG_ALLOW("ACK, INFO"),
+
+ NUTAG_APPL_METHOD("NOTIFY"),
+
+ SIPTAG_ALLOW_EVENTS_STR("reg"),
+ SIPTAG_ALLOW_EVENTS(sip_allow_events_make(tmphome, "presence")),
+ NUTAG_ALLOW_EVENTS("presence.winfo"),
+
+ NUTAG_INITIAL_ROUTE(NULL),
+ NUTAG_INITIAL_ROUTE(sip_route_make(nua_handle_home(nh), "<sip:1 at example.com;lr>")),
+ NUTAG_INITIAL_ROUTE_STR("<sip:2 at example.com;lr>"),
+ /* Check for sip_route_fix() */
+ NUTAG_INITIAL_ROUTE_STR("sip:3 at example.com;lr=foo"),
+ NUTAG_INITIAL_ROUTE_STR(NULL),
+
+ SIPTAG_USER_AGENT(sip_user_agent_make(tmphome, "test_nua")),
+
+ SIPTAG_ORGANIZATION(sip_organization_make(tmphome, "Pussy Galore's Flying Circus")),
+
+ NUTAG_MEDIA_ENABLE(0),
+ NUTAG_REGISTRAR(url_hdup(tmphome, (url_t *)"sip:sip.wonderland.org")),
+
+ TAG_END());
+
+ run_a_until(ctx, nua_r_set_params, until_final_response);
+
+ /* Modify something... */
+ nua_set_params(ctx->a.nua,
+ NUTAG_RETRY_COUNT(5),
+ TAG_END());
+ run_a_until(ctx, nua_r_set_params, until_final_response);
+
+ {
+ sip_from_t const *from = NONE;
+ char const *from_str = "NONE";
+
+ unsigned retry_count = (unsigned)-1;
+ unsigned max_subscriptions = (unsigned)-1;
+
+ char const *soa_name = "NONE";
+ int media_enable = -1;
+ int invite_enable = -1;
+ int auto_alert = -1;
+ int early_media = -1;
+ int only183_100rel = -1;
+ int auto_answer = -1;
+ int auto_ack = -1;
+ unsigned invite_timeout = (unsigned)-1;
+
+ unsigned session_timer = (unsigned)-1;
+ unsigned min_se = (unsigned)-1;
+ int refresher = -1;
+ int update_refresh = -1;
+
+ int message_enable = -1;
+ int win_messenger_enable = -1;
+ int message_auto_respond = -1;
+
+ int callee_caps = -1;
+ int media_features = -1;
+ int service_route_enable = -1;
+ int path_enable = -1;
+ int auth_cache = -1;
+ unsigned refer_expires = (unsigned)-1;
+ int refer_with_id = -1;
+ unsigned sub_expires = (unsigned)-1;
+ int substate = -1;
+
+ sip_allow_t const *allow = NONE;
+ char const *allow_str = "NONE";
+ char const *appl_method = "NONE";
+ sip_allow_events_t const *allow_events = NONE;
+ char const *allow_events_str = "NONE";
+ sip_supported_t const *supported = NONE;
+ char const *supported_str = "NONE";
+ sip_user_agent_t const *user_agent = NONE;
+ char const *user_agent_str = "NONE";
+ char const *ua_name = "NONE";
+ sip_organization_t const *organization = NONE;
+ char const *organization_str = "NONE";
+
+ sip_route_t const *initial_route = NONE;
+ char const *initial_route_str = NONE;
+
+ char const *outbound = "NONE";
+ char const *m_display = "NONE";
+ char const *m_username = "NONE";
+ char const *m_params = "NONE";
+ char const *m_features = "NONE";
+ char const *instance = "NONE";
+
+ url_string_t const *registrar = NONE;
+ unsigned keepalive = (unsigned)-1, keepalive_stream = (unsigned)-1;
+
+ nua_get_params(ctx->a.nua, TAG_ANY(), TAG_END());
+ run_a_until(ctx, nua_r_get_params, save_until_final_response);
+
+ TEST_1(e = ctx->a.specials->head);
+ TEST_E(e->data->e_event, nua_r_get_params);
+
+ n = tl_gets(e->data->e_tags,
+ SIPTAG_FROM_REF(from),
+ SIPTAG_FROM_STR_REF(from_str),
+
+ NUTAG_RETRY_COUNT_REF(retry_count),
+ NUTAG_MAX_SUBSCRIPTIONS_REF(max_subscriptions),
+
+ NUTAG_SOA_NAME_REF(soa_name),
+ NUTAG_MEDIA_ENABLE_REF(media_enable),
+ NUTAG_ENABLEINVITE_REF(invite_enable),
+ NUTAG_AUTOALERT_REF(auto_alert),
+ NUTAG_EARLY_MEDIA_REF(early_media),
+ NUTAG_ONLY183_100REL_REF(only183_100rel),
+ NUTAG_AUTOANSWER_REF(auto_answer),
+ NUTAG_AUTOACK_REF(auto_ack),
+ NUTAG_INVITE_TIMER_REF(invite_timeout),
+
+ NUTAG_SESSION_TIMER_REF(session_timer),
+ NUTAG_MIN_SE_REF(min_se),
+ NUTAG_SESSION_REFRESHER_REF(refresher),
+ NUTAG_UPDATE_REFRESH_REF(update_refresh),
+
+ NUTAG_ENABLEMESSAGE_REF(message_enable),
+ NUTAG_ENABLEMESSENGER_REF(win_messenger_enable),
+ /* NUTAG_MESSAGE_AUTOANSWER(message_auto_respond), */
+
+ NUTAG_CALLEE_CAPS_REF(callee_caps),
+ NUTAG_MEDIA_FEATURES_REF(media_features),
+ NUTAG_SERVICE_ROUTE_ENABLE_REF(service_route_enable),
+ NUTAG_PATH_ENABLE_REF(path_enable),
+ NUTAG_AUTH_CACHE_REF(auth_cache),
+ NUTAG_REFER_EXPIRES_REF(refer_expires),
+ NUTAG_REFER_WITH_ID_REF(refer_with_id),
+ NUTAG_SUBSTATE_REF(substate),
+ NUTAG_SUB_EXPIRES_REF(sub_expires),
+
+ SIPTAG_SUPPORTED_REF(supported),
+ SIPTAG_SUPPORTED_STR_REF(supported_str),
+ SIPTAG_ALLOW_REF(allow),
+ SIPTAG_ALLOW_STR_REF(allow_str),
+ NUTAG_APPL_METHOD_REF(appl_method),
+ SIPTAG_ALLOW_EVENTS_REF(allow_events),
+ SIPTAG_ALLOW_EVENTS_STR_REF(allow_events_str),
+ SIPTAG_USER_AGENT_REF(user_agent),
+ SIPTAG_USER_AGENT_STR_REF(user_agent_str),
+ NUTAG_USER_AGENT_REF(ua_name),
+
+ SIPTAG_ORGANIZATION_REF(organization),
+ SIPTAG_ORGANIZATION_STR_REF(organization_str),
+
+ NUTAG_INITIAL_ROUTE_REF(initial_route),
+ NUTAG_INITIAL_ROUTE_STR_REF(initial_route_str),
+
+ NUTAG_REGISTRAR_REF(registrar),
+ NUTAG_KEEPALIVE_REF(keepalive),
+ NUTAG_KEEPALIVE_STREAM_REF(keepalive_stream),
+
+ NUTAG_OUTBOUND_REF(outbound),
+ NUTAG_M_DISPLAY_REF(m_display),
+ NUTAG_M_USERNAME_REF(m_username),
+ NUTAG_M_PARAMS_REF(m_params),
+ NUTAG_M_FEATURES_REF(m_features),
+ NUTAG_INSTANCE_REF(instance),
+
+ TAG_END());
+ TEST(n, 51);
+
+ TEST_S(sip_header_as_string(tmphome, (void *)from), Alice);
+ TEST_S(from_str, Alice);
+
+ TEST(retry_count, 5);
+ TEST(max_subscriptions, 6);
+
+ TEST_S(soa_name, "test");
+ TEST(media_enable, 0);
+ TEST(invite_enable, 0);
+ TEST(auto_alert, 1);
+ TEST(early_media, 1);
+ TEST(auto_answer, 1);
+ TEST(auto_ack, 0);
+ TEST(invite_timeout, 60);
+
+ TEST(session_timer, 600);
+ TEST(min_se, 35);
+ TEST(refresher, nua_remote_refresher);
+ TEST(update_refresh, 1);
+
+ TEST(message_enable, 0);
+ TEST(win_messenger_enable, 1);
+ TEST(message_auto_respond, -1); /* XXX */
+
+ TEST(callee_caps, 1);
+ TEST(media_features, 1);
+ TEST(service_route_enable, 0);
+ TEST(path_enable, 0);
+ TEST(auth_cache, nua_auth_cache_challenged);
+ TEST(refer_expires, 333);
+ TEST(refer_with_id, 0);
+ TEST(substate, nua_substate_pending);
+ TEST(sub_expires, 3700);
+
+ TEST_S(sip_header_as_string(tmphome, (void *)allow), "OPTIONS, INFO, ACK");
+ TEST_S(allow_str, "OPTIONS, INFO, ACK");
+ TEST_S(appl_method, "INVITE, REGISTER, PUBLISH, SUBSCRIBE, NOTIFY");
+ TEST_S(sip_header_as_string(tmphome, (void *)allow_events),
+ "reg, presence, presence.winfo");
+ TEST_S(allow_events_str, "reg, presence, presence.winfo");
+ TEST_S(sip_header_as_string(tmphome, (void *)supported),
+ "foo, bar, baz");
+ TEST_S(supported_str, "foo, bar, baz");
+ TEST_S(sip_header_as_string(tmphome, (void *)user_agent), "test_nua");
+ TEST_S(user_agent_str, "test_nua");
+ TEST_S(sip_header_as_string(tmphome, (void *)organization),
+ "Pussy Galore's Flying Circus");
+ TEST_S(organization_str, "Pussy Galore's Flying Circus");
+
+ TEST_1(initial_route); TEST_1(initial_route != (void *)-1);
+ TEST_S(initial_route->r_url->url_user, "1");
+ TEST_1(url_has_param(initial_route->r_url, "lr"));
+ TEST_1(initial_route->r_next);
+ TEST_S(initial_route->r_next->r_url->url_user, "2");
+ TEST_1(url_has_param(initial_route->r_next->r_url, "lr"));
+ TEST_1(initial_route->r_next->r_next);
+ TEST_S(initial_route->r_next->r_next->r_url->url_user, "3");
+ TEST_1(url_has_param(initial_route->r_next->r_next->r_url, "lr"));
+ TEST_1(!initial_route->r_next->r_next->r_next);
+
+ TEST_S(url_as_string(tmphome, registrar->us_url),
+ "sip:sip.wonderland.org");
+ TEST(keepalive, 66);
+ TEST(keepalive_stream, 33);
+
+ TEST_S(instance, "urn:uuid:97701ad9-39df-1229-1083-dbc0a85f029c");
+ TEST_S(m_display, "Joe");
+ TEST_S(m_username, "joe");
+ TEST_S(m_params, "user=phone");
+ { char const *expect_m_features = "language=\"en\"";
+ TEST_S(m_features, expect_m_features); }
+ TEST_S(outbound, "foo");
+
+ free_events_in_list(ctx, ctx->a.specials);
+ }
+
+ /* Test that only those tags that have been set per handle are returned by nua_get_hparams() */
+
+ {
+ sip_from_t const *from = NONE;
+ char const *from_str = "NONE";
+
+ unsigned retry_count = (unsigned)-1;
+ unsigned max_subscriptions = (unsigned)-1;
+
+ int invite_enable = -1;
+ int auto_alert = -1;
+ int early_media = -1;
+ int auto_answer = -1;
+ int auto_ack = -1;
+ unsigned invite_timeout = (unsigned)-1;
+
+ unsigned session_timer = (unsigned)-1;
+ unsigned min_se = (unsigned)-1;
+ int refresher = -1;
+ int update_refresh = -1;
+
+ int message_enable = -1;
+ int win_messenger_enable = -1;
+ int message_auto_respond = -1;
+
+ int callee_caps = -1;
+ int media_features = -1;
+ int service_route_enable = -1;
+ int path_enable = -1;
+ int auth_cache = -1;
+ unsigned refer_expires = (unsigned)-1;
+ int refer_with_id = -1;
+ int substate = -1;
+ unsigned sub_expires = (unsigned)-1;
+
+ sip_allow_t const *allow = NONE;
+ char const *allow_str = "NONE";
+ sip_supported_t const *supported = NONE;
+ char const *supported_str = "NONE";
+ sip_user_agent_t const *user_agent = NONE;
+ char const *user_agent_str = "NONE";
+ sip_organization_t const *organization = NONE;
+ char const *organization_str = "NONE";
+
+ sip_route_t *initial_route = NONE;
+ char const *initial_route_str = "NONE";
+
+ url_string_t const *registrar = NONE;
+
+ char const *outbound = "NONE";
+ char const *m_display = "NONE";
+ char const *m_username = "NONE";
+ char const *m_params = "NONE";
+ char const *m_features = "NONE";
+ char const *instance = "NONE";
+
+ int n;
+ struct event *e;
+
+ nua_get_hparams(nh, TAG_ANY(), TAG_END());
+ run_a_until(ctx, nua_r_get_params, save_until_final_response);
+
+ TEST_1(e = ctx->a.events->head);
+ TEST_E(e->data->e_event, nua_r_get_params);
+
+ n = tl_gets(e->data->e_tags,
+ SIPTAG_FROM_REF(from),
+ SIPTAG_FROM_STR_REF(from_str),
+
+ NUTAG_RETRY_COUNT_REF(retry_count),
+ NUTAG_MAX_SUBSCRIPTIONS_REF(max_subscriptions),
+
+ NUTAG_ENABLEINVITE_REF(invite_enable),
+ NUTAG_AUTOALERT_REF(auto_alert),
+ NUTAG_EARLY_MEDIA_REF(early_media),
+ NUTAG_AUTOANSWER_REF(auto_answer),
+ NUTAG_AUTOACK_REF(auto_ack),
+ NUTAG_INVITE_TIMER_REF(invite_timeout),
+
+ NUTAG_SESSION_TIMER_REF(session_timer),
+ NUTAG_MIN_SE_REF(min_se),
+ NUTAG_SESSION_REFRESHER_REF(refresher),
+ NUTAG_UPDATE_REFRESH_REF(update_refresh),
+
+ NUTAG_ENABLEMESSAGE_REF(message_enable),
+ NUTAG_ENABLEMESSENGER_REF(win_messenger_enable),
+ /* NUTAG_MESSAGE_AUTOANSWER(message_auto_respond), */
+
+ NUTAG_CALLEE_CAPS_REF(callee_caps),
+ NUTAG_MEDIA_FEATURES_REF(media_features),
+ NUTAG_SERVICE_ROUTE_ENABLE_REF(service_route_enable),
+ NUTAG_PATH_ENABLE_REF(path_enable),
+ NUTAG_AUTH_CACHE_REF(auth_cache),
+ NUTAG_SUBSTATE_REF(substate),
+ NUTAG_SUB_EXPIRES_REF(sub_expires),
+
+ SIPTAG_SUPPORTED_REF(supported),
+ SIPTAG_SUPPORTED_STR_REF(supported_str),
+ SIPTAG_ALLOW_REF(allow),
+ SIPTAG_ALLOW_STR_REF(allow_str),
+ SIPTAG_USER_AGENT_REF(user_agent),
+ SIPTAG_USER_AGENT_STR_REF(user_agent_str),
+
+ SIPTAG_ORGANIZATION_REF(organization),
+ SIPTAG_ORGANIZATION_STR_REF(organization_str),
+
+ NUTAG_OUTBOUND_REF(outbound),
+ NUTAG_M_DISPLAY_REF(m_display),
+ NUTAG_M_USERNAME_REF(m_username),
+ NUTAG_M_PARAMS_REF(m_params),
+ NUTAG_M_FEATURES_REF(m_features),
+ NUTAG_INSTANCE_REF(instance),
+
+ NUTAG_REGISTRAR_REF(registrar),
+
+ TAG_END());
+ TEST(n, 3);
+
+ TEST(invite_timeout, 90);
+
+ TEST_1(from != NULL && from != NONE);
+ TEST_1(strcmp(from_str, "NONE"));
+
+ /* Nothing else should be set */
+ TEST(retry_count, (unsigned)-1);
+ TEST(max_subscriptions, (unsigned)-1);
+
+ TEST(invite_enable, -1);
+ TEST(auto_alert, -1);
+ TEST(early_media, -1);
+ TEST(auto_answer, -1);
+ TEST(auto_ack, -1);
+
+ TEST(session_timer, (unsigned)-1);
+ TEST(min_se, (unsigned)-1);
+ TEST(refresher, -1);
+ TEST(update_refresh, -1);
+
+ TEST(message_enable, -1);
+ TEST(win_messenger_enable, -1);
+ TEST(message_auto_respond, -1); /* XXX */
+
+ TEST(callee_caps, -1);
+ TEST(media_features, -1);
+ TEST(service_route_enable, -1);
+ TEST(path_enable, -1);
+ TEST(auth_cache, -1);
+ TEST(refer_expires, (unsigned)-1);
+ TEST(refer_with_id, -1);
+ TEST(substate, -1);
+ TEST(sub_expires, -1);
+
+ TEST_P(allow, NONE);
+ TEST_S(allow_str, "NONE");
+ TEST_P(supported, NONE);
+ TEST_S(supported_str, "NONE");
+ TEST_P(user_agent, NONE);
+ TEST_S(user_agent_str, "NONE");
+ TEST_P(organization, NONE);
+ TEST_S(organization_str, "NONE");
+
+ TEST_1(initial_route == (void *)-1);
+ TEST_S(initial_route_str, "NONE");
+
+ TEST_S(outbound, "NONE");
+ TEST_S(m_display, "NONE");
+ TEST_S(m_username, "NONE");
+ TEST_S(m_params, "NONE");
+ TEST_S(m_features, "NONE");
+ TEST_S(instance, "NONE");
+
+ TEST_P(registrar->us_url, NONE);
+
+ free_events_in_list(ctx, ctx->a.events);
+ }
+
+ nua_handle_destroy(nh);
+
+ nua_shutdown(ctx->a.nua);
+ run_a_until(ctx, nua_r_shutdown, until_final_response);
+ nua_destroy(ctx->a.nua), ctx->a.nua = NULL;
+
+ su_root_destroy(ctx->root), ctx->root = NULL;
+
+ su_home_deinit(tmphome);
+
+ if (print_headings)
+ printf("TEST NUA-1.1: PASSED\n");
+
+ END();
+}
Added: freeswitch/trunk/libs/sofia-sip/tests/test_offer_answer.c
==============================================================================
--- (empty file)
+++ freeswitch/trunk/libs/sofia-sip/tests/test_offer_answer.c Wed May 14 15:10:54 2008
@@ -0,0 +1,479 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2005 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at 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 test_offer_answer.c
+ * @brief Test offer/answer failures.
+ *
+ * @author Pekka Pessi <Pekka.Pessi at nokia.com>
+ *
+ * @date Created: Wed Aug 17 12:12:12 EEST 2005 ppessi
+ */
+
+#include "config.h"
+
+#include "test_nua.h"
+#include <sofia-sip/su_tag_class.h>
+
+#if HAVE_FUNC
+#elif HAVE_FUNCTION
+#define __func__ __FUNCTION__
+#else
+#define __func__ "test_offer_answer"
+#endif
+
+
+/* ======================================================================== */
+
+/* Send offer in INVITE, no answer:
+
+ A B
+ --nua_invite()-->| |
+ |-------INVITE------>|
+ <--nua_i_state---| |
+ |<----100 Trying-----|
+ | |
+ |<--------180--------|
+ <--nua_r_invite--| |
+ <--nua_i_state---| |
+ |<------200 OK-------|
+ <--nua_r_invite--| |
+ |--------ACK-------->|
+ |--------BYE-------->|
+ <--nua_i_state---| |
+ |<-------200 OK------|
+ <--nua_r_bye-----| |
+ <--nua_i_state---| |
+
+ Client transitions:
+ INIT -(C1)-> CALLING -(C2a)-> PROCEEDING -(C3+C4)->
+ TERMINATING -> TERMINATED
+ Server transitions:
+ INIT -(S1)-> RECEIVED -(S2a)-> EARLY -(S3b)-> COMPLETED -(S4)-> READY
+ -> TERMINATED
+
+*/
+
+int no_media_terminated(CONDITION_PARAMS);
+
+int test_no_answer_1(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+ sip_t *sip;
+
+ if (print_headings)
+ printf("TEST NUA-6.5: No SDP answer from callee\n");
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = NULL;
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ TEST_1(!nua_handle_has_active_call(a_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ NUTAG_M_USERNAME("a+a"),
+ NUTAG_M_DISPLAY("Alice"),
+ TAG_END());
+
+ run_ab_until(ctx, -1, until_terminated, -1, no_media_terminated);
+
+ TEST_1(!nua_handle_has_active_call(a_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+ TEST_1(!nua_handle_has_active_call(b_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(b_call->nh));
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C3)-> PROCEEDING: nua_r_invite, nua_i_state
+ PROCEEDING -(C3a+C5)-> TERMINATING: nua_r_invite, nua_i_state
+ TERMINATING -(C12)-> TERMINATED: nua_r_bye, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(!sip->sip_payload);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
+ TEST_1(!is_answer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(!sip->sip_payload);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_media_error);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminating); /* TERMINATING */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+ EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_contact);
+ TEST_S(sip->sip_contact->m_display, "Alice");
+ TEST_S(sip->sip_contact->m_url->url_user, "a+a");
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(!is_answer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(!is_answer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!is_answer_recv(e->data->e_tags));
+ TEST_1(!is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ free_events_in_list(ctx, b->events);
+
+ TEST_1(!nua_handle_has_active_call(a_call->nh));
+ TEST_1(!nua_handle_has_active_call(b_call->nh));
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-6.5: PASSED\n");
+
+ END();
+}
+
+int no_media_terminated(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_received:
+ RESPOND(ep, call, nh, SIP_180_RINGING,
+ NUTAG_MEDIA_ENABLE(0),
+ TAG_END());
+ return 0;
+ case nua_callstate_early:
+ RESPOND(ep, call, nh, SIP_200_OK,
+ NUTAG_MEDIA_ENABLE(0),
+ TAG_END());
+ return 0;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int accept_call_until_terminated(CONDITION_PARAMS);
+
+/* No offer in INVITE, offer in 200 OK, no answer in ACK */
+int test_no_answer_2(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+ sip_t const *sip;
+
+ if (print_headings)
+ printf("TEST NUA-6.6: No SDP offer from caller\n");
+
+ a_call->sdp = "v=0\r\n"
+ "o=- 1 1 IN IP4 127.0.0.1\r\n"
+ "s=-\r\n"
+ "c=IN IP4 127.0.0.1\r\n"
+ "t=0 0\r\n"
+ "m=audio 5008 RTP/AVP 8\r\n";
+
+ b_call->sdp =
+ "v=0\r\n"
+ "o=- 2 1 IN IP4 127.0.0.1\r\n"
+ "s=-\r\n"
+ "c=IN IP4 127.0.0.1\r\n"
+ "t=0 0\r\n"
+ "m=audio 5010 RTP/AVP 0 8\r\n";
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call,
+ SIPTAG_TO_STR("<sip:b at x.org>"),
+ TAG_END()));
+
+ TEST_1(!nua_handle_has_active_call(a_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+ INVITE(a, a_call, a_call->nh,
+ NUTAG_MEDIA_ENABLE(0),
+ NUTAG_URL(b->contact->m_url),
+ NUTAG_AUTOACK(1),
+ TAG_END());
+
+ run_ab_until(ctx, -1, until_terminated,
+ -1, accept_call_until_terminated);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state
+ PROCEEDING -(C3+C4)-> READY: nua_r_invite, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(!is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
+ TEST_1(!is_answer_recv(e->data->e_tags));
+ TEST_1(!is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_content_type);
+ TEST_S(sip->sip_content_type->c_type, "application/sdp");
+ TEST_1(sip->sip_payload); /* there is sdp in 200 OK */
+ TEST_1(sip->sip_contact);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!is_answer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* READY */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ TEST_1(!nua_handle_has_active_call(a_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+ EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(!is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(!is_answer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_media_error);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminating); /* TERMINATING */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ TEST_1(!nua_handle_has_active_call(b_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(b_call->nh));
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-6.6: PASSED\n");
+
+ END();
+}
+
+int accept_call_until_terminated(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (callstate(tags)) {
+ case nua_callstate_received:
+ RESPOND(ep, call, nh, SIP_180_RINGING,
+ TAG_END());
+ return 0;
+ case nua_callstate_early:
+ RESPOND(ep, call, nh, SIP_200_OK,
+ NUTAG_MEDIA_ENABLE(1),
+ TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
+ TAG_END());
+ return 0;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+
+/* No offer in INVITE, no user SDP in 200 OK */
+int test_missing_user_sdp(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+
+ if (print_headings)
+ printf("TEST NUA-6.6.2: No SDP offer from caller\n");
+
+ a_call->sdp = "v=0\r\n"
+ "o=- 1 1 IN IP4 127.0.0.1\r\n"
+ "s=-\r\n"
+ "c=IN IP4 127.0.0.1\r\n"
+ "t=0 0\r\n"
+ "m=audio 5008 RTP/AVP 8\r\n";
+
+ b_call->sdp = NULL;
+
+ nua_set_params(b->nua, NUTAG_MEDIA_ENABLE(0), TAG_END());
+ run_b_until(ctx, nua_r_set_params, until_final_response);
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call,
+ SIPTAG_TO_STR("<sip:b at x.org>"),
+ TAG_END()));
+
+ TEST_1(!nua_handle_has_active_call(a_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+ INVITE(a, a_call, a_call->nh,
+ NUTAG_MEDIA_ENABLE(0),
+ NUTAG_URL(b->contact->m_url),
+ NUTAG_AUTOACK(1),
+ TAG_END());
+
+ run_ab_until(ctx, -1, until_terminated,
+ -1, accept_call_until_terminated);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state
+ PROCEEDING -(C3+C4)-> READY: nua_r_invite, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(!is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
+ TEST_1(!is_answer_recv(e->data->e_tags));
+ TEST_1(!is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 500);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* READY */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ TEST_1(!nua_handle_has_active_call(a_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+ EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(!is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(!is_answer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_error);
+ TEST(e->data->e_status, 500);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* COMPLETED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ TEST_1(!nua_handle_has_active_call(b_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(b_call->nh));
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ nua_set_params(b->nua,
+ NUTAG_MEDIA_ENABLE(1),
+ SOATAG_USER_SDP_STR("m=audio 5006 RTP/AVP 8 0"),
+ TAG_END());
+ run_b_until(ctx, nua_r_set_params, until_final_response);
+
+ if (print_headings)
+ printf("TEST NUA-6.6.2: PASSED\n");
+
+ END();
+}
+
+
+
+int test_offer_answer(struct context *ctx)
+{
+ return
+ test_no_answer_1(ctx) ||
+ test_no_answer_2(ctx) ||
+ test_missing_user_sdp(ctx) ||
+ 0;
+}
Added: freeswitch/trunk/libs/sofia-sip/tests/test_ops.c
==============================================================================
--- (empty file)
+++ freeswitch/trunk/libs/sofia-sip/tests/test_ops.c Wed May 14 15:10:54 2008
@@ -0,0 +1,566 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2005 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at 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 test_ops.c
+ * @brief High-level test framework for Sofia SIP User Agent Engine
+ *
+ * @author Pekka Pessi <Pekka.Pessi at nokia.com>
+ * @author Martti Mela <Martti Mela at nokia.com>
+ *
+ * @date Created: Wed Aug 17 12:12:12 EEST 2005 ppessi
+ */
+
+#include "config.h"
+
+#include "test_nua.h"
+
+#if HAVE_FUNC
+#elif HAVE_FUNCTION
+#define __func__ __FUNCTION__
+#else
+#define __func__ name
+#endif
+
+int save_events(CONDITION_PARAMS)
+{
+ return save_event_in_list(ctx, event, ep, call) == event_is_normal;
+}
+
+int until_final_response(CONDITION_PARAMS)
+{
+ return status >= 200;
+}
+
+int save_until_final_response(CONDITION_PARAMS)
+{
+ save_event_in_list(ctx, event, ep, call);
+ return
+ nua_r_set_params <= event && event < nua_i_network_changed
+ && status >= 200;
+}
+
+/** Save events.
+ *
+ * Terminate when a event is saved.
+ */
+int save_until_received(CONDITION_PARAMS)
+{
+ return save_event_in_list(ctx, event, ep, call) == event_is_normal;
+}
+
+/** Save events until nua_i_outbound is received. */
+int save_until_special(CONDITION_PARAMS)
+{
+ return save_event_in_list(ctx, event, ep, call) == event_is_special;
+}
+
+/* Return call state from event tag list */
+int callstate(tagi_t const *tags)
+{
+ tagi_t const *ti = tl_find(tags, nutag_callstate);
+ return ti ? ti->t_value : -1;
+}
+
+/* Return true if offer is sent */
+int is_offer_sent(tagi_t const *tags)
+{
+ tagi_t const *ti = tl_find(tags, nutag_offer_sent);
+ return ti ? ti->t_value : 0;
+}
+
+/* Return true if answer is sent */
+int is_answer_sent(tagi_t const *tags)
+{
+ tagi_t const *ti = tl_find(tags, nutag_answer_sent);
+ return ti ? ti->t_value : 0;
+}
+
+/* Return true if offer is recv */
+int is_offer_recv(tagi_t const *tags)
+{
+ tagi_t const *ti = tl_find(tags, nutag_offer_recv);
+ return ti ? ti->t_value : 0;
+}
+
+/* Return true if answer is recv */
+int is_answer_recv(tagi_t const *tags)
+{
+ tagi_t const *ti = tl_find(tags, nutag_answer_recv);
+ return ti ? ti->t_value : 0;
+}
+
+/* Return true if offer/answer is sent/recv */
+int is_offer_answer_done(tagi_t const *tags)
+{
+ tagi_t const *ti;
+
+ return
+ ((ti = tl_find(tags, nutag_answer_recv)) && ti->t_value) ||
+ ((ti = tl_find(tags, nutag_offer_sent)) && ti->t_value) ||
+ ((ti = tl_find(tags, nutag_offer_recv)) && ti->t_value) ||
+ ((ti = tl_find(tags, nutag_answer_sent)) && ti->t_value);
+}
+
+/* Return audio state from event tag list */
+int audio_activity(tagi_t const *tags)
+{
+ tagi_t const *ti = tl_find(tags, soatag_active_audio);
+ return ti ? ti->t_value : -1;
+}
+
+/* Return video state from event tag list */
+int video_activity(tagi_t const *tags)
+{
+ tagi_t const *ti = tl_find(tags, soatag_active_video);
+ return ti ? ti->t_value : -1;
+}
+
+void print_event(nua_event_t event,
+ char const *operation,
+ int status, char const *phrase,
+ nua_t *nua, struct context *ctx,
+ struct endpoint *ep,
+ nua_handle_t *nh, struct call *call,
+ sip_t const *sip,
+ tagi_t tags[])
+{
+ tagi_t const *t;
+ static su_nanotime_t started = 0;
+ su_nanotime_t now;
+ char timestamp[32];
+
+ su_nanotime(&now);
+
+ if (started == 0) started = now;
+
+ now -= started; now /= 1000000;
+
+ snprintf(timestamp, sizeof timestamp, "%03u.%03u",
+ (unsigned)(now / 1000), (unsigned)(now % 1000));
+
+ if (event == nua_i_state) {
+ fprintf(stderr, "%s %s.nua(%p): event %s %s\n", timestamp,
+ ep->name, (void *)nh, nua_event_name(event),
+ nua_callstate_name(callstate(tags)));
+ }
+ else if ((int)event >= nua_r_set_params) {
+ t = tl_find(tags, nutag_substate);
+ if (t) {
+ fprintf(stderr, "%s %s.nua(%p): event %s status %u %s (%s)\n", timestamp,
+ ep->name, (void*)nh, nua_event_name(event), status, phrase,
+ nua_substate_name(t->t_value));
+ }
+ else {
+ fprintf(stderr, "%s %s.nua(%p): event %s status %u %s\n", timestamp,
+ ep->name, (void *)nh, nua_event_name(event), status, phrase);
+ }
+ }
+ else if (event == nua_i_notify) {
+ t = tl_find(tags, nutag_substate);
+ fprintf(stderr, "%s %s.nua(%p): event %s %s (%s)\n", timestamp,
+ ep->name, (void *)nh, nua_event_name(event), phrase,
+ nua_substate_name(t ? t->t_value : 0));
+ }
+ else if ((int)event >= nua_i_bye ||
+ event == nua_i_invite || event == nua_i_cancel ||
+ event == nua_i_ack) {
+ fprintf(stderr, "%s %s.nua(%p): event %s %03d %s\n", timestamp,
+ ep->name, (void *)nh, nua_event_name(event), status, phrase);
+ }
+ else if ((int)event >= 0) {
+ fprintf(stderr, "%s %s.nua(%p): event %s %s\n", timestamp,
+ ep->name, (void *)nh, nua_event_name(event), phrase);
+ }
+ else if (status > 0) {
+ fprintf(stderr, "%s %s.nua(%p): call %s() with status %u %s\n", timestamp,
+ ep->name, (void *)nh, operation, status, phrase);
+ }
+ else {
+ t = tl_find(tags, siptag_subject_str);
+ if (t && t->t_value) {
+ char const *subject = (char const *)t->t_value;
+ fprintf(stderr, "%s %s.nua(%p): call %s() \"%s\"\n", timestamp,
+ ep->name, (void *)nh, operation, subject);
+ }
+ else
+ fprintf(stderr, "%s %s.nua(%p): call %s()\n", timestamp,
+ ep->name, (void *)nh, operation);
+ }
+
+ if (tags &&
+ ((tstflags & tst_verbatim) || ctx->print_tags || ep->print_tags))
+ tl_print(stderr, "", tags);
+}
+
+void ep_callback(nua_event_t event,
+ int status, char const *phrase,
+ nua_t *nua, struct context *ctx,
+ struct endpoint *ep,
+ nua_handle_t *nh, struct call *call,
+ sip_t const *sip,
+ tagi_t tags[])
+{
+ if (ep->printer)
+ ep->printer(event, "", status, phrase, nua, ctx, ep, nh, call, sip, tags);
+
+ if (call == NULL && nh) {
+ for (call = ep->call; call; call = call->next) {
+ if (!call->nh)
+ break;
+ if (nh == call->nh)
+ break;
+ }
+
+ if (call && call->nh == NULL) {
+ call->nh = nh;
+ nua_handle_bind(nh, call);
+ }
+ }
+
+ if ((ep->next_condition == NULL ||
+ ep->next_condition(event, status, phrase,
+ nua, ctx, ep, nh, call, sip, tags))
+ &&
+ (ep->next_event == -1 || ep->next_event == event))
+ ep->running = 0;
+
+ ep->last_event = event;
+
+ if (call == NULL && nh)
+ nua_handle_destroy(nh);
+}
+
+void a_callback(nua_event_t event,
+ int status, char const *phrase,
+ nua_t *nua, struct context *ctx,
+ nua_handle_t *nh, struct call *call,
+ sip_t const *sip,
+ tagi_t tags[])
+{
+ ep_callback(event, status, phrase, nua, ctx, &ctx->a, nh, call, sip, tags);
+}
+
+void b_callback(nua_event_t event,
+ int status, char const *phrase,
+ nua_t *nua, struct context *ctx,
+ nua_handle_t *nh, struct call *call,
+ sip_t const *sip,
+ tagi_t tags[])
+{
+ ep_callback(event, status, phrase, nua, ctx, &ctx->b, nh, call, sip, tags);
+}
+
+void c_callback(nua_event_t event,
+ int status, char const *phrase,
+ nua_t *nua, struct context *ctx,
+ nua_handle_t *nh, struct call *call,
+ sip_t const *sip,
+ tagi_t tags[])
+{
+ ep_callback(event, status, phrase, nua, ctx, &ctx->c, nh, call, sip, tags);
+}
+
+void run_abc_until(struct context *ctx,
+ nua_event_t a_event, condition_function *a_condition,
+ nua_event_t b_event, condition_function *b_condition,
+ nua_event_t c_event, condition_function *c_condition)
+{
+ struct endpoint *a = &ctx->a, *b = &ctx->b, *c = &ctx->c;
+
+ a->next_event = a_event;
+ a->next_condition = a_condition;
+ a->last_event = -1;
+ a->running = a_condition != NULL && a_condition != save_events;
+ a->running |= a_event != -1;
+ memset(&a->flags, 0, sizeof a->flags);
+
+ b->next_event = b_event;
+ b->next_condition = b_condition;
+ b->last_event = -1;
+ b->running = b_condition != NULL && b_condition != save_events;
+ b->running |= b_event != -1;
+ memset(&b->flags, 0, sizeof b->flags);
+
+ c->next_event = c_event;
+ c->next_condition = c_condition;
+ c->last_event = -1;
+ c->running = c_condition != NULL && c_condition != save_events;
+ c->running |= c_event != -1;
+ memset(&c->flags, 0, sizeof c->flags);
+
+ for (; a->running || b->running || c->running;) {
+ su_root_step(ctx->root, 100);
+ }
+}
+
+void run_ab_until(struct context *ctx,
+ nua_event_t a_event, condition_function *a_condition,
+ nua_event_t b_event, condition_function *b_condition)
+{
+ run_abc_until(ctx, a_event, a_condition, b_event, b_condition, -1, NULL);
+}
+
+void run_bc_until(struct context *ctx,
+ nua_event_t b_event, condition_function *b_condition,
+ nua_event_t c_event, condition_function *c_condition)
+{
+ run_abc_until(ctx, -1, NULL, b_event, b_condition, c_event, c_condition);
+}
+
+int run_a_until(struct context *ctx,
+ nua_event_t a_event,
+ condition_function *a_condition)
+{
+ run_abc_until(ctx, a_event, a_condition, -1, NULL, -1, NULL);
+ return ctx->a.last_event;
+}
+
+int run_b_until(struct context *ctx,
+ nua_event_t b_event,
+ condition_function *b_condition)
+{
+ run_abc_until(ctx, -1, NULL, b_event, b_condition, -1, NULL);
+ return ctx->b.last_event;
+}
+
+int run_c_until(struct context *ctx,
+ nua_event_t event,
+ condition_function *condition)
+{
+ run_abc_until(ctx, -1, NULL, -1, NULL, event, condition);
+ return ctx->c.last_event;
+}
+
+#define OPERATION(X, x) \
+int X(struct endpoint *ep, \
+ struct call *call, nua_handle_t *nh, \
+ tag_type_t tag, tag_value_t value, \
+ ...) \
+{ \
+ ta_list ta; \
+ ta_start(ta, tag, value); \
+\
+ if (ep->printer) \
+ ep->printer(-1, "nua_" #x, 0, "", ep->nua, ep->ctx, ep, \
+ nh, call, NULL, ta_args(ta)); \
+\
+ nua_##x(nh, ta_tags(ta)); \
+\
+ ta_end(ta); \
+ return 0; \
+} extern int dummy
+
+OPERATION(INVITE, invite);
+OPERATION(ACK, ack);
+OPERATION(BYE, bye);
+OPERATION(CANCEL, cancel);
+OPERATION(AUTHENTICATE, authenticate);
+OPERATION(UPDATE, update);
+OPERATION(INFO, info);
+OPERATION(PRACK, prack);
+OPERATION(REFER, refer);
+OPERATION(MESSAGE, message);
+OPERATION(METHOD, method);
+OPERATION(OPTIONS, options);
+OPERATION(PUBLISH, publish);
+OPERATION(UNPUBLISH, unpublish);
+OPERATION(REGISTER, register);
+OPERATION(UNREGISTER, unregister);
+OPERATION(SUBSCRIBE, subscribe);
+OPERATION(UNSUBSCRIBE, unsubscribe);
+OPERATION(NOTIFY, notify);
+OPERATION(NOTIFIER, notifier);
+OPERATION(TERMINATE, terminate);
+OPERATION(AUTHORIZE, authorize);
+
+/* Respond via endpoint and handle */
+int RESPOND(struct endpoint *ep,
+ struct call *call,
+ nua_handle_t *nh,
+ int status, char const *phrase,
+ tag_type_t tag, tag_value_t value,
+ ...)
+{
+ ta_list ta;
+
+ ta_start(ta, tag, value);
+
+ if (ep->printer)
+ ep->printer(-1, "nua_respond", status, phrase, ep->nua, ep->ctx, ep,
+ nh, call, NULL, ta_args(ta));
+
+ nua_respond(nh, status, phrase, ta_tags(ta));
+ ta_end(ta);
+
+ return 0;
+}
+
+/* Destroy a handle */
+int DESTROY(struct endpoint *ep,
+ struct call *call,
+ nua_handle_t *nh)
+{
+ if (ep->printer)
+ ep->printer(-1, "nua_handle_destroy", 0, "", ep->nua, ep->ctx, ep,
+ nh, call, NULL, NULL);
+
+ nua_handle_destroy(nh);
+
+ if (call->nh == nh)
+ call->nh = NULL;
+
+ return 0;
+}
+
+
+/* Reject all but currently used handles */
+struct call *check_handle(struct endpoint *ep,
+ struct call *call,
+ nua_handle_t *nh,
+ int status, char const *phrase)
+{
+ if (call)
+ return call;
+
+ if (status)
+ RESPOND(ep, call, nh, status, phrase, TAG_END());
+
+ nua_handle_destroy(nh);
+ return NULL;
+}
+
+/* Save nua event in call-specific list */
+int save_event_in_list(struct context *ctx,
+ nua_event_t nevent,
+ struct endpoint *ep,
+ struct call *call)
+
+{
+ struct eventlist *list;
+ struct event *e;
+ int action = ep->is_special(nevent);
+
+ if (action == event_is_extra)
+ return 0;
+ else if (action == event_is_special || call == NULL)
+ list = ep->specials;
+ else if (call->events)
+ list = call->events;
+ else
+ list = ep->events;
+
+ e = su_zalloc(ctx->home, sizeof *e);
+
+ if (!e) { perror("su_zalloc"), abort(); }
+
+ if (!nua_save_event(ep->nua, e->saved_event)) {
+ su_free(ctx->home, e);
+ return -1;
+ }
+
+ *(e->prev = list->tail) = e; list->tail = &e->next;
+
+ e->call = call;
+ e->data = nua_event_data(e->saved_event);
+
+ return action;
+}
+
+/* Free nua events from endpoint list */
+void free_events_in_list(struct context *ctx,
+ struct eventlist *list)
+{
+ struct event *e;
+
+ while ((e = list->head)) {
+ if ((*e->prev = e->next))
+ e->next->prev = e->prev;
+ nua_destroy_event(e->saved_event);
+ su_free(ctx->home, e);
+ }
+
+ list->tail = &list->head;
+}
+
+void free_event_in_list(struct context *ctx,
+ struct eventlist *list,
+ struct event *e)
+{
+ if (e) {
+ if ((*e->prev = e->next))
+ e->next->prev = e->prev;
+ nua_destroy_event(e->saved_event);
+ su_free(ctx->home, e);
+
+ if (list->head == NULL)
+ list->tail = &list->head;
+ }
+}
+
+struct event *event_by_type(struct event *e, nua_event_t etype)
+{
+ for (; e; e = e->next) {
+ if (e->data->e_event == etype)
+ break;
+ }
+
+ return e;
+}
+
+size_t count_events(struct event const *e)
+{
+ size_t n;
+
+ for (n = 0; e; e = e->next)
+ n++;
+
+ return n;
+}
+
+
+int is_special(nua_event_t e)
+{
+ if (e == nua_i_active || e == nua_i_terminated)
+ return event_is_extra;
+ if (e == nua_i_outbound)
+ return event_is_special;
+
+ return event_is_normal;
+}
+
+void
+endpoint_init(struct context *ctx, struct endpoint *e, char id)
+{
+ e->name[0] = id;
+ e->ctx = ctx;
+
+ e->is_special = is_special;
+
+ call_init(e->call);
+ call_init(e->reg);
+ eventlist_init(e->events);
+ eventlist_init(e->specials);
+}
+
+void nolog(void *stream, char const *fmt, va_list ap) {}
Added: freeswitch/trunk/libs/sofia-sip/tests/test_proxy.c
==============================================================================
--- (empty file)
+++ freeswitch/trunk/libs/sofia-sip/tests/test_proxy.c Wed May 14 15:10:54 2008
@@ -0,0 +1,1604 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2005 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at 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 test_proxy.c
+ * @brief Extremely simple proxy and registrar for testing nua
+ *
+ * @author Pekka Pessi <Pekka.Pessi at nokia.com>
+ *
+ * @date Created: Thu Nov 3 22:49:46 EET 2005
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+struct proxy;
+struct domain;
+union proxy_or_domain;
+struct proxy_tr;
+struct client_tr;
+struct registration_entry;
+struct binding;
+
+#define SU_ROOT_MAGIC_T struct proxy
+#define NTA_LEG_MAGIC_T union proxy_or_domain
+#define NTA_OUTGOING_MAGIC_T struct client_tr
+#define NTA_INCOMING_MAGIC_T struct proxy_tr
+#define SU_TIMER_ARG_T struct proxy_tr
+
+#include <sofia-sip/su_wait.h>
+#include <sofia-sip/nta.h>
+#include <sofia-sip/sip_header.h>
+#include <sofia-sip/sip_status.h>
+#include <sofia-sip/sip_util.h>
+#include <sofia-sip/auth_module.h>
+#include <sofia-sip/su_tagarg.h>
+#include <sofia-sip/msg_addr.h>
+#include <sofia-sip/hostdomain.h>
+#include <sofia-sip/tport.h>
+#include <sofia-sip/nta_tport.h>
+
+#include <stdlib.h>
+#include <assert.h>
+
+#define LIST_PROTOS(STORAGE, PREFIX, T) \
+STORAGE void PREFIX ##_insert(T **list, T *node), \
+ PREFIX ##_remove(T *node)
+
+#define LIST_BODIES(STORAGE, PREFIX, T, NEXT, PREV) \
+STORAGE void PREFIX ##_insert(T **list, T *node) \
+{ \
+ if ((node->NEXT = *list)) { \
+ node->PREV = node->NEXT->PREV; \
+ node->NEXT->PREV = &node->NEXT; \
+ } \
+ else \
+ node->PREV = list; \
+ *list = node; \
+} \
+STORAGE void PREFIX ##_remove(T *node) \
+{ \
+ if (node->PREV) \
+ if ((*node->PREV = node->NEXT)) \
+ node->NEXT->PREV = node->PREV; \
+ node->PREV = NULL; \
+} \
+extern int LIST_DUMMY_VARIABLE
+
+#include "test_proxy.h"
+#include <sofia-sip/auth_module.h>
+
+struct proxy {
+ su_home_t home[1];
+ void *magic;
+ su_root_t *parent;
+ su_clone_r clone;
+ tagi_t *tags;
+
+ su_root_t *root;
+
+ nta_agent_t *agent;
+ url_t const *uri;
+ sip_route_t *lr;
+ char const *lr_str;
+ url_t const *rr_uri;
+
+ nta_leg_t *defleg;
+
+ sip_contact_t *transport_contacts;
+
+ struct proxy_tr *stateless;
+ struct proxy_tr *transactions;
+ struct proxy_tr *invite_waiting;
+
+ struct domain *domains;
+
+ struct {
+ unsigned t1x64;
+ sip_time_t session_expires, min_se;
+ } prefs;
+};
+
+struct domain {
+ su_home_t home[1];
+ void *magic;
+ struct proxy *proxy;
+ struct domain *next, **prev;
+
+ url_t *uri;
+
+ nta_leg_t *rleg, *uleg;
+
+ auth_mod_t *auth;
+ struct registration_entry *entries;
+
+ struct {
+ sip_time_t min_expires, expires, max_expires;
+ int outbound_tcp; /**< Use inbound TCP connection as outbound */
+ char const *authorize; /**< Authorization realm to use */
+ int record_route;
+ } prefs;
+
+ tagi_t *tags;
+};
+
+LIST_PROTOS(static, domain, struct domain);
+static int _domain_init(void *_d);
+static int domain_init(struct domain *domain);
+static void domain_destroy(struct domain *domain);
+
+LIST_BODIES(static, domain, struct domain, next, prev);
+
+LIST_PROTOS(static, registration_entry, struct registration_entry);
+static struct registration_entry *registration_entry_new(struct domain *,
+ url_t const *);
+static void registration_entry_destroy(struct registration_entry *e);
+
+struct registration_entry
+{
+ struct registration_entry *next, **prev;
+ struct domain *domain; /* backpointer */
+ url_t *aor; /* address-of-record */
+ struct binding *bindings; /* list of bindings */
+ sip_contact_t *contacts;
+};
+
+struct binding
+{
+ struct binding *next, **prev;
+ sip_contact_t *contact; /* binding */
+ sip_time_t registered, expires; /* When registered and when expires */
+ sip_call_id_t *call_id;
+ uint32_t cseq;
+ tport_t *tport; /**< Reference to tport */
+};
+
+static struct binding *binding_new(su_home_t *home,
+ sip_contact_t *contact,
+ tport_t *tport,
+ sip_call_id_t const *call_id,
+ uint32_t cseq,
+ sip_time_t registered,
+ sip_time_t expires);
+static void binding_destroy(su_home_t *home, struct binding *b);
+static int binding_is_active(struct binding const *b)
+{
+ return
+ b->expires > sip_now() &&
+ (b->tport == NULL || tport_is_clear_to_send(b->tport));
+}
+
+LIST_PROTOS(static, proxy_tr, struct proxy_tr);
+struct proxy_tr *proxy_tr_new(struct proxy *);
+static void proxy_tr_timeout(struct proxy_tr *t);
+static void proxy_tr_destroy(struct proxy_tr *t);
+
+struct proxy_tr
+{
+ struct proxy_tr *next, **prev;
+
+ struct proxy *proxy; /**< Backpointer to proxy */
+
+ struct domain *origin; /**< Originating domain */
+ struct domain *domain; /**< Destination domain */
+
+ sip_time_t now; /**< When received */
+
+ nta_incoming_t *server; /**< server transaction */
+ msg_t *msg; /**< request message */
+ sip_t *sip; /**< request headers */
+
+ sip_method_t method; /**< request method */
+ char const *method_name;
+ int status; /**< best status */
+ url_t *target; /**< request-URI */
+
+ struct client_tr *clients; /**< Client transactions */
+
+ struct registration_entry *entry;
+ /**< Registration entry */
+
+ auth_mod_t *am; /**< Authentication module */
+ auth_status_t *as; /**< Authentication status */
+ char const *realm; /**< Authentication realm to use */
+ unsigned use_auth; /**< Authentication method (401/407) to use */
+
+ su_timer_t *timer; /**< Timer */
+
+ unsigned rr:1;
+};
+
+LIST_PROTOS(static, client_tr, struct client_tr);
+
+struct client_tr
+{
+ struct client_tr *next, **prev;
+ struct proxy_tr *t;
+
+ int status; /* response status */
+ sip_request_t *rq; /* request line */
+ msg_t *msg; /* request message */
+ sip_t *sip; /* request headers */
+ nta_outgoing_t *client; /* transaction */
+};
+
+LIST_BODIES(static, client_tr, struct client_tr, next, prev);
+
+static sip_contact_t *create_transport_contacts(struct proxy *p);
+
+union proxy_or_domain { struct proxy proxy[1]; struct domain domain[1]; };
+
+static int proxy_request(union proxy_or_domain *proxy,
+ nta_leg_t *leg,
+ nta_incoming_t *irq,
+ sip_t const *sip);
+
+static int domain_request(union proxy_or_domain *domain,
+ nta_leg_t *leg,
+ nta_incoming_t *irq,
+ sip_t const *sip);
+
+static int proxy_response(struct client_tr *client,
+ nta_outgoing_t *orq,
+ sip_t const *sip);
+
+static int close_tports(void *proxy);
+
+static auth_challenger_t registrar_challenger[1];
+static auth_challenger_t proxy_challenger[1];
+
+/* Proxy entry point */
+static int
+test_proxy_init(su_root_t *root, struct proxy *proxy)
+{
+ struct proxy_tr *t;
+ struct client_tr *c;
+
+ auth_challenger_t _proxy_challenger[1] =
+ {{
+ SIP_407_PROXY_AUTH_REQUIRED,
+ sip_proxy_authenticate_class,
+ sip_proxy_authentication_info_class
+ }};
+
+ auth_challenger_t _registrar_challenger[1] =
+ {{
+ SIP_401_UNAUTHORIZED,
+ sip_www_authenticate_class,
+ sip_authentication_info_class
+ }};
+
+ *proxy_challenger = *_proxy_challenger;
+ *registrar_challenger = *_registrar_challenger;
+
+ proxy->root = root;
+
+ proxy->agent = nta_agent_create(root,
+ URL_STRING_MAKE("sip:0.0.0.0:*"),
+ NULL, NULL,
+ NTATAG_UA(0),
+ NTATAG_CANCEL_487(0),
+ NTATAG_SERVER_RPORT(1),
+ NTATAG_CLIENT_RPORT(1),
+ TAG_NEXT(proxy->tags));
+
+ if (!proxy->agent)
+ return -1;
+
+ proxy->transport_contacts = create_transport_contacts(proxy);
+
+ proxy->defleg = nta_leg_tcreate(proxy->agent,
+ proxy_request,
+ (union proxy_or_domain *)proxy,
+ NTATAG_NO_DIALOG(1),
+ TAG_END());
+
+ proxy->prefs.session_expires = 180;
+ proxy->prefs.min_se = 90;
+ proxy->prefs.t1x64 = 64 * 500;
+
+ nta_agent_get_params(proxy->agent,
+ NTATAG_SIP_T1X64_REF(proxy->prefs.t1x64),
+ TAG_END());
+
+ if (!proxy->defleg)
+ return -1;
+ /* if (!proxy->example_net || !proxy->example_org || !proxy->example_com)
+ return -1; */
+
+ /* Create stateless client */
+ t = su_zalloc(proxy->home, sizeof *t);
+ c = su_zalloc(proxy->home, sizeof *c);
+
+ if (!t || !c)
+ return -1;
+
+ proxy->stateless = t;
+ t->proxy = proxy;
+ c->t = t, client_tr_insert(&t->clients, c);
+ t->server = nta_incoming_default(proxy->agent);
+ c->client = nta_outgoing_default(proxy->agent, proxy_response, c);
+
+ if (!c->client || !t->server)
+ return -1;
+
+ proxy->uri = nta_agent_contact(proxy->agent)->m_url;
+ proxy->lr_str = su_sprintf(proxy->home, "<" URL_PRINT_FORMAT ";lr>", URL_PRINT_ARGS(proxy->uri));
+ proxy->lr = sip_route_make(proxy->home, proxy->lr_str);
+
+ if (!proxy->lr)
+ return -1;
+
+ return 0;
+}
+
+static void
+test_proxy_deinit(su_root_t *root, struct proxy *proxy)
+{
+ struct proxy_tr *t;
+
+ while (proxy->transactions)
+ proxy_tr_destroy(proxy->transactions);
+
+ if ((t = proxy->stateless)) {
+ proxy->stateless = NULL;
+ proxy_tr_destroy(t);
+ }
+
+ while (proxy->domains)
+ domain_destroy(proxy->domains);
+
+ nta_agent_destroy(proxy->agent);
+
+ free(proxy->tags);
+}
+
+/* Create test proxy object */
+struct proxy *test_proxy_create(su_root_t *root,
+ tag_type_t tag, tag_value_t value, ...)
+{
+ struct proxy *p = su_home_new(sizeof *p);
+
+ if (p) {
+ ta_list ta;
+
+ p->magic = test_proxy_create;
+
+ p->parent = root;
+
+ ta_start(ta, tag, value);
+ p->tags = tl_llist(ta_tags(ta));
+ ta_end(ta);
+
+ if (su_clone_start(root,
+ p->clone,
+ p,
+ test_proxy_init,
+ test_proxy_deinit) == -1)
+ su_home_unref(p->home), p = NULL;
+ }
+
+ return p;
+}
+
+/* Destroy the proxy object */
+void test_proxy_destroy(struct proxy *p)
+{
+ if (p) {
+ su_clone_wait(p->parent, p->clone);
+ su_home_unref(p->home);
+ }
+}
+
+/* Return the proxy URI */
+url_t const *test_proxy_uri(struct proxy const *p)
+{
+ return p ? p->uri : NULL;
+}
+
+/* Return the proxy route URI */
+char const *test_proxy_route_uri(struct proxy const *p,
+ sip_route_t const **return_route)
+{
+ if (p == NULL)
+ return NULL;
+
+ if (return_route)
+ *return_route = p->lr;
+
+ return p->lr_str;
+}
+
+struct _set_logging {
+ struct proxy *p;
+ int logging;
+};
+
+static int _set_logging(void *_a)
+{
+ struct _set_logging *a = _a;
+ return nta_agent_set_params(a->p->agent, TPTAG_LOG(a->logging), TAG_END());
+}
+
+void test_proxy_set_logging(struct proxy *p, int logging)
+{
+ if (p) {
+ struct _set_logging a[1] = {{ p, logging }};
+ su_task_execute(su_clone_task(p->clone), _set_logging, a, NULL);
+ }
+}
+
+void test_proxy_domain_set_expiration(struct domain *d,
+ sip_time_t min_expires,
+ sip_time_t expires,
+ sip_time_t max_expires)
+{
+ if (d) {
+ d->prefs.min_expires = min_expires;
+ d->prefs.expires = expires;
+ d->prefs.max_expires = max_expires;
+ }
+}
+
+void test_proxy_domain_get_expiration(struct domain *d,
+ sip_time_t *return_min_expires,
+ sip_time_t *return_expires,
+ sip_time_t *return_max_expires)
+{
+ if (d) {
+ if (return_min_expires) *return_min_expires = d->prefs.min_expires;
+ if (return_expires) *return_expires = d->prefs.expires;
+ if (return_max_expires) *return_max_expires = d->prefs.max_expires;
+ }
+}
+
+void test_proxy_set_session_timer(struct proxy *p,
+ sip_time_t session_expires,
+ sip_time_t min_se)
+{
+ if (p) {
+ p->prefs.session_expires = session_expires;
+ p->prefs.min_se = min_se;
+ }
+}
+
+void test_proxy_get_session_timer(struct proxy *p,
+ sip_time_t *return_session_expires,
+ sip_time_t *return_min_se)
+{
+ if (p) {
+ if (return_session_expires)
+ *return_session_expires = p->prefs.session_expires;
+ if (return_min_se) *return_min_se = p->prefs.min_se;
+ }
+}
+
+void test_proxy_domain_set_outbound(struct domain *d,
+ int use_outbound)
+{
+ if (d) {
+ d->prefs.outbound_tcp = use_outbound;
+ }
+}
+
+void test_proxy_domain_get_outbound(struct domain *d,
+ int *return_use_outbound)
+{
+ if (d) {
+ if (return_use_outbound)
+ *return_use_outbound = d->prefs.outbound_tcp;
+ }
+}
+
+void test_proxy_domain_set_record_route(struct domain *d,
+ int use_record_route)
+{
+ if (d) {
+ d->prefs.record_route = use_record_route;
+ }
+}
+
+void test_proxy_domain_get_record_route(struct domain *d,
+ int *return_use_record_route)
+{
+ if (d) {
+ if (return_use_record_route)
+ *return_use_record_route = d->prefs.record_route;
+ }
+}
+
+int test_proxy_domain_set_authorize(struct domain *d,
+ char const *realm)
+{
+ if (d) {
+ if (realm) {
+ realm = su_strdup(d->home, realm);
+ if (!realm)
+ return -1;
+ }
+
+ d->prefs.authorize = realm;
+
+ return 0;
+ }
+ return -1;
+}
+
+int test_proxy_domain_get_authorize(struct domain *d,
+ char const **return_realm)
+{
+ if (d) {
+ if (return_realm) {
+ *return_realm = d->prefs.authorize;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int test_proxy_close_tports(struct proxy *p)
+{
+ if (p) {
+ int retval = -EPROTO;
+
+ su_task_execute(su_clone_task(p->clone), close_tports, p, &retval);
+
+ if (retval < 0)
+ return errno = -retval, -1;
+ else
+ return 0;
+ }
+ return errno = EFAULT, -1;
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct domain *test_proxy_add_domain(struct proxy *p,
+ url_t const *uri,
+ tag_type_t tag, tag_value_t value, ...)
+{
+ struct domain *d;
+
+ if (p == NULL || uri == NULL)
+ return NULL;
+
+ d = su_home_clone(p->home, sizeof *d);
+
+ if (d) {
+ ta_list ta;
+ int init = 0;
+
+ ta_start(ta, tag, value);
+
+ d->magic = domain_init;
+
+ d->proxy = p;
+ d->uri = url_hdup(d->home, uri);
+ d->tags = tl_adup(d->home, ta_args(ta));
+
+ d->prefs.min_expires = 300;
+ d->prefs.expires = 3600;
+ d->prefs.max_expires = 36000;
+ d->prefs.outbound_tcp = 0;
+ d->prefs.authorize = NULL;
+
+ if (d->uri && d->tags &&
+ !su_task_execute(su_clone_task(p->clone), _domain_init, d, &init)) {
+ if (init == 0)
+ /* OK */;
+ else
+ d = NULL;
+ }
+ else
+ su_home_unref(d->home);
+ }
+
+ return d;
+}
+
+static int _domain_init(void *_d)
+{
+ return domain_init(_d);
+}
+
+static int domain_init(struct domain *d)
+{
+ struct proxy *p = d->proxy;
+ url_t uri[1];
+
+ *uri = *d->uri;
+
+ d->auth = auth_mod_create(p->root, TAG_NEXT(d->tags));
+
+ /* Leg for URIs without userpart */
+ d->rleg = nta_leg_tcreate(d->proxy->agent,
+ domain_request,
+ (union proxy_or_domain *)d,
+ NTATAG_NO_DIALOG(1),
+ URLTAG_URL(uri),
+ TAG_END());
+
+ /* Leg for URIs with wildcard userpart */
+ uri->url_user = "%";
+ d->uleg = nta_leg_tcreate(d->proxy->agent,
+ domain_request,
+ (union proxy_or_domain *)d,
+ NTATAG_NO_DIALOG(1),
+ URLTAG_URL(uri),
+ TAG_END());
+
+ if (d->auth && d->rleg && d->uleg) {
+ domain_insert(&p->domains, d);
+ return 0;
+ }
+
+ domain_destroy(d);
+
+ return -1;
+}
+
+static void domain_destroy(struct domain *d)
+{
+ while (d->entries)
+ registration_entry_destroy(d->entries);
+
+ nta_leg_destroy(d->rleg), d->rleg = NULL;
+ nta_leg_destroy(d->uleg), d->uleg = NULL;
+ auth_mod_destroy(d->auth), d->auth = NULL;
+
+ domain_remove(d);
+
+ su_home_unref(d->home);
+}
+
+/* ---------------------------------------------------------------------- */
+
+static sip_contact_t *create_transport_contacts(struct proxy *p)
+{
+ su_home_t *home = p->home;
+ sip_via_t *v;
+ sip_contact_t *retval = NULL, **mm = &retval;
+
+ if (!p->agent)
+ return NULL;
+
+ for (v = nta_agent_via(p->agent); v; v = v->v_next) {
+ char const *proto = v->v_protocol;
+
+ if (v->v_next &&
+ strcasecmp(v->v_host, v->v_next->v_host) == 0 &&
+ str0cmp(v->v_port, v->v_next->v_port) == 0 &&
+ ((proto == sip_transport_udp &&
+ v->v_next->v_protocol == sip_transport_tcp) ||
+ (proto == sip_transport_tcp &&
+ v->v_next->v_protocol == sip_transport_udp)))
+ /* We have udp/tcp pair, insert URL without tport parameter */
+ *mm = sip_contact_create_from_via_with_transport(home, v, NULL, NULL);
+ if (*mm) mm = &(*mm)->m_next;
+
+ *mm = sip_contact_create_from_via_with_transport(home, v, NULL, proto);
+
+ if (*mm) mm = &(*mm)->m_next;
+ }
+
+ return retval;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int proxy_tr_with(struct proxy *proxy,
+ struct domain *domain,
+ nta_incoming_t *irq,
+ sip_t const *sip,
+ int (*process)(struct proxy_tr *));
+static int proxy_transaction(struct proxy_tr *t);
+static int respond_transaction(struct proxy_tr *t,
+ int status, char const *phrase,
+ tag_type_t tag, tag_value_t value,
+ ...);
+static int validate_transaction(struct proxy_tr *t);
+static int originating_transaction(struct proxy_tr *t);
+static int challenge_transaction(struct proxy_tr *t);
+static int session_timers(struct proxy_tr *t);
+static int incoming_transaction(struct proxy_tr *t);
+static int target_transaction(struct proxy_tr *t,
+ url_t const *target,
+ tport_t *tport);
+static int process_register(struct proxy_tr *t);
+static int process_options(struct proxy_tr *t);
+
+static int proxy_ack_cancel(struct proxy_tr *t,
+ nta_incoming_t *irq,
+ sip_t const *sip);
+
+static struct registration_entry *
+registration_entry_find(struct domain const *domain, url_t const *uri);
+
+static int proxy_request(union proxy_or_domain *pod,
+ nta_leg_t *leg,
+ nta_incoming_t *irq,
+ sip_t const *sip)
+{
+ assert(pod->proxy->magic = test_proxy_init);
+
+ return proxy_tr_with(pod->proxy, NULL, irq, sip, proxy_transaction);
+}
+
+static int domain_request(union proxy_or_domain *pod,
+ nta_leg_t *leg,
+ nta_incoming_t *irq,
+ sip_t const *sip)
+{
+ int (*process)(struct proxy_tr *) = NULL;
+ sip_method_t method = sip->sip_request->rq_method;
+
+ assert(pod->domain->magic = domain_init);
+
+ if (leg == pod->domain->uleg)
+ process = proxy_transaction;
+ else if (method == sip_method_register)
+ process = process_register;
+ else if (method == sip_method_options)
+ process = process_options;
+
+ if (process == NULL)
+ return 501; /* Not implemented */
+
+ return proxy_tr_with(pod->domain->proxy, pod->domain, irq, sip, process);
+}
+
+static int proxy_tr_with(struct proxy *proxy,
+ struct domain *domain,
+ nta_incoming_t *irq,
+ sip_t const *sip,
+ int (*process)(struct proxy_tr *))
+{
+ struct proxy_tr *t = NULL;
+ int status = 500;
+
+ assert(proxy->magic = test_proxy_init);
+
+ t = proxy_tr_new(proxy);
+ if (t) {
+ t->proxy = proxy, t->domain = domain, t->server = irq;
+ t->msg = nta_incoming_getrequest(irq);
+ t->sip = sip_object(t->msg);
+
+ t->method = sip->sip_request->rq_method;
+ t->method_name = sip->sip_request->rq_method_name;
+ t->target = sip->sip_request->rq_url;
+ t->now = nta_incoming_received(irq, NULL);
+
+ if (t->method != sip_method_ack && t->method != sip_method_cancel)
+ nta_incoming_bind(irq, proxy_ack_cancel, t);
+
+ if (domain && domain->prefs.record_route)
+ t->rr = 1;
+
+ if (process(t) < 200)
+ return 0;
+
+ proxy_tr_destroy(t);
+ }
+ else {
+ nta_incoming_treply(irq, SIP_500_INTERNAL_SERVER_ERROR, TAG_END());
+ }
+
+ return status;
+}
+
+/** Forward request */
+static int proxy_transaction(struct proxy_tr *t)
+{
+ if (originating_transaction(t))
+ return t->status;
+
+ if (validate_transaction(t))
+ return t->status;
+
+ if (session_timers(t))
+ return t->status;
+
+ if (t->domain)
+ return incoming_transaction(t);
+
+ return target_transaction(t, t->target, NULL);
+}
+
+static int respond_transaction(struct proxy_tr *t,
+ int status, char const *phrase,
+ tag_type_t tag, tag_value_t value,
+ ...)
+{
+ ta_list ta;
+ void *info = NULL, *response = NULL;
+
+ ta_start(ta, tag, value);
+
+ if (t->as)
+ info = t->as->as_info, response = t->as->as_response;
+
+ if (nta_incoming_treply(t->server, t->status = status, phrase,
+ SIPTAG_HEADER(info),
+ SIPTAG_HEADER(response),
+ ta_tags(ta)) < 0)
+ t->status = status = 500;
+
+ ta_end(ta);
+
+ return status;
+}
+
+static int originating_transaction(struct proxy_tr *t)
+{
+ struct domain *o;
+ char const *host;
+
+ host = t->sip->sip_from->a_url->url_host;
+ if (!host)
+ return 0;
+
+ for (o = t->proxy->domains; o; o = o->next)
+ if (strcasecmp(host, o->uri->url_host) == 0)
+ break;
+
+ t->origin = o;
+
+ if (o && o->auth && o->prefs.authorize) {
+ t->am = o->auth;
+ t->realm = o->prefs.authorize;
+ t->use_auth = 407;
+ }
+
+ if (o && o->prefs.record_route)
+ t->rr = 1;
+
+ return 0;
+}
+
+static int validate_transaction(struct proxy_tr *t)
+{
+ sip_max_forwards_t *mf;
+
+ mf = t->sip->sip_max_forwards;
+
+ if (mf && mf->mf_count <= 1) {
+ if (t->method == sip_method_options)
+ return process_options(t);
+
+ return respond_transaction(t, SIP_483_TOO_MANY_HOPS, TAG_END());
+ }
+
+ /* Remove our routes */
+ while (t->sip->sip_route &&
+ url_has_param(t->sip->sip_route->r_url, "lr") &&
+ (url_cmp(t->proxy->lr->r_url, t->sip->sip_route->r_url) == 0 ||
+ url_cmp(t->proxy->rr_uri, t->sip->sip_route->r_url) == 0)) {
+ sip_route_remove(t->msg, t->sip);
+ /* add record-route also to the forwarded request */
+ }
+
+ if (t->use_auth)
+ return challenge_transaction(t);
+
+ return 0;
+}
+
+static int session_timers(struct proxy_tr *t)
+{
+ sip_t *sip = t->sip;
+ sip_session_expires_t *x = NULL, x0[1];
+ sip_min_se_t *min_se = NULL, min_se0[1];
+ char const *require = NULL;
+
+ if (t->method == sip_method_invite) {
+ if (t->proxy->prefs.min_se) {
+ if (!sip->sip_min_se ||
+ sip->sip_min_se->min_delta < t->proxy->prefs.min_se) {
+ min_se = sip_min_se_init(min_se0);
+ min_se->min_delta = t->proxy->prefs.min_se;
+ }
+
+ if (sip->sip_session_expires
+ && sip->sip_session_expires->x_delta < t->proxy->prefs.min_se
+ && sip_has_supported(sip->sip_supported, "timer")) {
+ if (min_se == NULL)
+ min_se = sip->sip_min_se; assert(min_se);
+ return respond_transaction(t, SIP_422_SESSION_TIMER_TOO_SMALL,
+ SIPTAG_MIN_SE(min_se),
+ TAG_END());
+ }
+ }
+
+ if (t->proxy->prefs.session_expires) {
+ if (!sip->sip_session_expires ||
+ sip->sip_session_expires->x_delta > t->proxy->prefs.session_expires) {
+ x = sip_session_expires_init(x0);
+ x->x_delta = t->proxy->prefs.session_expires;
+ if (!sip_has_supported(sip->sip_supported, "timer"))
+ require = "timer";
+ }
+ }
+
+ if (x || min_se || require)
+ sip_add_tl(t->msg, t->sip,
+ SIPTAG_REQUIRE_STR(require),
+ SIPTAG_MIN_SE(min_se),
+ SIPTAG_SESSION_EXPIRES(x),
+ TAG_END());
+ }
+
+ return 0;
+}
+
+static int incoming_transaction(struct proxy_tr *t)
+{
+ struct registration_entry *e;
+ struct binding *b;
+
+#if 0
+ if (sip->sip_request->rq_method == sip_method_register)
+ return process_register(proxy, irq, sip);
+#endif
+
+ t->entry = e = registration_entry_find(t->domain, t->target);
+ if (e == NULL)
+ return respond_transaction(t, SIP_404_NOT_FOUND, TAG_END());
+
+ for (b = e->bindings; b; b = b->next) {
+ if (binding_is_active(b))
+ target_transaction(t, b->contact->m_url, b->tport);
+
+ if (t->clients) /* XXX - enable forking */
+ break;
+ }
+
+ if (t->clients != NULL)
+ return 0;
+
+ return respond_transaction(t, SIP_480_TEMPORARILY_UNAVAILABLE, TAG_END());
+}
+
+static int target_transaction(struct proxy_tr *t,
+ url_t const *target,
+ tport_t *tport)
+{
+ struct client_tr *c = su_zalloc(t->proxy->home, sizeof *c);
+ int stateless = t->method == sip_method_ack;
+
+ if (c == NULL)
+ return 500;
+
+ c->t = t;
+ c->msg = msg_copy(t->msg);
+ c->sip = sip_object(c->msg);
+
+ if (c->msg)
+ c->rq = sip_request_create(msg_home(c->msg),
+ t->method, t->method_name,
+ (url_string_t *)target,
+ NULL);
+
+ msg_header_insert(c->msg, (msg_pub_t *)c->sip, (msg_header_t *)c->rq);
+
+ if (t->rr) {
+ sip_record_route_t rr[1];
+
+ if (t->proxy->rr_uri) {
+ *sip_record_route_init(rr)->r_url = *t->proxy->rr_uri;
+ msg_header_add_dup(c->msg, (msg_pub_t *)c->sip, (msg_header_t *)rr);
+ }
+ else if (t->proxy->lr) {
+ *sip_record_route_init(rr)->r_url = *t->proxy->lr->r_url;
+ msg_header_add_dup(c->msg, (msg_pub_t *)c->sip, (msg_header_t *)rr);
+ }
+ }
+
+ if (c->rq)
+ /* Forward request */
+ c->client = nta_outgoing_mcreate(t->proxy->agent,
+ proxy_response, c,
+ NULL,
+ msg_ref_create(c->msg),
+ NTATAG_TPORT(tport),
+ NTATAG_STATELESS(stateless),
+ TAG_END());
+
+ if (!c->client) {
+ msg_destroy(c->msg);
+ su_free(t->proxy->home, c);
+ return 500;
+ }
+
+ client_tr_insert(&t->clients, c);
+
+ return stateless ? 200 : 0;
+}
+
+static int challenge_transaction(struct proxy_tr *t)
+{
+ auth_status_t *as;
+ sip_t *sip = t->sip;
+
+ assert(t->am);
+
+ t->as = as = auth_status_new(t->proxy->home);
+ if (!as)
+ return respond_transaction(t, SIP_500_INTERNAL_SERVER_ERROR, TAG_END());
+
+ as->as_method = sip->sip_request->rq_method_name;
+ as->as_source = msg_addrinfo(t->msg);
+ as->as_realm = t->realm;
+
+ as->as_user_uri = sip->sip_from->a_url;
+ as->as_display = sip->sip_from->a_display;
+
+ if (sip->sip_payload)
+ as->as_body = sip->sip_payload->pl_data,
+ as->as_bodylen = sip->sip_payload->pl_len;
+
+ if (t->use_auth == 401)
+ auth_mod_check_client(t->am, as, sip->sip_authorization,
+ registrar_challenger);
+ else
+ auth_mod_check_client(t->am, as, sip->sip_proxy_authorization,
+ proxy_challenger);
+
+ if (as->as_status)
+ return respond_transaction(t, as->as_status, as->as_phrase, TAG_END());
+
+ if (as->as_match)
+ msg_header_remove(t->msg, (msg_pub_t *)sip, as->as_match);
+
+ return 0;
+}
+
+int proxy_ack_cancel(struct proxy_tr *t,
+ nta_incoming_t *irq,
+ sip_t const *sip)
+{
+ struct client_tr *c;
+ int status;
+
+ if (sip == NULL) { /* timeout */
+ proxy_tr_destroy(t);
+ return 0;
+ }
+
+ if (sip->sip_request->rq_method != sip_method_cancel)
+ return 500;
+
+ status = 200;
+
+ for (c = t->clients; c; c = c->next) {
+ if (c->client && c->status < 200)
+ /*
+ * We don't care about response to CANCEL (or ACK)
+ * so we give NULL as callback pointer (and nta immediately
+ * destroys transaction object or marks it disposable)
+ */
+ if (nta_outgoing_tcancel(c->client, NULL, NULL, TAG_END()) == NULL)
+ status = 500;
+ }
+
+ return status;
+}
+
+int proxy_response(struct client_tr *c,
+ nta_outgoing_t *client,
+ sip_t const *sip)
+{
+ int final, timeout = 0;
+
+ assert(c->t);
+
+ if (sip) {
+ msg_t *response = nta_outgoing_getresponse(client);
+ if (c->t->method == sip_method_invite)
+ final = sip->sip_status->st_status >= 300,
+ timeout = sip->sip_status->st_status >= 200;
+ else
+ final = sip->sip_status->st_status >= 200;
+ sip_via_remove(response, sip_object(response));
+ nta_incoming_mreply(c->t->server, response);
+ }
+ else {
+ int status = nta_outgoing_status(c->client);
+ char const *phrase;
+
+ if (status < 300 || status > 699)
+ status = 500;
+ phrase = sip_status_phrase(status);
+ respond_transaction(c->t, status, phrase, TAG_END());
+ final = 1;
+ }
+
+ if (final)
+ proxy_tr_destroy(c->t);
+ else if (timeout)
+ proxy_tr_timeout(c->t);
+
+ return 0;
+}
+
+int proxy_late_response(struct client_tr *c,
+ nta_outgoing_t *client,
+ sip_t const *sip)
+{
+ assert(c->t);
+
+ if (sip &&
+ sip->sip_status->st_status >= 200 &&
+ sip->sip_status->st_status < 300) {
+ msg_t *response = nta_outgoing_getresponse(client);
+ sip_via_remove(response, sip_object(response));
+ nta_incoming_mreply(c->t->server, response);
+ }
+
+ return 0;
+}
+
+static void proxy_tr_remove_late(su_root_magic_t *magic,
+ su_timer_t *timer,
+ struct proxy_tr *t)
+{
+ proxy_tr_destroy(t);
+}
+
+
+/** Proxy only late responses
+ *
+ * Keeping the invite transactions live
+ */
+static void proxy_tr_timeout(struct proxy_tr *t)
+{
+ struct client_tr *c;
+
+ if (t == t->proxy->stateless)
+ return;
+
+ for (c = t->clients; c; c = c->next) {
+ if (c->client && c->status < 300) {
+ nta_outgoing_bind(c->client, proxy_late_response, c);
+ if (c->status < 200) {
+ nta_outgoing_tcancel(c->client, NULL, NULL, TAG_END());
+ }
+ }
+ }
+
+ t->timer = su_timer_create(su_root_task(t->proxy->root), t->proxy->prefs.t1x64);
+ if (su_timer_set(t->timer, proxy_tr_remove_late, t) < 0) {
+ proxy_tr_destroy(t);
+ }
+}
+
+struct proxy_tr *
+proxy_tr_new(struct proxy *proxy)
+{
+ struct proxy_tr *t;
+
+ t = su_zalloc(proxy->home, sizeof *t);
+ if (t) {
+ t->proxy = proxy;
+ proxy_tr_insert(&proxy->transactions, t);
+ }
+ return t;
+}
+
+static
+void proxy_tr_destroy(struct proxy_tr *t)
+{
+ struct client_tr *c;
+
+ if (t == t->proxy->stateless)
+ return;
+
+ proxy_tr_remove(t);
+
+ if (t->as)
+ su_home_unref(t->as->as_home), t->as = NULL;
+
+ while (t->clients) {
+ client_tr_remove(c = t->clients);
+ nta_outgoing_destroy(c->client), c->client = NULL;
+ msg_destroy(c->msg), c->msg = NULL;
+ su_free(t->proxy->home, c);
+ }
+
+ su_timer_destroy(t->timer), t->timer = NULL;
+
+ msg_destroy(t->msg);
+
+ nta_incoming_destroy(t->server);
+
+ su_free(t->proxy->home, t);
+}
+
+LIST_BODIES(static, proxy_tr, struct proxy_tr, next, prev);
+
+/* ---------------------------------------------------------------------- */
+
+static int process_options(struct proxy_tr *t)
+{
+ return respond_transaction(t, SIP_200_OK,
+ SIPTAG_CONTACT(t->proxy->transport_contacts),
+ TAG_END());
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int check_received_contact(struct proxy_tr *t);
+static int validate_contacts(struct proxy_tr *t);
+static int check_out_of_order(struct proxy_tr *t);
+static int update_bindings(struct proxy_tr *t);
+
+int process_register(struct proxy_tr *t)
+{
+ /* This is before authentication because we want to be bug-compatible */
+ if (check_received_contact(t))
+ return t->status;
+
+ if (t->domain->auth) {
+ t->am = t->domain->auth, t->use_auth = 401;
+ if (t->domain->prefs.authorize)
+ t->realm = t->domain->prefs.authorize;
+ if (challenge_transaction(t))
+ return t->status;
+ }
+
+ if (validate_contacts(t))
+ return t->status;
+
+ t->entry = registration_entry_find(t->domain, t->sip->sip_to->a_url);
+
+ if (check_out_of_order(t))
+ return t->status;
+
+ return update_bindings(t);
+}
+
+static int check_received_contact(struct proxy_tr *t)
+{
+ sip_t *sip = t->sip;
+ sip_contact_t *m = sip->sip_contact;
+ sip_via_t *v = sip->sip_via;
+
+ if (m && v && v->v_received && m->m_url->url_host
+ && strcasecmp(v->v_received, m->m_url->url_host)
+ && host_is_ip_address(m->m_url->url_host))
+ return respond_transaction(t, 406, "Unacceptable Contact", TAG_END());
+
+ return 0;
+}
+
+/* Validate expiration times */
+static int validate_contacts(struct proxy_tr *t)
+{
+ sip_contact_t const *m = t->sip->sip_contact;
+ sip_expires_t const *ex = t->sip->sip_expires;
+ sip_date_t const *date = t->sip->sip_date;
+ sip_time_t expires;
+
+ if (m && m->m_url->url_type == url_any) {
+ if (!ex || ex->ex_delta || ex->ex_time || m->m_next)
+ return respond_transaction(t, SIP_400_BAD_REQUEST, TAG_END());
+ return 0;
+ }
+
+ for (; m; m = m->m_next) {
+ expires = sip_contact_expires(m, ex, date, t->domain->prefs.expires, t->now);
+
+ if (expires > 0 && expires < t->domain->prefs.min_expires) {
+ sip_min_expires_t me[1];
+
+ sip_min_expires_init(me)->me_delta = t->domain->prefs.min_expires;
+
+ return respond_transaction(t, SIP_423_INTERVAL_TOO_BRIEF,
+ SIPTAG_MIN_EXPIRES(me),
+ TAG_END());
+ }
+ }
+
+ return 0;
+}
+
+/** Check for out-of-order register request */
+static int check_out_of_order(struct proxy_tr *t)
+{
+ struct binding const *b;
+ sip_call_id_t const *id = t->sip->sip_call_id;
+ uint32_t cseq = t->sip->sip_cseq->cs_seq;
+ sip_contact_t *m;
+
+ if (t->entry == NULL || !t->sip->sip_contact)
+ return 0;
+
+ /* RFC 3261 subsection 10.3 step 6 and step 7 (p. 66): */
+ /* Check for reordered register requests */
+ for (b = t->entry->bindings; b; b = b->next) {
+ if (binding_is_active(b) &&
+ strcmp(id->i_id, b->call_id->i_id) == 0 &&
+ cseq <= b->cseq) {
+ for (m = t->sip->sip_contact; m; m = m->m_next) {
+ if (m->m_url->url_type == url_any ||
+ url_cmp_all(m->m_url, b->contact->m_url) == 0)
+ return respond_transaction(t, SIP_500_INTERNAL_SERVER_ERROR,
+ TAG_END());
+ }
+ }
+ }
+
+ return 0;
+}
+
+static struct registration_entry *
+registration_entry_find(struct domain const *d, url_t const *uri)
+{
+ struct registration_entry *e;
+
+ /* Our routing table */
+ for (e = d->entries; e; e = e->next) {
+ if (url_cmp(uri, e->aor) == 0)
+ return e;
+ }
+
+ return NULL;
+}
+
+static struct registration_entry *
+registration_entry_new(struct domain *d, url_t const *aor)
+{
+ struct registration_entry *e;
+
+ if (d == NULL)
+ return NULL;
+
+ e = su_zalloc(d->home, sizeof *e);
+ if (!e)
+ return NULL;
+
+ e->domain = d;
+ e->aor = url_hdup(d->home, aor);
+ if (!e->aor) {
+ su_free(d->home, e);
+ return NULL;
+ }
+
+ registration_entry_insert(&d->entries, e);
+
+ return e;
+}
+
+static void
+registration_entry_destroy(struct registration_entry *e)
+{
+ if (e) {
+ registration_entry_remove(e);
+ su_free(e->domain->home, e->aor);
+ while (e->bindings)
+ binding_destroy(e->domain->home, e->bindings);
+ msg_header_free(e->domain->home, (void *)e->contacts);
+ su_free(e->domain->home, e);
+ }
+}
+
+sip_contact_t *entry_contacts(struct registration_entry *entry)
+{
+ return entry ? entry->contacts : NULL;
+}
+
+LIST_BODIES(static, registration_entry, struct registration_entry, next, prev);
+
+/* ---------------------------------------------------------------------- */
+/* Bindings */
+
+static
+struct binding *binding_new(su_home_t *home,
+ sip_contact_t *contact,
+ tport_t *tport,
+ sip_call_id_t const *call_id,
+ uint32_t cseq,
+ sip_time_t registered,
+ sip_time_t expires)
+{
+ struct binding *b;
+
+ b = su_zalloc(home, sizeof *b);
+
+ if (b) {
+ sip_contact_t m[1];
+ *m = *contact; m->m_next = NULL;
+
+ b->contact = sip_contact_dup(home, m);
+ b->tport = tport_ref(tport);
+ b->call_id = sip_call_id_dup(home, call_id);
+ b->cseq = cseq;
+ b->registered = registered;
+ b->expires = expires;
+
+ if (!b->contact || !b->call_id)
+ binding_destroy(home, b), b = NULL;
+
+ if (b)
+ msg_header_remove_param(b->contact->m_common, "expires");
+ }
+
+ return b;
+}
+
+static
+void binding_destroy(su_home_t *home, struct binding *b)
+{
+ if (b->prev) {
+ if ((*b->prev = b->next))
+ b->next->prev = b->prev;
+ }
+ msg_header_free(home, (void *)b->contact);
+ msg_header_free(home, (void *)b->call_id);
+ tport_unref(b->tport);
+ su_free(home, b);
+}
+
+static int update_bindings(struct proxy_tr *t)
+{
+ struct domain *d = t->domain;
+ struct binding *b, *old, *next, *last, *bindings = NULL, **bb = &bindings;
+ sip_contact_t *m;
+ sip_call_id_t const *id = t->sip->sip_call_id;
+ uint32_t cseq = t->sip->sip_cseq->cs_seq;
+ sip_expires_t *ex = t->sip->sip_expires;
+ sip_date_t *date = t->sip->sip_date;
+ sip_time_t expires;
+ tport_t *tport = NULL;
+ sip_contact_t *contacts = NULL, **mm = &contacts;
+ void *tbf;
+
+ if (t->sip->sip_contact == NULL) {
+ if (t->entry)
+ contacts = t->entry->contacts;
+ goto ok200;
+ }
+
+ if (t->entry == NULL)
+ t->entry = registration_entry_new(d, t->sip->sip_to->a_url);
+ if (t->entry == NULL)
+ return respond_transaction(t, SIP_500_INTERNAL_SERVER_ERROR, TAG_END());
+
+ if (d->prefs.outbound_tcp &&
+ str0casecmp(t->sip->sip_via->v_protocol, sip_transport_tcp) == 0)
+ tport = nta_incoming_transport(t->proxy->agent, t->server, NULL);
+
+ /* Create new bindings */
+ for (m = t->sip->sip_contact; m; m = m->m_next) {
+ if (m->m_url->url_type == url_any)
+ break;
+
+ expires = sip_contact_expires(m, ex, date, d->prefs.expires, t->now);
+
+ if (expires > d->prefs.max_expires)
+ expires = d->prefs.max_expires;
+
+ msg_header_remove_param(m->m_common, "expires");
+
+ b = binding_new(d->home, m, tport, id, cseq, t->now, t->now + expires);
+ if (!b)
+ break;
+
+ *bb = b, b->prev = bb, bb = &b->next;
+ }
+
+ tport_unref(tport);
+
+ last = NULL;
+
+ if (m == NULL) {
+ /* Merge new bindings with old ones */
+ for (old = t->entry->bindings; old; old = next) {
+ next = old->next;
+
+ for (b = bindings; b != last; b = b->next) {
+ if (url_cmp_all(old->contact->m_url, b->contact->m_url) != 0)
+ continue;
+
+ if (strcmp(old->call_id->i_id, b->call_id->i_id) == 0) {
+ b->registered = old->registered;
+ }
+ binding_destroy(d->home, old);
+ break;
+ }
+ }
+
+ for (bb = &t->entry->bindings; *bb; bb = &(*bb)->next)
+ ;
+
+ if ((*bb = bindings))
+ bindings->prev = bb;
+ }
+ else if (m->m_url->url_type == url_any) {
+ /* Unregister all */
+ for (b = t->entry->bindings; b; b = b->next) {
+ b->expires = t->now;
+ }
+ }
+ else {
+ /* Infernal error */
+
+ for (old = bindings; old; old = next) {
+ next = old->next;
+ binding_destroy(d->home, old);
+ }
+
+ return respond_transaction(t, SIP_500_INTERNAL_SERVER_ERROR, TAG_END());
+ }
+
+ for (b = t->entry->bindings; b; b = b->next) {
+ char const *expires;
+
+ if (b->expires <= t->now)
+ continue;
+
+ *mm = sip_contact_copy(d->home, b->contact);
+ if (*mm) {
+ expires = su_sprintf(d->home, "expires=%u",
+ (unsigned)(b->expires - t->now));
+ msg_header_add_param(d->home, (*mm)->m_common, expires);
+ mm = &(*mm)->m_next;
+ }
+ }
+
+ tbf = t->entry->contacts;
+ t->entry->contacts = contacts;
+ msg_header_free(d->home, tbf);
+
+ ok200:
+ return respond_transaction(t, SIP_200_OK,
+ SIPTAG_CONTACT(contacts),
+ TAG_END());
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int close_tports(void *_proxy)
+{
+ struct proxy *p = _proxy;
+ struct domain *d;
+ struct registration_entry *e;
+ struct binding *b;
+
+ /* Close all outbound transports */
+ for (d = p->domains; d; d = d->next) {
+ for (e = d->entries; e; e = e->next) {
+ for (b = e->bindings; b; b = b->next) {
+ if (b->tport) {
+ tport_shutdown(b->tport, 1);
+ tport_unref(b->tport);
+ b->tport = NULL;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
Added: freeswitch/trunk/libs/sofia-sip/tests/test_proxy.h
==============================================================================
--- (empty file)
+++ freeswitch/trunk/libs/sofia-sip/tests/test_proxy.h Wed May 14 15:10:54 2008
@@ -0,0 +1,87 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2005 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at 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
+ *
+ */
+
+#ifndef TEST_PROXY_H
+#define TEST_PROXY_H
+
+#include <sofia-sip/su_wait.h>
+#include <sofia-sip/nta.h>
+
+SOFIA_BEGIN_DECLS
+
+struct proxy;
+struct domain;
+
+struct proxy *test_proxy_create(su_root_t *, tag_type_t, tag_value_t, ...);
+
+void test_proxy_destroy(struct proxy *);
+
+url_t const *test_proxy_uri(struct proxy const *);
+
+char const *test_proxy_route_uri(struct proxy const *p,
+ sip_route_t const **return_route);
+
+struct domain *test_proxy_add_domain(struct proxy *,
+ url_t const *domain,
+ tag_type_t, tag_value_t, ...);
+
+void test_proxy_set_logging(struct proxy *, int logging);
+
+void test_proxy_domain_set_expiration(struct domain *,
+ sip_time_t min_expires,
+ sip_time_t expires,
+ sip_time_t max_expires);
+
+void test_proxy_domain_get_expiration(struct domain *,
+ sip_time_t *return_min_expires,
+ sip_time_t *return_expires,
+ sip_time_t *return_max_expires);
+
+void test_proxy_set_session_timer(struct proxy *p,
+ sip_time_t session_expires,
+ sip_time_t min_se);
+
+void test_proxy_get_session_timer(struct proxy *p,
+ sip_time_t *return_session_expires,
+ sip_time_t *return_min_se);
+
+int test_proxy_domain_set_authorize(struct domain *, char const *realm);
+int test_proxy_domain_get_authorize(struct domain *,
+ char const **return_realm);
+
+void test_proxy_domain_set_outbound(struct domain *d,
+ int use_outbound);
+void test_proxy_domain_get_outbound(struct domain *d,
+ int *return_use_outbound);
+
+void test_proxy_domain_set_record_route(struct domain *d,
+ int use_record_route);
+void test_proxy_domain_get_record_route(struct domain *d,
+ int *return_use_record_route);
+
+int test_proxy_close_tports(struct proxy *p);
+
+SOFIA_END_DECLS
+
+#endif
Added: freeswitch/trunk/libs/sofia-sip/tests/test_refer.c
==============================================================================
--- (empty file)
+++ freeswitch/trunk/libs/sofia-sip/tests/test_refer.c Wed May 14 15:10:54 2008
@@ -0,0 +1,852 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2005 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at 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 test_nua_re_invite.c
+ * @brief Test re_inviteing, outbound, nat traversal.
+ *
+ * @author Pekka Pessi <Pekka.Pessi at nokia.com>
+ * @author Martti Mela <Martti Mela at nokia.com>
+ *
+ * @date Created: Wed Aug 17 12:12:12 EEST 2005 ppessi
+ */
+
+#include "config.h"
+
+#include "test_nua.h"
+#include <sofia-sip/su_tag_class.h>
+
+#if HAVE_FUNC
+#elif HAVE_FUNCTION
+#define __func__ __FUNCTION__
+#else
+#define __func__ "test_call_hold"
+#endif
+
+/* ======================================================================== */
+/* NUA-9 tests: REFER */
+
+int test_refer0(struct context *ctx, char const *tests,
+ int refer_with_id, int notify_by_appl);
+int notify_until_terminated(CONDITION_PARAMS);
+int test_challenge_refer(struct context *ctx);
+
+int test_refer(struct context *ctx)
+{
+ /* test twice, once without id and once with id */
+ return
+ test_challenge_refer(ctx) ||
+ test_refer0(ctx, "NUA-9.1", 0, 0) ||
+ test_refer0(ctx, "NUA-9.2", 1, 0) ||
+ test_refer0(ctx, "NUA-9.3", 0, 1) ||
+ test_refer0(ctx, "NUA-9.4", 1, 1);
+}
+
+/* Referred call:
+
+ A B
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<----180 Ringing----|
+ | |
+ |<------200 OK-------|
+ |--------ACK-------->|
+ | |
+ |<------REFER--------|
+ |-------200 OK------>| C
+ [|-------NOTIFY------>|] |
+ [|<------200 OK-------|] |
+ | | |
+ | | |
+ |<-----SUBSCRIBE-----| |
+ |-------200 OK------>| |
+ | | |
+ | | |
+ |-----------------INVITE-------------------->|
+ | | |
+ |<------------------180----------------------|
+ |-------NOTIFY------>| |
+ |<------200 OK-------| |
+ | | |
+ |<------------------200----------------------|
+ |-------NOTIFY------>| |
+ |<------200 OK-------| |
+ |-------------------ACK--------------------->|
+ | | |
+ |--------BYE-------->| |
+ |<------200 OK-------| |
+ | X |
+ | |
+ |-------------------BYE--------------------->|
+ |<------------------200----------------------|
+ | |
+
+*/
+
+int test_refer0(struct context *ctx, char const *tests,
+ int refer_with_id, int notify_by_appl)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b, *c = &ctx->c;
+ struct call *a_call = a->call, *b_call = b->call, *c_call = c->call;
+ struct call *a_refer, *a_c2, *b_refer;
+ struct eventlist *a_revents, *b_revents;
+ struct event *e, *notify_e;
+ sip_t const *sip;
+ sip_event_t const *a_event, *b_event;
+ sip_refer_to_t const *refer_to;
+ sip_referred_by_t const *referred_by;
+
+ sip_refer_to_t r0[1];
+ sip_to_t to[1];
+
+ su_home_t tmphome[SU_HOME_AUTO_SIZE(16384)];
+
+ su_home_auto(tmphome, sizeof(tmphome));
+
+ if (print_headings)
+ printf("TEST %s: REFER: refer A to C%s%s%s\n", tests,
+ refer_with_id ? " with Event id" : "",
+ refer_with_id && !notify_by_appl ? " and" : "",
+ !notify_by_appl ? " nua generating the NOTIFYs" : "");
+
+ if (print_headings)
+ printf("TEST %s.1: REFER: make a call between A and B\n", tests);
+
+ /* Do (not) include id with first implicit Event: refer */
+ nua_set_params(ctx->a.nua, NUTAG_REFER_WITH_ID(refer_with_id), TAG_END());
+ run_a_until(ctx, nua_r_set_params, until_final_response);
+
+ if (refer_with_id) {
+ TEST_1(a_refer = calloc(1, (sizeof *a_refer) + (sizeof *a_refer->events)));
+ call_init(a_refer);
+ a_refer->events = (void *)(a_refer + 1);
+ eventlist_init(a_refer->events);
+
+ a_call->next = a_refer;
+ a_revents = a_refer->events;
+
+ TEST_1(b_refer = calloc(1, (sizeof *b_refer) + (sizeof *b_refer->events)));
+ call_init(b_refer);
+ b_refer->events = (void *)(b_refer + 1);
+ eventlist_init(b_refer->events);
+
+ b_call->next = b_refer;
+ b_revents = b_refer->events;
+ }
+ else {
+ a_refer = a_call, b_refer = b_call;
+ a_revents = a->events, b_revents = b->events;
+ }
+
+ TEST_1(a_c2 = calloc(1, (sizeof *a_c2) + (sizeof *a_c2->events)));
+ call_init(a_c2);
+ a_c2->events = (void *)(a_c2 + 1);
+ eventlist_init(a_c2->events);
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+ a_c2->sdp = "m=audio 5012 RTP/AVP 8";
+ c_call->sdp = "m=audio 5014 RTP/AVP 0 8";
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ TAG_END());
+
+ run_ab_until(ctx, -1, until_ready, -1, accept_call);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state
+ PROCEEDING -(C3+C4)-> READY: nua_r_invite, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+ EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST %s.1: PASSED\n", tests);
+
+ /* ---------------------------------------------------------------------- */
+ /* REFER (initial NOTIFY is no more sent unless REFER creates a new dialog)
+ A B
+ |<------REFER--------|
+ |-------200 OK------>|
+ [|-------NOTIFY------>|] |
+ [|<------200 OK-------|] |
+ */
+
+ if (print_headings)
+ printf("TEST %s.2: B refers A to C\n", tests);
+
+ if (b_refer != b_call)
+ TEST_1(b_refer->nh =
+ nua_handle(b->nua, b_refer, SIPTAG_TO(a->to), TAG_END()));
+
+ *sip_refer_to_init(r0)->r_url = *c->contact->m_url;
+ r0->r_url->url_headers = "subject=referred";
+ r0->r_display = "C";
+
+ REFER(b, b_refer, b_refer->nh, SIPTAG_REFER_TO(r0),
+ TAG_IF(!ctx->proxy_tests && b_refer != b_call,
+ NUTAG_URL(a->contact->m_url)),
+ TAG_END());
+ run_ab_until(ctx, -1, save_until_received,
+ -1, save_until_final_response);
+
+ /*
+ Events in A:
+ nua_i_refer
+ */
+ TEST_1(e = a_revents->head); TEST_E(e->data->e_event, nua_i_refer);
+ TEST(e->data->e_status, 202);
+ a_event = NULL;
+ TEST(tl_gets(e->data->e_tags,
+ NUTAG_REFER_EVENT_REF(a_event),
+ TAG_END()), 1);
+ TEST_1(a_event); TEST_1(a_event = sip_event_dup(tmphome, a_event));
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_refer_to);
+ TEST_1(refer_to = sip_refer_to_dup(tmphome, sip->sip_refer_to));
+ TEST_1(sip->sip_referred_by);
+ TEST_1(referred_by = sip_referred_by_dup(tmphome, sip->sip_referred_by));
+
+ /*
+ Events in B after nua_refer():
+ nua_r_refer
+ */
+ TEST_1(e = b_revents->head); TEST_E(e->data->e_event, nua_r_refer);
+ TEST(e->data->e_status, 100);
+ TEST(tl_gets(e->data->e_tags,
+ NUTAG_REFER_EVENT_REF(b_event),
+ TAG_END()), 1);
+ TEST_1(b_event); TEST_1(b_event->o_id);
+ TEST_1(b_event = sip_event_dup(tmphome, b_event));
+
+ notify_e = NULL;
+
+ TEST_1(e = e->next);
+ if (e->data->e_event == nua_i_notify) {
+ notify_e = e;
+ TEST_1(e = e->next);
+ }
+ TEST_E(e->data->e_event, nua_r_refer);
+ TEST(e->data->e_status, 202);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_SIZE(strtoul(b_event->o_id, NULL, 10), sip->sip_cseq->cs_seq);
+
+ if (a_refer != a_call) {
+ while (!notify_e) {
+ for (e = b_revents->head; e; e = e->next) {
+ if (e->data->e_event == nua_i_notify) {
+ notify_e = e;
+ break;
+ }
+ }
+ if (!notify_e)
+ run_ab_until(ctx, -1, save_until_received, nua_i_notify, save_events);
+ }
+
+ if (a_revents->head->next == NULL)
+ run_a_until(ctx, -1, save_until_received);
+
+ TEST_1(e = a_revents->head->next); TEST_E(e->data->e_event, nua_r_notify);
+ TEST_1(!e->next);
+
+ TEST_1(e = notify_e);
+ TEST_E(e->data->e_event, nua_i_notify);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_event);
+ if (refer_with_id)
+ TEST_S(sip->sip_event->o_id, b_event->o_id);
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "pending");
+ TEST_1(sip->sip_payload && sip->sip_payload->pl_data);
+ TEST_M(sip->sip_payload->pl_data, "SIP/2.0 100 Trying\r\n",
+ sip->sip_payload->pl_len);
+ }
+
+ free_events_in_list(ctx, a_revents);
+ free_events_in_list(ctx, b_revents);
+
+ if (print_headings)
+ printf("TEST %s.2: PASSED\n", tests);
+
+#if 0
+ /* ---------------------------------------------------------------------- */
+ /*
+ A B
+ |<-----SUBSCRIBE-----|
+ |-------200 OK------>|
+ |-------NOTIFY------>| |
+ |<------200 OK-------| |
+ */
+
+ if (print_headings)
+ printf("TEST %s.3: extend expiration time for implied subscription\n", tests);
+
+ SUBSCRIBE(b, b_call, b_call->nh,
+ SIPTAG_EVENT(b_event),
+ SIPTAG_EXPIRES_STR("3600"),
+ TAG_END());
+ run_ab_until(ctx, -1, save_until_final_response,
+ -1, save_until_final_response);
+
+ /*
+ Events in A:
+ nua_i_subscribe, nua_r_notify
+ */
+ TEST_1(e = a->events->head);
+ if (e->data->e_event == nua_r_notify)
+ TEST_1(e = e->next);
+ TEST_E(e->data->e_event, nua_i_subscribe);
+ TEST(e->data->e_status, 202);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_event);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_notify);
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ /*
+ Events in B after nua_subscribe():
+ nua_r_subscribe, nua_i_notify
+ */
+ TEST_1(e = b->events->head);
+ if (e->data->e_event == nua_i_notify) {
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_event);
+ if (refer_with_id)
+ TEST_S(sip->sip_event->o_id, b_event->o_id);
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "pending");
+ TEST_1(sip->sip_payload && sip->sip_payload->pl_data);
+ TEST_M(sip->sip_payload->pl_data, "SIP/2.0 100 Trying\r\n",
+ sip->sip_payload->pl_len);
+ TEST_1(e = e->next);
+ }
+ TEST_E(e->data->e_event, nua_r_subscribe);
+ TEST(e->data->e_status, 202);
+ if (!e->next)
+ run_b_until(ctx, -1, save_until_received);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_notify);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_event);
+ if (refer_with_id)
+ TEST_S(sip->sip_event->o_id, b_event->o_id);
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "pending");
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST %s.3: PASSED\n", tests);
+#endif
+
+ /* ---------------------------------------------------------------------- */
+ /*
+ A B C
+ | | |
+ |-----------------INVITE-------------------->|
+ | | |
+ XXX | |
+ | | |
+ |<------------------180----------------------|
+ |-------NOTIFY------>| |
+ |<------200 OK-------| |
+ | | |
+ XXX | |
+ | | |
+ |<------------------200----------------------|
+ |-------NOTIFY------>| |
+ |<------200 OK-------| |
+ |-------------------ACK--------------------->|
+ */
+
+ if (print_headings)
+ printf("TEST %s.4: A invites C\n", tests);
+
+ *sip_to_init(to)->a_url = *refer_to->r_url;
+ to->a_display = refer_to->r_display;
+
+ a_refer->next = a_c2;
+
+ TEST_1(a_c2->nh = nua_handle(a->nua, a_c2, SIPTAG_TO(to), TAG_END()));
+
+ INVITE(a, a_c2, a_c2->nh, /* NUTAG_URL(refer_to->r_url), */
+ TAG_IF(!notify_by_appl, NUTAG_REFER_EVENT(a_event)),
+ TAG_IF(!notify_by_appl, NUTAG_NOTIFY_REFER(a_refer->nh)),
+ SOATAG_USER_SDP_STR(a_c2->sdp),
+ SIPTAG_REFERRED_BY(referred_by),
+ TAG_END());
+
+ run_abc_until(ctx,
+ -1, notify_by_appl ? notify_until_terminated : until_ready,
+ -1, save_until_received,
+ -1, accept_call);
+
+ /* Wait until both NOTIFY has been responded */
+ while (a_revents->head == NULL || a_revents->head->next == NULL)
+ run_ab_until(ctx, -1, save_until_received, -1, save_events);
+ while (b_revents->head == NULL || b_revents->head->next == NULL)
+ run_ab_until(ctx, -1, save_events, -1, save_until_received);
+
+ /* Client A transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C2+C4)-> PROCEEDING: nua_r_invite, nua_i_state
+ nua_r_notify
+ PROCEEDING -(C3+C4)-> READY: nua_r_invite, nua_i_state
+ nua_r_notify
+ */
+ TEST_1(e = a_c2->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a_c2->events);
+
+ TEST_1(e = a_revents->head); TEST_E(e->data->e_event, nua_r_notify);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_notify);
+ if (a_refer == a_call && notify_by_appl) {
+ free_event_in_list(ctx, a_revents, a_revents->head);
+ free_event_in_list(ctx, a_revents, a_revents->head);
+ }
+ else {
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a_revents);
+ }
+
+ /*
+ Events in B after nua_refer():
+ nua_i_notify
+ */
+ TEST_1(e = b_revents->head); TEST_E(e->data->e_event, nua_i_notify);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "active");
+ TEST_1(sip->sip_payload && sip->sip_payload->pl_data);
+ TEST_M(sip->sip_payload->pl_data, "SIP/2.0 180 Ringing\r\n", sip->sip_payload->pl_len);
+ TEST_1(sip->sip_event);
+ if (refer_with_id)
+ TEST_S(sip->sip_event->o_id, b_event->o_id);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_notify);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "terminated");
+ TEST_1(sip->sip_payload && sip->sip_payload->pl_data);
+ TEST_M(sip->sip_payload->pl_data, "SIP/2.0 200 OK\r\n", sip->sip_payload->pl_len);
+ TEST_1(sip->sip_event);
+ if (refer_with_id)
+ TEST_S(sip->sip_event->o_id, b_event->o_id);
+ if (b_refer == b_call && notify_by_appl) {
+ free_event_in_list(ctx, b_revents, b_revents->head);
+ free_event_in_list(ctx, b_revents, b_revents->head);
+ }
+ else {
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b_revents);
+ }
+
+ /*
+ C transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+ EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ */
+ TEST_1(e = c->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, c->events);
+
+ if (print_headings)
+ printf("TEST %s.4: PASSED\n", tests);
+
+ /* ---------------------------------------------------------------------- */
+ /*
+ A B
+ |---------BYE------->|
+ |<--------200--------|
+ */
+
+ if (print_headings)
+ printf("TEST %s.5.1: terminate call between A and B\n", tests);
+
+ if (notify_by_appl) {
+ if (!a->events->head || !a->events->head->next)
+ run_ab_until(ctx, -1, until_terminated, -1, save_events);
+ if (!b->events->head || !b->events->head->next)
+ run_ab_until(ctx, -1, save_events, -1, until_terminated);
+ }
+ else {
+ BYE(a, a_call, a_call->nh, TAG_END());
+ run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
+ }
+
+ /*
+ Transitions of A:
+ READY --(T2)--> TERMINATING: nua_bye()
+ TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ /* Transitions of B:
+ READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_bye);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST %s.5.1: PASSED\n", tests);
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+
+ /* ---------------------------------------------------------------------- */
+ /*
+ A C
+ |-------------------BYE--------------------->|
+ |<------------------200----------------------|
+ */
+
+ if (print_headings)
+ printf("TEST %s.5.2: terminate call between A and C\n", tests);
+
+ BYE(a, a_c2, a_c2->nh, TAG_END());
+ run_abc_until(ctx, -1, until_terminated, -1, NULL, -1, until_terminated);
+
+ /*
+ Transitions of A:
+ READY --(T2)--> TERMINATING: nua_bye()
+ TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
+ */
+ TEST_1(e = a_c2->events->head); TEST_E(e->data->e_event, nua_r_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a_c2->events);
+
+ /* Transitions of B:
+ READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state
+ */
+ TEST_1(e = c->events->head); TEST_E(e->data->e_event, nua_i_bye);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, c->events);
+
+ if (print_headings)
+ printf("TEST %s.5.2: PASSED\n", tests);
+
+ nua_handle_destroy(c_call->nh), c_call->nh = NULL;
+
+ nua_handle_destroy(a_c2->nh), a_c2->nh = NULL;
+ a_refer->next = NULL; free(a_c2);
+
+ if (a_refer != a_call) {
+ nua_handle_destroy(a_refer->nh), a_refer->nh = NULL;
+ a_call->next = NULL; free(a_refer);
+ }
+
+ if (b_refer != b_call) {
+ nua_handle_destroy(b_refer->nh), b_refer->nh = NULL;
+ b_call->next = NULL; free(b_refer);
+ }
+
+ if (print_headings)
+ printf("TEST %s: PASSED\n", tests);
+
+ su_home_deinit(tmphome);
+
+ END();
+}
+
+
+/*
+ X INVITE
+ | |
+ |-----------------INVITE-------------------->|
+ | | |
+ | | |
+ |<------------------200----------------------|
+ |-------NOTIFY------>| |
+ |--------BYE-------->| |
+ |-------------------ACK--------------------->|
+
+*/
+int notify_until_terminated(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ if (event == nua_r_invite) {
+ sip_status_t *st = sip->sip_status;
+ sip_payload_t *pl;
+ struct call *r_call;
+
+ if (!nua_handle_has_events(ep->call->nh))
+ r_call = ep->call->next;
+ else
+ r_call = ep->call;
+
+ assert(nua_handle_has_events(r_call->nh));
+
+ pl = sip_payload_format(NULL, "SIP/2.0 %u %s\r\n",
+ st->st_status, st->st_phrase);
+
+ NOTIFY(ep, r_call, r_call->nh,
+ SIPTAG_CONTENT_TYPE_STR("message/sipfrag"),
+ SIPTAG_PAYLOAD(pl),
+ NUTAG_SUBSTATE(st->st_status >= 200
+ ? nua_substate_terminated
+ : nua_substate_active),
+ TAG_END());
+
+ su_free(NULL, pl);
+
+ if (st->st_status >= 200)
+ BYE(ep, ep->call, ep->call->nh, TAG_END());
+
+ return 0;
+ }
+
+ if (call != ep->call)
+ return 0;
+
+ switch (callstate(tags)) {
+ case nua_callstate_terminated:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int authenticate_refer(CONDITION_PARAMS);
+int reject_refer_after_notified(CONDITION_PARAMS);
+
+int test_challenge_refer(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b, *c = &ctx->c;
+ struct call *a_call = a->call, *c_call = c->call;
+ struct event *e;
+ sip_t const *sip;
+
+ sip_refer_to_t r0[1];
+
+ if (!ctx->proxy_tests)
+ return 0;
+
+ if (print_headings)
+ printf("TEST NUA-9.0.1: challenge REFER\n");
+
+ nua_set_params(ctx->a.nua, NUTAG_APPL_METHOD("REFER"), TAG_END());
+ run_a_until(ctx, nua_r_set_params, until_final_response);
+
+ *sip_refer_to_init(r0)->r_url = *b->contact->m_url;
+ r0->r_url->url_headers = "subject=referred";
+ r0->r_display = "B";
+
+ TEST_1(c_call->nh = nua_handle(c->nua, c_call, SIPTAG_TO(a->to), TAG_END()));
+
+ REFER(c, c_call, c_call->nh,
+ SIPTAG_FROM(c->to),
+ SIPTAG_REFER_TO(r0),
+ TAG_END());
+
+ run_abc_until(ctx, -1, reject_refer_after_notified, -1, NULL, -1, authenticate_refer);
+
+ /*
+ Events in A:
+ nua_i_refer
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_refer);
+ TEST(e->data->e_status, 100);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_refer_to);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_notify);
+ TEST(e->data->e_status, 200);
+ TEST_1(!e->next);
+ /*
+ Events in C after nua_refer():
+ nua_r_refer
+ */
+ TEST_1(e = c->events->head); TEST_E(e->data->e_event, nua_r_refer);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_refer);
+ TEST(e->data->e_status, 407);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_refer);
+ TEST(e->data->e_status, 100);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_notify);
+ TEST(e->data->e_status, 200);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_refer);
+ TEST(e->data->e_status, 480);
+
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ free_events_in_list(ctx, c->events);
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(c_call->nh), c_call->nh = NULL;
+
+ nua_set_params(ctx->a.nua,
+ NUTAG_APPL_METHOD(NULL),
+ NUTAG_APPL_METHOD("INVITE, REGISTER, PUBLISH, SUBSCRIBE"),
+ TAG_END());
+ run_a_until(ctx, nua_r_set_params, until_final_response);
+
+ if (print_headings)
+ printf("TEST NUA-9.0.1: PASSED\n");
+
+ END();
+}
+
+int reject_refer_after_notified(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ if (event == nua_i_refer) {
+ }
+
+ if (event == nua_r_notify) {
+ /* Respond to refer only after initial notify has been responded */
+ struct eventlist *list;
+ struct event *e;
+
+ if (call->events)
+ list = call->events;
+ else
+ list = ep->events;
+
+ for (e = list->head; e; e = e->next)
+ if (e->data->e_event == nua_i_refer)
+ break;
+
+ if (e) {
+ RESPOND(ep, call, nh, SIP_480_TEMPORARILY_UNAVAILABLE,
+ NUTAG_WITH(e->data->e_msg),
+ TAG_END());
+ return 1;
+ }
+ return 0;
+ }
+
+ return 0;
+}
+
+int authenticate_refer(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ if (status == 401 || status == 407) {
+ AUTHENTICATE(ep, call, nh,
+ NUTAG_AUTH("Digest:\"test-proxy\":charlie:secret"),
+ TAG_END());
+ }
+
+ return status == 480;
+}
Added: freeswitch/trunk/libs/sofia-sip/tests/test_register.c
==============================================================================
--- (empty file)
+++ freeswitch/trunk/libs/sofia-sip/tests/test_register.c Wed May 14 15:10:54 2008
@@ -0,0 +1,1043 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2005 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at 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 test_register.c
+ * @brief Test registering, outbound, nat traversal.
+ *
+ * @author Pekka Pessi <Pekka.Pessi at nokia.com>
+ * @author Martti Mela <Martti Mela at nokia.com>
+ *
+ * @date Created: Wed Aug 17 12:12:12 EEST 2005 ppessi
+ */
+
+#include "config.h"
+
+#include "test_nua.h"
+#include <sofia-sip/su_tag_class.h>
+
+#if HAVE_FUNC
+#elif HAVE_FUNCTION
+#define __func__ __FUNCTION__
+#else
+#define __func__ "test_register"
+#endif
+
+/* ======================================================================== */
+/* Test REGISTER */
+
+int test_clear_registrations(struct context *ctx);
+int test_outbound_cases(struct context *ctx);
+int test_register_a(struct context *ctx);
+int test_register_b(struct context *ctx);
+int test_register_c(struct context *ctx);
+int test_register_refresh(struct context *ctx);
+
+int test_register_to_proxy(struct context *ctx)
+{
+ return
+ test_clear_registrations(ctx) ||
+ test_outbound_cases(ctx) ||
+ test_register_a(ctx) ||
+ test_register_b(ctx) ||
+ test_register_c(ctx) ||
+ test_register_refresh(ctx);
+}
+
+int test_clear_registrations(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b, *c = &ctx->c;
+ struct call *a_reg = a->reg, *b_reg = b->reg, *c_reg = c->reg;
+
+ if (print_headings)
+ printf("TEST NUA-2.3.0.1: un-REGISTER a\n");
+
+ TEST_1(a_reg->nh = nua_handle(a->nua, a_reg, TAG_END()));
+ UNREGISTER(a, a_reg, a_reg->nh, SIPTAG_TO(a->to),
+ SIPTAG_CONTACT_STR("*"),
+ TAG_END());
+ run_a_until(ctx, -1, until_final_response);
+ AUTHENTICATE(a, a_reg, a_reg->nh,
+ NUTAG_AUTH("Digest:\"test-proxy\":alice:secret"), TAG_END());
+ run_a_until(ctx, -1, until_final_response);
+ nua_handle_destroy(a_reg->nh);
+
+ if (print_headings)
+ printf("TEST NUA-2.3.0.1: PASSED\n");
+
+ if (print_headings)
+ printf("TEST NUA-2.3.0.2: un-REGISTER b\n");
+
+ TEST_1(b_reg->nh = nua_handle(b->nua, b_reg, TAG_END()));
+ UNREGISTER(b, b_reg, b_reg->nh, SIPTAG_TO(b->to),
+ SIPTAG_CONTACT_STR("*"),
+ TAG_END());
+ run_b_until(ctx, -1, until_final_response);
+ AUTHENTICATE(b, b_reg, b_reg->nh,
+ NUTAG_AUTH("Digest:\"test-proxy\":bob:secret"), TAG_END());
+ run_b_until(ctx, -1, until_final_response);
+ nua_handle_destroy(b_reg->nh);
+
+ if (print_headings)
+ printf("TEST NUA-2.3.0.2: PASSED\n");
+
+ if (print_headings)
+ printf("TEST NUA-2.3.0.3: un-REGISTER c\n");
+
+ TEST_1(c_reg->nh = nua_handle(c->nua, c_reg, TAG_END()));
+ UNREGISTER(c, c_reg, c_reg->nh,
+ SIPTAG_FROM(c->to), SIPTAG_TO(c->to),
+ SIPTAG_CONTACT_STR("*"),
+ TAG_END());
+ run_c_until(ctx, -1, until_final_response);
+ AUTHENTICATE(c, c_reg, c_reg->nh,
+ NUTAG_AUTH("Digest:\"test-proxy\":charlie:secret"), TAG_END());
+ run_c_until(ctx, -1, until_final_response);
+ nua_handle_destroy(c_reg->nh);
+
+ if (print_headings)
+ printf("TEST NUA-2.3.0.3: PASSED\n");
+
+ END();
+}
+
+int test_outbound_cases(struct context *ctx)
+{
+ BEGIN();
+
+#if 0
+
+ struct endpoint *a = &ctx->a, *x;
+ struct call *a_reg = a->reg;
+ struct event *e;
+ sip_t const *sip;
+ sip_contact_t m[1];
+
+/* REGISTER test
+
+ A R
+ |------REGISTER----->|
+ |<-------401---------|
+ |------REGISTER----->|
+ |<-------200---------|
+ | |
+
+*/
+
+ if (print_headings)
+ printf("TEST NUA-2.3.1: REGISTER a\n");
+
+ test_proxy_domain_set_expiration(ctx->a.domain, 5, 5, 10);
+
+ TEST_1(a_reg->nh = nua_handle(a->nua, a_reg, TAG_END()));
+
+ sip_contact_init(m);
+ m->m_display = "Lissu";
+ *m->m_url = *a->contact->m_url;
+ m->m_url->url_user = "a";
+ m->m_url->url_params = "transport=udp";
+
+ REGISTER(a, a_reg, a_reg->nh, SIPTAG_TO(a->to),
+ NUTAG_OUTBOUND("use-rport no-options-keepalive"),
+ SIPTAG_CONTACT(m),
+ TAG_END());
+ run_a_until(ctx, -1, save_until_final_response);
+
+ TEST_1(e = a->events->head);
+ TEST_E(e->data->e_event, nua_r_register);
+ TEST(e->data->e_status, 401);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST(sip->sip_status->st_status, 401);
+ TEST_1(!sip->sip_contact);
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ AUTHENTICATE(a, a_reg, a_reg->nh,
+ NUTAG_AUTH("Digest:\"test-proxy\":alice:secret"), TAG_END());
+ run_a_until(ctx, -1, save_until_final_response);
+
+ TEST_1(e = a->events->head);
+ TEST_E(e->data->e_event, nua_r_register);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_contact);
+ TEST_S(sip->sip_contact->m_display, "Lissu");
+ TEST_S(sip->sip_contact->m_url->url_user, "a");
+ TEST_1(strstr(sip->sip_contact->m_url->url_params, "transport=udp"));
+
+ if (ctx->nat) {
+ TEST_1(e = a->specials->head);
+ }
+
+ test_proxy_domain_set_expiration(ctx->a.domain, 600, 3600, 36000);
+
+ if (print_headings)
+ printf("TEST NUA-2.3.1: PASSED\n");
+
+ if (print_headings)
+ printf("TEST NUA-2.3.4: refresh REGISTER\n");
+
+ if (!ctx->p) {
+ free_events_in_list(ctx, a->events);
+ return 0;
+ }
+
+ /* Wait for A to refresh its registrations */
+
+ /*
+ * Avoid race condition: if X has already refreshed registration
+ * with expiration time of 3600 seconds, do not wait for new refresh
+ */
+ a->next_condition = save_until_final_response;
+
+ for (x = a; x; x = NULL) {
+ for (e = x->events->head; e; e = e->next) {
+ if (e->data->e_event == nua_r_register &&
+ e->data->e_status == 200 &&
+ (sip = sip_object(e->data->e_msg)) &&
+ sip->sip_contact &&
+ sip->sip_contact->m_expires &&
+ strcmp(sip->sip_contact->m_expires, "3600") == 0) {
+ x->next_condition = NULL;
+ break;
+ }
+ }
+ }
+
+ run_a_until(ctx, -1, a->next_condition);
+
+ for (e = a->events->head; e; e = e->next) {
+ TEST_E(e->data->e_event, nua_r_register);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_contact);
+ if (!e->next)
+ break;
+ }
+ TEST_1(e);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_S(sip->sip_contact->m_expires, "3600");
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ if (print_headings)
+ printf("TEST NUA-2.3.4: PASSED\n");
+
+ TEST_1(0);
+
+#endif
+
+ END();
+}
+
+int test_register_a(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a;
+ struct call *a_reg = a->reg;
+ struct event *e;
+ sip_t const *sip;
+ sip_cseq_t cseq[1];
+
+/* REGISTER test
+
+ A R
+ |------REGISTER----->|
+ |<-------401---------|
+ |------REGISTER----->|
+ |<-------200---------|
+ | |
+
+*/
+
+ if (print_headings)
+ printf("TEST NUA-2.3.1: REGISTER a\n");
+
+ test_proxy_domain_set_expiration(ctx->a.domain, 5, 5, 10);
+
+ TEST_1(a_reg->nh = nua_handle(a->nua, a_reg, TAG_END()));
+
+ sip_cseq_init(cseq)->cs_seq = 12;
+ cseq->cs_method = sip_method_register;
+ cseq->cs_method_name = sip_method_name_register;
+
+ REGISTER(a, a_reg, a_reg->nh, SIPTAG_TO(a->to),
+ NUTAG_OUTBOUND("natify options-keepalive validate"),
+ NUTAG_KEEPALIVE(1000),
+ NUTAG_M_DISPLAY("A&A"),
+ NUTAG_M_USERNAME("a"),
+ NUTAG_M_PARAMS("foo=bar"),
+ NUTAG_M_FEATURES("q=0.9"),
+ SIPTAG_CSEQ(cseq),
+ TAG_END());
+ run_a_until(ctx, -1, save_until_final_response);
+
+ TEST_1(e = a->events->head);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ if (ctx->nat && e->data->e_status == 100) {
+ TEST_E(e->data->e_event, nua_r_register);
+ TEST(e->data->e_status, 100);
+ TEST(sip->sip_status->st_status, 406);
+ /* Check that CSeq included in tags is actually used in the request */
+ TEST(sip->sip_cseq->cs_seq, 13);
+ TEST_1(!sip->sip_contact);
+ TEST_1(e = e->next);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST(sip->sip_cseq->cs_seq, 14);
+ }
+ else {
+ /* Check that CSeq included in tags is actually used in the request */
+ TEST(sip->sip_cseq->cs_seq, 13);
+ }
+ TEST_E(e->data->e_event, nua_r_register);
+ TEST(e->data->e_status, 401);
+ TEST(sip->sip_status->st_status, 401);
+ TEST_1(!sip->sip_contact);
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ AUTHENTICATE(a, a_reg, a_reg->nh,
+ NUTAG_AUTH("Digest:\"test-proxy\":alice:secret"), TAG_END());
+ run_a_until(ctx, -1, save_until_final_response);
+
+ TEST_1(e = a->events->head);
+ TEST_E(e->data->e_event, nua_r_register);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_contact);
+ { char const *expect_m_display = "\"A&A\"";
+ /* VC does not dig \" with TEST_S() */
+ TEST_S(sip->sip_contact->m_display, expect_m_display); }
+ TEST_S(sip->sip_contact->m_url->url_user, "a");
+ TEST_1(strstr(sip->sip_contact->m_url->url_params, "foo=bar"));
+ TEST_S(sip->sip_contact->m_q, "0.9");
+
+ if (ctx->nat) {
+ TEST_1(e = a->specials->head);
+ }
+
+ test_proxy_domain_set_expiration(ctx->a.domain, 600, 3600, 36000);
+
+ if (print_headings)
+ printf("TEST NUA-2.3.1: PASSED\n");
+
+ END();
+}
+
+int test_register_b(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *b = &ctx->b;
+ struct call *b_reg = b->reg;
+ struct event *e;
+ sip_t const *sip;
+
+ if (print_headings)
+ printf("TEST NUA-2.3.2: REGISTER b\n");
+
+ test_proxy_domain_set_expiration(ctx->b.domain, 5, 5, 10);
+
+ TEST_1(b_reg->nh = nua_handle(b->nua, b_reg, TAG_END()));
+
+ /* Test application-supplied contact */
+ {
+ sip_contact_t m[1];
+ sip_contact_init(m)->m_url[0] = b->contact->m_url[0];
+
+ m->m_display = "B";
+ m->m_url->url_user = "b";
+
+ /* Include "tcp" transport parameter in Contact */
+ if (ctx->p)
+ m->m_url->url_params = "transport=tcp";
+
+ REGISTER(b, b_reg, b_reg->nh, SIPTAG_TO(b->to),
+ SIPTAG_CONTACT(m),
+ /* Do not include credentials unless challenged */
+ NUTAG_AUTH_CACHE(nua_auth_cache_challenged),
+ TAG_END());
+ }
+ run_ab_until(ctx, -1, save_events, -1, save_until_final_response);
+
+ TEST_1(e = b->events->head);
+ TEST_E(e->data->e_event, nua_r_register);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST(e->data->e_status, 401);
+ TEST(sip->sip_status->st_status, 401);
+ TEST_1(!sip->sip_contact);
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ AUTHENTICATE(b, b_reg, b_reg->nh,
+ NUTAG_AUTH("Digest:\"test-proxy\":bob:secret"), TAG_END());
+ run_ab_until(ctx, -1, save_events, -1, save_until_final_response);
+
+ TEST_1(e = b->events->head);
+ TEST_E(e->data->e_event, nua_r_register);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_contact);
+ TEST_S(sip->sip_contact->m_display, "B");
+ TEST_S(sip->sip_contact->m_url->url_user, "b");
+ free_events_in_list(ctx, b->events);
+
+ test_proxy_domain_set_expiration(ctx->b.domain, 600, 3600, 36000);
+
+ if (print_headings)
+ printf("TEST NUA-2.3.2: PASSED\n");
+
+ END();
+}
+
+int test_register_c(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *c = &ctx->c;
+ struct call *c_reg = c->reg;
+ struct event *e;
+ sip_t const *sip;
+
+ if (print_headings)
+ printf("TEST NUA-2.3.3: REGISTER c\n");
+
+ test_proxy_domain_set_expiration(ctx->c.domain, 600, 3600, 36000);
+ test_proxy_domain_set_authorize(ctx->c.domain, "test-proxy-0");
+
+ TEST_1(c_reg->nh = nua_handle(c->nua, c_reg, TAG_END()));
+
+ REGISTER(c, c_reg, c_reg->nh, SIPTAG_TO(c->to),
+ SIPTAG_FROM(c->to),
+ NUTAG_OUTBOUND(NULL),
+ NUTAG_M_DISPLAY("C"),
+ NUTAG_M_USERNAME("c"),
+ NUTAG_M_PARAMS("c=1"),
+ NUTAG_M_FEATURES("q=0.987;expires=5"),
+ NUTAG_CALLEE_CAPS(1),
+ SIPTAG_EXPIRES_STR("5"), /* Test 423 negotiation */
+ TAG_END());
+ run_abc_until(ctx, -1, save_events, -1, save_events,
+ -1, save_until_final_response);
+
+ TEST_1(e = c->events->head);
+ TEST_E(e->data->e_event, nua_r_register);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST(e->data->e_status, 401);
+ TEST(sip->sip_status->st_status, 401);
+ TEST_1(!sip->sip_contact);
+ TEST_1(!e->next);
+ free_events_in_list(ctx, c->events);
+
+ AUTHENTICATE(c, c_reg, c_reg->nh,
+ NUTAG_AUTH("Digest:\"test-proxy-0\":charlie:secret"), TAG_END());
+ run_abc_until(ctx, -1, save_events, -1, save_events,
+ -1, save_until_final_response);
+
+ TEST_1(e = c->events->head);
+ TEST_E(e->data->e_event, nua_r_register);
+ TEST(e->data->e_status, 100);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST(sip->sip_status->st_status, 423);
+ TEST_1(e = e->next);
+ if (e->data->e_status == 100 && e->data->e_event == nua_r_register) {
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST(sip->sip_status->st_status, 401);
+ TEST_1(e = e->next);
+ }
+ TEST(e->data->e_status, 200); TEST_E(e->data->e_event, nua_r_register);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_contact);
+ TEST_S(sip->sip_contact->m_display, "C");
+ TEST_S(sip->sip_contact->m_url->url_user, "c");
+ TEST_1(strstr(sip->sip_contact->m_url->url_params, "c=1"));
+ TEST_S(sip->sip_contact->m_q, "0.987");
+ TEST_1(msg_header_find_param(sip->sip_contact->m_common, "methods="));
+ TEST_1(!e->next);
+ free_events_in_list(ctx, c->events);
+
+ if (print_headings)
+ printf("TEST NUA-2.3.3: PASSED\n");
+
+ END();
+}
+
+int test_register_refresh(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b, *x;
+ struct event *e;
+ sip_t const *sip;
+ int seen_401;
+
+ if (print_headings)
+ printf("TEST NUA-2.3.4: refresh REGISTER\n");
+
+ if (!ctx->p) {
+ free_events_in_list(ctx, a->events);
+ free_events_in_list(ctx, b->events);
+ return 0;
+ }
+
+ /* Wait for A and B to refresh their registrations */
+
+ /*
+ * Avoid race condition: if X has already refreshed registration
+ * with expiration time of 3600 seconds, do not wait for new refresh
+ */
+ a->next_condition = save_until_final_response;
+ b->next_condition = save_until_final_response;
+
+ for (x = a; x; x = x == a ? b : NULL) {
+ for (e = x->events->head; e; e = e->next) {
+ if (e->data->e_event == nua_r_register &&
+ e->data->e_status == 200 &&
+ (sip = sip_object(e->data->e_msg)) &&
+ sip->sip_contact &&
+ sip->sip_contact->m_expires &&
+ strcmp(sip->sip_contact->m_expires, "3600") == 0) {
+ x->next_condition = NULL;
+ break;
+ }
+ }
+ }
+
+ run_ab_until(ctx, -1, a->next_condition, -1, b->next_condition);
+
+ for (e = a->events->head; e; e = e->next) {
+ TEST_E(e->data->e_event, nua_r_register);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_contact);
+ if (!e->next)
+ break;
+ }
+ TEST_1(e);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_S(sip->sip_contact->m_expires, "3600");
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ seen_401 = 0;
+
+ for (e = b->events->head; e; e = e->next) {
+ TEST_E(e->data->e_event, nua_r_register);
+ TEST_1(sip = sip_object(e->data->e_msg));
+
+ if (e->data->e_status == 200) {
+ TEST(e->data->e_status, 200);
+ TEST_1(seen_401);
+ TEST_1(sip->sip_contact);
+ }
+ else if (sip->sip_status && sip->sip_status->st_status == 401) {
+ seen_401 = 1;
+ }
+
+ if (!e->next)
+ break;
+ }
+ TEST_1(e);
+ TEST_S(sip->sip_contact->m_expires, "3600");
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-2.3.4: PASSED\n");
+
+ if (!ctx->p)
+ return 0;
+
+ if (print_headings)
+ printf("TEST NUA-2.3.5: re-REGISTER when TCP connection is closed\n");
+
+ test_proxy_close_tports(ctx->p);
+
+ run_b_until(ctx, -1, save_until_final_response);
+
+ TEST_1(e = b->events->head);
+ TEST_E(e->data->e_event, nua_r_register);
+ if (e->data->e_status == 100)
+ TEST_1(e = e->next);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_contact);
+ TEST_S(sip->sip_contact->m_expires, "3600");
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-2.3.5: PASSED\n");
+
+ END();
+}
+
+int registrar_299(CONDITION_PARAMS)
+{
+ msg_t *request = nua_current_request(nua);
+
+ save_event_in_list(ctx, event, ep, ep->call);
+
+ if (event == nua_i_register) {
+ RESPOND(ep, call, nh, 299, "YES", NUTAG_WITH(request), TAG_END());
+ return 1;
+ }
+
+ return 0;
+}
+
+int test_register_to_c(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *b = &ctx->b, *c = &ctx->c;
+ struct call *b_call = b->call, *c_call = c->call;
+ struct event *e;
+ sip_t const *sip;
+
+ if (print_headings)
+ printf("TEST NUA-2.6.1: REGISTER b to c\n");
+
+ nua_set_params(ctx->c.nua,
+ NUTAG_ALLOW("REGISTER"),
+ TAG_END());
+ run_c_until(ctx, nua_r_set_params, until_final_response);
+
+ TEST_1(b_call->nh = nua_handle(b->nua, b_call, TAG_END()));
+
+ REGISTER(b, b_call, b_call->nh,
+ NUTAG_REGISTRAR((url_string_t *)c->contact->m_url),
+ SIPTAG_TO(b->to),
+ NUTAG_OUTBOUND(NULL),
+ SIPTAG_CONTACT_STR(NULL),
+ TAG_END());
+ run_bc_until(ctx, -1, save_until_final_response, -1, registrar_299);
+
+ TEST_1(e = b->events->head);
+ TEST_E(e->data->e_event, nua_r_register);
+ TEST(e->data->e_status, 299);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(!sip->sip_contact);
+
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ TEST_1(e = c->events->head);
+ TEST_E(e->data->e_event, nua_i_register);
+ TEST(e->data->e_status, 100);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(!sip->sip_contact);
+
+ free_events_in_list(ctx, c->events);
+ nua_handle_destroy(c_call->nh), c_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-2.6.1: PASSED\n");
+
+ END();
+}
+
+
+int test_register(struct context *ctx)
+{
+ if (test_register_to_c(ctx))
+ return 1;
+
+ if (ctx->proxy_tests)
+ if (test_register_to_proxy(ctx)) return 1;
+
+ return 0;
+}
+
+
+int test_connectivity(struct context *ctx)
+{
+ if (!ctx->proxy_tests)
+ return 0; /* No proxy */
+
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b, *c = &ctx->c;
+ struct call *a_call = a->call, *b_call = b->call, *c_call = c->call;
+ struct event *e;
+ sip_t const *sip;
+
+ /* Connectivity test using OPTIONS */
+
+ if (print_headings)
+ printf("TEST NUA-2.4.1: OPTIONS from A to B\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ OPTIONS(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ NUTAG_ALLOW("OPTIONS"),
+ TAG_END());
+
+ run_ab_until(ctx, -1, save_until_final_response, -1, save_until_received);
+
+ /* Client events: nua_options(), nua_r_options */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_options);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_allow); TEST_1(sip->sip_accept); TEST_1(sip->sip_supported);
+ /* TEST_1(sip->sip_content_type); */
+ /* TEST_1(sip->sip_payload); */
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ /* Server events: nua_i_options */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_options);
+ TEST(e->data->e_status, 200);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-2.4.1: PASSED\n");
+
+ if (print_headings)
+ printf("TEST NUA-2.4.2: OPTIONS from B to C\n");
+
+ TEST_1(b_call->nh = nua_handle(b->nua, b_call, SIPTAG_TO(c->to), TAG_END()));
+
+ OPTIONS(b, b_call, b_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(c->contact->m_url)),
+ TAG_END());
+
+ run_abc_until(ctx, -1, NULL,
+ -1, save_until_final_response,
+ -1, save_until_received);
+
+ /* Client events: nua_options(), nua_r_options */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_r_options);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_allow); TEST_1(sip->sip_accept); TEST_1(sip->sip_supported);
+ /* TEST_1(sip->sip_content_type); */
+ /* TEST_1(sip->sip_payload); */
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ /* Server events: nua_i_options */
+ TEST_1(e = c->events->head); TEST_E(e->data->e_event, nua_i_options);
+ TEST(e->data->e_status, 200);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, c->events);
+ nua_handle_destroy(c_call->nh), c_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-2.4.2: PASSED\n");
+
+ if (print_headings)
+ printf("TEST NUA-2.4.3: OPTIONS from C to A\n");
+
+ TEST_1(c_call->nh = nua_handle(c->nua, c_call, SIPTAG_TO(a->to), TAG_END()));
+
+ OPTIONS(c, c_call, c_call->nh,
+ SIPTAG_FROM(c->to),
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(a->contact->m_url)),
+ TAG_END());
+
+ if (ctx->proxy_tests) {
+ run_abc_until(ctx, -1, NULL, -1, NULL, -1, save_until_final_response);
+
+ /* Client events: nua_options(), nua_r_options */
+ TEST_1(e = c->events->head); TEST_E(e->data->e_event, nua_r_options);
+ TEST(e->data->e_status, 407);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, c->events);
+
+ /* Sneakily change the realm */
+
+ TEST(test_proxy_domain_set_authorize(ctx->c.domain, "test-proxy"), 0);
+
+ AUTHENTICATE(c, c_call, c_call->nh,
+ NUTAG_AUTH("Digest:\"test-proxy-0\":charlie:secret"),
+ TAG_END());
+
+ run_abc_until(ctx, -1, NULL, -1, NULL, -1, save_until_final_response);
+
+ /* Client events: nua_options(), nua_r_options */
+ TEST_1(e = c->events->head); TEST_E(e->data->e_event, nua_r_options);
+ TEST(e->data->e_status, 407);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, c->events);
+
+ AUTHENTICATE(c, c_call, c_call->nh,
+ NUTAG_AUTH("Digest:\"test-proxy\":charlie:secret"),
+ TAG_END());
+ }
+
+ run_abc_until(ctx, -1, save_until_received,
+ -1, NULL,
+ -1, save_until_final_response);
+
+ /* Client events: nua_options(), nua_r_options */
+ TEST_1(e = c->events->head); TEST_E(e->data->e_event, nua_r_options);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_allow); TEST_1(sip->sip_accept); TEST_1(sip->sip_supported);
+ /* TEST_1(sip->sip_content_type); */
+ /* TEST_1(sip->sip_payload); */
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, c->events);
+ nua_handle_destroy(c_call->nh), c_call->nh = NULL;
+
+ /* Server events: nua_i_options */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_options);
+ TEST(e->data->e_status, 200);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-2.4.3: PASSED\n");
+
+ END();
+}
+
+int test_nat_timeout(struct context *ctx)
+{
+ if (!ctx->proxy_tests || !ctx->nat)
+ return 0; /* No proxy */
+
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+ sip_t const *sip;
+
+ /* Test what happens when NAT bindings go away */
+
+ if (print_headings)
+ printf("TEST NUA-2.5.1: NAT binding change\n");
+
+ free_events_in_list(ctx, a->specials);
+
+ test_nat_flush(ctx->nat); /* Break our connections */
+
+ /* Run until we get final response to REGISTER */
+ run_a_until(ctx, -1, save_until_final_response);
+
+ TEST_1(e = a->specials->head);
+ TEST_E(e->data->e_event, nua_i_outbound);
+ TEST(e->data->e_status, 102);
+ TEST_S(e->data->e_phrase, "NAT binding changed");
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->specials);
+
+ TEST_1(e = a->events->head);
+ TEST_E(e->data->e_event, nua_r_register);
+ TEST(e->data->e_status, 200);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+
+ if (print_headings)
+ printf("TEST NUA-2.5.1: PASSED\n");
+
+ if (print_headings)
+ printf("TEST NUA-2.5.2: OPTIONS from B to A\n");
+
+ TEST_1(b_call->nh = nua_handle(b->nua, b_call, SIPTAG_TO(a->to), TAG_END()));
+
+ OPTIONS(b, b_call, b_call->nh, TAG_END());
+
+ run_ab_until(ctx, -1, save_until_received,
+ -1, save_until_final_response);
+
+ /* Client events: nua_options(), nua_r_options */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_r_options);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_allow); TEST_1(sip->sip_accept); TEST_1(sip->sip_supported);
+ /* TEST_1(sip->sip_content_type); */
+ /* TEST_1(sip->sip_payload); */
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ /* Server events: nua_i_options */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_options);
+ TEST(e->data->e_status, 200);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-2.5.2: PASSED\n");
+
+ END();
+}
+
+int test_unregister(struct context *ctx)
+{
+ if (!ctx->proxy_tests)
+ return 0; /* No proxy */
+
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b, *c = &ctx->c;
+ struct event *e;
+ sip_t const *sip;
+
+/* un-REGISTER test
+
+ A B
+ |----un-REGISTER---->|
+ |<-------200---------|
+ | |
+
+*/
+ if (print_headings)
+ printf("TEST NUA-13.1: un-REGISTER a\n");
+
+ if (a->reg->nh) {
+ free_events_in_list(ctx, a->events);
+ UNREGISTER(a, NULL, a->reg->nh, TAG_END());
+ run_a_until(ctx, -1, save_until_final_response);
+ TEST_1(e = a->events->head);
+ TEST_E(e->data->e_event, nua_r_unregister);
+ if (e->data->e_status == 100) {
+ TEST_1(e = e->next);
+ TEST_E(e->data->e_event, nua_r_unregister);
+ }
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(!sip->sip_contact);
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a->reg->nh), a->reg->nh = NULL;
+ }
+
+ if (print_headings)
+ printf("TEST NUA-13.1: PASSED\n");
+
+ if (print_headings)
+ printf("TEST NUA-13.2: un-REGISTER b\n");
+
+ if (b->reg->nh) {
+ free_events_in_list(ctx, b->events);
+ UNREGISTER(b, NULL, b->reg->nh, TAG_END());
+ run_b_until(ctx, -1, save_until_final_response);
+ TEST_1(e = b->events->head);
+ TEST_E(e->data->e_event, nua_r_unregister);
+ if (e->data->e_status == 100) {
+ TEST_1(e = e->next);
+ TEST_E(e->data->e_event, nua_r_unregister);
+ }
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(!sip->sip_contact);
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b->reg->nh), b->reg->nh = NULL;
+ }
+ if (print_headings)
+ printf("TEST NUA-13.2: PASSED\n");
+
+ if (print_headings)
+ printf("TEST NUA-13.3: un-REGISTER c\n");
+
+ /* Unregister using another handle */
+ free_events_in_list(ctx, c->events);
+ TEST_1(c->call->nh = nua_handle(c->nua, c->call, TAG_END()));
+ UNREGISTER(c, c->call, c->call->nh, SIPTAG_TO(c->to), SIPTAG_FROM(c->to),
+ NUTAG_M_DISPLAY("C"),
+ NUTAG_M_USERNAME("c"),
+ NUTAG_M_PARAMS("c=1"),
+ TAG_END());
+ run_c_until(ctx, -1, save_until_final_response);
+
+ TEST_1(e = c->events->head);
+ TEST_E(e->data->e_event, nua_r_unregister);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST(e->data->e_status, 401);
+ TEST(sip->sip_status->st_status, 401);
+ TEST_1(!sip->sip_contact);
+ TEST_1(!e->next);
+ free_events_in_list(ctx, c->events);
+
+ AUTHENTICATE(c, c->call, c->call->nh,
+ NUTAG_AUTH("Digest:\"test-proxy\":charlie:secret"), TAG_END());
+ run_c_until(ctx, -1, save_until_final_response);
+
+ TEST_1(e = c->events->head);
+ TEST_E(e->data->e_event, nua_r_unregister);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(!sip->sip_contact);
+ TEST_1(!e->next);
+ free_events_in_list(ctx, c->events);
+ nua_handle_destroy(c->call->nh), c->call->nh = NULL;
+
+ if (c->reg->nh) {
+ UNREGISTER(c, NULL, c->reg->nh, TAG_END());
+ run_c_until(ctx, -1, save_until_final_response);
+ TEST_1(e = c->events->head);
+ TEST_E(e->data->e_event, nua_r_unregister);
+ if (e->data->e_status == 100) {
+ TEST_1(e = e->next);
+ TEST_E(e->data->e_event, nua_r_unregister);
+ }
+ if (e->data->e_status == 401) {
+ TEST_1(!e->next);
+ free_events_in_list(ctx, c->events);
+ AUTHENTICATE(c, NULL, c->reg->nh,
+ NUTAG_AUTH("Digest:\"test-proxy\":charlie:secret"), TAG_END());
+ run_c_until(ctx, -1, save_until_final_response);
+ TEST_1(e = c->events->head);
+ TEST_E(e->data->e_event, nua_r_unregister);
+ }
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_from->a_url->url_user);
+ TEST_1(!sip->sip_contact);
+ TEST_1(!e->next);
+ free_events_in_list(ctx, c->events);
+ nua_handle_destroy(c->reg->nh), c->reg->nh = NULL;
+ }
+
+ if (print_headings)
+ printf("TEST NUA-13.3: PASSED\n");
+
+ END();
+}
Added: freeswitch/trunk/libs/sofia-sip/tests/test_session_timer.c
==============================================================================
--- (empty file)
+++ freeswitch/trunk/libs/sofia-sip/tests/test_session_timer.c Wed May 14 15:10:54 2008
@@ -0,0 +1,434 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2005 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at 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 test_session_timer.c
+ * @brief NUA-8 tests: Session timer, UPDATE
+ *
+ * @author Pekka Pessi <Pekka.Pessi at nokia.com>
+ * @author Martti Mela <Martti Mela at nokia.com>
+ *
+ * @date Created: Wed Aug 17 12:12:12 EEST 2005 ppessi
+ */
+
+#include "config.h"
+
+#include "test_nua.h"
+#include <sofia-sip/su_tag_class.h>
+
+#if HAVE_FUNC
+#elif HAVE_FUNCTION
+#define __func__ __FUNCTION__
+#else
+#define __func__ "test_session_timer"
+#endif
+
+/* ======================================================================== */
+
+static size_t remove_update(void *a, void *message, size_t len);
+
+int test_session_timer(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+ sip_t const *sip;
+
+/* Session timer test:
+
+ A P B
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<----180 Ringing----|
+ | |
+ |<------200 OK-------|
+ |------------------->|
+ | |
+ | |
+ |--INVITE->| |
+ |<--422----| |
+ |---ACK--->| |
+
+*/
+
+ if (print_headings)
+ printf("TEST NUA-8.1.1: Session timers\n");
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+ /* We negotiate session timer of 6 second */
+ /* Disable session timer from proxy */
+ test_proxy_set_session_timer(ctx->p, 0, 0);
+
+ nua_set_params(ctx->b.nua,
+ NUTAG_SESSION_REFRESHER(nua_any_refresher),
+ NUTAG_MIN_SE(1),
+ NUTAG_SESSION_TIMER(6),
+ NTATAG_SIP_T1X64(8000),
+ TAG_END());
+
+ run_b_until(ctx, nua_r_set_params, until_final_response);
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ SIPTAG_SUPPORTED_STR("100rel"),
+ TAG_END());
+
+ run_ab_until(ctx, -1, until_ready, -1, accept_call);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_i_state
+ CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state
+ PROCEEDING -(C3+C4)-> READY: nua_r_invite, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_session_expires);
+ TEST_S(sip->sip_session_expires->x_refresher, "uas");
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+ EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-8.1.1: PASSED\n");
+
+ if (ctx->expensive) {
+ nua_set_hparams(a_call->nh,
+ NUTAG_SUPPORTED("timer"),
+ NUTAG_MIN_SE(1),
+ NUTAG_SESSION_TIMER(5),
+ TAG_END());
+ run_a_until(ctx, nua_r_set_params, until_final_response);
+
+ if (print_headings)
+ printf("TEST NUA-8.1.2: Wait for refresh using INVITE\n");
+
+ run_ab_until(ctx, -1, until_ready, -1, until_ready);
+
+ free_events_in_list(ctx, a->events);
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-8.1.2: PASSED\n");
+
+ nua_set_hparams(b_call->nh,
+ NUTAG_UPDATE_REFRESH(1),
+ TAG_END());
+ run_b_until(ctx, nua_r_set_params, until_final_response);
+
+ if (print_headings)
+ printf("TEST NUA-8.1.3: Wait for refresh using UPDATE\n");
+
+ run_ab_until(ctx, -1, until_ready, -1, until_ready);
+
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_update);
+
+ free_events_in_list(ctx, a->events);
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-8.1.3: PASSED\n");
+
+ if (ctx->nat) {
+ struct nat_filter *f;
+
+ if (print_headings)
+ printf("TEST NUA-8.1.4: filter UPDATE, wait until session expires\n");
+
+ f = test_nat_add_filter(ctx->nat, remove_update, NULL, nat_inbound);
+ TEST_1(f);
+
+ run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
+
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_bye);
+
+ free_events_in_list(ctx, a->events);
+ free_events_in_list(ctx, b->events);
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ test_nat_remove_filter(ctx->nat, f);
+
+ if (print_headings)
+ printf("TEST NUA-8.1.4: PASSED\n");
+ }
+ }
+
+ if (b_call->nh) {
+ if (print_headings)
+ printf("TEST NUA-8.1.9: Terminate first session timer call\n");
+
+ BYE(b, b_call, b_call->nh, TAG_END());
+ run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
+ free_events_in_list(ctx, a->events);
+ free_events_in_list(ctx, b->events);
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-8.1.9: PASSED\n");
+ }
+
+ /*
+ | |
+ |-------INVITE------>|
+ |<-------422---------|
+ |--------ACK-------->|
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<----180 Ringing----|
+ | |
+ |<------200 OK-------|
+ |--------ACK-------->|
+ | |
+ |<-------BYE---------|
+ |-------200 OK-------|
+ | |
+ */
+
+ if (print_headings)
+ printf("TEST NUA-8.2: Session timers negotiation\n");
+
+ test_proxy_set_session_timer(ctx->p, 180, 90);
+
+ nua_set_params(a->nua,
+ NUTAG_SUPPORTED("timer"),
+ TAG_END());
+
+ run_a_until(ctx, nua_r_set_params, until_final_response);
+
+ nua_set_params(b->nua,
+ NUTAG_AUTOANSWER(0),
+ NUTAG_MIN_SE(120),
+ NTATAG_SIP_T1X64(2000),
+ TAG_END());
+
+ run_b_until(ctx, nua_r_set_params, until_final_response);
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to),
+ TAG_END()));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ SIPTAG_SUPPORTED_STR("100rel, timer"),
+ NUTAG_SESSION_TIMER(4),
+ NUTAG_MIN_SE(3),
+ TAG_END());
+
+ run_ab_until(ctx, -1, until_ready, -1, accept_call);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C6a)-> (TERMINATED/INIT): nua_r_invite
+ when testing with proxy, second 422 when call reaches UAS:
+ (INIT) -(C1)-> CALLING: nua_i_state
+ CALLING -(C6a)-> (TERMINATED/INIT): nua_r_invite
+ (INIT) -(C1)-> CALLING: nua_i_state
+ CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state
+ PROCEEDING -(C3+C4)-> READY: nua_r_invite, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 100);
+ TEST(sip_object(e->data->e_msg)->sip_status->st_status, 422);
+
+ if (ctx->proxy_tests) {
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 100);
+ TEST(sip_object(e->data->e_msg)->sip_status->st_status, 422);
+ }
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_session_expires);
+ TEST(sip->sip_session_expires->x_delta, 120);
+ TEST_S(sip->sip_session_expires->x_refresher, "uac");
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+ EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_session_expires);
+ TEST_1(!sip->sip_session_expires->x_refresher);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-8.2: PASSED\n");
+
+ if (print_headings)
+ printf("TEST NUA-8.3: UPDATE with session timer headers\n");
+
+ UPDATE(b, b_call, b_call->nh, TAG_END());
+ run_ab_until(ctx, -1, until_ready, -1, until_ready);
+
+ /* Events from B (who sent UPDATE) */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ if (!e->next)
+ run_b_until(ctx, -1, until_ready);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_update);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_session_expires);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(is_answer_recv(e->data->e_tags));
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ /* Events from A (who received UPDATE) */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_update);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_session_expires);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(is_answer_sent(e->data->e_tags));
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ BYE(b, b_call, b_call->nh, TAG_END());
+ run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
+
+ /* B transitions:
+ READY --(T2)--> TERMINATING: nua_bye()
+ TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_r_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ /* A: READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_bye);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-8.3: PASSED\n");
+
+ END();
+}
+
+static
+size_t remove_update(void *a, void *message, size_t len)
+{
+ (void)a;
+
+ if (strncmp("UPDATE ", message, 7) == 0) {
+ return 0;
+ }
+
+ return len;
+}
+
Added: freeswitch/trunk/libs/sofia-sip/tests/test_simple.c
==============================================================================
--- (empty file)
+++ freeswitch/trunk/libs/sofia-sip/tests/test_simple.c Wed May 14 15:10:54 2008
@@ -0,0 +1,2099 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2005 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at 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 test_nua_simple.c
+ * @brief NUA-11: Test SIMPLE methods: MESSAGE, PUBLISH and SUBSCRIBE/NOTIFY.
+ *
+ * @author Pekka Pessi <Pekka.Pessi at nokia.com>
+ * @author Martti Mela <Martti Mela at nokia.com>
+ *
+ * @date Created: Wed Aug 17 12:12:12 EEST 2005 ppessi
+ */
+
+#include "config.h"
+
+#include "test_nua.h"
+#include <sofia-sip/su_tag_class.h>
+
+#if HAVE_FUNC
+#elif HAVE_FUNCTION
+#define __func__ __FUNCTION__
+#else
+#define __func__ "test_simple"
+#endif
+
+extern int accept_request(CONDITION_PARAMS);
+
+int save_until_nth_final_response(CONDITION_PARAMS);
+int accept_n_notifys(CONDITION_PARAMS);
+
+int test_message(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+ sip_t const *sip;
+ url_t url[1];
+
+/* Message test
+
+ A B
+ |-------MESSAGE----->|
+ |<-------200---------|
+ | |
+
+*/
+ if (print_headings)
+ printf("TEST NUA-11.1.1: MESSAGE\n");
+
+ if (ctx->proxy_tests)
+ *url = *b->to->a_url;
+ else
+ *url = *b->contact->m_url;
+
+ /* Test that query part is included in request sent to B */
+ url->url_headers = "organization=United%20Testers";
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, TAG_END()));
+
+ MESSAGE(a, a_call, a_call->nh,
+ NUTAG_URL(url),
+ SIPTAG_SUBJECT_STR("NUA-11.1.1"),
+ SIPTAG_CONTENT_TYPE_STR("text/plain"),
+ SIPTAG_PAYLOAD_STR("Hello hellO!\n"),
+ TAG_END());
+
+ run_ab_until(ctx, -1, save_until_final_response, -1, save_until_received);
+
+ /* Client events:
+ nua_message(), nua_r_message
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_message);
+ TEST(e->data->e_status, 200);
+ TEST_1(!e->next);
+
+ /*
+ Server events:
+ nua_i_message
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_message);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_subject && sip->sip_subject->g_string);
+ TEST_S(sip->sip_subject->g_string, "NUA-11.1.1");
+ TEST_1(sip->sip_organization);
+ TEST_S(sip->sip_organization->g_string, "United Testers");
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-11.1.1: PASSED\n");
+
+/* MESSAGE as application method
+
+ A B
+ |-------MESSAGE----->|
+ |<-------202---------|
+ | |
+*/
+
+ if (print_headings)
+ printf("TEST NUA-11.1.2: MESSAGE\n");
+
+ nua_set_params(b->nua, NUTAG_APPL_METHOD("MESSAGE"), TAG_END());
+ run_b_until(ctx, nua_r_set_params, until_final_response);
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, TAG_END()));
+
+ MESSAGE(a, a_call, a_call->nh,
+ NUTAG_URL(url),
+ SIPTAG_SUBJECT_STR("NUA-11.1.2"),
+ SIPTAG_CONTENT_TYPE_STR("text/plain"),
+ SIPTAG_PAYLOAD_STR("Hello hellO!\n"),
+ TAG_END());
+
+ run_ab_until(ctx, -1, save_until_final_response, -1, accept_request);
+
+ /* Client events:
+ nua_message(), nua_r_message
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_message);
+ TEST(e->data->e_status, 202);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip_user_agent(sip));
+ TEST_S(sip_user_agent(sip)->g_value, "007");
+ TEST_1(!e->next);
+
+ /*
+ Server events:
+ nua_i_message
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_message);
+ TEST(e->data->e_status, 100);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_subject && sip->sip_subject->g_string);
+ TEST_S(sip->sip_subject->g_string, "NUA-11.1.2");
+ TEST_1(sip->sip_organization);
+ TEST_S(sip->sip_organization->g_string, "United Testers");
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-11.1.2: PASSED\n");
+
+
+/* Message test
+
+ A
+ |-------MESSAGE--\
+ |<---------------/
+ |--------200-----\
+ |<---------------/
+ |
+
+*/
+ if (print_headings)
+ printf("TEST NUA-11.2: MESSAGE to myself\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(a->to), TAG_END()));
+
+ MESSAGE(a, a_call, a_call->nh,
+ /* We cannot reach us by using our contact! */
+ NUTAG_URL(!ctx->p && !ctx->proxy_tests ? a->contact->m_url : NULL),
+ SIPTAG_SUBJECT_STR("NUA-11.2"),
+ SIPTAG_CONTENT_TYPE_STR("text/plain"),
+ SIPTAG_PAYLOAD_STR("Hello hellO!\n"),
+ TAG_END());
+
+ run_a_until(ctx, -1, save_until_final_response);
+
+ /* Events:
+ nua_message(), nua_i_message, nua_r_message
+ */
+ TEST_1(e = a->specials->head);
+ while (e->data->e_event == nua_i_outbound)
+ e = e->next;
+ TEST_E(e->data->e_event, nua_i_message);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_subject && sip->sip_subject->g_string);
+ TEST_S(sip->sip_subject->g_string, "NUA-11.2");
+
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_message);
+ TEST(e->data->e_status, 200);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ free_events_in_list(ctx, a->specials);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-11.2: PASSED\n");
+
+ END();
+}
+
+int accept_request(CONDITION_PARAMS)
+{
+ msg_t *with = nua_current_request(nua);
+
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ if (status < 200) {
+ RESPOND(ep, call, nh, SIP_202_ACCEPTED,
+ NUTAG_WITH(with),
+ SIPTAG_USER_AGENT_STR("007"),
+ TAG_END());
+ return 1;
+ }
+
+ return 0;
+}
+
+char const *test_etag = "tagtag";
+
+int respond_with_etag(CONDITION_PARAMS)
+{
+ msg_t *with = nua_current_request(nua);
+
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 1;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (event) {
+ char const *etag;
+ case nua_i_publish:
+ etag = sip->sip_if_match ? sip->sip_if_match->g_value : NULL;
+ if (sip->sip_if_match && (etag == NULL || strcmp(etag, test_etag))) {
+ RESPOND(ep, call, nh, SIP_412_PRECONDITION_FAILED,
+ NUTAG_WITH(with),
+ TAG_END());
+ }
+ else {
+ RESPOND(ep, call, nh, SIP_200_OK,
+ NUTAG_WITH(with),
+ SIPTAG_ETAG_STR(test_etag),
+ SIPTAG_EXPIRES_STR("3600"),
+ SIPTAG_EXPIRES(sip->sip_expires), /* overrides 3600 */
+ TAG_END());
+ }
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int close_handle(CONDITION_PARAMS)
+{
+ if (call->nh == nh)
+ call->nh = NULL;
+ nua_handle_destroy(nh);
+ return 1;
+}
+
+int test_publish(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+ sip_t const *sip;
+
+
+/* PUBLISH test
+
+ A B
+ |-------PUBLISH----->|
+ |<-------405---------| (method not allowed by default)
+ | |
+ |-------PUBLISH----->|
+ |<-------501---------| (no events allowed)
+ | |
+ |-------PUBLISH----->|
+ |<-------489---------| (event not allowed by default)
+ | |
+ |-------PUBLISH----->|
+ |<-------200---------| (event allowed, responded)
+ | |
+ |-----un-PUBLISH---->|
+ |<-------200---------| (event allowed, responded)
+
+*/
+ if (print_headings)
+ printf("TEST NUA-11.3: PUBLISH\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ PUBLISH(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SIPTAG_EVENT_STR("presence"),
+ SIPTAG_CONTENT_TYPE_STR("text/urllist"),
+ SIPTAG_PAYLOAD_STR("sip:example.com\n"),
+ TAG_END());
+
+ run_ab_until(ctx, -1, save_until_final_response, -1, NULL);
+
+ /* Client events:
+ nua_publish(), nua_r_publish
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_publish);
+ TEST(e->data->e_status, 405);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ nua_set_params(b->nua, NUTAG_ALLOW("PUBLISH"), TAG_END());
+
+ run_b_until(ctx, nua_r_set_params, until_final_response);
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ PUBLISH(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SIPTAG_EVENT_STR("presence"),
+ SIPTAG_CONTENT_TYPE_STR("text/urllist"),
+ SIPTAG_PAYLOAD_STR("sip:example.com\n"),
+ TAG_END());
+
+ run_a_until(ctx, -1, save_until_final_response);
+
+ /* Client events:
+ nua_publish(), nua_r_publish
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_publish);
+ TEST(e->data->e_status, 501); /* Not implemented */
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ /* Allow presence event */
+
+ nua_set_params(b->nua, NUTAG_ALLOW_EVENTS("presence"), TAG_END());
+
+ run_b_until(ctx, nua_r_set_params, until_final_response);
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ PUBLISH(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SIPTAG_EVENT_STR("reg"),
+ SIPTAG_CONTENT_TYPE_STR("text/urllist"),
+ SIPTAG_PAYLOAD_STR("sip:example.com\n"),
+ TAG_END());
+
+ run_a_until(ctx, -1, save_until_final_response);
+
+ /* Client events:
+ nua_publish(), nua_r_publish
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_publish);
+ TEST(e->data->e_status, 489); /* Bad Event */
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ PUBLISH(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SIPTAG_EVENT_STR("presence"),
+ SIPTAG_CONTENT_TYPE_STR("text/urllist"),
+ SIPTAG_PAYLOAD_STR("sip:example.com\n"),
+ SIPTAG_EXPIRES_STR("5"),
+ TAG_END());
+
+ run_ab_until(ctx, -1, save_until_final_response, -1, respond_with_etag);
+
+ /* Client events:
+ nua_publish(), nua_r_publish
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_publish);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_etag);
+ TEST_S(sip->sip_etag->g_string, test_etag);
+ TEST_1(!e->next);
+
+ /*
+ Server events:
+ nua_i_publish
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_publish);
+ TEST(e->data->e_status, 100);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (!ctx->expensive && 0)
+ goto skip_republish;
+
+ run_ab_until(ctx, -1, save_until_final_response, -1, respond_with_etag);
+
+ /* Client events: nua_r_publish
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_publish);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_etag);
+ TEST_S(sip->sip_etag->g_string, test_etag);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+
+ /*
+ Server events:
+ nua_i_publish
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_publish);
+ TEST(e->data->e_status, 100);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_if_match);
+ TEST_S(sip->sip_if_match->g_string, "tagtag");
+ TEST_1(!sip->sip_content_type);
+ TEST_1(!sip->sip_payload);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ skip_republish:
+
+ UNPUBLISH(a, a_call, a_call->nh, TAG_END());
+
+ run_ab_until(ctx, -1, save_until_final_response, -1, respond_with_etag);
+
+ /* Client events:
+ nua_publish(), nua_r_publish
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_unpublish);
+ TEST(e->data->e_status, 200);
+ TEST_1(!e->next);
+
+ /*
+ Server events:
+ nua_i_publish
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_publish);
+ TEST(e->data->e_status, 100);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ free_events_in_list(ctx, b->events);
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ /* Let server close handle without responding to PUBLISH */
+ PUBLISH(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SIPTAG_EVENT_STR("presence"),
+ SIPTAG_CONTENT_TYPE_STR("text/urllist"),
+ SIPTAG_PAYLOAD_STR("sip:example.com\n"),
+ TAG_END());
+
+ run_ab_until(ctx, -1, save_until_final_response, -1, close_handle);
+
+ /* Client events:
+ nua_publish(), nua_r_publish
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_publish);
+ TEST(e->data->e_status, 500);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ /* No Event header */
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ PUBLISH(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SIPTAG_CONTENT_TYPE_STR("text/urllist"),
+ SIPTAG_PAYLOAD_STR("sip:example.com\n"),
+ TAG_END());
+
+ run_ab_until(ctx, -1, save_until_final_response, -1, save_events);
+
+ /* Client events:
+ nua_publish(), nua_r_publish
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_publish);
+ TEST(e->data->e_status, 489);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+
+ /*
+ Server events: nothing
+ */
+ TEST_1(!b->events->head);
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-11.3: PASSED\n");
+
+ END();
+}
+
+static char const presence_open[] =
+ "<?xml version='1.0' encoding='UTF-8'?>\n"
+ "<presence xmlns='urn:ietf:params:xml:ns:cpim-pidf' \n"
+ " entity='pres:bob at example.org'>\n"
+ " <tuple id='ksac9udshce'>\n"
+ " <status><basic>open</basic></status>\n"
+ " <contact priority='1.0'>sip:bob at example.org</contact>\n"
+ " </tuple>\n"
+ "</presence>\n";
+
+static char const presence_closed[] =
+ "<?xml version='1.0' encoding='UTF-8'?>\n"
+ "<presence xmlns='urn:ietf:params:xml:ns:cpim-pidf' \n"
+ " entity='pres:bob at example.org'>\n"
+ " <tuple id='ksac9udshce'>\n"
+ " <status><basic>closed</basic></status>\n"
+ " </tuple>\n"
+ "</presence>\n";
+
+
+int accept_and_notify_twice(CONDITION_PARAMS)
+{
+ msg_t *with = nua_current_request(nua);
+
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (event) {
+ case nua_i_subscribe:
+ if (status < 200) {
+ NOTIFY(ep, call, nh,
+ SIPTAG_EVENT(sip->sip_event),
+ SIPTAG_CONTENT_TYPE_STR("application/pidf+xml"),
+ SIPTAG_PAYLOAD_STR(presence_closed),
+ NUTAG_SUBSTATE(nua_substate_pending),
+ TAG_END());
+ NOTIFY(ep, call, nh,
+ SIPTAG_EVENT(sip->sip_event),
+ SIPTAG_CONTENT_TYPE_STR("application/pidf+xml"),
+ SIPTAG_PAYLOAD_STR(presence_open),
+ NUTAG_SUBSTATE(nua_substate_active),
+ TAG_END());
+ RESPOND(ep, call, nh, SIP_202_ACCEPTED,
+ NUTAG_WITH(with),
+ SIPTAG_EXPIRES_STR("360"),
+ TAG_END());
+ }
+ return 0;
+
+ case nua_r_notify:
+ return status >= 200 &&
+ tl_find(tags, nutag_substate)->t_value == nua_substate_active;
+
+ default:
+ return 0;
+ }
+}
+
+int save_until_responded_and_notified_twice(CONDITION_PARAMS)
+{
+ save_event_in_list(ctx, event, ep, call);
+
+ if (event == nua_i_notify) {
+ if (ep->flags.bit0)
+ ep->flags.bit1 = 1;
+ ep->flags.bit0 = 1;
+ }
+
+ if (event == nua_r_subscribe || event == nua_r_unsubscribe) {
+ if (status >= 300)
+ return 1;
+ else if (status >= 200) {
+ ep->flags.bit2 = 1;
+ }
+ }
+
+ return ep->flags.bit0 && ep->flags.bit1 && ep->flags.bit2;
+}
+
+
+int accept_and_notify(CONDITION_PARAMS)
+{
+ msg_t *with = nua_current_request(nua);
+
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (event) {
+ case nua_i_subscribe:
+ if (status < 200) {
+ int fetch = sip->sip_expires && sip->sip_expires->ex_delta == 0;
+
+ RESPOND(ep, call, nh, SIP_202_ACCEPTED,
+ NUTAG_WITH(with),
+ SIPTAG_EXPIRES_STR("360"),
+ SIPTAG_EXPIRES(sip->sip_expires),
+ TAG_END());
+
+ NOTIFY(ep, call, nh,
+ SIPTAG_EVENT(sip->sip_event),
+ SIPTAG_CONTENT_TYPE_STR("application/pidf+xml"),
+ SIPTAG_PAYLOAD_STR(presence_closed),
+ NUTAG_SUBSTATE(fetch
+ ? nua_substate_pending
+ : nua_substate_terminated),
+ TAG_END());
+
+ }
+ return 0;
+
+ case nua_r_notify:
+ return status >= 200;
+
+ default:
+ return 0;
+ }
+}
+
+int save_and_notify(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (event) {
+ case nua_i_subscribe:
+ NOTIFY(ep, call, nh,
+ SIPTAG_EVENT(sip->sip_event),
+ SIPTAG_CONTENT_TYPE_STR("application/pidf+xml"),
+ SIPTAG_PAYLOAD_STR(presence_closed),
+ NUTAG_SUBSTATE(nua_substate_active),
+ TAG_END());
+ return 0;
+
+ case nua_r_notify:
+ return status >= 200;
+
+ default:
+ return 0;
+ }
+}
+
+extern int save_until_notified_and_responded(CONDITION_PARAMS);
+extern int save_until_notified(CONDITION_PARAMS);
+
+int test_subscribe_notify(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e, *en1, *en2, *es;
+ sip_t const *sip;
+ tagi_t const *n_tags, *r_tags;
+
+ if (print_headings)
+ printf("TEST NUA-11.4: notifier server using nua_notify()\n");
+
+ if (print_headings)
+ printf("TEST NUA-11.4.1: establishing subscription\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ SUBSCRIBE(a, a_call, a_call->nh, NUTAG_URL(b->contact->m_url),
+ SIPTAG_EVENT_STR("presence"),
+ SIPTAG_EXPIRES_STR("333"),
+ SIPTAG_ACCEPT_STR("application/xpidf, application/pidf+xml"),
+ TAG_END());
+
+ run_ab_until(ctx, -1, save_until_responded_and_notified_twice,
+ -1, accept_and_notify_twice);
+
+ /* Client events:
+ nua_subscribe(), nua_i_notify/nua_r_subscribe/nua_i_notify
+ */
+ for (en1 = en2 = es = NULL, e = a->events->head; e; e = e->next) {
+ if (en1 == NULL && e->data->e_event == nua_i_notify)
+ en1 = e;
+ else if (en2 == NULL && e->data->e_event == nua_i_notify)
+ en2 = e;
+ else if (e->data->e_event == nua_r_subscribe)
+ es = e;
+ else
+ TEST_1(!e);
+ }
+
+ TEST_1(e = en1);
+ TEST_E(e->data->e_event, nua_i_notify);
+ TEST_1(tl_find(e->data->e_tags, nutag_substate));
+ TEST(tl_find(e->data->e_tags, nutag_substate)->t_value, nua_substate_pending);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_event); TEST_S(sip->sip_event->o_type, "presence");
+ TEST_1(sip->sip_content_type);
+ TEST_S(sip->sip_content_type->c_type, "application/pidf+xml");
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "pending");
+ TEST_1(sip->sip_subscription_state->ss_expires);
+
+ TEST_1(e = es); TEST_E(e->data->e_event, nua_r_subscribe);
+ TEST_1(e->data->e_status == 202 || e->data->e_status == 200);
+ TEST_1(tl_find(e->data->e_tags, nutag_substate));
+ r_tags = tl_find(e->data->e_tags, nutag_substate);
+ if (es == a->events->head) {
+ TEST(r_tags->t_value, nua_substate_embryonic);
+ }
+ else if (es == a->events->head->next) {
+ TEST_1(r_tags->t_value == nua_substate_pending);
+ }
+ else {
+ TEST_1(r_tags->t_value == nua_substate_active);
+ }
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_expires);
+ TEST_1(sip->sip_expires->ex_delta <= 333);
+
+ free_events_in_list(ctx, a->events);
+
+ /* Server events: nua_i_subscribe, nua_r_notify */
+ TEST_1(e = b->events->head);
+ TEST_E(e->data->e_event, nua_i_subscribe);
+ TEST_E(e->data->e_status, 100);
+ TEST_1(sip = sip_object(e->data->e_msg));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_notify);
+ r_tags = e->data->e_tags;
+ TEST_1(tl_find(r_tags, nutag_substate));
+ TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_pending);
+
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-11.4.1: PASSED\n");
+
+ /* ---------------------------------------------------------------------- */
+
+/* NOTIFY with updated content
+
+ A B
+ | |
+ |<------NOTIFY-------|
+ |-------200 OK------>|
+ | |
+*/
+ if (print_headings)
+ printf("TEST NUA-11.4.2: send NOTIFY\n");
+
+ /* Update presence data */
+
+ NOTIFY(b, b_call, b_call->nh,
+ NUTAG_SUBSTATE(nua_substate_active),
+ SIPTAG_EVENT_STR("presence"),
+ SIPTAG_CONTENT_TYPE_STR("application/pidf+xml"),
+ SIPTAG_PAYLOAD_STR(presence_open),
+ TAG_END());
+
+ run_ab_until(ctx, -1, save_until_notified,
+ -1, save_until_final_response);
+
+ /* subscriber events:
+ nua_i_notify
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_notify);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_event); TEST_S(sip->sip_event->o_type, "presence");
+ TEST_1(sip->sip_content_type);
+ TEST_S(sip->sip_content_type->c_type, "application/pidf+xml");
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "active");
+ TEST_1(sip->sip_subscription_state->ss_expires);
+ n_tags = e->data->e_tags;
+ TEST_1(tl_find(n_tags, nutag_substate));
+ TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_active);
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ /* Notifier events: nua_r_notify */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_r_notify);
+ r_tags = e->data->e_tags;
+ TEST_1(tl_find(r_tags, nutag_substate));
+ TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_active);
+
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-11.4.2: PASSED\n");
+
+ /* ---------------------------------------------------------------------- */
+
+/* Re-SUBSCRIBE
+
+ A B
+ | |
+ |<------NOTIFY-------|
+ |-------200 OK------>|
+ | |
+*/
+ if (print_headings)
+ printf("TEST NUA-11.4.3: re-SUBSCRIBE\n");
+
+ /* Set default expiration time */
+ nua_set_hparams(b_call->nh, NUTAG_SUB_EXPIRES(365), TAG_END());
+ run_b_until(ctx, nua_r_set_params, until_final_response);
+
+ SUBSCRIBE(a, a_call, a_call->nh, NUTAG_URL(b->contact->m_url),
+ SIPTAG_EVENT_STR("presence"),
+ SIPTAG_EXPIRES_STR("3600"),
+ SIPTAG_ACCEPT_STR("application/xpidf, application/pidf+xml"),
+ TAG_END());
+
+ run_ab_until(ctx, -1, save_until_notified_and_responded,
+ -1, save_until_final_response);
+
+ /* Client events:
+ nua_subscribe(), nua_i_notify/nua_r_subscribe
+ */
+ for (en1 = en2 = es = NULL, e = a->events->head; e; e = e->next) {
+ if (en1 == NULL && e->data->e_event == nua_i_notify)
+ en1 = e;
+ else if (e->data->e_event == nua_r_subscribe)
+ es = e;
+ else
+ TEST_1(!e);
+ }
+ TEST_1(e = en1);
+ TEST_E(e->data->e_event, nua_i_notify);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_event); TEST_S(sip->sip_event->o_type, "presence");
+ TEST_1(sip->sip_content_type);
+ TEST_S(sip->sip_content_type->c_type, "application/pidf+xml");
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "active");
+ TEST_1(sip->sip_subscription_state->ss_expires);
+ n_tags = e->data->e_tags;
+ TEST_1(tl_find(n_tags, nutag_substate));
+ TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_active);
+
+ TEST_1(e = es); TEST_E(e->data->e_event, nua_r_subscribe);
+ TEST_1(tl_find(e->data->e_tags, nutag_substate));
+ TEST(tl_find(e->data->e_tags, nutag_substate)->t_value, nua_substate_active);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_expires);
+ TEST_1(sip->sip_expires->ex_delta <= 365);
+
+ free_events_in_list(ctx, a->events);
+
+ /* Server events: nua_i_subscribe, nua_r_notify */
+ TEST_1(e = b->events->head);
+ TEST_E(e->data->e_event, nua_i_subscribe);
+ TEST_E(e->data->e_status, 100);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_notify);
+ r_tags = e->data->e_tags;
+ TEST_1(tl_find(r_tags, nutag_substate));
+ TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_active);
+
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-11.4.3: PASSED\n");
+
+ /* ---------------------------------------------------------------------- */
+
+/* un-SUBSCRIBE
+
+ A B
+ | |
+ |------SUBSCRIBE---->|
+ |<--------202--------|
+ |<------NOTIFY-------|
+ |-------200 OK------>|
+ | |
+*/
+ if (print_headings)
+ printf("TEST NUA-11.4.4: un-SUBSCRIBE\n");
+
+ UNSUBSCRIBE(a, a_call, a_call->nh, TAG_END());
+
+ run_ab_until(ctx, -1, save_until_final_response,
+ -1, save_until_final_response);
+
+ /* Client events:
+ nua_unsubscribe(), nua_i_notify/nua_r_unsubscribe
+ */
+ for (en1 = en2 = es = NULL, e = a->events->head; e; e = e->next) {
+ if (en1 == NULL && e->data->e_event == nua_i_notify)
+ en1 = e;
+ else if (e->data->e_event == nua_r_unsubscribe)
+ es = e;
+ else
+ TEST_1(!e);
+ }
+ if (en1) {
+ TEST_1(e = en1); TEST_E(e->data->e_event, nua_i_notify);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_event);
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "terminated");
+ TEST_1(!sip->sip_subscription_state->ss_expires);
+ n_tags = e->data->e_tags;
+ TEST_1(tl_find(n_tags, nutag_substate));
+ TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_terminated);
+ }
+
+ TEST_1(e = es); TEST_E(e->data->e_event, nua_r_unsubscribe);
+ TEST(e->data->e_status, 200);
+ r_tags = e->data->e_tags;
+ TEST_1(tl_find(r_tags, nutag_substate));
+ TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_terminated);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_expires);
+ TEST_1(sip->sip_expires->ex_delta == 0);
+
+ free_events_in_list(ctx, a->events);
+
+ /* Notifier events: nua_r_notify */
+ TEST_1(e = b->events->head);
+ TEST_E(e->data->e_event, nua_i_subscribe);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(tl_find(e->data->e_tags, nutag_substate));
+ TEST(tl_find(e->data->e_tags, nutag_substate)->t_value,
+ nua_substate_terminated);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_notify);
+ TEST_1(e->data->e_status >= 200);
+ r_tags = e->data->e_tags;
+ TEST_1(tl_find(r_tags, nutag_substate));
+ TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_terminated);
+
+ free_events_in_list(ctx, b->events);
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-11.4.4: PASSED\n");
+
+ if (print_headings)
+ printf("TEST NUA-11.4: PASSED\n");
+
+ END();
+}
+
+/* ---------------------------------------------------------------------- */
+/* Subscriber gracefully terminates dialog upon 483 */
+
+static
+size_t change_status_to_483(void *a, void *message, size_t len);
+int save_until_notified_and_responded_twice(CONDITION_PARAMS);
+int save_until_notify_responded_twice(CONDITION_PARAMS);
+int accept_subscription_until_terminated(CONDITION_PARAMS);
+
+int test_subscribe_notify_graceful(struct context *ctx)
+{
+ if (!ctx->nat)
+ return 0;
+
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e, *en1, *en2, *es;
+ sip_t const *sip;
+ tagi_t const *n_tags, *r_tags;
+ struct nat_filter *f;
+
+ if (print_headings)
+ printf("TEST NUA-11.5.1: establishing subscription\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ SUBSCRIBE(a, a_call, a_call->nh, NUTAG_URL(b->contact->m_url),
+ SIPTAG_EVENT_STR("presence"),
+ SIPTAG_ACCEPT_STR("application/xpidf, application/pidf+xml"),
+ TAG_END());
+
+ run_ab_until(ctx, -1, save_until_notified_and_responded,
+ -1, accept_and_notify);
+
+ /* Client events:
+ nua_subscribe(), nua_i_notify/nua_r_subscribe
+ */
+ for (en1 = en2 = es = NULL, e = a->events->head; e; e = e->next) {
+ if (en1 == NULL && e->data->e_event == nua_i_notify)
+ en1 = e;
+ else if (es == NULL && e->data->e_event == nua_r_subscribe)
+ es = e;
+ else
+ TEST_1(!e);
+ }
+ TEST_1(e = en1); TEST_E(e->data->e_event, nua_i_notify);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_event); TEST_S(sip->sip_event->o_type, "presence");
+ TEST_1(sip->sip_content_type);
+ TEST_S(sip->sip_content_type->c_type, "application/pidf+xml");
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "pending");
+ TEST_1(sip->sip_subscription_state->ss_expires);
+ n_tags = e->data->e_tags;
+ TEST_1(tl_find(n_tags, nutag_substate));
+ TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_pending);
+
+ TEST_1(e = es); TEST_E(e->data->e_event, nua_r_subscribe);
+ r_tags = e->data->e_tags;
+ TEST_1(tl_find(r_tags, nutag_substate));
+ if (es == a->events->head)
+ TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_embryonic);
+ else
+ TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_pending);
+ free_events_in_list(ctx, a->events);
+
+ /* Server events: nua_i_subscribe, nua_r_notify */
+ TEST_1(e = b->events->head);
+ TEST_E(e->data->e_event, nua_i_subscribe);
+ TEST_E(e->data->e_status, 100);
+ TEST_1(sip = sip_object(e->data->e_msg));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_notify);
+ r_tags = e->data->e_tags;
+ TEST_1(tl_find(r_tags, nutag_substate));
+ TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_pending);
+
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-11.5.1: PASSED\n");
+
+ if (print_headings)
+ printf("TEST NUA-11.5.2: terminate gracefully upon 483\n");
+
+ TEST_1(f = test_nat_add_filter(ctx->nat,
+ change_status_to_483, NULL,
+ nat_inbound));
+
+ SUBSCRIBE(a, a_call, a_call->nh, TAG_END());
+
+ run_ab_until(ctx, -1, save_until_notified_and_responded_twice,
+ -1, accept_subscription_until_terminated);
+
+#if 0
+ /* Client events:
+ nua_unsubscribe(), nua_i_notify/nua_r_unsubscribe
+ */
+ TEST_1(e = a->events->head);
+ if (e->data->e_event == nua_i_notify) {
+ TEST_E(e->data->e_event, nua_i_notify);
+ n_tags = e->data->e_tags;
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_event);
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "terminated");
+ TEST_1(!sip->sip_subscription_state->ss_expires);
+ TEST_1(tl_find(n_tags, nutag_substate));
+ TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_terminated);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_unsubscribe);
+ TEST(e->data->e_status, 200);
+ r_tags = e->data->e_tags;
+ TEST_1(tl_find(r_tags, nutag_substate));
+ TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_terminated);
+ }
+ else {
+ TEST_E(e->data->e_event, nua_r_unsubscribe);
+ TEST(e->data->e_status, 200);
+ r_tags = e->data->e_tags;
+ TEST_1(tl_find(r_tags, nutag_substate));
+ TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_terminated);
+ }
+ TEST_1(!e->next);
+
+ /* Notifier events: nua_r_notify */
+ TEST_1(e = b->events->head);
+ TEST_E(e->data->e_event, nua_i_subscribe);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(tl_find(e->data->e_tags, nutag_substate));
+ TEST(tl_find(e->data->e_tags, nutag_substate)->t_value,
+ nua_substate_terminated);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_notify);
+ TEST_1(e->data->e_status >= 200);
+ r_tags = e->data->e_tags;
+ TEST_1(tl_find(r_tags, nutag_substate));
+ TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_terminated);
+#endif
+
+ free_events_in_list(ctx, a->events);
+ free_events_in_list(ctx, b->events);
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ test_nat_remove_filter(ctx->nat, f);
+
+ if (print_headings)
+ printf("TEST NUA-11.5.2: PASSED\n");
+
+ END();
+}
+
+static
+size_t change_status_to_483(void *a, void *message, size_t len)
+{
+ (void)a;
+
+ if (strncmp("SIP/2.0 2", message, 9) == 0) {
+ memcpy(message, "SIP/2.0 483", 11);
+ }
+ return len;
+}
+
+int save_until_notified_and_responded_twice(CONDITION_PARAMS)
+{
+ save_event_in_list(ctx, event, ep, call);
+
+ if (event == nua_i_notify) {
+ if (ep->flags.bit0)
+ ep->flags.bit1 = 1;
+ ep->flags.bit0 = 1;
+ }
+
+ if (event == nua_r_subscribe || event == nua_r_unsubscribe) {
+ if (status >= 300)
+ return 1;
+ else if (status >= 200) {
+ if (ep->flags.bit2)
+ ep->flags.bit3 = 1;
+ ep->flags.bit2 = 1;
+ }
+ }
+
+ return ep->flags.bit0 && ep->flags.bit1 && ep->flags.bit2 && ep->flags.bit3;
+}
+
+int save_until_notify_responded_twice(CONDITION_PARAMS)
+{
+ save_event_in_list(ctx, event, ep, call);
+
+ if (event == nua_r_notify) {
+ if (ep->flags.bit0)
+ ep->flags.bit1 = 1;
+ ep->flags.bit0 = 1;
+ }
+
+ return ep->flags.bit0 && ep->flags.bit1;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * When incoming SUBSCRIBE, send NOTIFY,
+ * 200 OK SUBSCRIBE when NOTIFY has been responded.
+ */
+int notify_and_accept(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ save_event_in_list(ctx, event, ep, call);
+
+ switch (event) {
+ case nua_i_subscribe:
+ if (status < 200) {
+ NOTIFY(ep, call, nh,
+ SIPTAG_EVENT(sip->sip_event),
+ SIPTAG_CONTENT_TYPE_STR("application/pidf+xml"),
+ SIPTAG_PAYLOAD_STR(presence_closed),
+ TAG_END());
+ }
+ return 0;
+
+ case nua_r_notify:
+ if (status >= 200) {
+ struct event *e;
+ for (e = ep->events->head; e; e = e->next) {
+ if (e->data->e_event == nua_i_subscribe) {
+ RESPOND(ep, call, nh, SIP_200_OK,
+ NUTAG_WITH(e->data->e_msg),
+ TAG_END());
+ break;
+ }
+ }
+ return 1;
+ }
+
+ default:
+ return 0;
+ }
+}
+
+int test_event_fetch(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e, *en1, *en2, *es;
+ sip_t const *sip;
+ tagi_t const *r_tags;
+
+ if (print_headings)
+ printf("TEST NUA-11.6.1: event fetch using nua_notify()\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+/* Fetch 1:
+
+ A B
+ | |
+ |------SUBSCRIBE----->|
+ | Expires: 0 |
+ |<---------202--------|
+ | |
+ |<-------NOTIFY-------|
+ | S-State: terminated |
+ |-------200 OK------->|
+ | |
+*/
+
+ SUBSCRIBE(a, a_call, a_call->nh, NUTAG_URL(b->contact->m_url),
+ SIPTAG_EVENT_STR("presence"),
+ SIPTAG_EXPIRES_STR("0"),
+ SIPTAG_ACCEPT_STR("application/xpidf, application/pidf+xml"),
+ TAG_END());
+
+ run_ab_until(ctx, -1, save_until_notified_and_responded,
+ -1, accept_and_notify);
+
+ /* Client events:
+ nua_subscribe(), nua_i_notify/nua_r_subscribe/nua_i_notify
+ */
+ for (en1 = en2 = es = NULL, e = a->events->head; e; e = e->next) {
+ if (en1 == NULL && e->data->e_event == nua_i_notify)
+ en1 = e;
+ else if (en2 == NULL && e->data->e_event == nua_i_notify)
+ en2 = e;
+ else if (e->data->e_event == nua_r_subscribe)
+ es = e;
+ else
+ TEST_1(!e);
+ }
+
+ TEST_1(e = en1);
+ TEST_E(e->data->e_event, nua_i_notify);
+ TEST_1(tl_find(e->data->e_tags, nutag_substate));
+ TEST(tl_find(e->data->e_tags, nutag_substate)->t_value, nua_substate_terminated);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_event); TEST_S(sip->sip_event->o_type, "presence");
+ TEST_1(sip->sip_content_type);
+ TEST_S(sip->sip_content_type->c_type, "application/pidf+xml");
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "terminated");
+
+ TEST_1(e = es); TEST_E(e->data->e_event, nua_r_subscribe);
+ TEST_1(e->data->e_status == 202 || e->data->e_status == 200);
+ TEST_1(tl_find(e->data->e_tags, nutag_substate));
+
+ if (es == a->events->head)
+ TEST(tl_find(e->data->e_tags, nutag_substate)->t_value, nua_substate_embryonic);
+ else
+ TEST(tl_find(e->data->e_tags, nutag_substate)->t_value, nua_substate_terminated);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_expires);
+ TEST_1(sip->sip_expires->ex_delta == 0);
+
+ free_events_in_list(ctx, a->events);
+
+ /* Server events: nua_i_subscribe, nua_r_notify */
+ TEST_1(e = b->events->head);
+ TEST_E(e->data->e_event, nua_i_subscribe);
+ TEST_E(e->data->e_status, 100);
+ TEST_1(sip = sip_object(e->data->e_msg));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_notify);
+ r_tags = e->data->e_tags;
+ TEST_1(tl_find(r_tags, nutag_substate));
+ TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_terminated);
+
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-11.6.1: PASSED\n");
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+
+ if (print_headings)
+ printf("TEST NUA-11.6.2: event fetch, NOTIFY comes before 202\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+/* Fetch 2:
+
+ A B
+ | |
+ |------SUBSCRIBE----->|
+ | Expires: 0 |
+ |<-------NOTIFY-------|
+ | S-State: terminated |
+ |-------200 OK------->|
+ | |
+ |<---------202--------|
+ | |
+*/
+
+ SUBSCRIBE(a, a_call, a_call->nh, NUTAG_URL(b->contact->m_url),
+ SIPTAG_EVENT_STR("presence"),
+ SIPTAG_EXPIRES_STR("0"),
+ SIPTAG_ACCEPT_STR("application/xpidf, application/pidf+xml"),
+ TAG_END());
+
+ run_ab_until(ctx, -1, save_until_notified_and_responded,
+ -1, notify_and_accept);
+
+ /* Client events:
+ nua_subscribe(), nua_i_notify/nua_r_subscribe/nua_i_notify
+ */
+ for (en1 = en2 = es = NULL, e = a->events->head; e; e = e->next) {
+ if (en1 == NULL && e->data->e_event == nua_i_notify)
+ en1 = e;
+ else if (en2 == NULL && e->data->e_event == nua_i_notify)
+ en2 = e;
+ else if (e->data->e_event == nua_r_subscribe)
+ es = e;
+ else
+ TEST_1(!e);
+ }
+
+ TEST_1(e = en1);
+ TEST_E(e->data->e_event, nua_i_notify);
+ TEST_1(tl_find(e->data->e_tags, nutag_substate));
+ TEST(tl_find(e->data->e_tags, nutag_substate)->t_value, nua_substate_terminated);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_event); TEST_S(sip->sip_event->o_type, "presence");
+ TEST_1(sip->sip_content_type);
+ TEST_S(sip->sip_content_type->c_type, "application/pidf+xml");
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "terminated");
+
+ TEST_1(e = es); TEST_E(e->data->e_event, nua_r_subscribe);
+ TEST_1(e->data->e_status == 202 || e->data->e_status == 200);
+ TEST_1(tl_find(e->data->e_tags, nutag_substate));
+
+ if (es == a->events->head)
+ TEST(tl_find(e->data->e_tags, nutag_substate)->t_value, nua_substate_embryonic);
+ else
+ TEST(tl_find(e->data->e_tags, nutag_substate)->t_value, nua_substate_terminated);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_expires);
+ TEST_1(sip->sip_expires->ex_delta == 0);
+
+ free_events_in_list(ctx, a->events);
+
+ /* Server events: nua_i_subscribe, nua_r_notify */
+ TEST_1(e = b->events->head);
+ TEST_E(e->data->e_event, nua_i_subscribe);
+ TEST_E(e->data->e_status, 100);
+ TEST_1(sip = sip_object(e->data->e_msg));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_notify);
+ r_tags = e->data->e_tags;
+ TEST_1(tl_find(r_tags, nutag_substate));
+ TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_terminated);
+
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-11.6.2: PASSED\n");
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ END();
+}
+
+/* ---------------------------------------------------------------------- */
+/* Unsolicited NOTIFY */
+
+int accept_notify(CONDITION_PARAMS);
+
+int test_newsub_notify(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e;
+ sip_t const *sip;
+ sip_call_id_t *i;
+ tagi_t const *n_tags, *r_tags;
+
+#if 0
+
+ if (print_headings)
+ printf("TEST NUA-11.7.1: rejecting NOTIFY without subscription locally\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ NOTIFY(a, a_call, a_call->nh, NUTAG_URL(b->contact->m_url),
+ SIPTAG_SUBJECT_STR("NUA-11.7.1"),
+ SIPTAG_EVENT_STR("message-summary"),
+ SIPTAG_CONTENT_TYPE_STR("application/simple-message-summary"),
+ SIPTAG_PAYLOAD_STR("Messages-Waiting: yes"),
+ TAG_END());
+
+ run_a_until(ctx, -1, save_until_final_response);
+
+ /* Client events:
+ nua_notify(), nua_r_notify
+ */
+ TEST_1(e = a->events->head);
+ TEST_E(e->data->e_event, nua_r_notify);
+ TEST(e->data->e_status, 481);
+ TEST_1(!e->data->e_msg);
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ if (print_headings)
+ printf("TEST NUA-11.7.1: PASSED\n");
+
+ if (print_headings)
+ printf("TEST NUA-11.7.2: rejecting NOTIFY without subscription\n");
+
+ TEST_1(i = sip_call_id_create(nua_handle_home(a_call->nh), NULL));
+
+ NOTIFY(a, a_call, a_call->nh, NUTAG_URL(b->contact->m_url),
+ NUTAG_NEWSUB(1),
+ SIPTAG_SUBJECT_STR("NUA-11.7.2 first"),
+ SIPTAG_FROM_STR("<sip:alice at example.com>;tag=nua-11.7.2"),
+ SIPTAG_CALL_ID(i),
+ SIPTAG_CSEQ_STR("1 NOTIFY"),
+ SIPTAG_EVENT_STR("message-summary"),
+ SIPTAG_CONTENT_TYPE_STR("application/simple-message-summary"),
+ SIPTAG_PAYLOAD_STR("Messages-Waiting: yes"),
+ TAG_END());
+
+ run_a_until(ctx, -1, save_until_final_response);
+
+ /* Client events:
+ nua_notify(), nua_r_notify
+ */
+ TEST_1(e = a->events->head);
+ TEST_E(e->data->e_event, nua_r_notify);
+ TEST(e->data->e_status, 481);
+ TEST_1(e->data->e_msg);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+
+ /* 2nd NOTIFY using same dialog */
+ /* Check that server really discards the dialog */
+
+ NOTIFY(a, a_call, a_call->nh, NUTAG_URL(b->contact->m_url),
+ NUTAG_NEWSUB(1),
+ SIPTAG_SUBJECT_STR("NUA-11.7.2 second"),
+ SIPTAG_FROM_STR("<sip:alice at example.com>;tag=nua-11.7.2"),
+ SIPTAG_CALL_ID(i),
+ SIPTAG_CSEQ_STR("2 NOTIFY"),
+ SIPTAG_EVENT_STR("message-summary"),
+ SIPTAG_CONTENT_TYPE_STR("application/simple-message-summary"),
+ SIPTAG_PAYLOAD_STR("Messages-Waiting: yes"),
+ TAG_END());
+
+ run_a_until(ctx, -1, save_until_final_response);
+
+ /* Client events:
+ nua_notify(), nua_r_notify
+ */
+ TEST_1(e = a->events->head);
+ TEST_E(e->data->e_event, nua_r_notify);
+ TEST(e->data->e_status, 481);
+ TEST_1(e->data->e_msg);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+
+ if (print_headings)
+ printf("TEST NUA-11.7.2: PASSED\n");
+
+ /* ---------------------------------------------------------------------- */
+
+ if (print_headings)
+ printf("TEST NUA-11.7.3: accept NOTIFY\n");
+
+ nua_set_params(b->nua, NUTAG_APPL_METHOD("NOTIFY"), TAG_END());
+ run_b_until(ctx, nua_r_set_params, until_final_response);
+
+ NOTIFY(a, a_call, a_call->nh,
+ NUTAG_URL(b->contact->m_url),
+ NUTAG_NEWSUB(1),
+ SIPTAG_SUBJECT_STR("NUA-11.7.3"),
+ SIPTAG_EVENT_STR("message-summary"),
+ SIPTAG_CONTENT_TYPE_STR("application/simple-message-summary"),
+ SIPTAG_PAYLOAD_STR("Messages-Waiting: yes"),
+ TAG_END());
+
+ run_ab_until(ctx, -1, save_until_final_response, -1, accept_notify);
+
+ /* Notifier events: nua_r_notify */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_notify);
+ TEST(e->data->e_status, 200);
+ r_tags = e->data->e_tags;
+ TEST_1(tl_find(r_tags, nutag_substate));
+ TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_terminated);
+
+ /* subscriber events:
+ nua_i_notify
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_notify);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "terminated");
+ n_tags = e->data->e_tags;
+ TEST_1(tl_find(n_tags, nutag_substate));
+ TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_terminated);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-11.7.3: PASSED\n");
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+#else
+ (void)i;
+ nua_set_params(b->nua, NUTAG_APPL_METHOD("NOTIFY"), TAG_END());
+ run_b_until(ctx, nua_r_set_params, until_final_response);
+#endif
+
+ /* ---------------------------------------------------------------------- */
+
+ if (print_headings)
+ printf("TEST NUA-11.7.4: multiple unsolicited NOTIFYs\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ NOTIFY(a, a_call, a_call->nh,
+ NUTAG_URL(b->contact->m_url),
+ NUTAG_NEWSUB(1),
+ SIPTAG_EXPIRES_STR("10"),
+ SIPTAG_SUBJECT_STR("NUA-11.7.4aa"),
+ SIPTAG_CONTENT_TYPE_STR("application/simple-message-summary"),
+ SIPTAG_PAYLOAD_STR("Messages-Waiting: no"),
+ TAG_END());
+
+ NOTIFY(a, a_call, a_call->nh,
+ NUTAG_URL(b->contact->m_url),
+ NUTAG_NEWSUB(1),
+ SIPTAG_SUBSCRIPTION_STATE_STR("active; expires=333"),
+ SIPTAG_SUBJECT_STR("NUA-11.7.4a"),
+ SIPTAG_EVENT_STR("message-summary"),
+ SIPTAG_CONTENT_TYPE_STR("application/simple-message-summary"),
+ SIPTAG_PAYLOAD_STR("Messages-Waiting: no"),
+ TAG_END());
+
+ NOTIFY(a, a_call, a_call->nh,
+ NUTAG_URL(b->contact->m_url),
+ NUTAG_NEWSUB(1),
+ SIPTAG_SUBSCRIPTION_STATE_STR("active; expires=3000"),
+ SIPTAG_SUBJECT_STR("NUA-11.7.4b"),
+ SIPTAG_EVENT_STR("message-summary"),
+ SIPTAG_CONTENT_TYPE_STR("application/simple-message-summary"),
+ SIPTAG_PAYLOAD_STR("Messages-Waiting: yes"),
+ TAG_END());
+
+ NOTIFY(a, a_call, a_call->nh,
+ NUTAG_URL(b->contact->m_url),
+ NUTAG_NEWSUB(1),
+ SIPTAG_SUBSCRIPTION_STATE_STR("terminated"),
+ SIPTAG_SUBJECT_STR("NUA-11.7.4c"),
+ SIPTAG_EVENT_STR("message-summary"),
+ SIPTAG_CONTENT_TYPE_STR("application/simple-message-summary"),
+ SIPTAG_PAYLOAD_STR("Messages-Waiting: yes"),
+ TAG_END());
+
+ a->state.n = 4;
+ b->state.n = 4;
+
+ run_ab_until(ctx, -1, save_until_nth_final_response,
+ -1, accept_n_notifys);
+
+ /* Notifier events: nua_r_notify nua_r_notify */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_notify);
+ TEST(e->data->e_status, 200);
+ r_tags = e->data->e_tags;
+ TEST_1(tl_find(r_tags, nutag_substate));
+ TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_terminated);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_notify);
+ TEST(e->data->e_status, 200);
+ r_tags = e->data->e_tags;
+ TEST_1(tl_find(r_tags, nutag_substate));
+ TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_active);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_notify);
+ TEST(e->data->e_status, 200);
+ r_tags = e->data->e_tags;
+ TEST_1(tl_find(r_tags, nutag_substate));
+ TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_active);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_notify);
+ TEST(e->data->e_status, 200);
+ r_tags = e->data->e_tags;
+ TEST_1(tl_find(r_tags, nutag_substate));
+ TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_terminated);
+ TEST_1(!e->next);
+
+ /* subscriber events:
+ nua_i_notify
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_notify);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "terminated");
+ n_tags = e->data->e_tags;
+ TEST_1(tl_find(n_tags, nutag_substate));
+ TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_terminated);
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_notify);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "active");
+ n_tags = e->data->e_tags;
+ TEST_1(tl_find(n_tags, nutag_substate));
+ TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_active);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_notify);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "active");
+ n_tags = e->data->e_tags;
+ TEST_1(tl_find(n_tags, nutag_substate));
+ TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_active);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_notify);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "terminated");
+ n_tags = e->data->e_tags;
+ TEST_1(tl_find(n_tags, nutag_substate));
+ TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_terminated);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ free_events_in_list(ctx, b->events);
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-11.7.4: PASSED\n");
+
+ /* ---------------------------------------------------------------------- */
+
+ if (print_headings)
+ printf("TEST NUA-11.7.5: multiple unsolicited NOTIFYs\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ NOTIFY(a, a_call, a_call->nh,
+ NUTAG_URL(b->contact->m_url),
+ NUTAG_NEWSUB(1),
+ SIPTAG_SUBSCRIPTION_STATE_STR("active; expires=333"),
+ SIPTAG_SUBJECT_STR("NUA-11.7.5a"),
+ SIPTAG_EVENT_STR("message-summary"),
+ SIPTAG_CONTENT_TYPE_STR("application/simple-message-summary"),
+ SIPTAG_PAYLOAD_STR("Messages-Waiting: no"),
+ TAG_END());
+
+ NOTIFY(a, a_call, a_call->nh,
+ NUTAG_URL(b->contact->m_url),
+ NUTAG_NEWSUB(1),
+ SIPTAG_SUBSCRIPTION_STATE_STR("active; expires=3000"),
+ SIPTAG_SUBJECT_STR("NUA-11.7.5b"),
+ SIPTAG_EVENT_STR("message-summary"),
+ SIPTAG_CONTENT_TYPE_STR("application/simple-message-summary"),
+ SIPTAG_PAYLOAD_STR("Messages-Waiting: yes"),
+ TAG_END());
+
+ NOTIFY(a, a_call, a_call->nh,
+ NUTAG_URL(b->contact->m_url),
+ NUTAG_NEWSUB(1),
+ SIPTAG_SUBSCRIPTION_STATE_STR("terminated"),
+ SIPTAG_SUBJECT_STR("NUA-11.7.5c"),
+ SIPTAG_EVENT_STR("message-summary"),
+ SIPTAG_CONTENT_TYPE_STR("application/simple-message-summary"),
+ SIPTAG_PAYLOAD_STR("Messages-Waiting: yes"),
+ TAG_END());
+
+ a->state.n = 3;
+ b->state.n = 3;
+
+ run_ab_until(ctx, -1, save_until_nth_final_response,
+ -1, accept_n_notifys);
+
+ /* Notifier events: nua_r_notify nua_r_notify */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_notify);
+ TEST(e->data->e_status, 200);
+ r_tags = e->data->e_tags;
+ TEST_1(tl_find(r_tags, nutag_substate));
+ TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_active);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_notify);
+ TEST(e->data->e_status, 200);
+ r_tags = e->data->e_tags;
+ TEST_1(tl_find(r_tags, nutag_substate));
+ TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_active);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_notify);
+ TEST(e->data->e_status, 200);
+ r_tags = e->data->e_tags;
+ TEST_1(tl_find(r_tags, nutag_substate));
+ TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_terminated);
+ TEST_1(!e->next);
+
+ /* subscriber events:
+ nua_i_notify
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_notify);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "active");
+ n_tags = e->data->e_tags;
+ TEST_1(tl_find(n_tags, nutag_substate));
+ TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_active);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_notify);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "active");
+ n_tags = e->data->e_tags;
+ TEST_1(tl_find(n_tags, nutag_substate));
+ TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_active);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_notify);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "terminated");
+ n_tags = e->data->e_tags;
+ TEST_1(tl_find(n_tags, nutag_substate));
+ TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_terminated);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+ free_events_in_list(ctx, b->events);
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-11.7.5: PASSED\n");
+
+ /* ---------------------------------------------------------------------- */
+
+#if 0
+
+ if (print_headings)
+ printf("TEST NUA-11.7.6: unsolicited NOTIFY handle destroyed\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ NOTIFY(a, a_call, a_call->nh,
+ NUTAG_URL(b->contact->m_url),
+ NUTAG_NEWSUB(1),
+ SIPTAG_SUBSCRIPTION_STATE_STR("active; expires=333"),
+ SIPTAG_SUBJECT_STR("NUA-11.7.6a"),
+ SIPTAG_EVENT_STR("message-summary"),
+ SIPTAG_CONTENT_TYPE_STR("application/simple-message-summary"),
+ SIPTAG_PAYLOAD_STR("Messages-Waiting: no"),
+ TAG_END());
+
+ NOTIFY(a, a_call, a_call->nh,
+ NUTAG_URL(b->contact->m_url),
+ NUTAG_NEWSUB(1),
+ SIPTAG_SUBSCRIPTION_STATE_STR("active; expires=3000"),
+ SIPTAG_SUBJECT_STR("NUA-11.7.6b"),
+ SIPTAG_EVENT_STR("message-summary"),
+ SIPTAG_CONTENT_TYPE_STR("application/simple-message-summary"),
+ SIPTAG_PAYLOAD_STR("Messages-Waiting: yes"),
+ TAG_END());
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+ a->state.n = 3;
+ b->state.n = 3;
+
+ run_b_until(ctx, -1, accept_n_notifys);
+
+ /* subscriber events:
+ nua_i_notify
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_notify);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "active");
+ n_tags = e->data->e_tags;
+ TEST_1(tl_find(n_tags, nutag_substate));
+ TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_active);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_notify);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "active");
+ n_tags = e->data->e_tags;
+ TEST_1(tl_find(n_tags, nutag_substate));
+ TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_active);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_notify);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "terminated");
+ n_tags = e->data->e_tags;
+ TEST_1(tl_find(n_tags, nutag_substate));
+ TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_terminated);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-11.7.6: PASSED\n");
+
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+#endif
+
+ if (print_headings)
+ printf("TEST NUA-11.7: PASSED\n");
+
+ END();
+}
+
+/**Terminate when received notify.
+ * Respond to NOTIFY with 200 OK if it has not been responded.
+ * Save events (except nua_i_active or terminated).
+ */
+int accept_notify(CONDITION_PARAMS)
+{
+ if (event == nua_i_notify && status < 200)
+ RESPOND(ep, call, nh, SIP_200_OK,
+ NUTAG_WITH_THIS(ep->nua),
+ TAG_END());
+
+ save_event_in_list(ctx, event, ep, call);
+
+ return event == nua_i_notify;
+}
+
+int save_until_nth_final_response(CONDITION_PARAMS)
+{
+ save_event_in_list(ctx, event, ep, call);
+
+ if (nua_r_set_params <= event && event < nua_i_network_changed
+ && status >= 200) {
+ if (ep->state.n > 0)
+ ep->state.n--;
+ return ep->state.n == 0;
+ }
+
+ return 0;
+}
+
+int accept_n_notifys(CONDITION_PARAMS)
+{
+ tagi_t const *substate = tl_find(tags, nutag_substate);
+
+ if (event == nua_i_notify && status < 200)
+ RESPOND(ep, call, nh, SIP_200_OK,
+ NUTAG_WITH_THIS(ep->nua),
+ TAG_END());
+
+ save_event_in_list(ctx, event, ep, call);
+
+ if (event != nua_i_notify)
+ return 0;
+
+ if (ep->state.n > 0)
+ ep->state.n--;
+
+ if (ep->state.n == 0)
+ return 1;
+
+ if (substate && substate->t_value == nua_substate_terminated) {
+ if (call && call->nh == nh) {
+ call->nh = NULL;
+ nua_handle_destroy(nh);
+ }
+ }
+
+ return 0;
+}
+
+/* ======================================================================== */
+
+int save_until_subscription_terminated(CONDITION_PARAMS);
+int accept_subscription_until_terminated(CONDITION_PARAMS);
+
+/* Timeout subscription */
+int test_subscription_timeout(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e, *en, *es;
+ sip_t const *sip;
+ tagi_t const *n_tags, *r_tags;
+
+ if (print_headings)
+ printf("TEST NUA-11.8: subscribe and wait until subscription times out\n");
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ METHOD(a, a_call, a_call->nh,
+ NUTAG_METHOD("SUBSCRIBE"),
+ NUTAG_URL(b->contact->m_url),
+ SIPTAG_EVENT_STR("presence"),
+ SIPTAG_ACCEPT_STR("application/xpidf, application/pidf+xml"),
+ SIPTAG_EXPIRES_STR("2"),
+ NUTAG_APPL_METHOD("NOTIFY"),
+ NUTAG_DIALOG(2),
+ TAG_END());
+
+ run_ab_until(ctx,
+ -1, save_until_subscription_terminated,
+ -1, accept_subscription_until_terminated);
+
+ /* Client events:
+ nua_method(), nua_i_notify/nua_r_method, nua_i_notify
+ */
+ TEST_1(en = event_by_type(a->events->head, nua_i_notify));
+ TEST_1(es = event_by_type(a->events->head, nua_r_method));
+
+ TEST_1(e = en); TEST_E(e->data->e_event, nua_i_notify);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ n_tags = e->data->e_tags;
+
+ TEST_1(e = es); TEST_E(e->data->e_event, nua_r_method);
+ r_tags = e->data->e_tags;
+
+ TEST_1(sip->sip_event); TEST_S(sip->sip_event->o_type, "presence");
+ TEST_1(sip->sip_content_type);
+ TEST_S(sip->sip_content_type->c_type, "application/pidf+xml");
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "pending");
+ TEST_1(sip->sip_subscription_state->ss_expires);
+ TEST_1(tl_find(n_tags, nutag_substate));
+ TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_pending);
+
+ if (es->next == en)
+ e = en->next;
+ else
+ e = es->next;
+
+ TEST_1(e); TEST_E(e->data->e_event, nua_i_notify);
+ n_tags = e->data->e_tags;
+ TEST_1(tl_find(n_tags, nutag_substate));
+ TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_terminated);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_event);
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "terminated");
+ TEST_1(!sip->sip_subscription_state->ss_expires);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, a->events);
+
+ /* Server events: nua_i_subscribe, nua_r_notify */
+ TEST_1(e = b->events->head);
+ TEST_E(e->data->e_event, nua_i_subscribe);
+ TEST_E(e->data->e_status, 100);
+ TEST_1(sip = sip_object(e->data->e_msg));
+
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_notify);
+ r_tags = e->data->e_tags;
+ TEST_1(tl_find(r_tags, nutag_substate));
+ TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_pending);
+
+ /* Notifier events: 2nd nua_r_notify */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_notify);
+ TEST_1(e->data->e_status >= 200);
+ r_tags = e->data->e_tags;
+ TEST_1(tl_find(r_tags, nutag_substate));
+ TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_terminated);
+
+ free_events_in_list(ctx, b->events);
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-11.8: PASSED\n");
+
+ END();
+}
+
+int save_until_subscription_terminated(CONDITION_PARAMS)
+{
+ void *with = nua_current_request(nua);
+
+ save_event_in_list(ctx, event, ep, call);
+
+ if (event == nua_i_notify) {
+ if (status < 200)
+ RESPOND(ep, call, nh, SIP_200_OK, NUTAG_WITH(with), TAG_END());
+
+ tags = tl_find(tags, nutag_substate);
+ return tags && tags->t_value == nua_substate_terminated;
+ }
+
+ return 0;
+}
+
+int accept_subscription_until_terminated(CONDITION_PARAMS)
+{
+ void *with = nua_current_request(nua);
+
+ save_event_in_list(ctx, event, ep, call);
+
+ if (event == nua_i_subscribe && status < 200) {
+ RESPOND(ep, call, nh, SIP_202_ACCEPTED,
+ NUTAG_WITH(with),
+ SIPTAG_EXPIRES_STR("360"),
+ TAG_END());
+ NOTIFY(ep, call, nh,
+ SIPTAG_EVENT(sip->sip_event),
+ SIPTAG_CONTENT_TYPE_STR("application/pidf+xml"),
+ SIPTAG_PAYLOAD_STR(presence_closed),
+ NUTAG_SUBSTATE(nua_substate_pending),
+ TAG_END());
+ }
+ else if (event == nua_r_notify) {
+ tags = tl_find(tags, nutag_substate);
+ return tags && tags->t_value == nua_substate_terminated;
+ }
+
+ return 0;
+}
+
+
+/* ======================================================================== */
+/* Test simple methods: MESSAGE, PUBLISH, SUBSCRIBE/NOTIFY */
+
+int test_simple(struct context *ctx)
+{
+ return 0
+ || test_message(ctx)
+ || test_publish(ctx)
+ || test_subscribe_notify(ctx)
+ || test_event_fetch(ctx)
+ || test_subscribe_notify_graceful(ctx)
+ || test_newsub_notify(ctx)
+ || test_subscription_timeout(ctx)
+ ;
+}
Added: freeswitch/trunk/libs/sofia-sip/tests/test_sip_events.c
==============================================================================
--- (empty file)
+++ freeswitch/trunk/libs/sofia-sip/tests/test_sip_events.c Wed May 14 15:10:54 2008
@@ -0,0 +1,601 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2005 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at 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 test_sip_events.c
+ * @brief NUA-12 tests: SUBSCRIBE/NOTIFY.
+ *
+ * @author Pekka Pessi <Pekka.Pessi at nokia.com>
+ * @author Martti Mela <Martti Mela at nokia.com>
+ *
+ * @date Created: Wed Aug 17 12:12:12 EEST 2005 ppessi
+ */
+
+#include "config.h"
+
+#include "test_nua.h"
+#include <sofia-sip/su_tag_class.h>
+#include <sofia-sip/nea.h>
+
+#if !HAVE_MEMMEM
+void *memmem(const void *haystack, size_t haystacklen,
+ const void *needle, size_t needlelen);
+#endif
+
+#if HAVE_FUNC
+#elif HAVE_FUNCTION
+#define __func__ __FUNCTION__
+#else
+#define __func__ "test_sip_events"
+#endif
+
+/* ======================================================================== */
+/* Test events methods: SUBSCRIBE/NOTIFY */
+
+/**Terminate until received notify.
+ * Save events (except nua_i_active or terminated).
+ */
+int save_until_notified(CONDITION_PARAMS)
+{
+ save_event_in_list(ctx, event, ep, call);
+ return event == nua_i_notify;
+}
+
+int save_until_notified_and_responded(CONDITION_PARAMS)
+{
+ save_event_in_list(ctx, event, ep, call);
+
+ if (event == nua_i_notify) ep->flags.bit0 = 1;
+ if (event == nua_r_subscribe || event == nua_r_unsubscribe) {
+ if (status == 407) {
+ AUTHENTICATE(ep, call, nh,
+ NUTAG_AUTH("Digest:\"test-proxy\":charlie:secret"),
+ TAG_END());
+ }
+ else if (status >= 300)
+ return 1;
+ else if (status >= 200)
+ ep->flags.bit1 = 1;
+ }
+
+ return ep->flags.bit0 && ep->flags.bit1;
+}
+
+
+int save_until_subscription(CONDITION_PARAMS)
+{
+ save_event_in_list(ctx, event, ep, call);
+ return event == nua_i_subscription;
+}
+
+
+int test_events(struct context *ctx)
+{
+ BEGIN();
+
+ struct endpoint *a = &ctx->a, *b = &ctx->b;
+ struct call *a_call = a->call, *b_call = b->call;
+ struct event *e, *en, *es;
+ sip_t const *sip;
+ tagi_t const *t, *n_tags, *r_tags;
+ url_t b_url[1];
+ enum nua_substate substate;
+ nea_sub_t *sub = NULL;
+
+ char const open[] =
+ "<?xml version='1.0' encoding='UTF-8'?>\n"
+ "<presence xmlns='urn:ietf:params:xml:ns:cpim-pidf' \n"
+ " entity='pres:bob at example.org'>\n"
+ " <tuple id='ksac9udshce'>\n"
+ " <status><basic>open</basic></status>\n"
+ " <contact priority='1.0'>sip:bob at example.org</contact>\n"
+ " </tuple>\n"
+ "</presence>\n";
+
+ char const closed[] =
+ "<?xml version='1.0' encoding='UTF-8'?>\n"
+ "<presence xmlns='urn:ietf:params:xml:ns:cpim-pidf' \n"
+ " entity='pres:bob at example.org'>\n"
+ " <tuple id='ksac9udshce'>\n"
+ " <status><basic>closed</basic></status>\n"
+ " </tuple>\n"
+ "</presence>\n";
+
+
+/* SUBSCRIBE test
+
+ A B
+ |------SUBSCRIBE---->|
+ |<--------405--------|
+ | |
+
+*/
+ if (print_headings)
+ printf("TEST NUA-12.1: SUBSCRIBE without notifier\n");
+
+ nua_set_params(b->nua, SIPTAG_ALLOW_EVENTS(NULL), TAG_END());
+ run_b_until(ctx, nua_r_set_params, until_final_response);
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ SUBSCRIBE(a, a_call, a_call->nh, NUTAG_URL(b->contact->m_url),
+ SIPTAG_EVENT_STR("presence"),
+ TAG_END());
+
+ run_ab_until(ctx, -1, save_until_final_response,
+ -1, NULL /* XXX save_until_received */);
+
+ /* Client events:
+ nua_subscribe(), nua_r_subscribe
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_subscribe);
+ TEST(e->data->e_status, 489);
+ TEST_1(!e->next);
+
+#if 0 /* XXX */
+ /*
+ Server events:
+ nua_i_subscribe
+ */
+ TEST_1(e = b->events->head);
+ TEST_E(e->data->e_event, nua_i_subscribe);
+ TEST(e->data->e_status, 405);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_event);
+ TEST_S(sip->sip_event->o_type, "presence");
+ TEST_1(!e->next);
+#endif
+
+ free_events_in_list(ctx, a->events);
+ free_events_in_list(ctx, b->events);
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ if (print_headings)
+ printf("TEST NUA-12.1: PASSED\n");
+
+ /* ---------------------------------------------------------------------- */
+
+/* SUBSCRIBE test using notifier and establishing subscription
+
+ A B
+ | |
+ |------SUBSCRIBE---->|
+ |<--------202--------|
+ |<------NOTIFY-------|
+ |-------200 OK------>|
+ | |
+*/
+
+ if (print_headings)
+ printf("TEST NUA-12.2: using notifier and establishing subscription\n");
+
+ TEST_1(b_call->nh = nua_handle(b->nua, b_call, TAG_END()));
+
+ *b_url = *b->contact->m_url;
+
+ NOTIFIER(b, b_call, b_call->nh,
+ NUTAG_URL(b_url),
+ SIPTAG_EVENT_STR("presence"),
+ SIPTAG_CONTENT_TYPE_STR("application/pidf+xml"),
+ SIPTAG_PAYLOAD_STR(closed),
+ NEATAG_THROTTLE(1),
+ TAG_END());
+ run_b_until(ctx, nua_r_notifier, until_final_response);
+
+ SUBSCRIBE(a, a_call, a_call->nh, NUTAG_URL(b->contact->m_url),
+ SIPTAG_EVENT_STR("presence"),
+ SIPTAG_ACCEPT_STR("application/xpidf, application/pidf+xml"),
+ TAG_END());
+
+ run_ab_until(ctx, -1, save_until_notified_and_responded,
+ -1, save_until_received);
+
+ /* Client events:
+ nua_subscribe(), nua_i_notify/nua_r_subscribe
+ */
+ TEST_1(en = event_by_type(a->events->head, nua_i_notify));
+ TEST_1(es = event_by_type(a->events->head, nua_r_subscribe));
+
+ TEST_1(e = es); TEST_E(e->data->e_event, nua_r_subscribe);
+ r_tags = e->data->e_tags;
+ TEST_1(tl_find(r_tags, nutag_substate));
+ if (es->next == en) {
+ TEST_1(200 <= e->data->e_status && e->data->e_status < 300);
+ TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_embryonic);
+ }
+ else {
+ TEST_1(200 <= e->data->e_status && e->data->e_status < 300);
+ TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_active);
+ }
+
+ TEST_1(e = en); TEST_E(e->data->e_event, nua_i_notify);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ n_tags = e->data->e_tags;
+
+ TEST_1(sip->sip_event); TEST_S(sip->sip_event->o_type, "presence");
+ TEST_1(sip->sip_content_type);
+ TEST_S(sip->sip_content_type->c_type, "application/pidf+xml");
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "active");
+ TEST_1(sip->sip_subscription_state->ss_expires);
+ TEST_1(tl_find(n_tags, nutag_substate));
+ TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_active);
+ TEST_1(!en->next || !es->next);
+ free_events_in_list(ctx, a->events);
+
+ /* XXX --- Do not check server side events */
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-12.2: PASSED\n");
+
+ /* ---------------------------------------------------------------------- */
+
+/* NOTIFY with updated content
+
+ A B
+ | |
+ |<------NOTIFY-------|
+ |-------200 OK------>|
+ | |
+*/
+ if (print_headings)
+ printf("TEST NUA-12.3: update notifier\n");
+
+ /* Update presence data */
+
+ NOTIFIER(b, b_call, b_call->nh,
+ SIPTAG_EVENT_STR("presence"),
+ SIPTAG_CONTENT_TYPE_STR("application/pidf+xml"),
+ SIPTAG_PAYLOAD_STR(open),
+ TAG_END());
+
+ run_ab_until(ctx, -1, save_until_notified, -1, save_until_received);
+
+ /* subscriber events:
+ nua_i_notify
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_notify);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_event); TEST_S(sip->sip_event->o_type, "presence");
+ TEST_1(sip->sip_content_type);
+ TEST_S(sip->sip_content_type->c_type, "application/pidf+xml");
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "active");
+ TEST_1(sip->sip_subscription_state->ss_expires);
+ n_tags = e->data->e_tags;
+ TEST_1(tl_find(n_tags, nutag_substate));
+ TEST(tl_find(n_tags, nutag_substate)->t_value,
+ nua_substate_active);
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ /* XXX --- Do not check server side events */
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-12.3: PASSED\n");
+
+ /* ---------------------------------------------------------------------- */
+
+/* un-SUBSCRIBE
+
+ A B
+ | |
+ |------SUBSCRIBE---->|
+ |<--------202--------|
+ |<------NOTIFY-------|
+ |-------200 OK------>|
+ | |
+*/
+ if (print_headings)
+ printf("TEST NUA-12.5: un-SUBSCRIBE\n");
+
+ UNSUBSCRIBE(a, a_call, a_call->nh, TAG_END());
+
+ run_ab_until(ctx, -1, save_until_final_response,
+ -1, save_until_subscription);
+
+ /* Client events:
+ nua_unsubscribe(), nua_i_notify/nua_r_unsubscribe
+ */
+ TEST_1(e = a->events->head);
+ if (e->data->e_event == nua_i_notify) {
+ TEST_E(e->data->e_event, nua_i_notify);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ n_tags = e->data->e_tags;
+ TEST_1(sip->sip_event);
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "terminated");
+ TEST_1(!sip->sip_subscription_state->ss_expires);
+ TEST_1(tl_find(n_tags, nutag_substate));
+ TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_terminated);
+ TEST_1(e = e->next);
+ }
+ TEST_E(e->data->e_event, nua_r_unsubscribe);
+ TEST_1(tl_find(e->data->e_tags, nutag_substate));
+ TEST(tl_find(e->data->e_tags, nutag_substate)->t_value,
+ nua_substate_terminated);
+ /* Currently, NOTIFY is dropped after successful response to unsubscribe */
+ /* But we don't really care.. */
+ /* TEST_1(!e->next); */
+ free_events_in_list(ctx, a->events);
+
+ /* Server events: nua_i_subscription with terminated status */
+ TEST_1(e = b->events->head);
+ TEST_E(e->data->e_event, nua_i_subscription);
+ TEST(tl_gets(e->data->e_tags,
+ NEATAG_SUB_REF(sub),
+ NUTAG_SUBSTATE_REF(substate),
+ TAG_END()), 2);
+ TEST_1(sub);
+ TEST(substate, nua_substate_terminated);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-12.5: PASSED\n");
+
+ /* ---------------------------------------------------------------------- */
+/* Fetch event, SUBSCRIBE with expires: 0
+
+ A B
+ | |
+ |------SUBSCRIBE---->|
+ |<--------202--------|
+ |<------NOTIFY-------|
+ |-------200 OK------>|
+ | |
+*/
+ if (print_headings)
+ printf("TEST NUA-12.5.1: event fetch\n");
+
+ SUBSCRIBE(a, a_call, a_call->nh, NUTAG_URL(b->contact->m_url),
+ SIPTAG_EVENT_STR("presence"),
+ SIPTAG_ACCEPT_STR("application/pidf+xml"),
+ SIPTAG_EXPIRES_STR("0"),
+ TAG_END());
+
+ run_ab_until(ctx, -1, save_until_notified_and_responded,
+ -1, save_until_subscription);
+
+ /* Client events:
+ nua_subscribe(), nua_i_notify/nua_r_subscribe
+ */
+ TEST_1(en = event_by_type(a->events->head, nua_i_notify));
+ TEST_1(es = event_by_type(a->events->head, nua_r_subscribe));
+
+ e = es; TEST_E(e->data->e_event, nua_r_subscribe);
+ TEST_1(t = tl_find(e->data->e_tags, nutag_substate));
+ TEST_1(t->t_value == nua_substate_pending ||
+ t->t_value == nua_substate_terminated ||
+ t->t_value == nua_substate_embryonic);
+
+ e = en; TEST_E(e->data->e_event, nua_i_notify);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ n_tags = e->data->e_tags;
+
+ TEST_1(sip->sip_event); TEST_S(sip->sip_event->o_type, "presence");
+ TEST_1(sip->sip_content_type);
+ TEST_S(sip->sip_content_type->c_type, "application/pidf+xml");
+ TEST_1(sip->sip_payload && sip->sip_payload->pl_data);
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "terminated");
+ TEST_1(tl_find(n_tags, nutag_substate));
+ TEST(tl_find(n_tags, nutag_substate)->t_value,
+ nua_substate_terminated);
+ TEST_1(!en->next || !es->next);
+ free_events_in_list(ctx, a->events);
+
+ /*
+ Server events:
+ nua_i_subscription
+ */
+ TEST_1(e = b->events->head);
+ TEST_E(e->data->e_event, nua_i_subscription);
+ TEST(tl_gets(e->data->e_tags, NEATAG_SUB_REF(sub), TAG_END()), 1);
+ TEST_1(sub);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-12.4.1: PASSED\n");
+
+
+ /* ---------------------------------------------------------------------- */
+/* 2nd SUBSCRIBE with event id
+
+ A B
+ | |
+ |------SUBSCRIBE---->|
+ |<--------202--------|
+ |<------NOTIFY-------|
+ |-------200 OK------>|
+ | |
+*/
+ /* XXX - we should do this before unsubscribing first one */
+ if (print_headings)
+ printf("TEST NUA-12.4.2: establishing 2nd subscription\n");
+
+ NOTIFIER(b, b_call, b_call->nh,
+ SIPTAG_EVENT_STR("presence"),
+ SIPTAG_CONTENT_TYPE_STR("application/xpidf+xml"),
+ SIPTAG_PAYLOAD_STR(open),
+ NEATAG_THROTTLE(1),
+ NUTAG_SUBSTATE(nua_substate_pending),
+ TAG_END());
+ run_b_until(ctx, nua_r_notifier, until_final_response);
+
+ NOTIFIER(b, b_call, b_call->nh,
+ SIPTAG_EVENT_STR("presence"),
+ SIPTAG_CONTENT_TYPE_STR("application/xpidf+xml"),
+ SIPTAG_PAYLOAD_STR(closed),
+ NEATAG_THROTTLE(1),
+ NEATAG_FAKE(1),
+ NUTAG_SUBSTATE(nua_substate_pending),
+ TAG_END());
+ run_b_until(ctx, nua_r_notifier, until_final_response);
+
+ SUBSCRIBE(a, a_call, a_call->nh, NUTAG_URL(b->contact->m_url),
+ SIPTAG_EVENT_STR("presence;id=1"),
+ SIPTAG_ACCEPT_STR("application/xpidf+xml"),
+ TAG_END());
+
+ run_ab_until(ctx, -1, save_until_notified_and_responded,
+ -1, save_until_subscription);
+
+ /* Client events:
+ nua_subscribe(), nua_i_notify/nua_r_subscribe
+ */
+ TEST_1(en = event_by_type(a->events->head, nua_i_notify));
+ TEST_1(es = event_by_type(a->events->head, nua_r_subscribe));
+
+ e = es; TEST_E(e->data->e_event, nua_r_subscribe);
+ TEST_1(t = tl_find(e->data->e_tags, nutag_substate));
+ TEST_1(t->t_value == nua_substate_pending ||
+ t->t_value == nua_substate_embryonic);
+
+ e = en; TEST_E(e->data->e_event, nua_i_notify);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ n_tags = e->data->e_tags;
+
+ TEST_1(sip->sip_event); TEST_S(sip->sip_event->o_type, "presence");
+ TEST_S(sip->sip_event->o_id, "1");
+ TEST_1(sip->sip_content_type);
+ TEST_S(sip->sip_content_type->c_type, "application/xpidf+xml");
+ TEST_1(sip->sip_payload && sip->sip_payload->pl_data);
+ /* Check that we really got "fake" content */
+ TEST_1(memmem(sip->sip_payload->pl_data, sip->sip_payload->pl_len,
+ "<basic>closed</basic>", strlen("<basic>closed</basic>")));
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "pending");
+ TEST_1(sip->sip_subscription_state->ss_expires);
+ TEST_1(tl_find(n_tags, nutag_substate));
+ TEST(tl_find(n_tags, nutag_substate)->t_value,
+ nua_substate_pending);
+ TEST_1(!en->next || !es->next);
+ free_events_in_list(ctx, a->events);
+
+ /*
+ Server events:
+ nua_i_subscription
+ */
+ TEST_1(e = b->events->head);
+ TEST_E(e->data->e_event, nua_i_subscription);
+ TEST(tl_gets(e->data->e_tags, NEATAG_SUB_REF(sub), TAG_END()), 1);
+ TEST_1(sub);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, b->events);
+
+ /* Authorize user A */
+ AUTHORIZE(b, b_call, b_call->nh,
+ NUTAG_SUBSTATE(nua_substate_active),
+ NEATAG_SUB(sub),
+ NEATAG_FAKE(0),
+ TAG_END());
+
+ run_ab_until(ctx, -1, save_until_notified,
+ -1, save_until_final_response);
+
+ /* subscriber events:
+ nua_i_notify with NUTAG_SUBSTATE(nua_substate_active)
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_notify);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_event); TEST_S(sip->sip_event->o_type, "presence");
+ TEST_1(sip->sip_content_type);
+ TEST_S(sip->sip_content_type->c_type, "application/xpidf+xml");
+ TEST_1(sip->sip_payload && sip->sip_payload->pl_data);
+ /* Check that we really got real content */
+ TEST_1(memmem(sip->sip_payload->pl_data, sip->sip_payload->pl_len,
+ "<basic>open</basic>", strlen("<basic>open</basic>")));
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "active");
+ TEST_1(sip->sip_subscription_state->ss_expires);
+ n_tags = e->data->e_tags;
+ TEST_1(tl_find(n_tags, nutag_substate));
+ TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_active);
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ /*
+ Server events:
+ nua_r_authorize
+ */
+ TEST_1(e = b->events->head);
+ TEST_E(e->data->e_event, nua_r_authorize);
+ TEST_1(!e->next);
+
+ free_events_in_list(ctx, b->events);
+
+ if (print_headings)
+ printf("TEST NUA-12.4: PASSED\n");
+
+ /* ---------------------------------------------------------------------- */
+
+/* NOTIFY terminating subscription
+
+ A B
+ | |
+ |<------NOTIFY-------|
+ |-------200 OK------>|
+ | |
+*/
+
+ if (print_headings)
+ printf("TEST NUA-12.6: terminate notifier\n");
+
+ TERMINATE(b, b_call, b_call->nh, TAG_END());
+
+ run_ab_until(ctx, -1, save_until_notified, -1, until_final_response);
+
+ /* Client events:
+ nua_i_notify
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_notify);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_event); TEST_S(sip->sip_event->o_type, "presence");
+ TEST_S(sip->sip_event->o_id, "1");
+ TEST_1(sip->sip_subscription_state);
+ TEST_S(sip->sip_subscription_state->ss_substate, "terminated");
+ TEST_1(!sip->sip_subscription_state->ss_expires);
+ n_tags = e->data->e_tags;
+ TEST_1(tl_find(n_tags, nutag_substate));
+ TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_terminated);
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ if (print_headings)
+ printf("TEST NUA-12.6: PASSED\n");
+
+ /* ---------------------------------------------------------------------- */
+
+
+ nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+ nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+ END(); /* test_events */
+}
More information about the Freeswitch-svn
mailing list