[Freeswitch-svn] [commit] r11967 - in freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/sresolv: . sofia-sip
FreeSWITCH SVN
mikej at freeswitch.org
Thu Feb 12 13:58:06 PST 2009
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 at nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+/**@CFILE check_sres_sip.c
+ *
+ * @brief Check-driven tester for SIP URI resolver
+ *
+ * @author Pekka Pessi <Pekka.Pessi at 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 at nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef 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 at 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 at nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+/**@CFILE sres_sip.c
+ * @brief RFC3263 SIP Resolver
+ *
+ * @author Pekka Pessi <Pekka.Pessi at 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;
+}
More information about the Freeswitch-svn
mailing list