[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