[asterisk-commits] file: branch group/dns_pjsip r433967 - in /team/group/dns_pjsip: include/aste...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Fri Apr 3 13:32:44 CDT 2015
Author: file
Date: Fri Apr 3 13:32:39 2015
New Revision: 433967
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=433967
Log:
Add resolver implementation which supports SRV, AAAA, and A records.
This is not used everywhere yet but out-of-dialog SIP requests seem happy with it!
Added:
team/group/dns_pjsip/res/res_pjsip/pjsip_resolver.c (with props)
Modified:
team/group/dns_pjsip/include/asterisk/res_pjsip.h
team/group/dns_pjsip/res/res_pjsip.c
team/group/dns_pjsip/res/res_pjsip_session.c
Modified: team/group/dns_pjsip/include/asterisk/res_pjsip.h
URL: http://svnview.digium.com/svn/asterisk/team/group/dns_pjsip/include/asterisk/res_pjsip.h?view=diff&rev=433967&r1=433966&r2=433967
==============================================================================
--- team/group/dns_pjsip/include/asterisk/res_pjsip.h (original)
+++ team/group/dns_pjsip/include/asterisk/res_pjsip.h Fri Apr 3 13:32:39 2015
@@ -1954,4 +1954,26 @@
*/
unsigned int ast_sip_get_keep_alive_interval(void);
+/*!
+ * \brief Callback which is invoked upon completion of the resolution process
+ *
+ * \param data The user specific data
+ * \param addresses The resolved target addresses
+ */
+typedef void (*ast_sip_resolve_callback)(void *data, pjsip_server_addresses *addresses);
+
+/*!
+ * \brief Perform DNS resolution according to RFC3263 and invoke a callback upon completion
+ *
+ * \param target The target we are looking up
+ * \param callback The callback to invoke upon completion
+ * \param data User specific data (must be ao2 allocated)
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ *
+ * \note This function bumps the reference count of user data, it does not steal
+ */
+int ast_sip_resolve(const pjsip_host_info *target, ast_sip_resolve_callback callback, void *user_data);
+
#endif /* _RES_PJSIP_H */
Modified: team/group/dns_pjsip/res/res_pjsip.c
URL: http://svnview.digium.com/svn/asterisk/team/group/dns_pjsip/res/res_pjsip.c?view=diff&rev=433967&r1=433966&r2=433967
==============================================================================
--- team/group/dns_pjsip/res/res_pjsip.c (original)
+++ team/group/dns_pjsip/res/res_pjsip.c Fri Apr 3 13:32:39 2015
@@ -2978,17 +2978,73 @@
return 0;
}
+struct sip_send_request_data {
+ struct pjsip_dialog *dlg;
+ struct ast_sip_endpoint *endpoint;
+ void *token;
+ void (*callback)(void *token, pjsip_event *e);
+ pjsip_tx_data *tdata;
+};
+
+static void sip_send_request_data_destroy(void *data)
+{
+ struct sip_send_request_data *send_request_data = data;
+
+ if (send_request_data->dlg) {
+ pjsip_dlg_dec_session(send_request_data->dlg, &supplement_module);
+ }
+
+ ao2_cleanup(send_request_data->endpoint);
+ ao2_cleanup(send_request_data->token);
+}
+
+static void sip_send_request_resolve_cb(void *data, pjsip_server_addresses *addresses)
+{
+ struct sip_send_request_data *send_request_data = data;
+
+ memcpy(&(send_request_data->tdata->dest_info.addr), addresses, sizeof(send_request_data->tdata->dest_info.addr));
+
+ if (send_request_data->dlg) {
+ send_in_dialog_request(send_request_data->tdata, send_request_data->dlg);
+ } else {
+ send_out_of_dialog_request(send_request_data->tdata, send_request_data->endpoint,
+ send_request_data->token, send_request_data->callback);
+ }
+}
+
int ast_sip_send_request(pjsip_tx_data *tdata, struct pjsip_dialog *dlg,
struct ast_sip_endpoint *endpoint, void *token,
void (*callback)(void *token, pjsip_event *e))
{
+ pjsip_host_info target;
+ struct sip_send_request_data *send_request_data;
+ int res;
+
ast_assert(tdata->msg->type == PJSIP_REQUEST_MSG);
+ if (pjsip_get_request_dest(tdata, &target) != PJ_SUCCESS) {
+ return -1;
+ }
+
+ send_request_data = ao2_alloc_options(sizeof(*send_request_data), sip_send_request_data_destroy,
+ AO2_ALLOC_OPT_LOCK_NOLOCK);
+ if (!send_request_data) {
+ return -1;
+ }
+
if (dlg) {
- return send_in_dialog_request(tdata, dlg);
- } else {
- return send_out_of_dialog_request(tdata, endpoint, token, callback);
- }
+ send_request_data->dlg = dlg;
+ pjsip_dlg_inc_session(dlg, &supplement_module);
+ }
+ send_request_data->endpoint = ao2_bump(endpoint);
+ send_request_data->token = ao2_bump(token);
+ send_request_data->callback = callback;
+ send_request_data->tdata = tdata;
+
+ res = ast_sip_resolve(&target, sip_send_request_resolve_cb, send_request_data);
+ ao2_ref(send_request_data, -1);
+
+ return res;
}
int ast_sip_set_outbound_proxy(pjsip_tx_data *tdata, const char *proxy)
Added: team/group/dns_pjsip/res/res_pjsip/pjsip_resolver.c
URL: http://svnview.digium.com/svn/asterisk/team/group/dns_pjsip/res/res_pjsip/pjsip_resolver.c?view=auto&rev=433967
==============================================================================
--- team/group/dns_pjsip/res/res_pjsip/pjsip_resolver.c (added)
+++ team/group/dns_pjsip/res/res_pjsip/pjsip_resolver.c Fri Apr 3 13:32:39 2015
@@ -1,0 +1,336 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2015, Digium, Inc.
+ *
+ * Joshua Colp <jcolp at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+#include "asterisk.h"
+
+#include <pjsip.h>
+
+#include <arpa/nameser.h>
+
+#include "asterisk/astobj2.h"
+#include "asterisk/dns_core.h"
+#include "asterisk/dns_query_set.h"
+#include "asterisk/dns_srv.h"
+#include "asterisk/res_pjsip.h"
+#include "include/res_pjsip_private.h"
+
+/*! \brief Structure which contains resolved target information */
+struct sip_resolved_target {
+ /*! \brief The record type that this target originated from */
+ /*! \brief The transport to be used */
+ pjsip_transport_type_e transport;
+ /*! \brief The port */
+ int port;
+ /*! \brief Resulting addresses */
+ pjsip_server_addresses addresses;
+};
+
+/*! \brief The vector used for addresses */
+AST_VECTOR(addresses, struct sip_resolved_target);
+
+/*! \brief Structure which keeps track of resolution */
+struct sip_resolve {
+ /*! \brief Addresses currently being resolved, indexed based on index of queries in query set */
+ struct addresses resolving;
+ /*! \brief Addresses that have been resolved, to ensure proper sorting go from back to front */
+ struct addresses resolved;
+ /*! \brief Active queries */
+ struct ast_dns_query_set *queries;
+ /*! \brief Callback to invoke upon completion */
+ ast_sip_resolve_callback callback;
+ /*! \brief User provided data */
+ void *user_data;
+};
+
+/*! \brief Destructor for resolution data */
+static void sip_resolve_destroy(void *data)
+{
+ struct sip_resolve *resolve = data;
+
+ AST_VECTOR_FREE(&resolve->resolving);
+ AST_VECTOR_FREE(&resolve->resolved);
+ ao2_cleanup(resolve->queries);
+ ao2_cleanup(resolve->user_data);
+}
+
+/*! \brief Perform resolution but keep transport and port information */
+static int sip_resolve_add(struct sip_resolve *resolve, const char *name, int rr_type, int rr_class, pjsip_transport_type_e transport, int port)
+{
+ struct sip_resolved_target target = {
+ .transport = transport,
+ .port = port,
+ };
+
+ if (!resolve->queries) {
+ resolve->queries = ast_dns_query_set_create();
+ }
+
+ if (!resolve->queries) {
+ return -1;
+ }
+
+ if (!port) {
+ target.port = pjsip_transport_get_default_port_for_type(transport);
+ }
+
+ if (AST_VECTOR_APPEND(&resolve->resolving, target)) {
+ return -1;
+ }
+
+ ast_debug(2, "[%p] Added target '%s' with record type '%d', transport '%s', and port '%d'\n", resolve, name, rr_type,
+ pjsip_transport_get_type_name(transport), target.port);
+
+ return ast_dns_query_set_add(resolve->queries, name, rr_type, rr_class);
+}
+
+/*! \brief Invoke the user specific callback from inside of a SIP thread */
+static int sip_resolve_invoke_user_callback(void *data)
+{
+ struct sip_resolve *resolve = data;
+ pjsip_server_addresses addresses = {
+ .count = 0,
+ };
+ int idx;
+
+ /* We start from the end because the records with the highest preference are there */
+ for (idx = AST_VECTOR_SIZE(&resolve->resolved) - 1; idx >= 0; --idx) {
+ struct sip_resolved_target *target = AST_VECTOR_GET_ADDR(&resolve->resolved, idx);
+ int address_pos;
+ char addr[256];
+
+ for (address_pos = 0; address_pos < target->addresses.count; ++address_pos) {
+ ast_debug(2, "[%p] Address '%d' is '%s' port '%d' with transport '%s'\n",
+ resolve, addresses.count, pj_sockaddr_print(&target->addresses.entry[address_pos].addr, addr, sizeof(addr), 0),
+ pj_sockaddr_get_port(&target->addresses.entry[address_pos].addr), pjsip_transport_get_type_name(target->addresses.entry[address_pos].type));
+ addresses.entry[addresses.count++] = target->addresses.entry[address_pos];
+ }
+
+ if (addresses.count == PJSIP_MAX_RESOLVED_ADDRESSES) {
+ break;
+ }
+ }
+
+ ast_debug(2, "[%p] Invoking user callback with '%d' addresses\n", resolve, addresses.count);
+ resolve->callback(resolve->user_data, &addresses);
+
+ ao2_ref(resolve, -1);
+
+ return 0;
+}
+
+/*! \brief Callback for when the first pass query set completes */
+static void sip_resolve_callback(const struct ast_dns_query_set *query_set)
+{
+ struct sip_resolve *resolve = ast_dns_query_set_get_data(query_set);
+ struct ast_dns_query_set *queries = resolve->queries;
+ struct addresses resolving;
+ int idx;
+
+ ast_debug(2, "[%p] All parallel queries completed\n", resolve);
+
+ resolve->queries = NULL;
+
+ /* This purposely steals the resolving list so we can add entries to the new one in the same loop and also have access
+ * to the old.
+ */
+ resolving = resolve->resolving;
+ AST_VECTOR_INIT(&resolve->resolving, 1);
+
+ /* Add any AAAA/A records to the resolved list */
+ for (idx = 0; idx < ast_dns_query_set_num_queries(queries); ++idx) {
+ struct ast_dns_query *query = ast_dns_query_set_get(queries, idx);
+ struct ast_dns_result *result = ast_dns_query_get_result(query);
+ struct sip_resolved_target *target;
+ const struct ast_dns_record *record;
+
+ if (!result) {
+ ast_debug(2, "[%p] No result information for target '%s' of type '%d'\n", resolve,
+ ast_dns_query_get_name(query), ast_dns_query_get_rr_type(query));
+ continue;
+ }
+
+ target = AST_VECTOR_GET_ADDR(&resolving, idx);
+ for (record = ast_dns_result_get_records(result); record; record = ast_dns_record_get_next(record)) {
+ if (ast_dns_record_get_rr_type(record) == ns_t_a) {
+ ast_debug(2, "[%p] A record received on target '%s'\n", resolve, ast_dns_query_get_name(query));
+ target->addresses.entry[target->addresses.count].type = target->transport;
+ target->addresses.entry[target->addresses.count].addr_len = sizeof(pj_sockaddr_in);
+ pj_sockaddr_init(pj_AF_INET(), &target->addresses.entry[target->addresses.count].addr, NULL, target->port);
+ target->addresses.entry[target->addresses.count++].addr.ipv4.sin_addr = *(struct pj_in_addr*)ast_dns_record_get_data(record);
+ } else if (ast_dns_record_get_rr_type(record) == ns_t_aaaa) {
+ ast_debug(2, "[%p] AAAA record received on target '%s'\n", resolve, ast_dns_query_get_name(query));
+ target->addresses.entry[target->addresses.count].type = target->transport;
+ target->addresses.entry[target->addresses.count].addr_len = sizeof(pj_sockaddr_in6);
+ pj_sockaddr_init(pj_AF_INET6(), &target->addresses.entry[target->addresses.count].addr, NULL, target->port);
+ pj_memcpy(&target->addresses.entry[target->addresses.count++].addr.ipv6.sin6_addr, ast_dns_record_get_data(record),
+ sizeof(pj_sockaddr_in6));
+ } else if (ast_dns_record_get_rr_type(record) == ns_t_srv) {
+ ast_debug(2, "[%p] SRV record received on target '%s'\n", resolve, ast_dns_query_get_name(query));
+ sip_resolve_add(resolve, ast_dns_srv_get_host(record), ns_t_a, ns_c_in, target->transport, ast_dns_srv_get_port(record));
+ sip_resolve_add(resolve, ast_dns_srv_get_host(record), ns_t_aaaa, ns_c_in, target->transport, ast_dns_srv_get_port(record));
+ }
+ }
+
+ /* Only add this finished result if there's actually addresses on it */
+ if (target->addresses.count) {
+ AST_VECTOR_APPEND(&resolve->resolved, *target);
+ }
+ }
+
+ /* Free the vector we stole as we are responsible for it */
+ AST_VECTOR_FREE(&resolving);
+
+ /* If additional queries were added start the resolution process again */
+ if (resolve->queries) {
+ ast_debug(2, "[%p] New queries added, performing parallel resolution again\n", resolve);
+ ast_dns_query_set_resolve_async(resolve->queries, sip_resolve_callback, resolve);
+ ao2_ref(queries, -1);
+ return;
+ }
+
+ /* Invoke callback with target resolved addresses */
+ ast_debug(2, "[%p] Resolution completed - %zd viable targets\n", resolve, AST_VECTOR_SIZE(&resolve->resolved));
+
+ /* Push a task to invoke the callback, we do this so it is guaranteed to run in a PJSIP thread */
+ ao2_ref(resolve, +1);
+ if (ast_sip_push_task(NULL, sip_resolve_invoke_user_callback, resolve)) {
+ ao2_ref(resolve, -1);
+ }
+
+ ao2_ref(queries, -1);
+}
+
+/*! \brief Determine if the host is already an IP address */
+static int sip_resolve_get_ip_addr_ver(const pj_str_t *host)
+{
+ pj_in_addr dummy;
+ pj_in6_addr dummy6;
+
+ if (pj_inet_aton(host, &dummy) > 0) {
+ return 4;
+ }
+
+ if (pj_inet_pton(pj_AF_INET6(), host, &dummy6) == PJ_SUCCESS) {
+ return 6;
+ }
+
+ return 0;
+}
+
+int ast_sip_resolve(const pjsip_host_info *target, ast_sip_resolve_callback callback, void *user_data)
+{
+ int ip_addr_ver;
+ pjsip_transport_type_e type = target->type;
+ struct sip_resolve *resolve;
+ char host[NI_MAXHOST], srv[NI_MAXHOST];
+ int res = 0;
+
+ ast_copy_pj_str(host, &target->addr.host, sizeof(host));
+
+ ast_debug(2, "Performing SIP DNS resolution of target '%s'\n", host);
+
+ /* If the provided target is already an address don't bother resolving */
+ ip_addr_ver = sip_resolve_get_ip_addr_ver(&target->addr.host);
+
+ /* Determine the transport to use if none has been explicitly specified */
+ if (type == PJSIP_TRANSPORT_UNSPECIFIED) {
+ /* If we've been told to use a secure or reliable transport restrict ourselves to that */
+#if PJ_HAS_TCP
+ if (target->flag & PJSIP_TRANSPORT_SECURE) {
+ type = PJSIP_TRANSPORT_TLS;
+ } else if (target->flag & PJSIP_TRANSPORT_RELIABLE) {
+ type = PJSIP_TRANSPORT_TCP;
+ } else
+#endif
+ /* According to the RFC otherwise if an explicit IP address OR an explicit port is specified
+ * we use UDP
+ */
+ if (ip_addr_ver || target->addr.port) {
+ type = PJSIP_TRANSPORT_UDP;
+ }
+ }
+
+ ast_debug(2, "Transport type for target '%s' is '%s'\n", host, pjsip_transport_get_type_name(type));
+
+ /* If it's already an address call the callback immediately */
+ if (ip_addr_ver) {
+ pjsip_server_addresses addresses;
+
+ if (ip_addr_ver == 4) {
+ pj_sockaddr_init(pj_AF_INET(), &addresses.entry[0].addr, NULL, 0);
+ pj_inet_aton(&target->addr.host, &addresses.entry[0].addr.ipv4.sin_addr);
+ } else {
+ pj_sockaddr_init(pj_AF_INET6(), &addresses.entry[0].addr, NULL, 0);
+ pj_inet_pton(pj_AF_INET6(), &target->addr.host, &addresses.entry[0].addr.ipv6.sin6_addr);
+ type = (pjsip_transport_type_e)((int)type + PJSIP_TRANSPORT_IPV6);
+ }
+ addresses.count++;
+
+ ast_debug(2, "Target '%s' is an IP address, skipping resolution\n", host);
+
+ callback(user_data, &addresses);
+
+ return 0;
+ }
+
+ resolve = ao2_alloc_options(sizeof(*resolve), sip_resolve_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
+ if (!resolve) {
+ return -1;
+ }
+
+ resolve->callback = callback;
+ resolve->user_data = ao2_bump(user_data);
+
+ if (AST_VECTOR_INIT(&resolve->resolving, 2) || AST_VECTOR_INIT(&resolve->resolved, 2)) {
+ ao2_ref(resolve, -1);
+ return -1;
+ }
+
+ ast_debug(2, "[%p] Created resolution tracking for target '%s'\n", resolve, host);
+
+ res |= sip_resolve_add(resolve, host, ns_t_a, ns_c_in, (type == PJSIP_TRANSPORT_UNSPECIFIED ? PJSIP_TRANSPORT_UDP : type), target->addr.port);
+ res |= sip_resolve_add(resolve, host, ns_t_aaaa, ns_c_in, (type == PJSIP_TRANSPORT_UNSPECIFIED ? PJSIP_TRANSPORT_UDP : type), target->addr.port);
+
+ /* If no port has been specified we can do NAPTR + SRV */
+ if (!target->addr.port) {
+ if (type == PJSIP_TRANSPORT_UDP || type == PJSIP_TRANSPORT_UNSPECIFIED) {
+ snprintf(srv, sizeof(srv), "_sip._udp.%s", host);
+ res |= sip_resolve_add(resolve, srv, ns_t_srv, ns_c_in, PJSIP_TRANSPORT_UDP, 0);
+ }
+ if (type == PJSIP_TRANSPORT_TCP || type == PJSIP_TRANSPORT_UNSPECIFIED) {
+ snprintf(srv, sizeof(srv), "_sip._tcp.%s", host);
+ res |= sip_resolve_add(resolve, srv, ns_t_srv, ns_c_in, PJSIP_TRANSPORT_TCP, 0);
+ }
+ if (type == PJSIP_TRANSPORT_TLS || type == PJSIP_TRANSPORT_UNSPECIFIED) {
+ snprintf(srv, sizeof(srv), "_sips._tcp.%s", host);
+ res |= sip_resolve_add(resolve, srv, ns_t_srv, ns_c_in, PJSIP_TRANSPORT_TLS, 0);
+ }
+ }
+
+ if (res) {
+ ao2_ref(resolve, -1);
+ return -1;
+ }
+
+ ast_debug(2, "[%p] Starting initial resolution using parallel queries for target '%s'\n", resolve, host);
+ ast_dns_query_set_resolve_async(resolve->queries, sip_resolve_callback, resolve);
+
+ ao2_ref(resolve, -1);
+
+ return 0;
+}
Propchange: team/group/dns_pjsip/res/res_pjsip/pjsip_resolver.c
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: team/group/dns_pjsip/res/res_pjsip/pjsip_resolver.c
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Propchange: team/group/dns_pjsip/res/res_pjsip/pjsip_resolver.c
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: team/group/dns_pjsip/res/res_pjsip_session.c
URL: http://svnview.digium.com/svn/asterisk/team/group/dns_pjsip/res/res_pjsip_session.c?view=diff&rev=433967&r1=433966&r2=433967
==============================================================================
--- team/group/dns_pjsip/res/res_pjsip_session.c (original)
+++ team/group/dns_pjsip/res/res_pjsip_session.c Fri Apr 3 13:32:39 2015
@@ -1050,10 +1050,15 @@
.on_rx_request = session_reinvite_on_rx_request,
};
+static void sip_session_resolve_cb(void *data, pjsip_server_addresses *addresses)
+{
+}
+
void ast_sip_session_send_request_with_cb(struct ast_sip_session *session, pjsip_tx_data *tdata,
ast_sip_session_response_cb on_response)
{
pjsip_inv_session *inv_session = session->inv_session;
+ pjsip_host_info target;
if (inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {
/* Don't try to do anything with a hung-up call */
@@ -1077,6 +1082,10 @@
}
handle_outgoing_request(session, tdata);
+
+ pjsip_get_request_dest(tdata, &target);
+ ast_sip_resolve(&target, sip_session_resolve_cb, NULL);
+
pjsip_inv_send_msg(session->inv_session, tdata);
return;
}
More information about the asterisk-commits
mailing list