Freeswitch-trunk
Threads by month
- ----- 2026 -----
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- 14172 discussions
[commit] r11969 - in freeswitch/trunk/libs: sofia-sip/libsofia-sip-ua/su win32/sofia
by FreeSWITCH SVN 12 Feb '09
by FreeSWITCH SVN 12 Feb '09
12 Feb '09
Author: mikej
Date: Thu Feb 12 16:09:11 2009
New Revision: 11969
Log:
add sres_sip
Modified:
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/su/ (props changed)
freeswitch/trunk/libs/win32/sofia/libsofia_sip_ua_static.2008.vcproj
freeswitch/trunk/libs/win32/sofia/libsofia_sip_ua_static.vcproj
Modified: freeswitch/trunk/libs/win32/sofia/libsofia_sip_ua_static.2008.vcproj
==============================================================================
--- freeswitch/trunk/libs/win32/sofia/libsofia_sip_ua_static.2008.vcproj (original)
+++ freeswitch/trunk/libs/win32/sofia/libsofia_sip_ua_static.2008.vcproj Thu Feb 12 16:09:11 2009
@@ -618,6 +618,10 @@
>
</File>
<File
+ RelativePath="..\..\sofia-sip\libsofia-sip-ua\sresolv\sres_sip.c"
+ >
+ </File>
+ <File
RelativePath="..\..\sofia-sip\libsofia-sip-ua\sresolv\sresolv.c"
>
</File>
@@ -1347,6 +1351,10 @@
>
</File>
<File
+ RelativePath="..\..\sofia-sip\libsofia-sip-ua\sresolv\sofia-sip\sres_sip.h"
+ >
+ </File>
+ <File
RelativePath="..\..\sofia-sip\libsofia-sip-ua\sresolv\sofia-sip\sresolv.h"
>
</File>
Modified: freeswitch/trunk/libs/win32/sofia/libsofia_sip_ua_static.vcproj
==============================================================================
--- freeswitch/trunk/libs/win32/sofia/libsofia_sip_ua_static.vcproj (original)
+++ freeswitch/trunk/libs/win32/sofia/libsofia_sip_ua_static.vcproj Thu Feb 12 16:09:11 2009
@@ -619,6 +619,10 @@
>
</File>
<File
+ RelativePath="..\..\sofia-sip\libsofia-sip-ua\sresolv\sres_sip.c"
+ >
+ </File>
+ <File
RelativePath="..\..\sofia-sip\libsofia-sip-ua\sresolv\sresolv.c"
>
</File>
@@ -1348,6 +1352,10 @@
>
</File>
<File
+ RelativePath="..\..\sofia-sip\libsofia-sip-ua\sresolv\sofia-sip\sres_sip.h"
+ >
+ </File>
+ <File
RelativePath="..\..\sofia-sip\libsofia-sip-ua\sresolv\sofia-sip\sresolv.h"
>
</File>
1
0
Author: brian
Date: Thu Feb 12 16:06:29 2009
New Revision: 11968
Log:
no longer dialing your own extension checks your own voicemail
Modified:
freeswitch/trunk/conf/dialplan/default.xml
Modified: freeswitch/trunk/conf/dialplan/default.xml
==============================================================================
--- freeswitch/trunk/conf/dialplan/default.xml (original)
+++ freeswitch/trunk/conf/dialplan/default.xml Thu Feb 12 16:06:29 2009
@@ -215,31 +215,26 @@
<condition field="destination_number" expression="^(10[01][0-9])$">
<action application="set" data="dialed_extension=$1"/>
<action application="export" data="dialed_extension=$1"/>
- </condition>
- <condition field="${sip_authorized}" expression="^true">
+ <!-- bind_meta_app can have these args <key> [a|b|ab] [a|b|o|s] <app> -->
+ <action application="bind_meta_app" data="1 b s execute_extension::dx XML features"/>
+ <action application="bind_meta_app" data="2 b s record_session::$${base_dir}/recordings/${caller_id_number}.${strftime(%Y-%m-%d-%H-%M-%S)}.wav"/>
+ <action application="bind_meta_app" data="3 b s execute_extension::cf XML features"/>
+ <action application="set" data="ringback=${us-ring}"/>
+ <action application="set" data="transfer_ringback=$${hold_music}"/>
+ <action application="set" data="call_timeout=30"/>
+ <!-- <action application="set" data="sip_exclude_contact=${network_addr}"/> -->
+ <action application="set" data="hangup_after_bridge=true"/>
+ <!--<action application="set" data="continue_on_fail=NORMAL_TEMPORARY_FAILURE,USER_BUSY,NO_ANSWER,TIMEOUT,NO_ROUTE_DESTINATION"/> -->
+ <action application="set" data="continue_on_fail=true"/>
+ <action application="db" data="insert/${domain_name}-call_return/${dialed_extension}/${caller_id_number}"/>
+ <action application="db" data="insert/${domain_name}-last_dial_ext/${dialed_extension}/${uuid}"/>
+ <action application="set" data="called_party_callgroup=${user_data(${dialed_extension}@${domain_name} var callgroup)}"/>
+ <!--<action application="export" data="nolocal:sip_secure_media=${user_data(${dialed_extension}@${domain_name} var sip_secure_media)}"/>-->
+ <action application="db" data="insert/${domain_name}-last_dial/${called_party_callgroup}/${uuid}"/>
+ <action application="bridge" data="user/${dialed_extension}@${domain_name}"/>
<action application="answer"/>
<action application="sleep" data="1000"/>
- <action application="voicemail" data="check default ${domain_name} ${dialed_extension}"/>
- <!-- bind_meta_app can have these args <key> [a|b|ab] [a|b|o|s] <app> -->
- <anti-action application="bind_meta_app" data="1 b s execute_extension::dx XML features"/>
- <anti-action application="bind_meta_app" data="2 b s record_session::$${base_dir}/recordings/${caller_id_number}.${strftime(%Y-%m-%d-%H-%M-%S)}.wav"/>
- <anti-action application="bind_meta_app" data="3 b s execute_extension::cf XML features"/>
- <anti-action application="set" data="ringback=${us-ring}"/>
- <anti-action application="set" data="transfer_ringback=$${hold_music}"/>
- <anti-action application="set" data="call_timeout=30"/>
- <!-- <anti-action application="set" data="sip_exclude_contact=${network_addr}"/> -->
- <anti-action application="set" data="hangup_after_bridge=true"/>
- <!--<anti-action application="set" data="continue_on_fail=NORMAL_TEMPORARY_FAILURE,USER_BUSY,NO_ANSWER,TIMEOUT,NO_ROUTE_DESTINATION"/> -->
- <anti-action application="set" data="continue_on_fail=true"/>
- <anti-action application="db" data="insert/${domain_name}-call_return/${dialed_extension}/${caller_id_number}"/>
- <anti-action application="db" data="insert/${domain_name}-last_dial_ext/${dialed_extension}/${uuid}"/>
- <anti-action application="set" data="called_party_callgroup=${user_data(${dialed_extension}@${domain_name} var callgroup)}"/>
- <!--<anti-action application="export" data="nolocal:sip_secure_media=${user_data(${dialed_extension}@${domain_name} var sip_secure_media)}"/>-->
- <anti-action application="db" data="insert/${domain_name}-last_dial/${called_party_callgroup}/${uuid}"/>
- <anti-action application="bridge" data="user/${dialed_extension}@${domain_name}"/>
- <anti-action application="answer"/>
- <anti-action application="sleep" data="1000"/>
- <anti-action application="voicemail" data="default ${domain_name} ${dialed_extension}"/>
+ <action application="voicemail" data="default ${domain_name} ${dialed_extension}"/>
</condition>
</extension>
@@ -271,7 +266,7 @@
<!-- voicemail main extension -->
<extension name="vmain">
- <condition field="destination_number" expression="^vmain|4000$">
+ <condition field="destination_number" expression="^vmain|4000|\*98$">
<action application="answer"/>
<action application="sleep" data="1000"/>
<action application="voicemail" data="check default ${domain_name}"/>
1
0
[commit] r11967 - in freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/sresolv: . sofia-sip
by FreeSWITCH SVN 12 Feb '09
by FreeSWITCH SVN 12 Feb '09
12 Feb '09
Author: mikej
Date: Thu Feb 12 15:58:06 2009
New Revision: 11967
Log:
add missing files
Added:
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/sresolv/check_sres_sip.c
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/sresolv/sofia-sip/sres_sip.h
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/sresolv/sres_sip.c
Added: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/sresolv/check_sres_sip.c
==============================================================================
--- (empty file)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/sresolv/check_sres_sip.c Thu Feb 12 15:58:06 2009
@@ -0,0 +1,998 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2009 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi(a)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_sres_sip.c
+ *
+ * @brief Check-driven tester for SIP URI resolver
+ *
+ * @author Pekka Pessi <Pekka.Pessi(a)nokia.com>
+ *
+ * @copyright (C) 2009 Nokia Corporation.
+ */
+
+#include "config.h"
+
+#define SRES_SIP_MAGIC_T void
+
+#include <sofia-sip/sres_sip.h>
+
+#include "s2check.h"
+#include "s2dns.h"
+
+#include <sofia-sip/su_wait.h>
+#include <sofia-sip/sresolv.h>
+#include <sofia-sip/su_log.h>
+#include <sofia-sip/su_string.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <assert.h>
+
+SOFIAPUBVAR su_log_t sresolv_log[];
+
+static void
+setup_logs(int level)
+{
+ su_log_soft_set_level(sresolv_log, level);
+}
+
+/* */
+struct context {
+ su_root_t *root;
+ sres_resolver_t *sres;
+} x[1];
+
+static su_addrinfo_t hint_udp_tcp[2];
+static su_addrinfo_t hint_udp_tcp_tls[2];
+static su_addrinfo_t hint_udp_tcp_ip4[2];
+static su_addrinfo_t hint_tls[1];
+static su_addrinfo_t hint_tls_udp_tcp[1];
+
+static void
+resolver_setup(void)
+{
+ x->root = su_root_create(NULL);
+ assert(x->root);
+ s2_dns_setup(x->root);
+
+ s2_dns_record("test.example.com.", sres_type_a,
+ "test.example.com.", sres_type_a, "12.13.14.25",
+ NULL);
+
+ x->sres = sres_resolver_create(
+ x->root, NULL,
+#if HAVE_WIN32
+ SRESTAG_RESOLV_CONF("NUL"),
+#else
+ SRESTAG_RESOLV_CONF("/dev/null"),
+#endif
+ TAG_END());
+
+ hint_udp_tcp[0].ai_protocol = TPPROTO_UDP;
+ hint_udp_tcp[0].ai_next = &hint_udp_tcp[1];
+ hint_udp_tcp[1].ai_protocol = TPPROTO_TCP;
+
+ hint_udp_tcp_ip4[0].ai_protocol = TPPROTO_UDP;
+ hint_udp_tcp_ip4[0].ai_family = AF_INET;
+ hint_udp_tcp_ip4[0].ai_next = &hint_udp_tcp_ip4[1];
+ hint_udp_tcp_ip4[1].ai_protocol = TPPROTO_TCP;
+ hint_udp_tcp_ip4[1].ai_family = AF_INET;
+
+ hint_tls[0].ai_protocol = TPPROTO_TLS;
+
+ hint_tls_udp_tcp[0].ai_protocol = TPPROTO_TLS;
+ hint_tls_udp_tcp[0].ai_next = hint_udp_tcp;
+
+ hint_udp_tcp_tls[0].ai_protocol = TPPROTO_UDP;
+ hint_udp_tcp_tls[0].ai_next = &hint_udp_tcp_tls[1];
+ hint_udp_tcp_tls[1].ai_protocol = TPPROTO_TCP;
+ hint_udp_tcp_tls[1].ai_next = &hint_udp_tcp_tls[2];
+ hint_udp_tcp_tls[2].ai_protocol = TPPROTO_TLS;
+ hint_udp_tcp_tls[2].ai_next = NULL;
+
+ setup_logs(0);
+}
+
+static void
+resolver_teardown(void)
+{
+ sres_resolver_unref(x->sres), x->sres = NULL;
+ s2_dns_teardown();
+}
+
+/* ---------------------------------------------------------------------- */
+
+static uint16_t
+ai_port(su_addrinfo_t const *ai)
+{
+ return ai && ai->ai_addr ?
+ ntohs(((struct sockaddr_in *)ai->ai_addr)->sin_port) : 0;
+}
+
+static int
+ai_ip4_match(su_addrinfo_t const *ai,
+ char const *addr,
+ uint16_t port)
+{
+ struct in_addr in, in_in_ai;
+
+ if (ai_port(ai) != port)
+ return 0;
+
+ if (ai->ai_family != AF_INET)
+ return 0;
+
+ if (su_inet_pton(AF_INET, addr, &in) <= 0)
+ return 0;
+
+ in_in_ai = ((struct sockaddr_in *)ai->ai_addr)->sin_addr;
+
+ return memcmp(&in, &in_in_ai, (sizeof in)) == 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+START_TEST(invalid)
+{
+ sres_sip_t *srs;
+
+#if 0
+ s2_case("1.0", "Check invalid input values",
+ "Detailed explanation for empty test case.");
+#endif
+
+ srs = sres_sip_new(NULL, (void *)"sip:test.example.com.", NULL,
+ 1, 1,
+ NULL, NULL);
+ fail_if(srs != NULL);
+
+ srs = sres_sip_new(x->sres, NULL, NULL,
+ 1, 1,
+ NULL, NULL);
+
+ fail_if(srs != NULL);
+
+ srs = sres_sip_new(x->sres, (void *)"tilu.liu.", NULL, 1, 1, NULL, NULL);
+ fail_unless(srs != NULL);
+ fail_if(sres_sip_error(srs) != SRES_SIP_ERR_BAD_URI);
+ fail_if(sres_sip_next_step(srs));
+ fail_if(sres_sip_results(srs) != NULL);
+ sres_sip_unref(srs);
+}
+END_TEST
+
+static void
+resolver_callback(void *userdata,
+ sres_sip_t *srs,
+ int final)
+{
+ if (final)
+ su_root_break((su_root_t *)userdata);
+}
+
+START_TEST(not_found)
+{
+ sres_sip_t *srs;
+ su_addrinfo_t const *ai;
+
+ srs = sres_sip_new(x->sres, (void *)"sip:notfound.example.net", NULL,
+ 1, 1,
+ resolver_callback, x->root);
+ fail_if(srs == NULL);
+
+ while (sres_sip_next_step(srs))
+ su_root_run(x->root);
+
+ ai = sres_sip_results(srs);
+ fail_if(ai != NULL);
+
+ fail_if(sres_sip_error(srs) != SRES_SIP_ERR_NO_NAME);
+
+ sres_sip_unref(srs);
+}
+END_TEST
+
+
+START_TEST(found_a)
+{
+ sres_sip_t *srs;
+ su_addrinfo_t const *ai;
+
+ srs = sres_sip_new(x->sres, (void *)"sip:test.example.com", NULL,
+ 1, 1,
+ resolver_callback, x->root);
+ fail_if(srs == NULL);
+
+ while (sres_sip_next_step(srs))
+ su_root_run(x->root);
+
+ ai = sres_sip_results(srs);
+ assert(ai != NULL);
+ fail_if(ai->ai_protocol != TPPROTO_UDP);
+ fail_if(!(ai = ai->ai_next));
+ fail_if(ai->ai_protocol != TPPROTO_TCP);
+ fail_if((ai = ai->ai_next));
+
+ sres_sip_unref(srs);
+}
+END_TEST
+
+START_TEST(found_ip)
+{
+ sres_sip_t *srs;
+ su_addrinfo_t const *ai;
+ url_string_t *uri;
+
+ srs = sres_sip_new(x->sres, (void *)"sip:127.21.50.37", NULL,
+ 1, 1,
+ resolver_callback, x->root);
+ fail_if(srs == NULL);
+ fail_if(sres_sip_next_step(srs));
+ ai = sres_sip_results(srs);
+ assert(ai != NULL);
+ fail_if(ai->ai_protocol != TPPROTO_UDP);
+ fail_if(!(ai = ai->ai_next));
+ fail_if(ai->ai_protocol != TPPROTO_TCP);
+ fail_if((ai = ai->ai_next));
+
+ sres_sip_unref(srs);
+
+ srs = sres_sip_new(x->sres, (void *)"sip:127.21.50.37:", NULL,
+ 1, 1,
+ resolver_callback, x->root);
+ fail_if(srs == NULL);
+ fail_if(sres_sip_next_step(srs));
+ ai = sres_sip_results(srs);
+ assert(ai != NULL);
+ fail_if(ai->ai_protocol != TPPROTO_UDP);
+ fail_if(!(ai = ai->ai_next));
+ fail_if(ai->ai_protocol != TPPROTO_TCP);
+ fail_if((ai = ai->ai_next));
+
+ sres_sip_unref(srs);
+
+#if SU_HAVE_IN6
+
+ uri = (void *)"sip:[3ff0:0010:3012:c000:02c0:95ff:fee2:4b78]";
+
+ srs = sres_sip_new(x->sres, uri, NULL,
+ 1, 1,
+ resolver_callback, x->root);
+ fail_if(srs == NULL);
+ fail_if(sres_sip_next_step(srs));
+ ai = sres_sip_results(srs);
+ assert(ai != NULL);
+ fail_if(ai->ai_protocol != TPPROTO_UDP);
+ fail_if(!(ai = ai->ai_next));
+ fail_if(ai->ai_protocol != TPPROTO_TCP);
+ fail_if((ai = ai->ai_next));
+
+ sres_sip_unref(srs);
+
+#endif
+}
+END_TEST
+
+START_TEST(found_maddr_a)
+{
+ sres_sip_t *srs;
+ su_addrinfo_t const *ai;
+ url_string_t *uri;
+
+ s2_dns_record("maddr.example.com", sres_type_a,
+ "", sres_type_a, "11.12.13.14",
+ NULL);
+
+ uri = (void *)"sip:test.example.com;lr=1;maddr=maddr.example.com;more";
+
+ srs = sres_sip_new(x->sres, uri, NULL,
+ 1, 1,
+ resolver_callback, x->root);
+ fail_if(srs == NULL);
+
+ while (sres_sip_next_step(srs))
+ su_root_run(x->root);
+
+ ai = sres_sip_results(srs);
+ assert(ai != NULL);
+ fail_if(ai->ai_protocol != TPPROTO_UDP);
+ fail_unless(ai_ip4_match(ai, "11.12.13.14", 5060));
+ fail_if(!(ai = ai->ai_next));
+ fail_if(ai->ai_protocol != TPPROTO_TCP);
+ fail_if((ai = ai->ai_next));
+
+ sres_sip_unref(srs);
+}
+END_TEST
+
+START_TEST(found_cname)
+{
+ sres_sip_t *srs;
+ su_addrinfo_t const *ai;
+ url_string_t *uri;
+
+ s2_dns_record("cname1.example.com", sres_type_a,
+ "", sres_type_cname, "a.example.com.",
+ NULL);
+
+ s2_dns_record("cname1.example.com", sres_type_naptr,
+ "", sres_type_cname, "a.example.com.",
+ NULL);
+
+ s2_dns_record("a.example.com", sres_type_a,
+ "", sres_type_a, "11.12.13.14",
+ NULL);
+
+ s2_dns_record("cname2.example.com", sres_type_a,
+ "", sres_type_cname, "a.example.com.",
+ "test.example.com", sres_type_a, "11.12.13.14",
+ NULL);
+
+ uri = (void *)"sip:cname1.example.com";
+
+ srs = sres_sip_new(x->sres, uri, NULL, 1, 1, resolver_callback, x->root);
+ fail_if(srs == NULL);
+
+ while (sres_sip_next_step(srs))
+ su_root_run(x->root);
+
+ ai = sres_sip_results(srs);
+ assert(ai != NULL);
+ fail_if(ai->ai_protocol != TPPROTO_UDP);
+ fail_unless(ai_ip4_match(ai, "11.12.13.14", 5060));
+ fail_if(!(ai = ai->ai_next));
+ fail_if(ai->ai_protocol != TPPROTO_TCP);
+ fail_if((ai = ai->ai_next));
+
+ sres_sip_unref(srs);
+}
+END_TEST
+
+START_TEST(found_maddr_ip)
+{
+ sres_sip_t *srs;
+ su_addrinfo_t const *ai;
+ url_string_t *uri;
+
+ uri = (void *)"sip:not.existing.net;maddr=127.21.50.37";
+
+ srs = sres_sip_new(x->sres, uri, NULL, 1, 1, resolver_callback, x->root);
+ fail_if(srs == NULL);
+ fail_if(sres_sip_next_step(srs));
+ ai = sres_sip_results(srs);
+ assert(ai != NULL);
+ fail_if(ai->ai_protocol != TPPROTO_UDP);
+ fail_if(!(ai = ai->ai_next));
+ fail_if(ai->ai_protocol != TPPROTO_TCP);
+ fail_if((ai = ai->ai_next));
+
+ sres_sip_unref(srs);
+
+#if SU_HAVE_IN6
+
+ uri = (void *)"sip:test.example.com:"
+ ";maddr=3ff0:0010:3012:c000:02c0:95ff:fee2:4b78";
+
+ srs = sres_sip_new(x->sres, uri, NULL,
+ 1, 1,
+ resolver_callback, x->root);
+ fail_if(srs == NULL);
+ fail_if(sres_sip_next_step(srs));
+ ai = sres_sip_results(srs);
+ assert(ai != NULL);
+ fail_if(ai->ai_protocol != TPPROTO_UDP);
+ fail_if(!(ai = ai->ai_next));
+ fail_if(ai->ai_protocol != TPPROTO_TCP);
+ fail_if((ai = ai->ai_next));
+
+ sres_sip_unref(srs);
+
+ uri = (void *)"sip:test.example.com:"
+ ";maddr=[3ff0:0010:3012:c000:02c0:95ff:fee2:4b78]";
+
+ srs = sres_sip_new(x->sres, uri, NULL,
+ 1, 1,
+ resolver_callback, x->root);
+ fail_if(srs == NULL);
+ fail_if(sres_sip_next_step(srs));
+ ai = sres_sip_results(srs);
+ assert(ai != NULL);
+ fail_if(ai->ai_protocol != TPPROTO_UDP);
+ fail_if(!(ai = ai->ai_next));
+ fail_if(ai->ai_protocol != TPPROTO_TCP);
+ fail_if((ai = ai->ai_next));
+
+ sres_sip_unref(srs);
+
+#endif
+}
+END_TEST
+
+START_TEST(found_a_aaaa)
+{
+ sres_sip_t *srs;
+ su_addrinfo_t const *ai;
+
+ s2_dns_default("example.com.");
+
+ s2_dns_record("ip6ip4.example.com.", sres_type_a,
+ "", sres_type_a, "12.13.14.25",
+ "", sres_type_aaaa, "3ff0:0010:3012:c000:02c0:95ff:fee2:4b78",
+ NULL);
+
+ s2_dns_record("ip6ip4", sres_type_aaaa,
+ "", sres_type_aaaa, "3ff0:0010:3012:c000:02c0:95ff:fee2:4b78",
+ "", sres_type_a, "12.13.14.25",
+ NULL);
+
+ srs = sres_sip_new(x->sres, (void *)"sip:ip6ip4.example.com", NULL,
+ 1, 1,
+ resolver_callback, x->root);
+ fail_if(srs == NULL);
+
+ while (sres_sip_next_step(srs))
+ su_root_run(x->root);
+
+ ai = sres_sip_results(srs);
+ assert(ai != NULL);
+#if SU_HAVE_IN6
+ fail_if(ai->ai_protocol != TPPROTO_UDP);
+ fail_unless(ai->ai_family == AF_INET6);
+ fail_if(!(ai = ai->ai_next));
+ fail_if(ai->ai_protocol != TPPROTO_TCP);
+ fail_unless(ai->ai_family == AF_INET6);
+ fail_if(!(ai = ai->ai_next));
+#endif
+ fail_if(ai->ai_protocol != TPPROTO_UDP);
+ fail_unless(ai->ai_family == AF_INET);
+ fail_if(!(ai = ai->ai_next));
+ fail_if(ai->ai_protocol != TPPROTO_TCP);
+ fail_unless(ai->ai_family == AF_INET);
+ fail_unless(!(ai = ai->ai_next));
+
+ sres_sip_unref(srs);
+}
+END_TEST
+
+START_TEST(found_naptr)
+{
+ sres_sip_t *srs;
+ su_addrinfo_t const *ai;
+ char const *d;
+
+ s2_dns_default(d = "example.com.");
+
+ s2_dns_record(d, sres_type_naptr,
+ /* order priority flags services regexp target */
+ d, sres_type_naptr, 20, 50, "s", "SIP+D2T", "", "_sip._tcp",
+ /* priority weight port target */
+ "_sip._tcp", sres_type_srv, 2, 100, 5060, "sip00",
+ "sip00", sres_type_a, "12.13.14.15",
+ NULL);
+
+ srs = sres_sip_new(x->sres, (void *)"sip:example.com", NULL,
+ 1, 1,
+ resolver_callback, x->root);
+ fail_if(srs == NULL);
+
+ while (sres_sip_next_step(srs))
+ su_root_step(x->root, 1000);
+
+ ai = sres_sip_results(srs);
+ assert(ai != NULL);
+ fail_if(ai->ai_protocol != TPPROTO_TCP);
+ fail_unless(ai->ai_family == AF_INET);
+ fail_unless(!(ai = ai->ai_next));
+
+ sres_sip_unref(srs);
+}
+END_TEST
+
+START_TEST(ignore_naptr)
+{
+ sres_sip_t *srs;
+ su_addrinfo_t const *ai;
+ char const *d;
+
+ s2_dns_default(d = "example.com.");
+
+ s2_dns_record(d, sres_type_naptr,
+ /* order priority flags services regexp target */
+ d, sres_type_naptr, 20, 50, "s", "SIP+D2T", "", "_sip._tcp",
+ /* priority weight port target */
+ "_sip._tcp", sres_type_srv, 2, 100, 5060, "sip00",
+ "sip00", sres_type_a, "12.13.14.15",
+ NULL);
+
+ s2_dns_record(d, sres_type_a,
+ "", sres_type_a, "13.14.15.16",
+ NULL);
+
+ srs = sres_sip_new(x->sres, (void *)"sip:example.com:5060", NULL,
+ 1, 1,
+ resolver_callback, x->root);
+ fail_if(srs == NULL);
+
+ while (sres_sip_next_step(srs))
+ su_root_step(x->root, 1000);
+
+ ai = sres_sip_results(srs);
+ assert(ai != NULL);
+ fail_if(ai->ai_protocol != TPPROTO_UDP);
+ ai = ai->ai_next; assert(ai != NULL);
+ fail_if(ai->ai_protocol != TPPROTO_TCP);
+ ai = ai->ai_next; assert(ai == NULL);
+
+ sres_sip_unref(srs);
+}
+END_TEST
+
+START_TEST(found_naptr2)
+{
+ sres_sip_t *srs;
+ su_addrinfo_t const *ai;
+ char const *d;
+
+ /* Test case without caching, two A addresses */
+
+ s2_dns_default(d = "example.com.");
+
+ s2_dns_record(d, sres_type_naptr,
+ /* order priority flags services regexp target */
+ "", sres_type_naptr, 20, 50, "s", "SIP+D2T", "", "_sip._tcp",
+ NULL);
+
+ s2_dns_record("_sip._tcp", sres_type_srv,
+ /* priority weight port target */
+ "", sres_type_srv, 2, 100, 5060, "sip00",
+ NULL);
+
+ s2_dns_record("sip00", sres_type_a,
+ "", sres_type_a, "12.13.14.15",
+ "", sres_type_a, "12.13.14.16",
+ NULL);
+
+ srs = sres_sip_new(x->sres, (void *)"sip:example.com", NULL,
+ 1, 1,
+ resolver_callback, x->root);
+ fail_if(srs == NULL);
+
+ while (sres_sip_next_step(srs))
+ su_root_step(x->root, 1000);
+
+ ai = sres_sip_results(srs);
+ assert(ai != NULL);
+ fail_if(ai->ai_protocol != TPPROTO_TCP);
+ fail_unless(ai->ai_family == AF_INET);
+ fail_if(!(ai = ai->ai_next));
+ fail_if(ai->ai_protocol != TPPROTO_TCP);
+ fail_unless(ai->ai_family == AF_INET);
+ fail_unless(!(ai = ai->ai_next));
+
+ sres_sip_unref(srs);
+}
+END_TEST
+
+START_TEST(found_naptr3)
+{
+ sres_sip_t *srs;
+ su_addrinfo_t const *ai;
+ char const *d;
+
+ /* Check that sip: resolves only to TLS */
+
+ s2_dns_default(d = "example.com.");
+
+ s2_dns_record(d, sres_type_naptr,
+ /* order priority flags services regexp target */
+ d, sres_type_naptr, 30, 50, "s", "SIP+D2T", "", "_sip._tcp.srv00",
+ d, sres_type_naptr, 20, 50, "s", "SIPS+D2T", "", "_sips._tcp.srv00",
+ /* priority weight port target */
+ "_sips._tcp.srv00", sres_type_srv, 2, 100, 5061, "sips00",
+ "sips00", sres_type_a, "12.13.14.15",
+ "_sip._tcp.srv00", sres_type_srv, 2, 100, 5060, "sip00",
+ "sip00", sres_type_a, "12.13.14.16",
+ NULL);
+
+ srs = sres_sip_new(x->sres, (void *)"sip:example.com", NULL,
+ 1, 1,
+ resolver_callback, x->root);
+ fail_if(srs == NULL);
+
+ while (sres_sip_next_step(srs))
+ su_root_step(x->root, 1000);
+
+ ai = sres_sip_results(srs);
+ assert(ai != NULL);
+ fail_if(ai->ai_protocol != TPPROTO_TLS);
+ fail_unless(ai->ai_family == AF_INET);
+ fail_unless(!(ai = ai->ai_next));
+
+ sres_sip_unref(srs);
+
+ /* Hint no support for TLS */
+ srs = sres_sip_new(x->sres, (void *)"sip:example.com", hint_udp_tcp,
+ 1, 1,
+ resolver_callback, x->root);
+ fail_if(srs == NULL);
+
+ while (sres_sip_next_step(srs))
+ su_root_step(x->root, 1000);
+
+ ai = sres_sip_results(srs);
+ assert(ai != NULL);
+ fail_if(ai->ai_protocol != TPPROTO_TCP);
+ fail_unless(ai->ai_family == AF_INET);
+ fail_unless(!(ai = ai->ai_next));
+
+ sres_sip_unref(srs);
+}
+END_TEST
+
+START_TEST(found_naptr_with_a)
+{
+ sres_sip_t *srs;
+ su_addrinfo_t const *ai;
+ char const *d;
+ int i;
+
+ s2_dns_default(d = "example.com.");
+
+ s2_dns_record(d, sres_type_naptr,
+ /* order priority flags services regexp target */
+ "", sres_type_naptr, 20, 50, "a", "SIP+D2U", "", "sip00",
+ "", sres_type_naptr, 20, 50, "a", "SIP+D2T", "", "sip00",
+ "", sres_type_naptr, 20, 50, "a", "SIPS+D2T", "", "sip00",
+ "sip00", sres_type_a, "12.13.14.15",
+ NULL);
+
+ /* Check that cache does not change ordering */
+ for (i = 0; i < 3; i++) {
+ srs = sres_sip_new(x->sres, (void *)"sip:example.com", NULL,
+ 1, 1,
+ resolver_callback, x->root);
+ fail_if(srs == NULL);
+
+
+ if (i == 0) {
+ while (sres_sip_next_step(srs))
+ su_root_step(x->root, 1000);
+ }
+ else {
+ fail_if(sres_sip_next_step(srs));
+ }
+
+ ai = sres_sip_results(srs);
+ assert(ai != NULL);
+ fail_if(ai->ai_protocol != TPPROTO_UDP);
+ fail_unless(ai->ai_family == AF_INET);
+
+ ai = ai->ai_next;
+ assert(ai != NULL);
+ fail_if(ai->ai_protocol != TPPROTO_TCP);
+ fail_unless(ai->ai_family == AF_INET);
+
+ ai = ai->ai_next;
+ assert(ai != NULL);
+ fail_if(ai->ai_protocol != TPPROTO_TLS);
+ fail_unless(ai->ai_family == AF_INET);
+
+ fail_if(ai->ai_next);
+
+ sres_sip_unref(srs);
+ }
+}
+END_TEST
+
+START_TEST(found_srv)
+{
+ sres_sip_t *srs;
+ su_addrinfo_t const *ai;
+ char const *d;
+
+ /* Check that hints are followed: */
+
+ s2_dns_default(d = "example.com.");
+
+ s2_dns_record("_sips._tcp", sres_type_srv,
+ "", sres_type_srv, 2, 100, 5061, "sips00",
+ "sips00", sres_type_a, "12.13.14.15",
+ NULL);
+ s2_dns_record("_sip._tcp", sres_type_srv,
+ "", sres_type_srv, 2, 100, 5060, "sip00",
+ "sip00", sres_type_a, "12.13.14.16",
+ NULL);
+
+ srs = sres_sip_new(x->sres, (void *)"sip:example.com", hint_udp_tcp,
+ 1, 1,
+ resolver_callback, x->root);
+ fail_if(srs == NULL);
+
+ while (sres_sip_next_step(srs))
+ su_root_step(x->root, 1000);
+
+ ai = sres_sip_results(srs);
+ assert(ai != NULL);
+ fail_if(ai->ai_protocol != TPPROTO_TCP);
+ fail_unless(ai->ai_family == AF_INET);
+ fail_unless(!(ai = ai->ai_next));
+
+ sres_sip_unref(srs);
+
+ /* Hints prefer TLS */
+ srs = sres_sip_new(x->sres, (void *)"sip:example.com", hint_tls_udp_tcp,
+ 1, 1,
+ resolver_callback, x->root);
+ fail_if(srs == NULL);
+
+ while (sres_sip_next_step(srs))
+ su_root_step(x->root, 1000);
+
+ ai = sres_sip_results(srs);
+ assert(ai != NULL);
+ fail_if(ai->ai_protocol != TPPROTO_TLS);
+ fail_unless(ai->ai_family == AF_INET);
+ fail_if(!(ai = ai->ai_next));
+ fail_if(ai->ai_protocol != TPPROTO_TCP);
+ fail_unless(ai->ai_family == AF_INET);
+ fail_unless(!(ai = ai->ai_next));
+
+ sres_sip_unref(srs);
+
+ /* Hints support TLS, prefer TCP */
+ srs = sres_sip_new(x->sres, (void *)"sip:example.com", hint_udp_tcp_tls,
+ 1, 1,
+ resolver_callback, x->root);
+ fail_if(srs == NULL);
+
+ while (sres_sip_next_step(srs))
+ su_root_step(x->root, 1000);
+
+ ai = sres_sip_results(srs);
+ assert(ai != NULL);
+ fail_if(ai->ai_protocol != TPPROTO_TCP);
+ fail_unless(ai->ai_family == AF_INET);
+ fail_if(!(ai = ai->ai_next));
+ fail_if(ai->ai_protocol != TPPROTO_TLS);
+ fail_unless(ai->ai_family == AF_INET);
+ fail_unless(!(ai = ai->ai_next));
+
+ sres_sip_unref(srs);
+ sres_resolver_unref(x->sres);
+
+ x->sres = sres_resolver_create(
+ x->root, NULL,
+#if HAVE_WIN32
+ SRESTAG_RESOLV_CONF("NUL"),
+#else
+ SRESTAG_RESOLV_CONF("/dev/null"),
+#endif
+ TAG_END());
+
+ /* Add UDP record */
+ s2_dns_record("_sip._udp", sres_type_srv,
+ "", sres_type_srv, 1, 100, 5060, "sip00",
+ "sip00", sres_type_a, "12.13.14.16",
+ NULL);
+
+ srs = sres_sip_new(x->sres, (void *)"sip:example.com", hint_udp_tcp_tls,
+ 1, 1,
+ resolver_callback, x->root);
+ fail_if(srs == NULL);
+
+ while (sres_sip_next_step(srs))
+ su_root_step(x->root, 1000);
+
+ ai = sres_sip_results(srs);
+ assert(ai != NULL);
+ fail_if(ai->ai_protocol != TPPROTO_UDP);
+ fail_unless(ai->ai_family == AF_INET);
+ fail_if(!(ai = ai->ai_next));
+ fail_if(ai->ai_protocol != TPPROTO_TCP);
+ fail_unless(ai->ai_family == AF_INET);
+ fail_if(!(ai = ai->ai_next));
+ fail_if(ai->ai_protocol != TPPROTO_TLS);
+ fail_unless(ai->ai_family == AF_INET);
+ fail_unless(!(ai = ai->ai_next));
+
+ sres_sip_unref(srs);
+}
+END_TEST
+
+START_TEST(found_multi_srv)
+{
+ sres_sip_t *srs;
+ su_addrinfo_t const *ai;
+ char const *d;
+ int i;
+ su_addrinfo_t hints[1];
+
+ /* Check that srv records are selected correctly by priority and weight */
+
+ s2_dns_default(d = "example.com.");
+
+ s2_dns_record("_sips._tcp", sres_type_srv,
+ "", sres_type_srv, 2, 100, 5061, "sips00",
+ "", sres_type_srv, 2, 100, 5061, "sips01",
+ "", sres_type_srv, 2, 200, 5061, "sips02",
+ "sips00", sres_type_a, "12.13.0.10",
+ "sips01", sres_type_a, "12.13.1.10",
+ "sips02", sres_type_a, "12.13.2.10",
+ NULL);
+
+ mark_point();
+
+ s2_dns_record("_sip._tcp", sres_type_srv,
+ "", sres_type_srv, 4, 1, 5060, "sip00",
+ "", sres_type_srv, 3, 100, 5060, "sip01",
+ "", sres_type_srv, 3, 10, 5060, "sip02",
+ "sip00", sres_type_a, "12.13.0.1",
+ "sip00", sres_type_a, "12.13.0.2",
+ "sip01", sres_type_a, "12.13.1.2",
+ "sip01", sres_type_a, "12.13.1.1",
+ "sip02", sres_type_a, "12.13.2.1",
+ "sip02", sres_type_a, "12.13.2.2",
+ NULL);
+ mark_point();
+
+ s2_dns_record("_sip._udp", sres_type_srv,
+ "", sres_type_srv, 10, 100, 5060, "sip00",
+ "", sres_type_srv, 10, 0, 5060, "sip01",
+ "", sres_type_srv, 1, 100, 5060, "sip02",
+ "sip00", sres_type_a, "12.13.0.1",
+ "sip00", sres_type_a, "12.13.0.2",
+ "sip01", sres_type_a, "12.13.1.2",
+ "sip01", sres_type_a, "12.13.1.1",
+ "sip02", sres_type_a, "12.13.2.1",
+ "sip02", sres_type_a, "12.13.2.2",
+ NULL);
+
+ mark_point();
+
+ /* Ask resolver to return canonic names */
+ *hints = *hint_udp_tcp_tls, hints->ai_flags |= AI_CANONNAME;
+
+ srs = sres_sip_new(x->sres, (void *)"sip:example.com", hints,
+ 1, 1,
+ resolver_callback, x->root);
+ fail_if(srs == NULL);
+
+ while (sres_sip_next_step(srs))
+ su_root_step(x->root, 1000);
+
+ ai = sres_sip_results(srs);
+
+ for (i = 0; i < 6; i++) {
+ static char const * const names[] = {
+ "sip02.", "sip02.", /* Selected because priority */
+ "sip00.", "sip00.", /* Because of sip01 has weight=0 */
+ "sip01.", "sip01."
+ };
+ assert(ai != NULL);
+ fail_if(ai->ai_protocol != TPPROTO_UDP);
+ fail_unless(ai->ai_family == AF_INET);
+ fail_unless(ai_port(ai) == 5060);
+ fail_if(memcmp(ai->ai_canonname, names[i], strlen(names[i])) != 0);
+ ai = ai->ai_next;
+ }
+
+ for (i = 0; i < 6; i++) {
+ static char const * const names[] = {
+ "sip", "sip",
+ "sip", "sip",
+ "sip00.", "sip00."
+ };
+ assert(ai != NULL);
+ fail_if(ai->ai_protocol != TPPROTO_TCP);
+ fail_unless(ai->ai_family == AF_INET);
+ fail_unless(ai_port(ai) == 5060);
+ fail_if(memcmp(ai->ai_canonname, names[i], strlen(names[i])) != 0);
+ ai = ai->ai_next;
+ }
+
+ for (i = 0; i < 3; i++) {
+ assert(ai != NULL);
+ fail_if(ai->ai_protocol != TPPROTO_TLS);
+ fail_unless(ai->ai_family == AF_INET);
+ fail_unless(ai_port(ai) == 5061);
+ ai = ai->ai_next;
+ }
+
+ sres_sip_unref(srs);
+}
+END_TEST
+
+TCase *api_tcase(void)
+{
+ TCase *tc = tcase_create("1 - Test simple resolving");
+
+ tcase_add_checked_fixture(tc, resolver_setup, resolver_teardown);
+
+ tcase_add_test(tc, invalid);
+ tcase_add_test(tc, not_found);
+ tcase_add_test(tc, found_a);
+ tcase_add_test(tc, found_cname);
+ tcase_add_test(tc, found_ip);
+ tcase_add_test(tc, found_maddr_a);
+ tcase_add_test(tc, found_maddr_ip);
+ tcase_add_test(tc, found_a_aaaa);
+ tcase_add_test(tc, found_naptr);
+ tcase_add_test(tc, found_naptr2);
+ tcase_add_test(tc, found_naptr3);
+ tcase_add_test(tc, found_naptr_with_a);
+ tcase_add_test(tc, ignore_naptr);
+ tcase_add_test(tc, found_srv);
+ tcase_add_test(tc, found_multi_srv);
+
+ return tc;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void usage(int exitcode)
+{
+ fprintf(exitcode ? stderr : stdout,
+ "usage: check_sres_sip [--xml=logfile] case,...\n");
+ exit(exitcode);
+}
+
+int main(int argc, char *argv[])
+{
+ int i, failed = 0;
+
+ Suite *suite = suite_create("Unit tests for SIP URI resolver");
+ SRunner *runner;
+ char const *xml = NULL;
+
+ s2_select_tests(getenv("CHECK_CASES"));
+
+ for (i = 1; argv[i]; i++) {
+ if (su_strnmatch(argv[i], "--xml=", strlen("--xml="))) {
+ xml = argv[i] + strlen("--xml=");
+ }
+ else if (su_strmatch(argv[i], "--xml")) {
+ if (!(xml = argv[++i]))
+ usage(2);
+ }
+ else if (su_strmatch(argv[i], "-?") ||
+ su_strmatch(argv[i], "-h") ||
+ su_strmatch(argv[i], "--help"))
+ usage(0);
+ else
+ s2_select_tests(argv[i]);
+ }
+
+ suite_add_tcase(suite, api_tcase());
+
+ runner = srunner_create(suite);
+ if (xml)
+ srunner_set_xml(runner, xml);
+ 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/sresolv/sofia-sip/sres_sip.h
==============================================================================
--- (empty file)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/sresolv/sofia-sip/sres_sip.h Thu Feb 12 15:58:06 2009
@@ -0,0 +1,121 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2009 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi(a)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 SOFIA_SIP_SRES_SIP_H
+/** Defined when <sofia-sip/sres_sip.h> has been included. */
+#define SOFIA_SIP_SRES_SIP_H
+
+/**@file sofia-sip/sres_sip.h
+ * @brief Asynchronous resolver for SIP
+ *
+ * @author Pekka Pessi <Pekka.Pessi(a)nokia.com>
+ */
+
+#ifndef SU_TYPES_H
+#include <sofia-sip/su_types.h>
+#endif
+
+#ifndef SU_ADDRINFO_H
+#include <sofia-sip/su_addrinfo.h>
+#endif
+
+#ifndef URL_H
+#include <sofia-sip/url.h>
+#endif
+
+#ifndef SOFIA_RESOLV_SRES_H
+#include <sofia-resolv/sres.h>
+#endif
+
+SOFIA_BEGIN_DECLS
+
+typedef struct sres_sip_s sres_sip_t;
+
+#ifndef SRES_SIP_MAGIC_T
+/** Default type of application context for NTA resolver.
+ *
+ * Application may define this to appropriate type before including
+ * <sofia-sip/sres_sip.h>. */
+#define SRES_SIP_MAGIC_T struct sres_sip_magic_s
+#endif
+
+/** Application context for NTA resolver. */
+typedef SRES_SIP_MAGIC_T sres_sip_magic_t;
+
+typedef void sres_sip_notify_f(sres_sip_magic_t *context,
+ sres_sip_t *resolver,
+ int error);
+
+SOFIAPUBFUN sres_sip_t *sres_sip_new(
+ sres_resolver_t *sres,
+ url_string_t const *url,
+ su_addrinfo_t const *hints,
+ int naptr, int srv,
+ sres_sip_notify_f *callback,
+ sres_sip_magic_t *magic);
+
+SOFIAPUBFUN sres_sip_t *sres_sip_ref(sres_sip_t *);
+SOFIAPUBFUN void sres_sip_unref(sres_sip_t *);
+
+SOFIAPUBFUN su_addrinfo_t const *sres_sip_results(sres_sip_t *);
+
+SOFIAPUBFUN su_addrinfo_t const *sres_sip_next(sres_sip_t *);
+
+SOFIAPUBFUN int sres_sip_next_step(sres_sip_t *nr);
+
+SOFIAPUBFUN int sres_sip_error(sres_sip_t *nr);
+
+/* Errors */
+enum {
+ SRES_SIP_ERR_FAULT = -1, /* Invalid pointers */
+ SRES_SIP_ERR_BAD_URI = -2, /* Invalid URI */
+ SRES_SIP_ERR_BAD_HINTS = -3, /* Invalid hints */
+ SRES_SIP_ERR_NO_NAME = -4, /* No domain found */
+ SRES_SIP_ERR_NO_DATA = -5, /* No matching records */
+ SRES_SIP_ERR_NO_TPORT = -6, /* Unknown transport */
+ SRES_SIP_ERR_FAIL = -7, /* Permanent resolving error */
+ SRES_SIP_ERR_AGAIN = -8, /* Temporary resolving error */
+ SRES_SIP_ERR_INTERNAL = -9, /* Internal error */
+ _SRES_SIP_ERR_LAST
+};
+
+/* Well-known transport numbers */
+enum {
+ TPPROTO_TCP = 6,
+ TPPROTO_UDP = 17,
+ TPPROTO_SCTP = 132,
+ TPPROTO_SECURE = 256,
+ TPPROTO_TLS = TPPROTO_SECURE | TPPROTO_TCP,
+ TPPROTO_NONE = 0
+};
+
+#define TPPROTO_TCP TPPROTO_TCP
+#define TPPROTO_UDP TPPROTO_UDP
+#define TPPROTO_SCTP TPPROTO_SCTP
+#define TPPROTO_TLS TPPROTO_TLS
+#define TPPROTO_NONE TPPROTO_NONE
+
+SOFIA_END_DECLS
+
+#endif /* SOFIA_SIP_SRES_SIP_H */
Added: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/sresolv/sres_sip.c
==============================================================================
--- (empty file)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/sresolv/sres_sip.c Thu Feb 12 15:58:06 2009
@@ -0,0 +1,1685 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi(a)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 sres_sip.c
+ * @brief RFC3263 SIP Resolver
+ *
+ * @author Pekka Pessi <Pekka.Pessi(a)nokia.com>
+ *
+ * @sa
+ * @RFC3263
+ */
+
+#include "config.h"
+
+struct sres_sip_tport;
+struct srs_step;
+struct srs_hint;
+struct sres_sip_s;
+
+#include <sofia-sip/su_string.h>
+#include <sofia-sip/hostdomain.h>
+#include <sofia-sip/url.h>
+#include <sofia-sip/su_errno.h>
+#include <sofia-sip/su.h>
+
+#define SRES_CONTEXT_T struct srs_step
+
+#include <sofia-sip/su_uniqueid.h>
+#include "sofia-sip/sres_sip.h"
+#include "sofia-resolv/sres.h"
+#include "sofia-resolv/sres_record.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+
+#define SU_LOG sresolv_log
+
+#include <sofia-sip/su_debug.h>
+
+/**
+ * For each transport, we have name used by tport module, SRV prefixes used
+ * for resolving, and NAPTR service/conversion.
+ */
+struct sres_sip_tport {
+ int stp_type; /**< Default protocol for this URL type */
+ uint16_t stp_number; /**< Protocol number used by tport module */
+ uint16_t stp_port; /**< Default port number */
+ char stp_name[6]; /**< Named used by tport module */
+ char stp_prefix[14]; /**< Prefix for SRV domains */
+ char stp_service[10]; /**< NAPTR service (empty if none) */
+};
+
+#define N_TRANSPORTS 20
+
+static struct sres_sip_tport const sres_sip_tports[N_TRANSPORTS] = {
+ { url_sip, TPPROTO_UDP, 5060, "udp", "_sip._udp.", "SIP+D2U" },
+ { url_sip, TPPROTO_TCP, 5060, "tcp", "_sip._tcp.", "SIP+D2T" },
+ { url_sips, TPPROTO_TLS, 5061, "tls", "_sips._tcp.", "SIPS+D2T" },
+ { url_any, TPPROTO_SCTP, 5060, "sctp", "_sip._sctp.", "SIP+D2S" },
+};
+
+struct srs_step;
+struct srs_hint;
+
+struct sres_sip_s
+{
+ su_home_t srs_home[1];
+ sres_resolver_t *srs_resolver;
+
+ sres_sip_notify_f *srs_callback;
+ sres_sip_magic_t *srs_magic;
+
+ /* What we are resolving */
+ url_t *srs_url;
+
+ /* Parsed */
+ char const *srs_target;
+ short srs_maddr;
+ short srs_transport; /**< URL-specified transport protocol */
+ uint16_t srs_port;
+
+ int srs_error;
+
+ /** Resolving steps (to take) */
+ struct srs_step *srs_head, **srs_queue; /**< List of subqueries */
+ struct srs_step **srs_process; /**< Next result to process */
+ struct srs_step **srs_send; /**< Next query to make */
+
+ /** Results */
+ su_addrinfo_t *srs_results, **srs_next, **srs_tail;
+
+ unsigned srs_try_naptr:1, srs_try_srv:1, srs_try_a:1;
+ unsigned srs_canonname:1, srs_numeric:1;
+
+ unsigned srs_blocking:1, srs_complete:1, :0;
+
+ /** Hints for this request. */
+ struct srs_hint {
+ struct sres_sip_tport const *hint_stp;
+ uint16_t hint_qtype;
+ uint16_t hint_port;
+ } srs_hints[2 * N_TRANSPORTS + 2];
+ /* srs_hints[0] is ignored, srs_hints[2*N_TRANSPORTS + 1] is sentinel. */
+};
+
+/** Intermediate steps */
+struct srs_step
+{
+ struct srs_step *sp_next;
+ sres_sip_t *sp_srs; /**< Backpointer */
+ struct srs_step *sp_already; /**< Step with identical query
+ - itself if first one */
+ struct srs_step *sp_trace; /**< Step that resulted in this step */
+ sres_record_t const *sp_origin; /**< DNS record that resulted in this step */
+
+ char const *sp_target;
+ sres_query_t *sp_query;
+
+ /** Query status.
+ *
+ * STEP_NEW (-4) when created,
+ * STEP_QUEUED (-3) when queued,
+ * STEP_CACHED (-2) when found cached response
+ * STEP_SENT (-1) when query has been sent
+ * (positive) status from response otherwise
+ */
+ int sp_status;
+
+ sres_record_t **sp_results;
+
+ uint16_t sp_hint; /* Number of hint */
+
+ uint16_t sp_port; /* port number */
+ uint16_t sp_type; /* query type */
+ uint16_t sp_prefer; /* order */
+ uint16_t sp_priority; /* priority */
+ uint16_t sp_weight; /* weight */
+
+ uint16_t sp_grayish; /* candidate for graylisting */
+};
+
+#define SP_HINT(sp) ((sp)->sp_hint ? &(sp)->sp_srs[sp->sp_hint] : NULL)
+#define SP_STP(sp) ((sp)->sp_hint ? (sp)->sp_srs[sp->sp_hint].hint_stp : NULL);
+
+enum {
+ STEP_NEW = -4,
+ STEP_QUEUED = -3,
+ STEP_CACHED = -2,
+ STEP_SENT = -1,
+ STEP_OK = 0
+};
+
+static void _sres_sip_destruct(void *_srs);
+static sres_sip_t *sres_sip_fatal(sres_sip_t *srs, int error);
+static void sres_sip_hint(sres_sip_t *srs, int qtype, int protocol);
+static int sres_sip_url_transport(url_t const *uri);
+static char const *sres_sip_transport_name(int number);
+void sres_sip_graylist(sres_sip_t *srs, struct srs_step *step);
+
+static int sres_sip_is_waiting(sres_sip_t const *srs);
+static void sres_sip_return_results(sres_sip_t *srs, int final);
+
+static void sres_sip_try_next_steps(sres_sip_t *srs);
+static void sres_sip_try_naptr_steps(sres_sip_t *srs);
+static void sres_sip_try_srv_steps(sres_sip_t *srs);
+static void sres_sip_try_a_aaaa_steps(sres_sip_t *srs);
+
+static struct srs_step *sres_sip_step_new(
+ sres_sip_t *srs,
+ int type,
+ char const *prefix,
+ char const *domain);
+
+static void sres_sip_append_step(sres_sip_t *srs,
+ struct srs_step *step);
+
+static void sres_sip_insert_step(sres_sip_t *srs,
+ struct srs_step *step);
+
+static int sres_sip_send_steps(sres_sip_t *srs);
+static void sres_sip_answer(struct srs_step *step,
+ sres_query_t *q,
+ sres_record_t *answers[]);
+static int sres_sip_status_of_answers(sres_record_t *answers[], uint16_t type);
+static int sres_sip_count_answers(sres_record_t *answers[], uint16_t type);
+static void sres_sip_log_answers(sres_sip_t *srs,
+ struct srs_step *step,
+ sres_record_t *answers[]);
+
+static int sres_sip_process(sres_sip_t *srs);
+
+static void sres_sip_process_naptr(sres_sip_t *srs,
+ struct srs_step *nq,
+ sres_record_t *answers[]);
+static void sres_sip_sort_naptr(sres_record_t *answers[]);
+
+static void sres_sip_step_by_naptr(sres_sip_t *srs,
+ struct srs_step *,
+ uint16_t hint,
+ sres_naptr_record_t const *na);
+
+static void sres_sip_process_srv(sres_sip_t *srs,
+ struct srs_step *nq,
+ sres_record_t *answers[]);
+
+static void sres_sip_sort_srv(sres_record_t *answers[]);
+
+#if SU_HAVE_IN6
+static void sres_sip_process_aaaa(sres_sip_t *srs, struct srs_step *,
+ sres_record_t *answers[]);
+#endif
+static void sres_sip_process_a(sres_sip_t *srs, struct srs_step *,
+ sres_record_t *answers[]);
+
+static void sres_sip_process_cname(sres_sip_t *srs,
+ struct srs_step *step0,
+ sres_record_t *answers[]);
+
+static void sres_sip_process_numeric(sres_sip_t *srs);
+
+static void sres_sip_append_result(sres_sip_t *srs,
+ su_addrinfo_t const *result);
+
+/** Resolve a SIP uri.
+ *
+ */
+sres_sip_t *
+sres_sip_new(sres_resolver_t *sres,
+ url_string_t const *uri,
+ su_addrinfo_t const *hints,
+ int naptr, int srv,
+ sres_sip_notify_f *callback,
+ sres_sip_magic_t *magic)
+{
+ sres_sip_t *srs;
+ url_t *u;
+ char const *target, *port;
+ su_addrinfo_t const hints0 = { 0 };
+ int numeric;
+ int transport;
+ isize_t maddr;
+
+ if (sres == NULL || uri == NULL)
+ return su_seterrno(EFAULT), NULL;
+
+ srs = su_home_new(sizeof *srs);
+ if (srs == NULL)
+ return NULL;
+
+ srs->srs_queue = srs->srs_send = srs->srs_process = &srs->srs_head;
+ srs->srs_next = srs->srs_tail = &srs->srs_results;
+
+ su_home_destructor(srs->srs_home, _sres_sip_destruct);
+
+ srs->srs_url = u = url_hdup(srs->srs_home, (url_t *)uri);
+ if (u == NULL)
+ return sres_sip_fatal(srs, SRES_SIP_ERR_BAD_URI);
+ if (u->url_type != url_sip && u->url_type != url_sips)
+ return sres_sip_fatal(srs, SRES_SIP_ERR_BAD_URI);
+
+ /* RFC 3263:
+ We define TARGET as the value of the maddr parameter of the URI, if
+ present, otherwise, the host value of the hostport component of the
+ URI.
+ */
+ maddr = url_param(u->url_params, "maddr=", NULL, 0);
+
+ if (maddr) {
+ target = su_alloc(srs->srs_home, maddr);
+ url_param(u->url_params, "maddr=", (char *)target, maddr);
+ }
+ else {
+ target = u->url_host;
+ }
+
+ if (!target)
+ return sres_sip_fatal(srs, SRES_SIP_ERR_BAD_URI);
+
+ srs->srs_target = target;
+ srs->srs_maddr = maddr != 0;
+ port = u->url_port;
+
+ srs->srs_transport = transport = sres_sip_url_transport(u);
+ if (transport == -1)
+ return sres_sip_fatal(srs, SRES_SIP_ERR_NO_TPORT);
+
+ if (transport && u->url_type == url_sips)
+ /* <sips:host.domain;transport=tcp> */
+ srs->srs_transport = transport | TPPROTO_SECURE;
+
+ numeric = host_is_ip_address(target);
+
+ if (numeric) {
+ naptr = 0, srv = 0;
+ if (!port || !strlen(port))
+ port = url_port_default(u->url_type);
+ }
+
+ /* RFC 3263:
+ If the TARGET was not a numeric IP address, but a port is present in
+ the URI, the client performs an A or AAAA record lookup of the domain
+ name. The result will be a list of IP addresses, each of which can
+ be contacted at the specific port from the URI and transport protocol
+ determined previously. The client SHOULD try the first record. If
+ an attempt should fail, based on the definition of failure in Section
+ 4.3, the next SHOULD be tried, and if that should fail, the next
+ SHOULD be tried, and so on.
+
+ This is a change from RFC 2543. Previously, if the port was
+ explicit, but with a value of 5060, SRV records were used. Now, A
+ or AAAA records will be used.
+ */
+ if (port && strlen(port)) {
+ unsigned long number;
+ naptr = 0, srv = 0;
+ srs->srs_port = number = strtoul(port, 0, 10);
+ if (number == 0 || number >= 65536)
+ return sres_sip_fatal(srs, SRES_SIP_ERR_BAD_URI);
+ }
+
+ if (hints == NULL)
+ hints = &hints0;
+
+ srs->srs_canonname = (hints->ai_flags & AI_CANONNAME) != 0;
+ srs->srs_numeric = (hints->ai_flags & AI_NUMERICHOST) != 0;
+
+ srs->srs_resolver = sres_resolver_ref(sres);
+ srs->srs_blocking = sres_is_blocking(sres);
+
+ srs->srs_try_srv = srv;
+ srs->srs_try_naptr = naptr;
+ srs->srs_try_a = !numeric;
+
+ for (;hints; hints = hints->ai_next) {
+#if SU_HAVE_IN6
+ if (hints->ai_family == 0 || hints->ai_family == AF_INET6) {
+ sres_sip_hint(srs, sres_type_aaaa, hints->ai_protocol);
+ }
+#endif
+ if (hints->ai_family == 0 || hints->ai_family == AF_INET) {
+ sres_sip_hint(srs, sres_type_a, hints->ai_protocol);
+ }
+ }
+
+ SU_DEBUG_5(("srs(%p): resolving <%s:%s%s%s%s%s%s%s>\n",
+ (void *)srs,
+ u->url_scheme, u->url_host,
+ u->url_port ? ":" : "", u->url_port ? u->url_port : "",
+ maddr ? ";maddr=" : "",
+ maddr ? target : "",
+ transport ? ";transport=" : "",
+ transport ? sres_sip_transport_name(transport) : ""));
+
+ if (numeric)
+ sres_sip_process_numeric(srs);
+ else
+ sres_sip_next_step(srs);
+
+ srs->srs_callback = callback;
+ srs->srs_magic = magic;
+
+ return srs;
+}
+
+sres_sip_t *
+sres_sip_ref(sres_sip_t *srs)
+{
+ return (sres_sip_t *)su_home_ref(srs->srs_home);
+}
+
+void
+sres_sip_unref(sres_sip_t *srs)
+{
+ su_home_unref(srs->srs_home);
+}
+
+static void
+_sres_sip_destruct(void *_srs)
+{
+ sres_sip_t *srs = _srs;
+ sres_resolver_t *sres = srs->srs_resolver;
+ struct srs_step *step;
+
+ SU_DEBUG_5(("srs(%p): destroyed\n", _srs));
+
+ srs->srs_resolver = NULL;
+
+ for (step = srs->srs_head; step; step = step->sp_next) {
+ if (step->sp_already == step)
+ sres_free_answers(sres, step->sp_results);
+ step->sp_results = NULL;
+ sres_query_bind(step->sp_query, NULL, NULL), step->sp_query = NULL;
+ }
+
+ sres_resolver_unref(sres);
+}
+
+int
+sres_sip_error(sres_sip_t *srs)
+{
+ return srs ? srs->srs_error : SRES_SIP_ERR_FAULT;
+}
+
+su_addrinfo_t const *
+sres_sip_results(sres_sip_t *srs)
+{
+ return srs ? srs->srs_results : NULL;
+}
+
+su_addrinfo_t const *
+sres_sip_next(sres_sip_t *srs)
+{
+ su_addrinfo_t *next = NULL;
+
+ if (srs) {
+ next = *srs->srs_next;
+ if (next)
+ srs->srs_next = &next->ai_next;
+ }
+
+ return next;
+}
+
+static sres_sip_t *
+sres_sip_fatal(sres_sip_t *srs, int error)
+{
+ srs->srs_error = error;
+ srs->srs_try_naptr = 0, srs->srs_try_srv = 0, srs->srs_try_a = 0;
+ return srs;
+}
+
+static void
+sres_sip_hint(sres_sip_t *srs, int qtype, int protocol)
+{
+ size_t i, j;
+ int match = 0;
+ uint16_t port = srs->srs_port;
+
+ for (j = 0; sres_sip_tports[j].stp_number; j++) {
+ struct sres_sip_tport const *stp = &sres_sip_tports[j];
+ int already = 0;
+
+ if (protocol && stp->stp_number != protocol)
+ continue;
+
+ /* Use only secure transports with SIPS */
+ if (srs->srs_url->url_type == url_sips) {
+ if (!(stp->stp_number & TPPROTO_SECURE))
+ continue;
+ }
+
+ /* If transport is specified, use it. */
+ if (srs->srs_transport && stp->stp_number != srs->srs_transport)
+ continue;
+
+ for (i = 1; srs->srs_hints[i].hint_stp; i++) {
+ if (srs->srs_hints[i].hint_stp == stp &&
+ srs->srs_hints[i].hint_qtype == qtype) {
+ match = 1, already = 1;
+ break;
+ }
+ assert(i <= 2 * N_TRANSPORTS);
+ }
+
+ if (!already) {
+ srs->srs_hints[i].hint_stp = stp;
+ srs->srs_hints[i].hint_qtype = qtype;
+ srs->srs_hints[i].hint_port = port ? port : stp->stp_port;
+ }
+ }
+
+ if (!match) {
+ /* XXX - hint did not match configured protocols */
+ }
+}
+
+/** Return protocol number for transport parameter in URI */
+static int
+sres_sip_url_transport(url_t const *uri)
+{
+ char parameter[64];
+ isize_t len = 0;
+ size_t i;
+
+ if (!uri)
+ return -1;
+ if (!uri->url_params)
+ return 0;
+
+ len = url_param(uri->url_params, "transport", parameter, (sizeof parameter));
+ if (len == 0)
+ return 0;
+ if (len >= sizeof parameter)
+ return -1;
+
+ for (i = 0; sres_sip_tports[i].stp_name; i++) {
+ if (su_casematch(parameter, sres_sip_tports[i].stp_name))
+ return sres_sip_tports[i].stp_number;
+ }
+
+ return -1;
+}
+
+static char const *
+sres_sip_transport_name(int number)
+{
+ size_t i;
+
+ for (i = 0; sres_sip_tports[i].stp_number; i++) {
+ if (number == sres_sip_tports[i].stp_number)
+ return sres_sip_tports[i].stp_name;
+ }
+
+ return NULL;
+}
+
+/** Cancel resolver query */
+void
+sres_sip_cancel_resolver(sres_sip_t *srs)
+{
+ struct srs_step *step;
+
+ if (!srs)
+ return;
+
+ for (step = srs->srs_head; step; step = step->sp_next) {
+ sres_query_bind(step->sp_query, NULL, NULL), step->sp_query = NULL;
+ }
+}
+
+#if 0
+
+/** Graylist SRV records */
+static void
+sres_sip_graylist(sres_sip_t *srs,
+ struct srs_step *step)
+{
+ char const *target = step->sp_target, *proto = step->sp_proto;
+ unsigned prio = step->sp_priority, maxprio = prio;
+
+ /* Don't know how to graylist but SRV records */
+ if (step->sp_otype != sres_type_srv)
+ return;
+
+ SU_DEBUG_5(("srs(%p): graylisting %s:%u;transport=%s\n", target, step->sp_port, proto));
+
+ for (step = srs->srs_send; step; step = step->sp_next)
+ if (step->sp_otype == sres_type_srv && step->sp_priority > maxprio)
+ maxprio = step->sp_priority;
+
+ for (step = srs->srs_done; step; step = step->sp_next)
+ if (step->sp_otype == sres_type_srv && step->sp_priority > maxprio)
+ maxprio = step->sp_priority;
+
+ for (step = srs->srs_done; step; step = step->sp_next) {
+ int modified;
+
+ if (step->sp_type != sres_type_srv || strcmp(proto, step->sp_proto))
+ continue;
+
+ /* modify the SRV record(s) corresponding to the latest A/AAAA record */
+ modified = sres_set_cached_srv_priority(
+ srs->orq_agent->sa_resolver,
+ step->sp_target,
+ target,
+ step->sp_port,
+ srs->orq_agent->sa_graylist,
+ maxprio + 1);
+
+ if (modified >= 0)
+ SU_DEBUG_3(("srs(%p): reduced priority of %d %s SRV records (increase value to %u)\n",
+ (void *)srs,
+ modified, step->sp_target, maxprio + 1));
+ else
+ SU_DEBUG_3(("srs(%p): failed to reduce %s SRV priority\n",
+ (void *)srs,
+ step->sp_target));
+ }
+}
+#endif
+
+/** Take next step in resolving process.
+ *
+ * @retval 1 if there is more steps left
+ * @retval 0 if resolving process is complete
+ */
+int
+sres_sip_next_step(sres_sip_t *srs)
+{
+ if (srs == NULL)
+ return 0;
+
+ for (;;) {
+ sres_sip_process(srs); /* Process answers */
+
+ if (sres_sip_is_waiting(srs))
+ return 1;
+
+ sres_sip_try_next_steps(srs);
+
+ if (!sres_sip_send_steps(srs))
+ break;
+ }
+
+ if (sres_sip_is_waiting(srs))
+ return 1;
+
+ if (!srs->srs_complete) {
+ SU_DEBUG_5(("srs(%p): complete\n", (void *)srs));
+ srs->srs_complete = 1;
+ }
+
+ assert(*srs->srs_process == NULL);
+ assert(!srs->srs_try_naptr && !srs->srs_try_srv && !srs->srs_try_a);
+
+ return 0;
+}
+
+static int
+sres_sip_is_waiting(sres_sip_t const *srs)
+{
+ return *srs->srs_process && (*srs->srs_process)->sp_query;
+}
+
+static void
+sres_sip_try_next_steps(sres_sip_t *srs)
+{
+ if (*srs->srs_send == NULL) {
+ if (srs->srs_try_naptr) {
+ sres_sip_try_naptr_steps(srs);
+ }
+ else if (srs->srs_try_srv) {
+ sres_sip_try_srv_steps(srs);
+ }
+ else if (srs->srs_try_a) {
+ sres_sip_try_a_aaaa_steps(srs);
+ }
+ }
+}
+
+/** Try target NAPTR. */
+static void
+sres_sip_try_naptr_steps(sres_sip_t *srs)
+{
+ struct srs_step *step;
+
+ srs->srs_try_naptr = 0;
+ step = sres_sip_step_new(srs, sres_type_naptr, NULL, srs->srs_target);
+ sres_sip_append_step(srs, step);
+}
+
+/** If no NAPTR is found, try target SRVs. */
+static void
+sres_sip_try_srv_steps(sres_sip_t *srs)
+{
+ int i;
+ char const *target = srs->srs_target;
+
+ srs->srs_try_srv = 0;
+
+ for (i = 1; srs->srs_hints[i].hint_stp; i++) {
+ struct sres_sip_tport const *stp;
+ struct srs_step *step;
+
+ stp = srs->srs_hints[i].hint_stp;
+ step = sres_sip_step_new(srs, sres_type_srv, stp->stp_prefix, target);
+
+ if (step) {
+ step->sp_hint = i;
+ step->sp_prefer = i + 1;
+ step->sp_priority = 1;
+ step->sp_weight = 1;
+ sres_sip_append_step(srs, step);
+ }
+ }
+}
+
+/** If not NAPTR or SRV was found, try target A/AAAAs. */
+static void
+sres_sip_try_a_aaaa_steps(sres_sip_t *srs)
+{
+ int i;
+
+ srs->srs_try_a = 0;
+
+ for (i = 1; srs->srs_hints[i].hint_stp; i++) {
+ struct srs_hint *hint = srs->srs_hints + i;
+ struct sres_sip_tport const *stp = hint->hint_stp;
+ struct srs_step *step;
+
+ /* Consider only transport from uri parameter */
+ if (!srs->srs_transport &&
+ /* ...or default transports for this URI type */
+ stp->stp_type != srs->srs_url->url_type)
+ continue;
+
+ step = sres_sip_step_new(srs, hint->hint_qtype, NULL, srs->srs_target);
+
+ if (step) {
+ step->sp_hint = i;
+ step->sp_prefer = 2;
+ step->sp_priority = i;
+ step->sp_port = srs->srs_port ? srs->srs_port : stp->stp_port;
+ sres_sip_append_step(srs, step);
+ }
+ }
+}
+
+/** Send queries or lookup from cache.
+ *
+ * Return true if got data to process.
+ */
+static int
+sres_sip_send_steps(sres_sip_t *srs)
+{
+ int process = 0;
+
+ /* New queries to send */
+ while (*srs->srs_send) {
+ struct srs_step *step = *srs->srs_send, *step2;
+ uint16_t type = step->sp_type;
+ char const *domain = step->sp_target;
+ sres_record_t **answers;
+ int parallel = 0;
+
+ srs->srs_send = &step->sp_next;
+
+ if (step->sp_status != STEP_QUEUED) {
+ assert(step->sp_already != step);
+ continue;
+ }
+
+ assert(step->sp_already == step);
+
+ answers = sres_cached_answers(srs->srs_resolver, type, domain);
+
+ for (step2 = step; step2; step2 = step2->sp_next) {
+ if (step2->sp_already == step)
+ step2->sp_status = answers ? STEP_CACHED : STEP_SENT;
+ }
+
+
+ SU_DEBUG_5(("srs(%p): query %s %s%s\n",
+ (void *)srs, sres_record_type(type, NULL), domain,
+ answers ? " (cached)" : ""));
+
+ if (answers) {
+ sres_sip_answer(step, NULL, answers);
+ return process = 1;
+ }
+ else if (srs->srs_blocking) {
+ sres_blocking_query(srs->srs_resolver, type, domain, 0, &answers);
+ sres_sip_answer(step, NULL, answers);
+ return process = 1;
+ }
+ else {
+ step->sp_query = sres_query(srs->srs_resolver,
+ sres_sip_answer, step,
+ type, domain);
+ /* Query all self-generated SRV records at the same time */
+ parallel = step->sp_trace == NULL && type == sres_type_srv;
+ if (!parallel)
+ break;
+ }
+ }
+
+ return process = 0;
+}
+
+static void
+sres_sip_answer(struct srs_step *step,
+ sres_query_t *q,
+ sres_record_t *answers[])
+{
+ sres_sip_t *srs = step->sp_srs;
+ struct srs_step *step2;
+ int status = sres_sip_status_of_answers(answers, step->sp_type);
+
+ SU_DEBUG_5(("srs(%p): %s %s (answers=%u) for %s %s\n",
+ (void *)srs,
+ step->sp_status == STEP_SENT ? "received" : "cached",
+ sres_record_status(status, NULL),
+ sres_sip_count_answers(answers, step->sp_type),
+ sres_record_type(step->sp_type, NULL), step->sp_target));
+
+ sres_sip_log_answers(srs, step, answers);
+
+ assert(step->sp_already == step);
+ assert(step->sp_query == q); step->sp_query = NULL;
+ assert(step->sp_status == STEP_SENT ||
+ step->sp_status == STEP_CACHED);
+
+ step->sp_results = answers;
+ step->sp_status = status;
+
+ /* Check if answer is valid for another query, too */
+ for (step2 = step->sp_next; step2; step2 = step2->sp_next) {
+ if (step2->sp_already == step) {
+ step2->sp_results = answers;
+ step2->sp_status = status;
+ }
+ }
+
+ if (!sres_sip_next_step(srs)) {
+ sres_sip_return_results(srs, 1);
+ }
+ else if (q && *srs->srs_next) {
+ sres_sip_return_results(srs, 0);
+ }
+}
+
+static int
+sres_sip_status_of_answers(sres_record_t *answers[], uint16_t type)
+{
+ int i;
+
+ for (i = 0; answers && answers[i]; i++) {
+ if (answers[i]->sr_record->r_type == type) {
+ return answers[i]->sr_record->r_status;
+ }
+ }
+
+ return SRES_RECORD_ERR;
+}
+
+static int
+sres_sip_count_answers(sres_record_t *answers[], uint16_t type)
+{
+ int n;
+
+ for (n = 0; answers && answers[n]; n++) {
+ if (type != answers[n]->sr_record->r_type ||
+ answers[n]->sr_record->r_status != 0)
+ break;
+ }
+
+ return n;
+}
+
+static void
+sres_sip_log_answers(sres_sip_t *srs,
+ struct srs_step *step,
+ sres_record_t *answers[])
+{
+ char const *previous;
+ int i;
+
+ if (answers == NULL)
+ return;
+ if (SU_LOG->log_level < 5)
+ return;
+
+ previous = step->sp_target;
+
+ for (i = 0; answers[i]; i++) {
+ sres_record_t *sr = answers[i];
+ int type = sr->sr_record->r_type;
+ char const *domain = sr->sr_record->r_name;
+ char addr[SU_ADDRSIZE];
+
+ if (sr->sr_record->r_status)
+ continue;
+
+ if (su_casematch(previous, domain))
+ domain = "";
+ else
+ previous = domain;
+
+ if (type == sres_type_a) {
+ sres_a_record_t const *a = sr->sr_a;
+ su_inet_ntop(AF_INET, &a->a_addr, addr, sizeof(addr));
+ SU_DEBUG_5(("srs(%p): %s IN A %s\n", (void *)srs, domain, addr));
+ }
+ else if (type == sres_type_cname) {
+ char const *cname = sr->sr_cname->cn_cname;
+ SU_DEBUG_5(("srs(%p): %s IN CNAME %s\n", (void *)srs, domain, cname));
+ }
+#if SU_HAVE_IN6
+ else if (type == sres_type_cname) {
+ sres_aaaa_record_t const *aaaa = sr->sr_aaaa;
+ su_inet_ntop(AF_INET6, &aaaa->aaaa_addr, addr, sizeof(addr));
+ SU_DEBUG_5(("srs(%p): %s IN AAAA %s\n", (void *)srs, domain, addr));
+ }
+#endif
+ else if (type == sres_type_naptr) {
+ sres_naptr_record_t const *na = sr->sr_naptr;
+ SU_DEBUG_5(("srs(%p): %s IN NAPTR %u %u \"%s\" \"%s\" \"%s\" %s\n",
+ (void *)srs,
+ domain,
+ na->na_order, na->na_prefer,
+ na->na_flags, na->na_services,
+ na->na_regexp, na->na_replace));
+ }
+ else if (type == sres_type_srv) {
+ sres_srv_record_t const *srv = sr->sr_srv;
+ SU_DEBUG_5(("srs(%p): %s IN SRV %u %u %u %s\n",
+ (void *)srs,
+ domain,
+ (unsigned)srv->srv_priority, (unsigned)srv->srv_weight,
+ srv->srv_port, srv->srv_target));
+ }
+ else {
+ /* Ignore */
+ }
+ }
+}
+
+
+
+
+/** Return error response */
+static void
+sres_sip_return_results(sres_sip_t *srs, int final)
+{
+ if (final && srs->srs_results == NULL && !srs->srs_error) {
+ /* Get best error */
+ struct srs_step *step;
+
+ for (step = srs->srs_head; step; step = step->sp_next) {
+ switch (step->sp_status) {
+ case SRES_NAME_ERR:
+ if (su_casematch(step->sp_target, srs->srs_target))
+ srs->srs_error = SRES_SIP_ERR_NO_NAME;
+ break;
+ case SRES_OK:
+ case SRES_RECORD_ERR:
+ /* Something was found */
+ srs->srs_error = SRES_SIP_ERR_NO_DATA;
+ break;
+ case SRES_AUTH_ERR:
+ case SRES_FORMAT_ERR:
+ srs->srs_error = SRES_SIP_ERR_FAIL;
+ break;
+ case SRES_SERVER_ERR:
+ case SRES_TIMEOUT_ERR:
+ srs->srs_error = SRES_SIP_ERR_AGAIN;
+ break;
+ }
+ if (srs->srs_error)
+ break;
+ }
+ }
+
+ if (srs->srs_callback) {
+ sres_sip_notify_f *callback = srs->srs_callback;
+ sres_sip_magic_t *magic = srs->srs_magic;
+
+ if (final)
+ srs->srs_callback = NULL, srs->srs_magic = NULL;
+
+ callback(magic, srs, final);
+ }
+}
+
+static int
+sres_sip_process(sres_sip_t *srs)
+{
+ while (*srs->srs_process) {
+ struct srs_step *step = *srs->srs_process;
+
+ if (step->sp_query)
+ return 1;
+
+ if (step->sp_status < 0)
+ break;
+
+ if (srs->srs_process != srs->srs_send)
+ srs->srs_process = &step->sp_next;
+ else
+ srs->srs_send = srs->srs_process = &step->sp_next;
+
+ if (step->sp_status == SRES_RECORD_ERR) {
+ sres_sip_process_cname(srs, step, step->sp_results);
+ continue;
+ }
+
+ if (step->sp_status)
+ continue;
+
+ SU_DEBUG_5(("srs(%p): process %s %s record%s\n",
+ (void *)srs,
+ sres_record_type(step->sp_type, NULL), step->sp_target,
+ step->sp_already != step ? " (again)" : ""));
+
+ switch (step->sp_type) {
+ case sres_type_naptr:
+ sres_sip_process_naptr(srs, step, step->sp_results);
+ break;
+ case sres_type_srv:
+ sres_sip_process_srv(srs, step, step->sp_results);
+ break;
+#if SU_HAVE_IN6
+ case sres_type_aaaa:
+ sres_sip_process_aaaa(srs, step, step->sp_results);
+ break;
+#endif
+ case sres_type_a:
+ sres_sip_process_a(srs, step, step->sp_results);
+ break;
+
+ default:
+ assert(!"unknown query type");
+ }
+ }
+
+ return 0;
+}
+
+
+static struct srs_step *
+sres_sip_step_new(sres_sip_t *srs,
+ int type,
+ char const *prefix,
+ char const *domain)
+{
+ struct srs_step *step, *step0;
+ size_t plen = prefix ? strlen(prefix) : 0;
+ size_t extra = 0;
+
+ for (step0 = srs->srs_head; step0; step0 = step0->sp_next) {
+ if (step0->sp_type == type &&
+ su_casenmatch(prefix, step0->sp_target, plen) &&
+ su_casematch(step0->sp_target + plen, domain))
+ break;
+ }
+
+ if (prefix && !step0)
+ extra = plen + strlen(domain) + 1;
+
+ step = su_zalloc(srs->srs_home, (sizeof *step) + extra);
+
+ if (step) {
+ step->sp_srs = srs;
+ step->sp_type = type;
+ step->sp_status = STEP_NEW;
+
+ if (step0) {
+ step->sp_already = step0;
+ step->sp_results = step0->sp_results;
+ step->sp_target = step0->sp_target;
+ }
+ else {
+ step->sp_already = step;
+
+ if (prefix) {
+ char *target = (char *)(step + 1);
+ step->sp_target = memcpy(target, prefix, plen);
+ strcpy(target + plen, domain);
+ }
+ else {
+ step->sp_target = domain;
+ }
+ }
+ }
+
+ return step;
+}
+
+
+/** Append a step to queue */
+static void
+sres_sip_append_step(sres_sip_t *srs,
+ struct srs_step *step)
+{
+ if (step == NULL)
+ return;
+
+ assert(step->sp_status == STEP_NEW);
+
+ *srs->srs_queue = step, srs->srs_queue = &step->sp_next;
+
+ if (step->sp_already == step) {
+ step->sp_status = STEP_QUEUED;
+ }
+ else {
+ step->sp_status = step->sp_already->sp_status;
+ step->sp_results = step->sp_already->sp_results;
+ }
+}
+
+/** Insert a step to queue.
+ *
+ * Sort by 1) order 2) priority/preference 3) weight.
+ */
+static void
+sres_sip_insert_step(sres_sip_t *srs,
+ struct srs_step *step)
+{
+ struct srs_step *already, *step2, **insert, **at, **next;
+ struct srs_hint *hint = &srs->srs_hints[step->sp_hint];
+ struct sres_sip_tport const *stp = hint->hint_stp;
+ int weight = 0, N = 0, by;
+
+ /* Currently, inserted steps are results from CNAME or SRV.
+ They have hint at this point */
+ assert(step->sp_hint);
+
+ step->sp_srs = srs;
+
+ insert = srs->srs_send;
+
+ for (at = insert; *at; at = next) {
+ next = &(*at)->sp_next;
+
+ if (step->sp_prefer < (*at)->sp_prefer) {
+ break;
+ }
+ if (step->sp_prefer > (*at)->sp_prefer) {
+ insert = next, weight = 0, N = 0;
+ continue;
+ }
+ if (step->sp_priority < (*at)->sp_priority) {
+ break;
+ }
+ if (step->sp_priority > (*at)->sp_priority) {
+ insert = next, weight = 0, N = 0;
+ continue;
+ }
+
+ weight += (*at)->sp_weight, N++;
+ }
+
+ if (step->sp_weight)
+ weight += step->sp_weight;
+ else
+ insert = at;
+
+ if (insert != at)
+ by = su_randint(0, weight - 1);
+ else
+ by = weight;
+
+ SU_DEBUG_5(("srs(%p): %s %s query for %s;transport=%s (N=%u %d/%d)\n",
+ (void *)srs,
+ insert != at ? "inserting" : "appending",
+ sres_record_type(step->sp_type, NULL), step->sp_target,
+ stp->stp_name,
+ N, by, weight));
+
+ if (insert != at) {
+ for (;by > step->sp_weight; insert = &(*insert)->sp_next) {
+ assert(*insert); assert((*insert)->sp_prefer == step->sp_prefer);
+ assert((*insert)->sp_priority == step->sp_priority);
+ by -= (*insert)->sp_weight;
+ }
+ }
+
+ step->sp_next = *insert, *insert = step;
+ if (insert == srs->srs_queue)
+ srs->srs_queue = &step->sp_next;
+
+ step->sp_status = STEP_QUEUED;
+
+ if (step->sp_already == step)
+ return;
+
+ /* Check if sp_already is after step */
+ for (already = step->sp_next; already; already = already->sp_next) {
+ if (already == step->sp_already)
+ break;
+ }
+
+ if (already) {
+ assert(already->sp_status == STEP_QUEUED);
+ step->sp_already = step;
+ for (step2 = step->sp_next; step2; step2 = step2->sp_next) {
+ if (step2->sp_already == already)
+ step2->sp_already = step;
+ }
+ }
+ else {
+ step->sp_status = step->sp_already->sp_status;
+ step->sp_results = step->sp_already->sp_results;
+ }
+}
+
+/* Process NAPTR records */
+static void
+sres_sip_process_naptr(sres_sip_t *srs,
+ struct srs_step *step,
+ sres_record_t *answers[])
+{
+ int i, j, order = -1, found;
+
+ assert(answers);
+
+ /* Sort NAPTR first by order then by preference */
+ /* sres_sort_answers() sorts with flags etc. too */
+ sres_sip_sort_naptr(answers);
+
+ for (i = 0; answers[i]; i++) {
+ sres_naptr_record_t const *na = answers[i]->sr_naptr;
+ struct sres_sip_tport const *stp;
+ int supported = 0;
+
+ if (na->na_record->r_status)
+ continue;
+ if (na->na_record->r_type != sres_type_naptr)
+ continue;
+
+ /* RFC 2915 p 4:
+ * Order
+ * A 16-bit unsigned integer specifying the order in which the
+ * NAPTR records MUST be processed to ensure the correct ordering
+ * of rules. Low numbers are processed before high numbers, and
+ * once a NAPTR is found whose rule "matches" the target, the
+ * client MUST NOT consider any NAPTRs with a higher value for
+ * order (except as noted below for the Flags field).
+ */
+ if (order >= 0 && order != na->na_order)
+ break;
+
+ /* RFC3263 p. 6:
+ First, a client resolving a SIPS URI MUST discard any services that
+ do not contain "SIPS" as the protocol in the service field. The
+ converse is not true, however. A client resolving a SIP URI SHOULD
+ retain records with "SIPS" as the protocol, if the client supports
+ TLS.
+ */
+ if (su_casenmatch(na->na_services, "SIPS+", 5) ||
+ su_casenmatch(na->na_services, "SIP+", 4))
+ /* Use NAPTR results, don't try extra SRV/A/AAAA records */
+ srs->srs_try_srv = 0, srs->srs_try_a = 0, found = 1;
+ else
+ continue; /* Not a SIP/SIPS service */
+
+ /* Check if we have a transport matching with service */
+ for (j = 1; (stp = srs->srs_hints[j].hint_stp); j++) {
+ /*
+ * Syntax of services is actually more complicated
+ * but comparing the values in the transport list
+ * match with those values that make any sense
+ */
+ if (su_casematch(na->na_services, stp->stp_service)) {
+ /* We found matching NAPTR */
+ order = na->na_order;
+ supported = 1;
+ sres_sip_step_by_naptr(srs, step, j, na);
+ }
+ }
+
+ SU_DEBUG_5(("srs(%p): %s IN NAPTR %u %u \"%s\" \"%s\" \"%s\" %s%s\n",
+ (void *)srs,
+ na->na_record->r_name,
+ na->na_order, na->na_prefer,
+ na->na_flags, na->na_services,
+ na->na_regexp, na->na_replace,
+ supported ? "" : " (not supported)"));
+ }
+
+ if (found && order < 0)
+ /* There was a SIP/SIPS natpr, but it has no matching hints */
+ srs->srs_error = SRES_SIP_ERR_NO_TPORT;
+}
+
+static void
+sres_sip_sort_naptr(sres_record_t *answers[])
+{
+ int i, j;
+
+ for (i = 0; answers[i]; i++) {
+ sres_naptr_record_t const *na = answers[i]->sr_naptr;
+
+ if (na->na_record->r_type != sres_type_naptr)
+ break;
+
+ for (j = 0; j < i; j++) {
+ sres_naptr_record_t const *nb = answers[j]->sr_naptr;
+
+ if (na->na_order < nb->na_order)
+ break;
+ if (na->na_order > nb->na_order)
+ continue;
+ if (na->na_prefer < nb->na_prefer)
+ break;
+ }
+
+ if (j < i) {
+ sres_record_t *r = answers[i];
+ for (; j < i; i--) {
+ answers[i] = answers[i - 1];
+ }
+ answers[j] = r;
+ }
+ }
+}
+
+static void
+sres_sip_step_by_naptr(sres_sip_t *srs,
+ struct srs_step *step0,
+ uint16_t hint,
+ sres_naptr_record_t const *na)
+{
+ struct srs_step *step;
+ uint16_t qtype;
+ uint16_t port = srs->srs_hints[hint].hint_stp->stp_port;
+ uint16_t hint_qtype = srs->srs_hints[hint].hint_qtype;
+
+ if (na->na_flags[0] == 's' || na->na_flags[0] == 'S')
+ /* "S" flag means that the next lookup should be for SRV records */
+ qtype = sres_type_srv; /* SRV */
+ else if (na->na_flags[0] == 'a' || na->na_flags[0] == 'A')
+ /* "A" means that the next lookup should be A/AAAA */
+ qtype = hint_qtype;
+ else
+ return;
+
+ step = sres_sip_step_new(srs, qtype, NULL, na->na_replace);
+
+ if (step == NULL)
+ return;
+
+ step->sp_trace = step0;
+ step->sp_origin = (sres_record_t const *)na;
+ step->sp_prefer = na->na_prefer;
+ step->sp_priority = hint;
+ step->sp_weight = 1;
+ step->sp_hint = hint;
+ step->sp_port = port;
+
+ sres_sip_append_step(srs, step);
+}
+
+/* Process SRV records */
+static void
+sres_sip_process_srv(sres_sip_t *srs,
+ struct srs_step *step0,
+ sres_record_t *answers[])
+{
+ int i;
+ struct srs_hint *hint = &srs->srs_hints[step0->sp_hint];
+
+ sres_sip_sort_srv(answers); /* Sort by priority (only) */
+
+ for (i = 0; answers[i]; i++) {
+ sres_srv_record_t const *srv = answers[i]->sr_srv;
+ struct srs_step *step;
+
+ if (srv->srv_record->r_status /* There was an error */ ||
+ srv->srv_record->r_type != sres_type_srv)
+ continue;
+
+ srs->srs_try_a = 0;
+
+ step = sres_sip_step_new(srs, hint->hint_qtype, NULL, srv->srv_target);
+ if (step) {
+ step->sp_hint = step0->sp_hint;
+ step->sp_trace = step0;
+ step->sp_origin = (sres_record_t const *)srv;
+ step->sp_port = srv->srv_port;
+ step->sp_prefer = step0->sp_prefer;
+ step->sp_priority = srv->srv_priority;
+ step->sp_weight = srv->srv_weight;
+
+ /* Insert sorted by priority, randomly select by weigth */
+ sres_sip_insert_step(srs, step);
+ }
+ }
+}
+
+static void
+sres_sip_sort_srv(sres_record_t *answers[])
+{
+ int i, j;
+
+ for (i = 0; answers[i]; i++) {
+ sres_srv_record_t const *a = answers[i]->sr_srv;
+
+ if (a->srv_record->r_type != sres_type_srv)
+ break;
+
+ for (j = 0; j < i; j++) {
+ sres_srv_record_t const *b = answers[j]->sr_srv;
+
+ if (a->srv_priority < b->srv_priority)
+ break;
+ }
+
+ if (j < i) {
+ sres_record_t *r = answers[i];
+ for (; j < i; i--) {
+ answers[i] = answers[i - 1];
+ }
+ answers[j] = r;
+ }
+ }
+}
+
+#if SU_HAVE_IN6
+/* Process AAAA records */
+static void
+sres_sip_process_aaaa(sres_sip_t *srs,
+ struct srs_step *step,
+ sres_record_t *answers[])
+{
+ struct srs_hint *hint = &srs->srs_hints[step->sp_hint];
+ struct sres_sip_tport const *stp = hint->hint_stp;
+ su_addrinfo_t ai[1];
+ su_sockaddr_t su[1];
+ size_t i, j;
+
+ for (i = j = 0; answers && answers[i]; i++) {
+ sres_aaaa_record_t const *aaaa = answers[i]->sr_aaaa;
+
+ if (aaaa->aaaa_record->r_status ||
+ aaaa->aaaa_record->r_type != sres_type_aaaa)
+ continue; /* There was an error */
+
+ memset(ai, 0, (sizeof ai));
+ ai->ai_protocol = stp->stp_number;
+ ai->ai_addr = memset(su, 0, (sizeof su->su_sin6));
+ su->su_len = ai->ai_addrlen = (sizeof su->su_sin6);
+ su->su_family = ai->ai_family = AF_INET6;
+ su->su_port = htons(step->sp_port);
+
+ memcpy(&su->su_sin6.sin6_addr,
+ &aaaa->aaaa_addr,
+ (sizeof aaaa->aaaa_addr));
+
+ ai->ai_canonname = aaaa->aaaa_record->r_name;
+
+ sres_sip_append_result(srs, ai);
+ }
+}
+#endif /* SU_HAVE_IN6 */
+
+/* Process A records */
+static void
+sres_sip_process_a(sres_sip_t *srs,
+ struct srs_step *step,
+ sres_record_t *answers[])
+{
+ struct srs_hint *hint = &srs->srs_hints[step->sp_hint];
+ struct sres_sip_tport const *stp = hint->hint_stp;
+ su_addrinfo_t ai[1];
+ su_sockaddr_t su[1];
+ int i, j;
+
+ for (i = j = 0; answers[i]; i++) {
+ sres_a_record_t const *a = answers[i]->sr_a;
+
+ if (a->a_record->r_status ||
+ a->a_record->r_type != sres_type_a)
+ continue; /* There was an error */
+
+ memset(ai, 0, (sizeof ai));
+ ai->ai_protocol = stp->stp_number;
+ ai->ai_addr = memset(su, 0, (sizeof su->su_sin));
+ su->su_len = ai->ai_addrlen = (sizeof su->su_sin);
+ su->su_family = ai->ai_family = AF_INET;
+ su->su_port = htons(step->sp_port);
+
+ memcpy(&su->su_sin.sin_addr, &a->a_addr, (sizeof a->a_addr));
+
+ ai->ai_canonname = a->a_record->r_name;
+
+ sres_sip_append_result(srs, ai);
+ }
+}
+
+static void
+sres_sip_process_cname(sres_sip_t *srs,
+ struct srs_step *step0,
+ sres_record_t *answers[])
+{
+ struct srs_step *step;
+ int i;
+
+ if (answers == NULL)
+ return;
+
+ for (i = 0; answers[i]; i++) {
+ sres_cname_record_t *cn = answers[i]->sr_cname;
+
+ if (cn->cn_record->r_type != sres_type_cname ||
+ cn->cn_record->r_status != SRES_OK)
+ continue;
+
+ step = sres_sip_step_new(srs, step0->sp_type, NULL, cn->cn_cname);
+ if (!step)
+ return;
+
+ step->sp_trace = step0;
+ step->sp_origin = answers[i];
+
+ step->sp_hint = step0->sp_hint;
+ step->sp_port = step0->sp_port;
+ step->sp_prefer = step0->sp_prefer;
+ step->sp_priority = step0->sp_priority;
+ step->sp_weight = step0->sp_weight;
+
+ sres_sip_insert_step(srs, step);
+
+ return;
+ }
+}
+
+
+/* Process URI with numeric host */
+static void
+sres_sip_process_numeric(sres_sip_t *srs)
+{
+ char const *target = srs->srs_target;
+ su_addrinfo_t ai[1];
+ su_sockaddr_t su[1];
+ char buffer[64];
+ int i;
+
+ memset(ai, 0, (sizeof ai));
+
+ if (host_is_ip4_address(target)) {
+ ai->ai_addr = memset(su, 0, (sizeof su->su_sin));
+ su->su_len = ai->ai_addrlen = (sizeof su->su_sin);
+ if (su_inet_pton(su->su_family = ai->ai_family = AF_INET,
+ target, &su->su_sin.sin_addr) <= 0) {
+ srs->srs_error = SRES_SIP_ERR_BAD_URI;
+ return;
+ }
+ }
+#if SU_HAVE_IN6
+ else if (host_is_ip6_address(target)) {
+ ai->ai_addr = memset(su, 0, (sizeof su->su_sin6));
+ su->su_len = ai->ai_addrlen = (sizeof su->su_sin6);
+ if (su_inet_pton(su->su_family = ai->ai_family = AF_INET6,
+ target, &su->su_sin6.sin6_addr) <= 0) {
+ srs->srs_error = SRES_SIP_ERR_BAD_URI;
+ return;
+ }
+ }
+ else if (host_is_ip6_reference(target)) {
+ size_t len = strlen(target) - 2;
+
+ ai->ai_addr = memset(su, 0, (sizeof su->su_sin6));
+ su->su_len = ai->ai_addrlen = (sizeof su->su_sin6);
+
+ if (len >= sizeof buffer ||
+ !memcpy(buffer, target + 1, len) ||
+ (buffer[len] = 0) ||
+ su_inet_pton(su->su_family = ai->ai_family = AF_INET6,
+ buffer, &su->su_sin6.sin6_addr) <= 0) {
+ srs->srs_error = SRES_SIP_ERR_BAD_URI;
+ return;
+ }
+
+ target = buffer;
+ }
+#endif
+ else {
+ srs->srs_error = SRES_SIP_ERR_BAD_URI;
+ return;
+ }
+
+ ai->ai_canonname = (char *)target;
+
+ for (i = 1; srs->srs_hints[i].hint_stp; i++) {
+ struct srs_hint const *hint = srs->srs_hints + i;
+
+ /* Consider only transport from uri parameter */
+ if (!srs->srs_transport &&
+ /* ...or default transports for this URI type */
+ hint->hint_stp->stp_type != srs->srs_url->url_type)
+ continue;
+
+#if SU_HAVE_IN6
+ if (ai->ai_family == AF_INET6 &&
+ srs->srs_hints[i].hint_qtype != sres_type_aaaa)
+ continue;
+#endif
+ ai->ai_protocol = srs->srs_hints[i].hint_stp->stp_number;
+ sres_sip_append_result(srs, ai);
+ }
+}
+
+/** Store A/AAAA query result */
+static void
+sres_sip_append_result(sres_sip_t *srs,
+ su_addrinfo_t const *result)
+{
+ su_addrinfo_t *ai, **tail;
+ int duplicate = 0;
+ char const *canonname = result->ai_canonname;
+ char numeric[64];
+ size_t clen = 0;
+
+ for (ai = srs->srs_results; ai && !duplicate; ai = ai->ai_next) {
+ duplicate =
+ ai->ai_family == result->ai_family &&
+ ai->ai_protocol == result->ai_protocol &&
+ ai->ai_addrlen == result->ai_addrlen &&
+ !memcmp(ai->ai_addr, result->ai_addr, result->ai_addrlen);
+ /* Note - different canonnames are not considered */
+ if (duplicate)
+ break;
+ }
+
+ if ((srs->srs_canonname && srs->srs_numeric) || (SU_LOG->log_level >= 0)) {
+ unsigned port = 0;
+ char const *lb = "", *rb = "";
+
+ if (result->ai_family == AF_INET) {
+ struct sockaddr_in const *sin = (struct sockaddr_in *)result->ai_addr;
+ su_inet_ntop(AF_INET, &sin->sin_addr, numeric, sizeof numeric);
+ port = ntohs(sin->sin_port);
+ }
+#if SU_HAVE_IN6
+ else if (result->ai_family == AF_INET6) {
+ struct sockaddr_in6 const *sin6 = (struct sockaddr_in6 *)result->ai_addr;
+ su_inet_ntop(AF_INET6, &sin6->sin6_addr, numeric, (sizeof numeric));
+ port = ntohs(sin6->sin6_port);
+ lb = "[", rb = "]";
+ }
+#endif
+ else {
+ strcpy(numeric, "UNKNOWN");
+ }
+
+ SU_DEBUG_5(("srs(%p): %s result %s%s%s:%u;transport=%s\n",
+ srs, duplicate ? "duplicate" : "returning",
+ lb , numeric, rb, port,
+ sres_sip_transport_name(result->ai_protocol)));
+
+ if (srs->srs_numeric)
+ canonname = numeric;
+ }
+
+ if (duplicate)
+ return;
+
+ if (!srs->srs_canonname)
+ canonname = NULL;
+ if (canonname) {
+ clen = strlen(canonname);
+ if (clen && canonname[clen - 1] == '.')
+ /* Do not include final dot in canonname */;
+ else
+ clen++;
+ }
+
+ ai = su_zalloc(srs->srs_home, (sizeof *ai) + result->ai_addrlen + clen);
+
+ if (ai == NULL)
+ return;
+
+ *ai = *result;
+ ai->ai_next = NULL;
+ ai->ai_addr = memcpy(ai + 1, ai->ai_addr, ai->ai_addrlen);
+ if (canonname) {
+ ai->ai_canonname = (char *)(ai->ai_addr) + ai->ai_addrlen;
+ memcpy(ai->ai_canonname, canonname, clen - 1);
+ ai->ai_canonname[clen - 1] = '\0';
+ }
+ else {
+ ai->ai_canonname = NULL;
+ }
+
+ for (tail = srs->srs_next; *tail; tail = &(*tail)->ai_next)
+ ;
+
+ *tail = ai;
+
+ srs->srs_error = 0;
+}
1
0
12 Feb '09
Author: mrene
Date: Thu Feb 12 15:53:17 2009
New Revision: 11966
Log:
tweak
Modified:
freeswitch/trunk/src/mod/applications/mod_limit/mod_limit.c
Modified: freeswitch/trunk/src/mod/applications/mod_limit/mod_limit.c
==============================================================================
--- freeswitch/trunk/src/mod/applications/mod_limit/mod_limit.c (original)
+++ freeswitch/trunk/src/mod/applications/mod_limit/mod_limit.c Thu Feb 12 15:53:17 2009
@@ -764,7 +764,7 @@
switch_safe_free(sql);
got = atoi(buf);
- if (max > 0 && got + 1 > max) {
+ if (max >= 0 && got + 1 > max) {
switch_ivr_session_transfer(session, xfer_exten, argv[4], argv[5]);
goto done;
}
1
0
[commit] r11965 - freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip
by FreeSWITCH SVN 12 Feb '09
by FreeSWITCH SVN 12 Feb '09
12 Feb '09
Author: mikej
Date: Thu Feb 12 15:37:20 2009
New Revision: 11965
Log:
Thu Feb 12 15:35:06 CST 2009 Pekka Pessi <first.last(a)nokia.com>
* sofia-sip/su_addrinfo.h: #include <netdb.h> only if bsdsock
Modified:
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_addrinfo.h
Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_addrinfo.h
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_addrinfo.h (original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_addrinfo.h Thu Feb 12 15:37:20 2009
@@ -43,7 +43,9 @@
#include <sofia-sip/su_types.h>
#endif
+#if SU_HAVE_BSDSOCK
#include <netdb.h>
+#endif
SOFIA_BEGIN_DECLS
1
0
[commit] r11964 - in freeswitch/trunk/libs/sofia-sip: . libsofia-sip-ua/tport
by FreeSWITCH SVN 12 Feb '09
by FreeSWITCH SVN 12 Feb '09
12 Feb '09
Author: mikej
Date: Thu Feb 12 15:30:05 2009
New Revision: 11964
Log:
Thu Feb 12 12:02:48 CST 2009 Jarod Neuner <janeuner(a)networkharbor.com>
* tport_tls: add/fix some tls init logging
Modified:
freeswitch/trunk/libs/sofia-sip/.update
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/tport/tport_tls.c
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_tls.c
Modified: freeswitch/trunk/libs/sofia-sip/.update
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/.update (original)
+++ freeswitch/trunk/libs/sofia-sip/.update Thu Feb 12 15:30:05 2009
@@ -1 +1 @@
-Thu Feb 12 15:29:22 CST 2009
+Thu Feb 12 15:29:54 CST 2009
Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/tport/tport_tls.c
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/tport/tport_tls.c (original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/tport/tport_tls.c Thu Feb 12 15:30:05 2009
@@ -271,7 +271,7 @@
if (ti->configured > 0) {
SU_DEBUG_1(("%s: invalid local certificate: %s\n",
"tls_init_context", ti->cert));
- tls_log_errors(1, "tls_init_context", 0);
+ tls_log_errors(3, "tls_init_context", 0);
#if require_client_certificate
errno = EIO;
return -1;
@@ -283,7 +283,9 @@
ti->key,
SSL_FILETYPE_PEM)) {
if (ti->configured > 0) {
- tls_log_errors(1, "tls_init_context", 0);
+ SU_DEBUG_1(("%s: invalid private key: %s\n",
+ "tls_init_context", ti->key));
+ tls_log_errors(3, "tls_init_context(key)", 0);
#if require_client_certificate
errno = EIO;
return -1;
@@ -305,8 +307,10 @@
if (!SSL_CTX_load_verify_locations(tls->ctx,
ti->CAfile,
ti->CApath)) {
+ SU_DEBUG_1(("%s: error loading CA list: %s\n",
+ "tls_init_context", ti->CAfile));
if (ti->configured > 0)
- tls_log_errors(1, "tls_init_context", 0);
+ tls_log_errors(3, "tls_init_context(CA)", 0);
errno = EIO;
return -1;
}
@@ -328,7 +332,7 @@
if (!SSL_CTX_set_cipher_list(tls->ctx, ti->cipher)) {
SU_DEBUG_1(("%s: error setting cipher list\n", "tls_init_context"));
- tls_log_errors(1, "tls_init_context", 0);
+ tls_log_errors(3, "tls_init_context", 0);
errno = EIO;
return -1;
}
Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_tls.c
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_tls.c (original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_tls.c Thu Feb 12 15:30:05 2009
@@ -222,13 +222,21 @@
su_home_zap(autohome);
if (!tlspri->tlspri_master) {
+ /*
if (!path || ti.configured) {
SU_DEBUG_1(("tls_init_master: %s\n", strerror(errno)));
}
else {
SU_DEBUG_5(("tls_init_master: %s\n", strerror(errno)));
}
+ */
return *return_culprit = "tls_init_master", -1;
+ } else {
+ char buf[TPORT_HOSTPORTSIZE];
+ su_sockaddr_t *sa = ai ? (void *)(ai->ai_addr) : NULL;
+ if (sa && tport_hostport(buf, sizeof(buf), sa, 2))
+ SU_DEBUG_5(("%s(%p): tls context initialized for %s\n", \
+ __func__, (void *)pri, buf));
}
if (tls_subjects)
1
0
[commit] r11963 - in freeswitch/trunk/libs/sofia-sip: . libsofia-sip-ua/su/sofia-sip
by FreeSWITCH SVN 12 Feb '09
by FreeSWITCH SVN 12 Feb '09
12 Feb '09
Author: mikej
Date: Thu Feb 12 15:29:30 2009
New Revision: 11963
Log:
Thu Feb 12 15:14:27 CST 2009 Pekka Pessi <first.last(a)nokia.com>
* su_addrinfo.h: #include <netdb.h>
Modified:
freeswitch/trunk/libs/sofia-sip/.update
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_addrinfo.h
Modified: freeswitch/trunk/libs/sofia-sip/.update
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/.update (original)
+++ freeswitch/trunk/libs/sofia-sip/.update Thu Feb 12 15:29:30 2009
@@ -1 +1 @@
-Thu Feb 12 15:28:20 CST 2009
+Thu Feb 12 15:29:22 CST 2009
Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_addrinfo.h
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_addrinfo.h (original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_addrinfo.h Thu Feb 12 15:29:30 2009
@@ -43,6 +43,8 @@
#include <sofia-sip/su_types.h>
#endif
+#include <netdb.h>
+
SOFIA_BEGIN_DECLS
#if !SU_HAVE_ADDRINFO
1
0
[commit] r11962 - in freeswitch/trunk/libs/sofia-sip: . libsofia-sip-ua/soa libsofia-sip-ua/soa/sofia-sip
by FreeSWITCH SVN 12 Feb '09
by FreeSWITCH SVN 12 Feb '09
12 Feb '09
Author: mikej
Date: Thu Feb 12 15:28:58 2009
New Revision: 11962
Log:
Thu Feb 12 15:14:07 CST 2009 Pekka Pessi <first.last(a)nokia.com>
* soa: make better effort in keeping addresses (c= and o= lines) in user SDP
Ignore-this: a6da9ed249dba3309e3dbbbdb4262082
The address selection logic now prefers (unicast) addresses in already
present in SDP, if they get returned by su_getlocalinfo().
The process currently tries to avoid link-local addresses.
Modified:
freeswitch/trunk/libs/sofia-sip/.update
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/soa/Makefile.am
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/soa/soa.c
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/soa/soa_static.c
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/soa/sofia-sip/soa_session.h
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/soa/test_soa.c
Modified: freeswitch/trunk/libs/sofia-sip/.update
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/.update (original)
+++ freeswitch/trunk/libs/sofia-sip/.update Thu Feb 12 15:28:58 2009
@@ -1 +1 @@
-Thu Feb 12 15:25:06 CST 2009
+Thu Feb 12 15:28:20 CST 2009
Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/soa/Makefile.am
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/soa/Makefile.am (original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/soa/Makefile.am Thu Feb 12 15:28:58 2009
@@ -14,7 +14,8 @@
-I$(srcdir)/../url -I../url \
-I$(srcdir)/../ipt -I../ipt \
-I$(srcdir)/../bnf -I../bnf \
- -I$(srcdir)/../su -I../su
+ -I$(srcdir)/../su -I../su \
+ -I$(top_srcdir)/s2check
# ----------------------------------------------------------------------
# Build targets
@@ -39,7 +40,8 @@
COVERAGE_INPUT = $(libsoa_la_SOURCES) $(include_sofia_HEADERS)
-LDADD = libsoa.la \
+test_soa_LDADD = $(top_builddir)/s2check/libs2.a \
+ libsoa.la \
../sip/libsip.la \
../msg/libmsg.la \
../url/liburl.la \
Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/soa/soa.c
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/soa/soa.c (original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/soa/soa.c Thu Feb 12 15:28:58 2009
@@ -48,6 +48,7 @@
#include "sofia-sip/soa_add.h"
#include <sofia-sip/hostdomain.h>
+#include <sofia-sip/bnf.h>
#include <sofia-sip/su_tagarg.h>
#include <sofia-sip/su_localinfo.h>
#include <sofia-sip/su_uniqueid.h>
@@ -2166,15 +2167,36 @@
su_free(ss->ss_home, tbf4);
}
-
/** Initialize SDP o= line */
int
soa_init_sdp_origin(soa_session_t *ss, sdp_origin_t *o, char buffer[64])
{
- sdp_connection_t *c;
+ return soa_init_sdp_origin_with_session(ss, o, buffer, NULL);
+}
- if (ss == NULL || o == NULL)
- return su_seterrno(EFAULT), -1;
+
+/** Check if c= has valid address
+ */
+int
+soa_check_sdp_connection(sdp_connection_t const *c)
+{
+ return c != NULL &&
+ c->c_nettype &&c->c_address &&
+ strcmp(c->c_address, "") &&
+ strcmp(c->c_address, "0.0.0.0") &&
+ strcmp(c->c_address, "::");
+}
+
+
+/** Initialize SDP o= line with values from @a sdp session. */
+int
+soa_init_sdp_origin_with_session(soa_session_t *ss,
+ sdp_origin_t *o,
+ char buffer[64],
+ sdp_session_t const *sdp)
+{
+ if (ss == NULL || o == NULL || buffer == NULL)
+ return su_seterrno(EFAULT);
assert(o->o_address);
@@ -2189,49 +2211,13 @@
su_randmem(&o->o_version, sizeof o->o_version);
o->o_version &= ((unsigned longlong)1 << 63) - 1;
- c = o->o_address;
-
- if (!c->c_nettype ||
- !c->c_address ||
- strcmp(c->c_address, "") == 0 ||
- strcmp(c->c_address, "0.0.0.0") == 0 ||
- strcmp(c->c_address, "::") == 0 ||
- !host_is_local(c->c_address)) {
- return soa_init_sdp_connection(ss, c, buffer);
- }
+ if (!soa_check_sdp_connection(o->o_address) ||
+ host_is_local(o->o_address->c_address))
+ return soa_init_sdp_connection_with_session(ss, o->o_address, buffer, sdp);
return 0;
}
-/** Search for an local address item from string provided by user */
-static
-su_localinfo_t *li_in_list(su_localinfo_t *li0, char const **llist)
-{
- char const *list = *llist;
- size_t n;
-
- if (!list)
- return NULL;
-
- while ((n = strcspn(list, ", "))) {
- su_localinfo_t *li;
-
- for (li = li0; li; li = li->li_next) {
- if (su_casenmatch(li->li_canonname, list, n) &&
- li->li_canonname[n] == '\0')
- break;
- }
-
- list += n; while (list[0] == ' ' || list[0] == ',') list++;
- *llist = list;
-
- if (li)
- return li;
- }
-
- return NULL;
-}
-
/** Obtain a local address for SDP connection structure */
int
@@ -2239,12 +2225,35 @@
sdp_connection_t *c,
char buffer[64])
{
- su_localinfo_t *res, hints[1] = {{ LI_CANONNAME | LI_NUMERIC }};
- su_localinfo_t *li, *li4, *li6;
+ return soa_init_sdp_connection_with_session(ss, c, buffer, NULL);
+}
+
+static su_localinfo_t const *best_listed_address_in_localinfo(
+ su_localinfo_t const *res, char const *address, int ip4, int ip6);
+static sdp_connection_t const *best_listed_address_in_session(
+ sdp_session_t const *sdp, char const *address0, int ip4, int ip6);
+static su_localinfo_t const *best_listed_address(
+ su_localinfo_t *li0, char const *address, int ip4, int ip6);
+
+
+/** Obtain a local address for SDP connection structure.
+ *
+ * Prefer an address already found in @a sdp.
+ */
+int
+soa_init_sdp_connection_with_session(soa_session_t *ss,
+ sdp_connection_t *c,
+ char buffer[64],
+ sdp_session_t const *sdp)
+{
+ su_localinfo_t *res, hints[1] = {{ LI_CANONNAME | LI_NUMERIC }}, li0[1];
+ su_localinfo_t const *li, *li4, *li6;
char const *address;
+ char const *source = NULL;
int ip4, ip6, error;
+ char abuffer[64]; /* getting value from ss_address */
- if (ss == NULL || c == NULL)
+ if (ss == NULL || c == NULL || buffer == NULL)
return su_seterrno(EFAULT), -1;
address = ss->ss_address;
@@ -2268,20 +2277,39 @@
c->c_address = memcpy(buffer, address + 1, len - 1);
buffer[len - 1] = '\0';
}
+
+ SU_DEBUG_5(("%s: using SOATAG_ADDRESS(\"%s\")\n", __func__, c->c_address));
+
return 0;
}
/* XXX - using LI_SCOPE_LINK requires some tweaking */
hints->li_scope = LI_SCOPE_GLOBAL | LI_SCOPE_SITE /* | LI_SCOPE_LINK */;
+ for (res = NULL; res == NULL;) {
+ if ((error = su_getlocalinfo(hints, &res)) < 0
+ && error != ELI_NOADDRESS) {
+ SU_DEBUG_1(("%s: su_localinfo: %s\n", __func__,
+ su_gli_strerror(error)));
+ return -1;
+ }
+ if (hints->li_scope & LI_SCOPE_HOST)
+ break;
+ hints->li_scope |= LI_SCOPE_HOST;
+ }
+
+ if (c->c_nettype != sdp_net_in ||
+ (c->c_addrtype != sdp_addr_ip4 && c->c_addrtype != sdp_addr_ip6)) {
+ c->c_nettype = sdp_net_in, c->c_addrtype = 0;
+ c->c_address = strcpy(buffer, "");
+ }
+
switch (ss->ss_af) {
case SOA_AF_IP4_ONLY:
- hints->li_family = AF_INET, ip4 = 1, ip6 = 0;
+ ip4 = 1, ip6 = 0;
break;
-
-#if HAVE_SIN6
case SOA_AF_IP6_ONLY:
- hints->li_family = AF_INET6, ip6 = 1, ip4 = 0;
+ ip6 = 1, ip4 = 0;
break;
case SOA_AF_IP4_IP6:
ip4 = 2, ip6 = 1;
@@ -2289,79 +2317,251 @@
case SOA_AF_IP6_IP4:
ip4 = 1, ip6 = 2;
break;
-#endif
default:
ip4 = ip6 = 1;
}
- for (res = NULL; res == NULL;) {
- if ((error = su_getlocalinfo(hints, &res)) < 0
- && error != ELI_NOADDRESS) {
- SU_DEBUG_1(("%s: su_localinfo: %s\n", __func__,
- su_gli_strerror(error)));
- return -1;
+ if (ip4 && ip6) {
+ /* Prefer address family already used in session, if any */
+ sdp_addrtype_e addrtype = 0;
+ char const *because = "error";
+
+ if (sdp && sdp->sdp_connection &&
+ sdp->sdp_connection->c_nettype == sdp_net_in) {
+ addrtype = sdp->sdp_connection->c_addrtype;
+ because = "an existing c= line";
+ }
+ else if (sdp) {
+ int mip4 = 0, mip6 = 0;
+ sdp_media_t const *m;
+
+ for (m = sdp->sdp_media; m; m = m->m_next) {
+ sdp_connection_t const *mc;
+
+ if (m->m_rejected)
+ continue;
+
+ for (mc = m->m_connections; mc; mc = mc->c_next) {
+ if (mc->c_nettype == sdp_net_in) {
+ if (mc->c_addrtype == sdp_addr_ip4)
+ mip4++;
+ else if (mc->c_addrtype == sdp_addr_ip6)
+ mip6++;
+ }
+ }
+ }
+
+ if (mip4 && mip6)
+ /* Mixed. */;
+ else if (mip4)
+ addrtype = sdp_addr_ip4, because = "an existing c= line under m=";
+ else if (mip6)
+ addrtype = sdp_addr_ip6, because = "an existing c= line under m=";
+ }
+
+ if (addrtype == 0)
+ addrtype = c->c_addrtype, because = "the user sdp";
+
+ if (addrtype == sdp_addr_ip4) {
+ if (ip6 >= ip4)
+ SU_DEBUG_5(("%s: prefer %s because of %s\n", __func__, "IP4", because));
+ ip4 = 2, ip6 = 1;
+ }
+ else if (addrtype == sdp_addr_ip6) {
+ if (ip4 >= ip6)
+ SU_DEBUG_5(("%s: prefer %s because of %s\n", __func__, "IP4", because));
+ ip6 = 2, ip4 = 1;
}
- if (hints->li_scope & LI_SCOPE_HOST)
- break;
- hints->li_scope |= LI_SCOPE_HOST;
}
- if (!(ip4 & ip6 && c->c_nettype == sdp_net_in))
- /* Use ss_af preference */;
- else if (c->c_addrtype == sdp_addr_ip4)
- ip4 = 2, ip6 = 1;
- else if (c->c_addrtype == sdp_addr_ip6)
- ip6 = 2, ip4 = 1;
+ li = NULL, li4 = NULL, li6 = NULL;
- if (address)
- SU_DEBUG_3(("%s: searching for %s from list \"%s\"\n",
- __func__, ip6 && !ip4 ? "IP6 " : !ip6 && ip4 ? "IP4 " : "",
- address));
+ if (li == NULL && ss->ss_address) {
+ li = best_listed_address_in_localinfo(res, ss->ss_address, ip4, ip6);
+ if (li)
+ source = "local address from SOATAG_ADDRESS() list";
+ }
- li = res, li4 = NULL, li6 = NULL;
+ if (li == NULL && ss->ss_address && sdp) {
+ sdp_connection_t const *c;
+ c = best_listed_address_in_session(sdp, ss->ss_address, ip4, ip6);
+ if (c) {
+ li = memset(li0, 0, sizeof li0);
+ if (c->c_addrtype == sdp_addr_ip4)
+ li0->li_family = AF_INET;
+#if SU_HAVE_IN6
+ else
+ li0->li_family = AF_INET6;
+#endif
+ li0->li_canonname = (char *)c->c_address;
+ source = "address from SOATAG_ADDRESS() list already in session";
+ }
+ }
- for (;;) {
- if (address)
- li = li_in_list(li, &address);
+ if (li == NULL && ss->ss_address) {
+ memset(li0, 0, sizeof li0);
+ li0->li_canonname = abuffer;
+ li = best_listed_address(li0, ss->ss_address, ip4, ip6);
+ if (li)
+ source = "address from SOATAG_ADDRESS() list";
+ }
- if (!li)
- break;
-#if HAVE_SIN6
- else if (li->li_family == AF_INET6) {
- if (ip6 >= ip4)
+ if (li == NULL) {
+ for (li = res; li; li = li->li_next) {
+ if (su_casematch(li->li_canonname, c->c_address))
break;
- else if (!li6)
- li6 = li; /* Best IP6 address */
}
+ if (li)
+ source = "the proposed local address";
+ }
+
+ /* Check if session-level c= address is local */
+ if (li == NULL && sdp && sdp->sdp_connection) {
+ for (li = res; li; li = li->li_next) {
+ if (!su_casematch(li->li_canonname, sdp->sdp_connection->c_address))
+ continue;
+#if HAVE_SIN6
+ if (li->li_family == AF_INET6) {
+ if (ip6 >= ip4)
+ break;
+ else if (!li6)
+ li6 = li; /* Best IP6 address */
+ }
#endif
- else if (li->li_family == AF_INET) {
- if (ip4 > ip6)
+ else if (li->li_family == AF_INET) {
+ if (ip4 >= ip6)
+ break;
+ else if (!li4)
+ li4 = li; /* Best IP4 address */
+ }
+ }
+
+ if (li == NULL && ip4)
+ li = li4;
+ if (li == NULL && ip6)
+ li = li6;
+ if (li)
+ source = "an existing session-level c= line";
+ }
+
+ /* Check for best local media-level c= address */
+ if (li == NULL && sdp) {
+ sdp_media_t const *m;
+
+ for (m = sdp->sdp_media; m; m = m->m_next) {
+ sdp_connection_t const *mc;
+
+ if (m->m_rejected)
+ continue;
+
+ for (mc = m->m_connections; mc; mc = mc->c_next) {
+ for (li = res; li; li = li->li_next) {
+ if (!su_casematch(li->li_canonname, sdp->sdp_connection->c_address))
+ continue;
+#if HAVE_SIN6
+ if (li->li_family == AF_INET6) {
+ if (ip6 > ip4)
+ break;
+ else if (!li6)
+ li6 = li; /* Best IP6 address */
+ }
+#endif
+ else if (li->li_family == AF_INET) {
+ if (ip4 > ip6)
+ break;
+ else if (!li4)
+ li4 = li; /* Best IP4 address */
+ }
+ }
+ }
+
+ if (li)
break;
- else if (!li4)
- li4 = li; /* Best IP4 address */
}
- if (!address)
- li = li->li_next;
+ if (li == NULL && ip4)
+ li = li4;
+ if (li == NULL && ip6)
+ li = li6;
+ if (li)
+ source = "an existing c= address from media descriptions";
}
- if (li == NULL)
- li = li4;
- if (li == NULL)
- li = li6;
+ /* Check if o= address is local */
+ if (li == NULL && sdp && sdp->sdp_origin) {
+ char const *address = sdp->sdp_origin->o_address->c_address;
- if (li == NULL)
- ;
- else if (li->li_family == AF_INET)
- c->c_nettype = sdp_net_in, c->c_addrtype = sdp_addr_ip4;
+ for (li = res; li; li = li->li_next) {
+ if (!su_casematch(li->li_canonname, address))
+ continue;
#if HAVE_SIN6
- else if (li->li_family == AF_INET6)
- c->c_nettype = sdp_net_in, c->c_addrtype = sdp_addr_ip6;
+ if (li->li_family == AF_INET6) {
+ if (ip6 >= ip4)
+ break;
+ else if (!li6)
+ li6 = li; /* Best IP6 address */
+ }
#endif
+ else if (li->li_family == AF_INET) {
+ if (ip4 >= ip6)
+ break;
+ else if (!li4)
+ li4 = li; /* Best IP4 address */
+ }
+ }
+
+ if (li == NULL && ip4)
+ li = li4;
+ if (li == NULL && ip6)
+ li = li6;
+ if (li)
+ source = "an existing address from o= line";
+ }
+
+ if (li == NULL) {
+ for (li = res; li; li = li->li_next) {
+#if HAVE_SIN6
+ if (li->li_family == AF_INET6) {
+ if (ip6 >= ip4)
+ break;
+ else if (!li6)
+ li6 = li; /* Best IP6 address */
+ }
+#endif
+ else if (li->li_family == AF_INET) {
+ if (ip4 >= ip6)
+ break;
+ else if (!li4)
+ li4 = li; /* Best IP4 address */
+ }
+ }
+
+ if (li == NULL && ip4)
+ li = li4;
+ if (li == NULL && ip6)
+ li = li6;
+
+ if (li)
+ source = "a local address";
+ }
if (li) {
+ char const *typename;
+
+ if (li->li_family == AF_INET)
+ c->c_nettype = sdp_net_in, c->c_addrtype = sdp_addr_ip4, typename = "IP4";
+#if HAVE_SIN6
+ else if (li->li_family == AF_INET6)
+ c->c_nettype = sdp_net_in, c->c_addrtype = sdp_addr_ip6, typename = "IP6";
+#endif
+ else
+ typename = "???";
+
assert(strlen(li->li_canonname) < 64);
c->c_address = strcpy(buffer, li->li_canonname);
+
+ SU_DEBUG_5(("%s: selected IN %s %s (%s)\n", __func__,
+ typename, li->li_canonname, source));
}
su_freelocalinfo(res);
@@ -2371,3 +2571,174 @@
else
return 0;
}
+
+/* Search for first entry from SOATAG_ADDRESS() list on localinfo list */
+static su_localinfo_t const *
+best_listed_address_in_localinfo(su_localinfo_t const *res,
+ char const *address,
+ int ip4,
+ int ip6)
+{
+ su_localinfo_t const *li = NULL, *best = NULL;
+ size_t n;
+
+ SU_DEBUG_3(("%s: searching for %s from list \"%s\"\n",
+ __func__, ip6 && !ip4 ? "IP6 " : !ip6 && ip4 ? "IP4 " : "",
+ address));
+
+ for (; address[0]; address += n + strspn(address, ", ")) {
+ n = strcspn(address, ", ");
+ if (n == 0)
+ continue;
+
+ for (li = res; li; li = li->li_next) {
+ if (su_casenmatch(li->li_canonname, address, n) &&
+ li->li_canonname[n] == '\0')
+ break;
+ }
+
+ if (li == NULL)
+ continue;
+#if HAVE_SIN6
+ else if (li->li_family == AF_INET6) {
+ if (ip6 >= ip4)
+ return li;
+ else if (ip6 && !best)
+ best = li; /* Best IP6 address */
+ }
+#endif
+ else if (li->li_family == AF_INET) {
+ if (ip4 >= ip6)
+ return li;
+ else if (ip4 && !best)
+ best = li; /* Best IP4 address */
+ }
+ }
+
+ return best;
+}
+
+/* Search for first entry from SOATAG_ADDRESS() list in session */
+static sdp_connection_t const *
+best_listed_address_in_session(sdp_session_t const *sdp,
+ char const *address0,
+ int ip4,
+ int ip6)
+{
+ sdp_connection_t *c = NULL, *best = NULL;
+ sdp_media_t *m;
+ char const *address;
+ size_t n;
+
+ for (address = address0; address[0]; address += n + strspn(address, ", ")) {
+ n = strcspn(address, ", ");
+ if (n == 0)
+ continue;
+
+ c = sdp->sdp_connection;
+
+ if (c && su_casenmatch(c->c_address, address, n) && c->c_address[n] == 0)
+ ;
+ else
+ for (m = sdp->sdp_media; m; m = m->m_next) {
+ if (m->m_connections && m->m_connections != sdp->sdp_connection) {
+ c = sdp->sdp_connection;
+ if (su_casenmatch(c->c_address, address, n) && c->c_address[n] == 0)
+ break;
+ c = NULL;
+ }
+ }
+
+ if (c == NULL || c->c_nettype != sdp_net_in)
+ continue;
+#if HAVE_SIN6
+ else if (c->c_addrtype == sdp_addr_ip6) {
+ if (ip6 >= ip4)
+ return c;
+ else if (ip6 && !best)
+ best = c; /* Best IP6 address */
+ }
+#endif
+ else if (c->c_addrtype == sdp_addr_ip4) {
+ if (ip4 >= ip6)
+ return c;
+ else if (ip4 && !best)
+ best = c; /* Best IP4 address */
+ }
+ }
+
+ if (best || sdp->sdp_origin == NULL)
+ return best;
+
+ /* Check if address on list is already been used on o= line */
+ for (address = address0; address[0]; address += n + strspn(address, ", ")) {
+ n = strcspn(address, ", ");
+ if (n == 0)
+ continue;
+ c = sdp->sdp_origin->o_address;
+
+ if (su_casenmatch(c->c_address, address, n) && c->c_address[n] != 0)
+ continue;
+#if HAVE_SIN6
+ else if (c->c_addrtype == sdp_addr_ip6) {
+ if (ip6 >= ip4)
+ return c;
+ else if (ip6 && !best)
+ best = c; /* Best IP6 address */
+ }
+#endif
+ else if (c->c_addrtype == sdp_addr_ip4) {
+ if (ip4 >= ip6)
+ return c;
+ else if (ip4 && !best)
+ best = c; /* Best IP4 address */
+ }
+ }
+
+ return best;
+}
+
+static su_localinfo_t const *
+best_listed_address(su_localinfo_t *li0,
+ char const *address,
+ int ip4,
+ int ip6)
+{
+ size_t n, best = 0;
+ char *buffer = (char *)li0->li_canonname;
+
+ for (; address[0]; address += n + strspn(address + n, " ,")) {
+ if ((n = span_ip6_address(address))) {
+#if SU_HAVE_IN6
+ if (ip6 > ip4) {
+ li0->li_family = AF_INET6;
+ strncpy(buffer, address, n)[n] = '\0';
+ return li0;
+ }
+ else if (ip6 && !best) {
+ li0->li_family = AF_INET6;
+ strncpy(buffer, address, best = n)[n] = '\0';
+ }
+#endif
+ }
+ else if ((n = span_ip4_address(address))) {
+ if (ip4 > ip6) {
+ li0->li_family = AF_INET;
+ strncpy(buffer, address, n)[n] = '\0';
+ return li0;
+ }
+ else if (ip4 && !best) {
+ li0->li_family = AF_INET;
+ strncpy(buffer, address, best = n)[n] = '\0';
+ }
+ }
+ else {
+ n = strcspn(address, " ,");
+ }
+ }
+
+ if (best)
+ return li0;
+ else
+ return NULL;
+}
Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/soa/soa_static.c
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/soa/soa_static.c (original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/soa/soa_static.c Thu Feb 12 15:28:58 2009
@@ -1092,7 +1092,6 @@
{
soa_static_session_t *sss = (soa_static_session_t *)ss;
- char c_address[64];
sdp_session_t *local = ss->ss_local->ssd_sdp;
sdp_session_t local0[1];
@@ -1102,8 +1101,12 @@
sdp_session_t *remote = ss->ss_remote->ssd_sdp;
unsigned remote_version = ss->ss_remote_version;
+ int fresh = 0;
+
sdp_origin_t o[1] = {{ sizeof(o) }};
sdp_connection_t *c, c0[1] = {{ sizeof(c0) }};
+ char c0_buffer[64];
+
sdp_time_t t[1] = {{ sizeof(t) }};
int *u2s = NULL, *s2u = NULL, *tbf;
@@ -1150,22 +1153,16 @@
SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by,
"generating local description"));
+ fresh = 1;
local = local0;
*local = *user, local->sdp_media = NULL;
- if (local->sdp_origin) {
- o->o_username = local->sdp_origin->o_username;
- /* o->o_address = local->sdp_origin->o_address; */
- }
- if (!o->o_address)
- o->o_address = c0;
- local->sdp_origin = o;
-
- if (soa_init_sdp_origin(ss, o, c_address) < 0) {
- phrase = "Cannot Get IP Address for Media";
- goto internal_error;
- }
+ o->o_username = "-";
+ o->o_address = c0;
+ c0->c_address = c0_buffer;
+ if (!local->sdp_origin)
+ local->sdp_origin = o;
break;
case process_answer:
@@ -1283,28 +1280,46 @@
break;
}
- /* Step F: Update c= line */
+ /* Step F0: Initialize o= line */
+ if (fresh) {
+ if (user->sdp_origin)
+ o->o_username = user->sdp_origin->o_username;
+
+ if (soa_init_sdp_origin_with_session(ss, o, c0_buffer, local) < 0) {
+ phrase = "Cannot Get IP Address for Session Description";
+ goto internal_error;
+ }
+
+ local->sdp_origin = o;
+ }
+
+ /* Step F: Update c= line(s) */
switch (action) {
+ sdp_connection_t *user_c, *local_c;
+
case generate_offer:
case generate_answer:
- /* Upgrade local SDP based of user SDP */
- if (ss->ss_local_user_version == user_version &&
- local->sdp_connection)
- break;
-
- if (local->sdp_connection == NULL ||
- (user->sdp_connection != NULL &&
- sdp_connection_cmp(local->sdp_connection, user->sdp_connection))) {
+ user_c = user->sdp_connection;
+ if (!soa_check_sdp_connection(user_c))
+ user_c = NULL;
+
+ local_c = local->sdp_connection;
+ if (!soa_check_sdp_connection(local_c))
+ local_c = NULL;
+
+ if (ss->ss_local_user_version != user_version ||
+ local_c == NULL ||
+ (user_c != NULL && sdp_connection_cmp(local_c, user_c))) {
sdp_media_t *m;
- /* Every m= line (even rejected one) must have a c= line
- * or there must be a c= line at session level
- */
- if (user->sdp_connection)
- c = user->sdp_connection;
+ if (user_c)
+ c = user_c;
else
c = local->sdp_origin->o_address;
+ /* Every m= line (even rejected one) must have a c= line
+ * or there must be a c= line at session level
+ */
for (m = local->sdp_media; m; m = m->m_next)
if (m->m_connections == NULL)
break;
Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/soa/sofia-sip/soa_session.h
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/soa/sofia-sip/soa_session.h (original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/soa/sofia-sip/soa_session.h Thu Feb 12 15:28:58 2009
@@ -238,7 +238,11 @@
SOFIAPUBFUN int soa_set_status(soa_session_t *ss,
int status, char const *phrase);
-enum soa_activity { soa_activity_local, soa_activity_remote, soa_activity_session };
+enum soa_activity {
+ soa_activity_local,
+ soa_activity_remote,
+ soa_activity_session
+};
SOFIAPUBFUN void soa_set_activity(soa_session_t *ss,
sdp_media_t const *m,
@@ -259,8 +263,18 @@
SOFIAPUBFUN int soa_init_sdp_origin(soa_session_t *ss,
sdp_origin_t *o, char buf[64]);
+SOFIAPUBFUN int soa_init_sdp_origin_with_session(soa_session_t *ss,
+ sdp_origin_t *o,
+ char buffer[64],
+ sdp_session_t const *sdp);
+SOFIAPUBFUN int soa_check_sdp_connection(sdp_connection_t const *c);
SOFIAPUBFUN int soa_init_sdp_connection(soa_session_t *,
sdp_connection_t *, char buf[64]);
+SOFIAPUBFUN int soa_init_sdp_connection_with_session(soa_session_t *,
+ sdp_connection_t *, char buf[64],
+ sdp_session_t const *sdp);
+
+SOFIAPUBFUN sdp_connection_t *soa_find_local_sdp_connection(sdp_session_t const*);
/* ====================================================================== */
/* Debug log settings */
Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/soa/test_soa.c
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/soa/test_soa.c (original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/soa/test_soa.c Thu Feb 12 15:28:58 2009
@@ -55,6 +55,10 @@
#include <sofia-sip/su_log.h>
#include <sofia-sip/sip_tag.h>
+#include <s2_localinfo.h>
+
+S2_LOCALINFO_STUBS();
+
extern su_log_t soa_log[];
char const name[] = "test_soa";
@@ -72,6 +76,98 @@
#define NONE ((void*)-1)
+static char const *test_ifaces1[] = {
+ "eth0\0" "11.12.13.14\0" "2001:1508:1003::21a:a0ff:fe71:813\0" "fe80::21a:a0ff:fe71:813\0",
+ "eth1\0" "12.13.14.15\0" "2001:1508:1004::21a:a0ff:fe71:814\0" "fe80::21a:a0ff:fe71:814\0",
+ "eth2\0" "192.168.2.15\0" "fec0::21a:a0ff:fe71:815\0" "fe80::21a:a0ff:fe71:815\0",
+ "lo0\0" "127.0.0.1\0" "::1\0",
+ NULL
+};
+
+int test_localinfo_replacement(void)
+{
+ BEGIN();
+ su_localinfo_t *res, *li, hints[1];
+ int error, n;
+ struct results {
+ struct afresult { unsigned global, site, link, host; } ip6[1], ip4[1];
+ } results[1];
+
+ s2_localinfo_ifaces(test_ifaces1);
+
+ error = su_getlocalinfo(NULL, &res);
+ TEST(error, ELI_NOERROR);
+ TEST_1(res != NULL);
+ memset(results, 0, sizeof results);
+ for (li = res, n = 0; li; li = li->li_next) {
+ struct afresult *afr;
+ TEST_1(li->li_family == AF_INET || li->li_family == AF_INET6);
+ if (li->li_family == AF_INET)
+ afr = results->ip4;
+ else
+ afr = results->ip6;
+
+ if (li->li_scope == LI_SCOPE_GLOBAL)
+ afr->global++;
+ else if (li->li_scope == LI_SCOPE_SITE)
+ afr->site++;
+ else if (li->li_scope == LI_SCOPE_LINK)
+ afr->link++;
+ else if (li->li_scope == LI_SCOPE_HOST)
+ afr->host++;
+ n++;
+ }
+ TEST(n, 11);
+ TEST(results->ip4->global, 2);
+ TEST(results->ip4->site, 1);
+ TEST(results->ip4->link, 0);
+ TEST(results->ip4->host, 1);
+#if SU_HAVE_IN6
+ TEST(results->ip6->global, 2);
+ TEST(results->ip6->site, 1);
+ TEST(results->ip6->link, 3);
+ TEST(results->ip6->host, 1);
+#endif
+ su_freelocalinfo(res);
+
+ error = su_getlocalinfo(memset(hints, 0, sizeof hints), &res);
+ TEST(error, ELI_NOERROR);
+ TEST_1(res != NULL);
+ for (li = res, n = 0; li; li = li->li_next)
+ n++;
+ TEST(n, 11);
+ su_freelocalinfo(res);
+
+ hints->li_flags = LI_CANONNAME;
+
+ error = su_getlocalinfo(hints, &res);
+ TEST(error, ELI_NOERROR);
+ TEST_1(res != NULL);
+ for (li = res, n = 0; li; li = li->li_next) {
+ TEST_1(li->li_canonname != NULL);
+ n++;
+ }
+ TEST(n, 11);
+ su_freelocalinfo(res);
+
+ hints->li_flags = LI_IFNAME | LI_CANONNAME;
+ hints->li_ifname = "eth1";
+
+ error = su_getlocalinfo(hints, &res);
+ TEST(error, ELI_NOERROR);
+ TEST_1(res != NULL);
+ for (li = res, n = 0; li; li = li->li_next) {
+ TEST_1(li->li_canonname != NULL);
+ TEST_S(li->li_ifname, "eth1");
+ n++;
+ }
+ TEST(n, 3);
+ su_freelocalinfo(res);
+
+ END();
+}
+/* ========================================================================= */
+
struct context
{
su_home_t home[1];
@@ -1871,6 +1967,463 @@
END();
}
+#define TEST_OC_ADDRESS(s, address, ip) \
+ TEST(test_address_in_offer(s, address, sdp_addr_ ## ip, address, sdp_addr_ ## ip), 0);
+
+static int
+test_address_in_offer(soa_session_t *ss,
+ char const *o_address,
+ int o_addrtype,
+ char const *c_address,
+ int c_addrtype)
+{
+ sdp_session_t const *sdp = NULL;
+ sdp_connection_t const *c;
+
+ TEST(soa_get_local_sdp(ss, &sdp, NULL, NULL), 1);
+ TEST_1(sdp != NULL);
+ TEST_1(c = sdp->sdp_connection);
+ TEST(c->c_nettype, sdp_net_in);
+ if (c_addrtype) TEST(c->c_addrtype, c_addrtype);
+ if (c_address) TEST_S(c->c_address, c_address);
+
+ TEST_1(c = sdp->sdp_origin->o_address);
+ TEST(c->c_nettype, sdp_net_in);
+ if (o_addrtype) TEST(c->c_addrtype, o_addrtype);
+ if (o_address) TEST_S(c->c_address, o_address);
+
+ return 0;
+}
+
+/** This tests the IP address selection logic.
+ *
+ * The IP address is selected based on the SOATAG_AF() preference,
+ * SOATAG_ADDRESS(), and locally obtained address list.
+ */
+int test_address_selection(struct context *ctx)
+{
+ BEGIN();
+ int n;
+
+ static char const *ifaces1[] = {
+ "eth2\0" "192.168.2.15\0" "fec0::21a:a0ff:fe71:815\0" "fe80::21a:a0ff:fe71:815\0",
+ "eth0\0" "11.12.13.14\0" "2001:1508:1003::21a:a0ff:fe71:813\0" "fe80::21a:a0ff:fe71:813\0",
+ "eth1\0" "12.13.14.15\0" "2001:1508:1004::21a:a0ff:fe71:814\0" "fe80::21a:a0ff:fe71:814\0",
+ "lo0\0" "127.0.0.1\0" "::1\0",
+ NULL
+ };
+
+ static char const *ifaces_ip6only[] = {
+ "eth2\0" "fec0::21a:a0ff:fe71:815\0" "fe80::21a:a0ff:fe71:815\0",
+ "eth0\0" "2001:1508:1003::21a:a0ff:fe71:813\0" "fe80::21a:a0ff:fe71:813\0",
+ "eth1\0" "2001:1508:1004::21a:a0ff:fe71:814\0" "fe80::21a:a0ff:fe71:814\0",
+ "lo0\0" "127.0.0.1\0" "::1\0",
+ NULL
+ };
+
+ static char const *ifaces_ip4only[] = {
+ "eth2\0" "192.168.2.15\0" "fe80::21a:a0ff:fe71:815\0",
+ "eth0\0" "11.12.13.14\0" "fe80::21a:a0ff:fe71:813\0",
+ "eth1\0" "12.13.14.15\0" "fe80::21a:a0ff:fe71:814\0",
+ "lo0\0" "127.0.0.1\0" "::1\0",
+ NULL
+ };
+
+ soa_session_t *a, *b;
+ sdp_origin_t *o;
+
+ su_home_t home[1] = { SU_HOME_INIT(home) };
+
+ s2_localinfo_ifaces(ifaces1);
+
+ TEST_1(a = soa_clone(ctx->a, ctx->root, ctx));
+
+ /* SOATAG_AF(SOA_AF_IP4_ONLY) => select IP4 address */
+ n = soa_set_params(a, SOATAG_AF(SOA_AF_IP4_ONLY), TAG_END());
+ n = soa_set_user_sdp(a, 0, "m=audio 5008 RTP/AVP 0 8", -1); TEST(n, 1);
+ n = soa_generate_offer(a, 1, test_completed); TEST(n, 0);
+ TEST_OC_ADDRESS(a, "11.12.13.14", ip4);
+ /* Should flush the session */
+ TEST_VOID(soa_process_reject(a, NULL));
+
+ /* SOATAG_AF(SOA_AF_IP6_ONLY) => select IP6 address */
+ n = soa_set_params(a, SOATAG_AF(SOA_AF_IP6_ONLY), TAG_END());
+ n = soa_generate_offer(a, 1, test_completed); TEST(n, 0);
+ TEST_OC_ADDRESS(a, "2001:1508:1003::21a:a0ff:fe71:813", ip6);
+ TEST_VOID(soa_terminate(a, NULL));
+
+ /* SOATAG_AF(SOA_AF_IP4_IP6) => select IP4 address */
+ n = soa_set_params(a, SOATAG_AF(SOA_AF_IP4_IP6), TAG_END());
+ n = soa_generate_offer(a, 1, test_completed); TEST(n, 0);
+ TEST_OC_ADDRESS(a, "11.12.13.14", ip4);
+ TEST_VOID(soa_process_reject(a, NULL));
+
+ /* SOATAG_AF(SOA_AF_IP6_IP4) => select IP6 address */
+ n = soa_set_params(a, SOATAG_AF(SOA_AF_IP6_IP4), TAG_END());
+ n = soa_generate_offer(a, 1, test_completed); TEST(n, 0);
+ TEST_OC_ADDRESS(a, "2001:1508:1003::21a:a0ff:fe71:813", ip6);
+ TEST_VOID(soa_terminate(a, NULL));
+
+ /* SOATAG_AF(SOA_AF_IP4_IP6) but session mentions IP6 => select IP6 */
+ n = soa_set_params(a, SOATAG_AF(SOA_AF_IP4_IP6), TAG_END());
+ n = soa_set_user_sdp(a, 0, "c=IN IP6 ::\r\nm=audio 5008 RTP/AVP 0 8", -1); TEST(n, 1);
+ n = soa_generate_offer(a, 1, test_completed); TEST(n, 0);
+ TEST_OC_ADDRESS(a, "2001:1508:1003::21a:a0ff:fe71:813", ip6);
+ TEST_VOID(soa_terminate(a, NULL));
+
+ /* SOATAG_AF(SOA_AF_IP4_IP6), o= mentions IP6 => select IP4 */
+ n = soa_set_user_sdp(a, 0, "o=- 1 1 IN IP6 ::\r\n"
+ "m=audio 5008 RTP/AVP 0 8", -1); TEST(n, 1);
+ n = soa_generate_offer(a, 1, test_completed); TEST(n, 0);
+ TEST_OC_ADDRESS(a, "11.12.13.14", ip4);
+ TEST_VOID(soa_process_reject(a, NULL));
+
+ /* SOATAG_AF(SOA_AF_IP4_IP6), c= uses non-local IP6
+ => select local IP6 on o= */
+ n = soa_set_user_sdp(a, 0,
+ "c=IN IP6 2001:1508:1004::21a:a0ff:fe71:819\r\n"
+ "m=audio 5008 RTP/AVP 0 8", -1);
+ TEST(n, 1);
+ n = soa_generate_offer(a, 1, test_completed); TEST(n, 0);
+ TEST(test_address_in_offer(a,
+ /* o= has local address */
+ "2001:1508:1003::21a:a0ff:fe71:813", sdp_addr_ip6,
+ /* c= has sdp-provided address */
+ "2001:1508:1004::21a:a0ff:fe71:819", sdp_addr_ip6), 0);
+ TEST_VOID(soa_terminate(a, NULL));
+
+ /* SOATAG_AF(SOA_AF_IP4_ONLY), no IP4 addresses */
+ s2_localinfo_ifaces(ifaces_ip6only);
+ n = soa_set_params(a, SOATAG_AF(SOA_AF_IP4_ONLY), TAG_END());
+ n = soa_set_user_sdp(a, 0, "m=audio 5008 RTP/AVP 0 8", -1);
+ TEST(soa_generate_offer(a, 1, test_completed), -1);
+
+ /* Retry with IP6 enabled */
+ n = soa_set_params(a, SOATAG_AF(SOA_AF_IP4_IP6), TAG_END());
+ TEST(soa_generate_offer(a, 1, test_completed), 0);
+ TEST_OC_ADDRESS(a, "2001:1508:1003::21a:a0ff:fe71:813", ip6);
+ TEST_VOID(soa_terminate(a, NULL));
+
+ /* SOATAG_AF(SOA_AF_IP6_ONLY), no IP6 addresses */
+ s2_localinfo_ifaces(ifaces_ip4only);
+ n = soa_set_params(a, SOATAG_AF(SOA_AF_IP6_ONLY), TAG_END());
+ TEST(soa_generate_offer(a, 1, test_completed), -1); /* should fail */
+ TEST_VOID(soa_terminate(a, NULL));
+
+ /* SOATAG_AF(SOA_AF_IP4_ONLY), no IP4 addresses */
+ s2_localinfo_ifaces(ifaces_ip6only);
+ n = soa_set_params(a, SOATAG_AF(SOA_AF_IP4_ONLY), TAG_END());
+ TEST(soa_generate_offer(a, 1, test_completed), -1); /* should fail */
+ TEST_VOID(soa_terminate(a, NULL));
+
+ /* Select locally available address from the SOATAG_ADDRESS() list */
+ s2_localinfo_ifaces(ifaces1);
+ n = soa_set_params(a, SOATAG_AF(SOA_AF_IP4_IP6),
+ SOATAG_ADDRESS("test.com 17.18.19.20 12.13.14.15"),
+ TAG_END());
+ n = soa_generate_offer(a, 1, test_completed); TEST(n, 0);
+ TEST_OC_ADDRESS(a, "12.13.14.15", ip4);
+ TEST_VOID(soa_process_reject(a, NULL));
+
+ /* Select locally available IP6 address from the SOATAG_ADDRESS() list */
+ n = soa_set_params(a, SOATAG_AF(SOA_AF_IP6_IP4),
+ SOATAG_ADDRESS("test.com 12.13.14.15 fec0::21a:a0ff:fe71:815"),
+ TAG_END());
+ n = soa_generate_offer(a, 1, test_completed); TEST(n, 0);
+ TEST_OC_ADDRESS(a, "fec0::21a:a0ff:fe71:815", ip6);
+ TEST_VOID(soa_process_reject(a, NULL));
+
+ /* Select first available address from the SOATAG_ADDRESS() list */
+ n = soa_set_params(a, SOATAG_AF(SOA_AF_ANY),
+ SOATAG_ADDRESS("test.com 12.13.14.15 fec0::21a:a0ff:fe71:815"),
+ TAG_END());
+ n = soa_generate_offer(a, 1, test_completed); TEST(n, 0);
+ TEST_OC_ADDRESS(a, "12.13.14.15", ip4);
+ TEST_VOID(soa_process_reject(a, NULL));
+
+ /* Select preferred address from the SOATAG_ADDRESS() list */
+ s2_localinfo_ifaces(ifaces1);
+ n = soa_set_params(a, SOATAG_AF(SOA_AF_IP4_IP6),
+ SOATAG_ADDRESS("test.com fec0::22a:a0ff:fe71:815 19.18.19.20"),
+ TAG_END());
+ n = soa_generate_offer(a, 1, test_completed); TEST(n, 0);
+ TEST_OC_ADDRESS(a, "19.18.19.20", ip4);
+ TEST_VOID(soa_process_reject(a, NULL));
+
+ /* Select preferred address from the SOATAG_ADDRESS() list */
+ s2_localinfo_ifaces(ifaces1);
+ n = soa_set_params(a, SOATAG_AF(SOA_AF_IP6_IP4),
+ SOATAG_ADDRESS("test.com 19.18.19.20 fec0::22a:a0ff:fe71:815 fec0::22a:a0ff:fe71:819"),
+ TAG_END());
+ n = soa_generate_offer(a, 1, test_completed); TEST(n, 0);
+ TEST_OC_ADDRESS(a, "fec0::22a:a0ff:fe71:815", ip6);
+ TEST_VOID(soa_process_reject(a, NULL));
+
+ TEST_VOID(soa_destroy(a));
+
+ (void)b; (void)o;
+#if 0
+ TEST_1(b = soa_clone(ctx->b, ctx->root, ctx));
+
+ n = soa_set_remote_sdp(b, 0, offer, offerlen); TEST(n, 1);
+
+ n = soa_get_local_sdp(b, NULL, &answer, &answerlen); TEST(n, 0);
+
+ n = soa_set_params(b,
+ SOATAG_LOCAL_SDP_STR("m=audio 5004 RTP/AVP 8"),
+ SOATAG_AF(SOA_AF_IP4_ONLY),
+ SOATAG_ADDRESS("1.2.3.4"),
+ TAG_END());
+
+ n = soa_generate_answer(b, test_completed); TEST(n, 0);
+
+ TEST_1(soa_is_complete(b));
+ TEST(soa_activate(b, NULL), 0);
+
+ n = soa_get_local_sdp(b, NULL, &answer, &answerlen); TEST(n, 1);
+ TEST_1(answer != NULL && answer != NONE);
+ TEST_1(strstr(answer, "c=IN IP4 1.2.3.4"));
+
+ n = soa_set_remote_sdp(a, 0, answer, -1); TEST(n, 1);
+
+ n = soa_process_answer(a, test_completed); TEST(n, 0);
+
+ TEST_1(soa_is_complete(a));
+ TEST(soa_activate(a, NULL), 0);
+
+ TEST(soa_is_audio_active(a), SOA_ACTIVE_SENDRECV);
+ TEST(soa_is_video_active(a), SOA_ACTIVE_DISABLED);
+ TEST(soa_is_image_active(a), SOA_ACTIVE_DISABLED);
+ TEST(soa_is_chat_active(a), SOA_ACTIVE_DISABLED);
+
+ TEST(soa_is_remote_audio_active(a), SOA_ACTIVE_SENDRECV);
+ TEST(soa_is_remote_video_active(a), SOA_ACTIVE_DISABLED);
+ TEST(soa_is_remote_image_active(a), SOA_ACTIVE_DISABLED);
+ TEST(soa_is_remote_chat_active(a), SOA_ACTIVE_DISABLED);
+
+ /* 'A' will put call on hold */
+ offer = NONE;
+ TEST(soa_set_params(a, SOATAG_HOLD("*"), TAG_END()), 1);
+
+ TEST(soa_generate_offer(a, 1, test_completed), 0);
+ TEST(soa_get_local_sdp(a, NULL, &offer, &offerlen), 1);
+ TEST_1(offer != NULL && offer != NONE);
+ TEST_1(strstr(offer, "a=sendonly"));
+ TEST(soa_set_remote_sdp(b, 0, offer, offerlen), 1);
+ TEST(soa_generate_answer(b, test_completed), 0);
+ TEST_1(soa_is_complete(b));
+ TEST(soa_activate(b, NULL), 0);
+ TEST(soa_get_local_sdp(b, NULL, &answer, &answerlen), 1);
+ TEST_1(answer != NULL && answer != NONE);
+ TEST_1(strstr(answer, "a=recvonly"));
+ TEST(soa_set_remote_sdp(a, 0, answer, -1), 1);
+ TEST(soa_process_answer(a, test_completed), 0);
+ TEST(soa_activate(a, NULL), 0);
+
+ TEST(soa_is_audio_active(a), SOA_ACTIVE_SENDONLY);
+ TEST(soa_is_remote_audio_active(a), SOA_ACTIVE_SENDONLY);
+
+ /* 'A' will put call inactive */
+ offer = NONE;
+ TEST(soa_set_params(a, SOATAG_HOLD("#"), TAG_END()), 1);
+
+ TEST(soa_generate_offer(a, 1, test_completed), 0);
+ TEST(soa_get_local_sdp(a, NULL, &offer, &offerlen), 1);
+ TEST_1(offer != NULL && offer != NONE);
+ TEST_1(strstr(offer, "a=inactive"));
+ TEST(soa_set_remote_sdp(b, 0, offer, offerlen), 1);
+ TEST(soa_generate_answer(b, test_completed), 0);
+ TEST_1(soa_is_complete(b));
+ TEST(soa_activate(b, NULL), 0);
+ TEST(soa_get_local_sdp(b, NULL, &answer, &answerlen), 1);
+ TEST_1(answer != NULL && answer != NONE);
+ TEST_1(strstr(answer, "a=inactive"));
+ TEST(soa_set_remote_sdp(a, 0, answer, -1), 1);
+ TEST(soa_process_answer(a, test_completed), 0);
+ TEST(soa_activate(a, NULL), 0);
+
+ TEST(soa_is_audio_active(a), SOA_ACTIVE_INACTIVE);
+ TEST(soa_is_remote_audio_active(a), SOA_ACTIVE_INACTIVE);
+
+ /* B will send an offer to A, but there is no change in O/A status */
+ TEST(soa_generate_offer(b, 1, test_completed), 0);
+ TEST(soa_get_local_sdp(b, NULL, &offer, &offerlen), 1);
+ TEST_1(offer != NULL && offer != NONE);
+ TEST_1(!strstr(offer, "a=inactive"));
+ /* printf("offer:\n%s", offer); */
+ TEST(soa_set_remote_sdp(a, 0, offer, offerlen), 1);
+ TEST(soa_is_remote_audio_active(a), SOA_ACTIVE_SENDRECV);
+ TEST(soa_generate_answer(a, test_completed), 0);
+ TEST(soa_is_audio_active(a), SOA_ACTIVE_INACTIVE);
+ TEST(soa_is_remote_audio_active(a), SOA_ACTIVE_INACTIVE);
+ TEST_1(soa_is_complete(a));
+ TEST(soa_activate(a, NULL), 0);
+ TEST(soa_get_local_sdp(a, NULL, &answer, &answerlen), 1);
+ TEST_1(answer != NULL && answer != NONE);
+ TEST_1(strstr(answer, "a=inactive"));
+ /* printf("answer:\n%s", answer); */
+ TEST(soa_set_remote_sdp(b, 0, answer, -1), 1);
+ TEST(soa_process_answer(b, test_completed), 0);
+ TEST(soa_activate(b, NULL), 0);
+
+
+ TEST(soa_is_audio_active(b), SOA_ACTIVE_INACTIVE);
+ TEST(soa_is_remote_audio_active(b), SOA_ACTIVE_INACTIVE);
+
+ /* 'A' will release hold. */
+ TEST(soa_set_params(a, SOATAG_HOLD(NULL), TAG_END()), 1);
+
+ TEST(soa_generate_offer(a, 1, test_completed), 0);
+ TEST(soa_get_local_sdp(a, NULL, &offer, &offerlen), 1);
+ TEST_1(offer != NULL && offer != NONE);
+ TEST_1(!strstr(offer, "a=sendonly") && !strstr(offer, "a=inactive"));
+ TEST(soa_set_remote_sdp(b, 0, offer, offerlen), 1);
+ TEST(soa_generate_answer(b, test_completed), 0);
+ TEST_1(soa_is_complete(b));
+ TEST(soa_activate(b, NULL), 0);
+ TEST(soa_get_local_sdp(b, NULL, &answer, &answerlen), 1);
+ TEST_1(answer != NULL && answer != NONE);
+ TEST_1(!strstr(answer, "a=recvonly") && !strstr(answer, "a=inactive"));
+ TEST(soa_set_remote_sdp(a, 0, answer, -1), 1);
+ TEST(soa_process_answer(a, test_completed), 0);
+ TEST(soa_activate(a, NULL), 0);
+
+ TEST(soa_is_audio_active(a), SOA_ACTIVE_SENDRECV);
+ TEST(soa_is_remote_audio_active(a), SOA_ACTIVE_SENDRECV);
+
+ /* 'A' will put B on hold but this time with c=IN IP4 0.0.0.0 */
+ TEST(soa_set_params(a, SOATAG_HOLD("*"), TAG_END()), 1);
+ TEST(soa_generate_offer(a, 1, test_completed), 0);
+
+ {
+ sdp_session_t const *o_sdp;
+ sdp_session_t *sdp;
+ sdp_printer_t *p;
+ sdp_connection_t *c;
+
+ TEST(soa_get_local_sdp(a, &o_sdp, NULL, NULL), 1);
+ TEST_1(o_sdp != NULL && o_sdp != NONE);
+ TEST_1(sdp = sdp_session_dup(home, o_sdp));
+
+ /* Remove mode, change c=, encode offer */
+ if (sdp->sdp_media->m_connections)
+ c = sdp->sdp_media->m_connections;
+ else
+ c = sdp->sdp_connection;
+ TEST_1(c);
+ c->c_address = "0.0.0.0";
+
+ TEST_1(p = sdp_print(home, sdp, NULL, 0, sdp_f_realloc));
+ TEST_1(sdp_message(p));
+ offer = sdp_message(p); offerlen = strlen(offer);
+ }
+
+ TEST(soa_set_remote_sdp(b, 0, offer, -1), 1);
+ TEST(soa_generate_answer(b, test_completed), 0);
+ TEST_1(soa_is_complete(b));
+ TEST(soa_activate(b, NULL), 0);
+ TEST(soa_get_local_sdp(b, NULL, &answer, &answerlen), 1);
+ TEST_1(answer != NULL && answer != NONE);
+ TEST_1(strstr(answer, "a=recvonly"));
+ TEST(soa_set_remote_sdp(a, 0, answer, -1), 1);
+ TEST(soa_process_answer(a, test_completed), 0);
+ TEST(soa_activate(a, NULL), 0);
+
+ TEST(soa_is_audio_active(a), SOA_ACTIVE_SENDONLY);
+ TEST(soa_is_remote_audio_active(a), SOA_ACTIVE_SENDONLY);
+ TEST(soa_is_audio_active(b), SOA_ACTIVE_RECVONLY);
+ TEST(soa_is_remote_audio_active(b), SOA_ACTIVE_RECVONLY);
+
+ /* 'A' will propose adding video. */
+ /* 'B' will reject. */
+ TEST(soa_set_params(a,
+ SOATAG_HOLD(NULL), /* 'A' will release hold. */
+ SOATAG_USER_SDP_STR("m=audio 5008 RTP/AVP 0 8\r\ni=x\r\n"
+ "m=video 5006 RTP/AVP 34\r\n"),
+ TAG_END()), 2);
+
+ TEST(soa_generate_offer(a, 1, test_completed), 0);
+ TEST(soa_get_local_sdp(a, NULL, &offer, &offerlen), 1);
+ TEST_1(offer != NULL && offer != NONE);
+ TEST_1(!strstr(offer, "a=sendonly"));
+ TEST_1(strstr(offer, "m=video"));
+ TEST(soa_set_remote_sdp(b, 0, offer, offerlen), 1);
+ TEST(soa_generate_answer(b, test_completed), 0);
+ TEST_1(soa_is_complete(b));
+ TEST(soa_activate(b, NULL), 0);
+ TEST(soa_get_local_sdp(b, NULL, &answer, &answerlen), 1);
+ TEST_1(answer != NULL && answer != NONE);
+ TEST_1(!strstr(answer, "a=recvonly"));
+ TEST_1(strstr(answer, "m=video"));
+ TEST(soa_set_remote_sdp(a, 0, answer, -1), 1);
+ TEST(soa_process_answer(a, test_completed), 0);
+ TEST(soa_activate(a, NULL), 0);
+
+ TEST(soa_is_audio_active(a), SOA_ACTIVE_SENDRECV);
+ TEST(soa_is_remote_audio_active(a), SOA_ACTIVE_SENDRECV);
+ TEST(soa_is_video_active(a), SOA_ACTIVE_REJECTED);
+
+ {
+ /* Test tags */
+ sdp_session_t const *l = NULL, *u = NULL, *r = NULL;
+ sdp_media_t const *m;
+
+ TEST(soa_get_params(b,
+ SOATAG_LOCAL_SDP_REF(l),
+ SOATAG_USER_SDP_REF(u),
+ SOATAG_REMOTE_SDP_REF(r),
+ TAG_END()), 3);
+
+ TEST_1(l); TEST_1(u); TEST_1(r);
+ TEST_1(m = l->sdp_media); TEST(m->m_type, sdp_media_audio);
+ TEST_1(!m->m_rejected);
+ TEST_1(m = m->m_next); TEST(m->m_type, sdp_media_video);
+ TEST_1(m->m_rejected);
+ }
+
+ /* 'B' will now propose adding video. */
+ /* 'A' will accept. */
+ TEST(soa_set_params(b,
+ SOATAG_USER_SDP_STR("m=audio 5004 RTP/AVP 0 8\r\n"
+ "m=video 5006 RTP/AVP 34\r\n"),
+ TAG_END()), 1);
+
+ TEST(soa_generate_offer(b, 1, test_completed), 0);
+ TEST(soa_get_local_sdp(b, NULL, &offer, &offerlen), 1);
+ TEST_1(offer != NULL && offer != NONE);
+ TEST_1(!strstr(offer, "b=sendonly"));
+ TEST_1(strstr(offer, "m=video"));
+ TEST(soa_set_remote_sdp(a, 0, offer, offerlen), 1);
+ TEST(soa_generate_answer(a, test_completed), 0);
+ TEST_1(soa_is_complete(a));
+ TEST(soa_activate(a, NULL), 0);
+ TEST(soa_get_local_sdp(a, NULL, &answer, &answerlen), 1);
+ TEST_1(answer != NULL && answer != NONE);
+ TEST_1(!strstr(answer, "b=recvonly"));
+ TEST_1(strstr(answer, "m=video"));
+ TEST(soa_set_remote_sdp(b, 0, answer, -1), 1);
+ TEST(soa_process_answer(b, test_completed), 0);
+ TEST(soa_activate(b, NULL), 0);
+
+ TEST(soa_is_audio_active(a), SOA_ACTIVE_SENDRECV);
+ TEST(soa_is_remote_audio_active(a), SOA_ACTIVE_SENDRECV);
+ TEST(soa_is_video_active(a), SOA_ACTIVE_SENDRECV);
+
+ TEST_VOID(soa_terminate(a, NULL));
+
+ TEST(soa_is_audio_active(a), SOA_ACTIVE_DISABLED);
+ TEST(soa_is_remote_audio_active(a), SOA_ACTIVE_DISABLED);
+
+ TEST_VOID(soa_terminate(b, NULL));
+
+ TEST_VOID(soa_destroy(a));
+ TEST_VOID(soa_destroy(b));
+#endif
+ su_home_deinit(home);
+
+ END();
+}
+
int test_deinit(struct context *ctx)
{
BEGIN();
@@ -1974,10 +2527,14 @@
if (retval && quit_on_single_failure) { su_deinit(); return retval; } \
} while(0)
+ retval |= test_localinfo_replacement(); SINGLE_FAILURE_CHECK();
+
retval |= test_api_errors(ctx); SINGLE_FAILURE_CHECK();
retval |= test_soa_tags(ctx); SINGLE_FAILURE_CHECK();
retval |= test_init(ctx, argv + i); SINGLE_FAILURE_CHECK();
+
if (retval == 0) {
+ retval |= test_address_selection(ctx); SINGLE_FAILURE_CHECK();
retval |= test_params(ctx); SINGLE_FAILURE_CHECK();
retval |= test_static_offer_answer(ctx); SINGLE_FAILURE_CHECK();
1
0
12 Feb '09
Author: mikej
Date: Thu Feb 12 15:27:58 2009
New Revision: 11961
Log:
Thu Feb 12 15:08:05 CST 2009 Pekka Pessi <first.last(a)nokia.com>
* check_nua.c: allow setting test case names from command line. usage.
Modified:
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/check_nua.c
Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/check_nua.c
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/check_nua.c (original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/check_nua.c Thu Feb 12 15:27:58 2009
@@ -45,21 +45,49 @@
#include "test_s2.h"
+static void usage(int exitcode)
+{
+ fprintf(exitcode ? stderr : stdout,
+ "usage: %s [--xml=logfile] case,...\n", s2_tester);
+ exit(exitcode);
+}
+
int main(int argc, char *argv[])
{
- int failed = 0;
+ int i, failed = 0, selected = 0;
int threading;
-
- SRunner *runner;
-
+ char const *xml = NULL;
Suite *suite = suite_create("Unit tests for Sofia-SIP UA Engine");
+ SRunner *runner;
s2_tester = "check_nua";
if (getenv("CHECK_NUA_VERBOSE"))
s2_start_stop = strtoul(getenv("CHECK_NUA_VERBOSE"), NULL, 10);
- s2_select_tests(getenv("CHECK_NUA_CASES"));
+ for (i = 1; argv[i]; i++) {
+ if (su_strnmatch(argv[i], "--xml=", strlen("--xml="))) {
+ xml = argv[i] + strlen("--xml=");
+ }
+ else if (su_strmatch(argv[i], "--xml")) {
+ if (!(xml = argv[++i]))
+ usage(2);
+ }
+ else if (su_strmatch(argv[i], "-v")) {
+ s2_start_stop = 1;
+ }
+ else if (su_strmatch(argv[i], "-?") ||
+ su_strmatch(argv[i], "-h") ||
+ su_strmatch(argv[i], "--help"))
+ usage(0);
+ else {
+ s2_select_tests(argv[i]);
+ selected = 1;
+ }
+ }
+
+ if (!selected)
+ s2_select_tests(getenv("CHECK_NUA_CASES"));
check_register_cases(suite, threading = 0);
check_simple_cases(suite, threading = 0);
@@ -73,11 +101,10 @@
runner = srunner_create(suite);
- if (argv[1]) {
+ if (xml)
srunner_set_xml(runner, argv[1]);
- }
- srunner_run_all(runner, CK_ENV);
+ srunner_run_all(runner, CK_ENV);
failed = srunner_ntests_failed(runner);
srunner_free(runner);
1
0
12 Feb '09
Author: mikej
Date: Thu Feb 12 15:27:30 2009
New Revision: 11960
Log:
Thu Feb 12 15:07:13 CST 2009 Pekka Pessi <first.last(a)nokia.com>
* check_simple.c: adedd notifier test cases
Modified:
freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/check_simple.c
Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/check_simple.c
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/check_simple.c (original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/check_simple.c Thu Feb 12 15:27:30 2009
@@ -68,15 +68,6 @@
NUTAG_OUTBOUND("no-options-keepalive, no-validate"),
TAG_END());
- soa = soa_create(NULL, s2->root, NULL);
-
- fail_if(!soa);
-
- soa_set_params(soa,
- SOATAG_USER_SDP_STR("m=audio 5008 RTP/AVP 8 0" CRLF
- "m=video 5010 RTP/AVP 34" CRLF),
- TAG_END());
-
dialog = su_home_new(sizeof *dialog); fail_if(!dialog);
s2_register_setup();
@@ -507,6 +498,285 @@
return tc;
}
+nua_handle_t *
+subscribe_to_nua(char const *event,
+ tag_type_t tag, tag_value_t value, ...)
+{
+ ta_list ta;
+ struct event *subscribe;
+ struct message *response;
+ nua_handle_t *nh;
+
+ nua_set_params(nua, NUTAG_APPL_METHOD("SUBSCRIBE"),
+ SIPTAG_ALLOW_EVENTS_STR(event),
+ TAG_END());
+ s2_check_event(nua_r_set_params, 200);
+
+ ta_start(ta, tag, value);
+ s2_request_to(dialog, SIP_METHOD_SUBSCRIBE, NULL,
+ SIPTAG_EVENT_STR(event),
+ ta_tags(ta));
+ ta_end(ta);
+
+ subscribe = s2_wait_for_event(nua_i_subscribe, 100);
+ nh = subscribe->nh;
+ nua_respond(nh, SIP_202_ACCEPTED,
+ NUTAG_WITH_SAVED(subscribe->event),
+ TAG_END());
+ s2_free_event(subscribe);
+
+ response = s2_wait_for_response(202, SIP_METHOD_SUBSCRIBE);
+ s2_update_dialog(dialog, response);
+ fail_unless(response->sip->sip_expires != NULL);
+ s2_free_message(response);
+
+ return nh;
+}
+
+START_TEST(notify_6_3_1)
+{
+ nua_handle_t *nh;
+ struct event *subscribe;
+ struct message *notify, *response;
+ sip_t *sip;
+
+ s2_case("6.3.1", "Basic NOTIFY server",
+ "NUA receives SUBSCRIBE, sends 202 and NOTIFY. "
+ "First NOTIFY terminates subscription. ");
+
+ s2_request_to(dialog, SIP_METHOD_SUBSCRIBE, NULL,
+ SIPTAG_EVENT_STR("presence"),
+ TAG_END());
+ /* 489 Bad Event by default */
+ s2_check_response(489, SIP_METHOD_SUBSCRIBE);
+
+ nua_set_params(nua, NUTAG_APPL_METHOD("SUBSCRIBE"), TAG_END());
+ s2_check_event(nua_r_set_params, 200);
+
+ s2_request_to(dialog, SIP_METHOD_SUBSCRIBE, NULL,
+ SIPTAG_EVENT_STR("presence"),
+ TAG_END());
+ s2_check_response(489, SIP_METHOD_SUBSCRIBE);
+
+ nua_set_params(nua, SIPTAG_ALLOW_EVENTS_STR("presence"), TAG_END());
+ s2_check_event(nua_r_set_params, 200);
+
+ s2_request_to(dialog, SIP_METHOD_SUBSCRIBE, NULL,
+ SIPTAG_EVENT_STR("presence"),
+ TAG_END());
+ subscribe = s2_wait_for_event(nua_i_subscribe, 100);
+ nh = subscribe->nh;
+ nua_respond(nh, SIP_403_FORBIDDEN,
+ NUTAG_WITH_SAVED(subscribe->event),
+ TAG_END());
+ s2_free_event(subscribe);
+
+ s2_check_response(403, SIP_METHOD_SUBSCRIBE);
+
+ nua_handle_destroy(nh);
+
+ s2_request_to(dialog, SIP_METHOD_SUBSCRIBE, NULL,
+ SIPTAG_EVENT_STR("presence"),
+ TAG_END());
+ subscribe = s2_wait_for_event(nua_i_subscribe, 100);
+ nh = subscribe->nh;
+ nua_respond(nh, SIP_202_ACCEPTED,
+ NUTAG_WITH_SAVED(subscribe->event),
+ TAG_END());
+ s2_free_event(subscribe);
+
+ response = s2_wait_for_response(202, SIP_METHOD_SUBSCRIBE);
+ s2_update_dialog(dialog, response);
+ fail_unless(response->sip->sip_expires != NULL);
+ s2_free_message(response);
+
+ nua_notify(nh,
+ NUTAG_SUBSTATE(nua_substate_terminated),
+ SIPTAG_PAYLOAD_STR(presence_closed),
+ TAG_END());
+ notify = s2_wait_for_request(SIP_METHOD_NOTIFY);
+ fail_unless(notify != NULL);
+ sip = notify->sip;
+ fail_unless(sip->sip_subscription_state != NULL);
+ fail_unless(su_strmatch(sip->sip_subscription_state->ss_substate,
+ "terminated"));
+ s2_respond_to(notify, dialog, SIP_200_OK, TAG_END());
+
+ s2_check_event(nua_r_notify, 200);
+ nua_handle_destroy(nh);
+}
+END_TEST
+
+START_TEST(notify_6_3_2)
+{
+ nua_handle_t *nh;
+ struct event *subscribe;
+ struct message *notify, *response;
+ sip_t *sip;
+
+ s2_case("6.3.2", "NOTIFY server - automatic subscription termination",
+ "NUA receives SUBSCRIBE, sends 202 and NOTIFY. "
+ "The subscription terminates with timeout. ");
+
+ nh = subscribe_to_nua("presence", SIPTAG_EXPIRES_STR("300"), TAG_END());
+
+ nua_notify(nh,
+ NUTAG_SUBSTATE(nua_substate_active),
+ SIPTAG_PAYLOAD_STR(presence_closed),
+ TAG_END());
+ notify = s2_wait_for_request(SIP_METHOD_NOTIFY);
+ fail_unless(notify != NULL);
+ sip = notify->sip;
+ fail_unless(sip->sip_subscription_state != NULL);
+ fail_unless(su_strmatch(sip->sip_subscription_state->ss_substate,
+ "active"));
+ s2_respond_to(notify, dialog, SIP_200_OK, TAG_END());
+ s2_check_event(nua_r_notify, 200);
+
+ s2_fast_forward(300, s2->root);
+
+ notify = s2_wait_for_request(SIP_METHOD_NOTIFY);
+ fail_unless(notify != NULL);
+ sip = notify->sip;
+ fail_unless(sip->sip_subscription_state != NULL);
+ fail_unless(su_strmatch(sip->sip_subscription_state->ss_substate,
+ "terminated"));
+ s2_respond_to(notify, dialog, SIP_200_OK, TAG_END());
+ s2_check_event(nua_r_notify, 200);
+
+ nua_handle_destroy(nh);
+}
+END_TEST
+
+static int
+s2_event_substate(struct event *event)
+{
+ if (event) {
+ tagi_t const *t = tl_find(event->data->e_tags, nutag_substate);
+ if (t)
+ return t->t_value;
+ }
+ return -1;
+}
+
+START_TEST(notify_6_3_3)
+{
+ nua_handle_t *nh;
+ struct message *notify;
+ struct event *response;
+ sip_t *sip;
+
+ s2_case("6.3.3", "NOTIFY server - terminate with error response to NOTIFY",
+ "NUA receives SUBSCRIBE, sends 202 and NOTIFY. "
+ "The subscription terminates when watcher "
+ "returns 481 to second NOTIFY.");
+
+ nh = subscribe_to_nua("presence", SIPTAG_EXPIRES_STR("300"), TAG_END());
+
+ nua_notify(nh,
+ NUTAG_SUBSTATE(nua_substate_active),
+ SIPTAG_PAYLOAD_STR(presence_closed),
+ TAG_END());
+ notify = s2_wait_for_request(SIP_METHOD_NOTIFY);
+ fail_unless(notify != NULL);
+ sip = notify->sip;
+ fail_unless(sip->sip_subscription_state != NULL);
+ fail_unless(su_strmatch(sip->sip_subscription_state->ss_substate,
+ "active"));
+ s2_respond_to(notify, dialog, SIP_200_OK, TAG_END());
+ s2_check_event(nua_r_notify, 200);
+
+ nua_notify(nh,
+ NUTAG_SUBSTATE(nua_substate_active),
+ SIPTAG_PAYLOAD_STR(presence_closed),
+ TAG_END());
+ notify = s2_wait_for_request(SIP_METHOD_NOTIFY);
+ fail_unless(notify != NULL);
+ sip = notify->sip;
+ fail_unless(sip->sip_subscription_state != NULL);
+ fail_unless(su_strmatch(sip->sip_subscription_state->ss_substate,
+ "active"));
+ s2_respond_to(notify, dialog, SIP_481_NO_TRANSACTION, TAG_END());
+ response = s2_wait_for_event(nua_r_notify, 481);
+ fail_unless(s2_event_substate(response) == nua_substate_terminated);
+
+ nua_handle_destroy(nh);
+}
+END_TEST
+
+START_TEST(notify_6_3_4)
+{
+ nua_handle_t *nh;
+ struct message *notify;
+ struct event *response;
+ sip_t *sip;
+
+ s2_case("6.3.3", "NOTIFY server - terminate with error response to NOTIFY",
+ "NUA receives SUBSCRIBE, sends 202 and NOTIFY. "
+ "The subscription terminates when watcher "
+ "returns 481 to second NOTIFY. The queued 3rd NOTIFY gets "
+ "responded by stack.");
+
+ nh = subscribe_to_nua("presence", SIPTAG_EXPIRES_STR("300"), TAG_END());
+
+ tport_set_params(s2->master, TPTAG_LOG(1), TAG_END());
+ s2_setup_logs(7);
+
+ nua_notify(nh,
+ NUTAG_SUBSTATE(nua_substate_active),
+ SIPTAG_PAYLOAD_STR(presence_closed),
+ TAG_END());
+ notify = s2_wait_for_request(SIP_METHOD_NOTIFY);
+ fail_unless(notify != NULL);
+ sip = notify->sip;
+ fail_unless(sip->sip_subscription_state != NULL);
+ fail_unless(su_strmatch(sip->sip_subscription_state->ss_substate,
+ "active"));
+ s2_respond_to(notify, dialog, SIP_200_OK, TAG_END());
+ s2_check_event(nua_r_notify, 200);
+
+ nua_notify(nh,
+ NUTAG_SUBSTATE(nua_substate_active),
+ SIPTAG_PAYLOAD_STR(presence_open),
+ TAG_END());
+ nua_notify(nh,
+ NUTAG_SUBSTATE(nua_substate_active),
+ SIPTAG_PAYLOAD_STR(presence_closed),
+ TAG_END());
+ notify = s2_wait_for_request(SIP_METHOD_NOTIFY);
+ fail_unless(notify != NULL);
+ sip = notify->sip;
+ fail_unless(sip->sip_subscription_state != NULL);
+ fail_unless(su_strmatch(sip->sip_subscription_state->ss_substate,
+ "active"));
+ s2_respond_to(notify, dialog, SIP_481_NO_TRANSACTION, TAG_END());
+ response = s2_wait_for_event(nua_r_notify, 481);
+ fail_unless(s2_event_substate(response) == nua_substate_terminated);
+ response = s2_wait_for_event(nua_r_notify, 481);
+ fail_unless(s2_event_substate(response) == nua_substate_terminated);
+
+ nua_handle_destroy(nh);
+}
+END_TEST
+
+
+TCase *notifier_tcase(int threading)
+{
+ TCase *tc = tcase_create("6.3 - Basic event server with NOTIFY ");
+ void (*simple_setup)(void);
+
+ simple_setup = threading ? simple_thread_setup : simple_threadless_setup;
+ tcase_add_checked_fixture(tc, simple_setup, simple_teardown);
+
+ {
+ tcase_add_test(tc, notify_6_3_1);
+ tcase_add_test(tc, notify_6_3_2);
+ tcase_add_test(tc, notify_6_3_3);
+ tcase_add_test(tc, notify_6_3_4);
+ }
+ return tc;
+}
+
/* ====================================================================== */
/* Test case template */
@@ -543,6 +813,7 @@
{
suite_add_tcase(suite, subscribe_tcase(threading));
suite_add_tcase(suite, fetch_tcase(threading));
+ suite_add_tcase(suite, notifier_tcase(threading));
if (0) /* Template */
suite_add_tcase(suite, empty_tcase(threading));
1
0