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 <jc...@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; } -- _____________________________________________________________________ -- Bandwidth and Colocation Provided by http://www.api-digital.com -- svn-commits mailing list To UNSUBSCRIBE or update options visit: http://lists.digium.com/mailman/listinfo/svn-commits