diff --git a/res/res_pjsip.exports.in b/res/res_pjsip.exports.in index 58868e398fd00d41304eb331839f60f28ac92941..4a0f2181bf7e094defdfbfc1d46cdf396661eebb 100644 --- a/res/res_pjsip.exports.in +++ b/res/res_pjsip.exports.in @@ -6,6 +6,7 @@ LINKER_SYMBOL_PREFIXast_copy_pj_str; LINKER_SYMBOL_PREFIXast_copy_pj_str2; LINKER_SYMBOL_PREFIXast_pjsip_rdata_get_endpoint; + LINKER_SYMBOL_PREFIXpjsip_resolver_*; local: *; }; diff --git a/res/res_pjsip/pjsip_resolver.c b/res/res_pjsip/pjsip_resolver.c index eb370117e5153c1798453e8a5ae8cd959812bca8..792062087c88f8643afc467d497be849034b52e0 100644 --- a/res/res_pjsip/pjsip_resolver.c +++ b/res/res_pjsip/pjsip_resolver.c @@ -32,9 +32,14 @@ #include "include/res_pjsip_private.h" #include "asterisk/taskprocessor.h" #include "asterisk/threadpool.h" +#include "asterisk/pjsip_resolver.h" #ifdef HAVE_PJSIP_EXTERNAL_RESOLVER +/*! DNS cache function pointers */ +dns_cache_get_addr_t get_dns_cache_addr = NULL; +dns_cache_update_t update_dns_cache = NULL; + /*! \brief Structure which contains transport+port information for an active query */ struct sip_target { /*! \brief The transport to be used */ @@ -59,8 +64,18 @@ struct host_track_entry { AST_LIST_ENTRY(host_track_entry) next; }; +/*! \brief Structure which contains cached resolved server addresses */ +struct dns_cache_entry { + char *host; + pjsip_server_addresses addresses; + int ttl; + time_t entry_time; + AST_LIST_ENTRY(dns_cache_entry) next; +}; + /*! \brief the list of all tracked hosts */ AST_LIST_HEAD(, host_track_entry) sip_resolve_track; +AST_LIST_HEAD(, dns_cache_entry) sip_dns_cache; /*! \brief compare the target host info with track record */ static int host_track_target_cmp(pjsip_host_info *host, struct host_track_entry *hostc) @@ -266,6 +281,10 @@ struct sip_resolve { pjsip_resolver_callback *callback; /*! \brief User provided data */ void *token; + /*! \brief Hostname requested to resolve */ + char host[NI_MAXHOST]; + /*! \brief Minimum TTL for the queries resulting in viable server addresses */ + int ttl; }; /*! \brief Our own defined transports, reduces the size of sip_available_transports */ @@ -410,6 +429,18 @@ static int sip_resolve_invoke_user_callback(void *data) sip_resolve_target_track(&resolve->target, &resolve->addresses); + if (update_dns_cache && get_dns_cache_addr && resolve->addresses.count != 0) { + ast_debug(2, "Updating DNS cache...\n"); + + if (!(*update_dns_cache)(resolve->host, &resolve->addresses, resolve->ttl)) { + ast_debug(2, "Getting addr from DNS cache\n"); + + if ((*get_dns_cache_addr)(resolve->host, &resolve->addresses)) { + ast_log(LOG_ERROR, "Failed to get addr from cache after updating!"); + } + } + } + ast_debug(2, "[%p] Invoking user callback with '%d' addresses\n", resolve, resolve->addresses.count); resolve->callback(resolve->addresses.count ? PJ_SUCCESS : PJLIB_UTIL_EDNSNOANSWERREC, resolve->token, &resolve->addresses); @@ -541,6 +572,12 @@ static void sip_resolve_callback(const struct ast_dns_query_set *query_set) ast_dns_record_get_data_size(record)); } + int min_ttl = ast_dns_result_get_lowest_ttl(result); + if (min_ttl > 0 && resolve->ttl < min_ttl) { + ast_debug(2, "Updating DNS TTL for query set from %d to %d\n", resolve->ttl, min_ttl); + resolve->ttl = min_ttl; + } + address_count++; } else if (ast_dns_record_get_rr_type(record) == T_SRV) { if (have_naptr) { @@ -735,6 +772,19 @@ static void sip_resolve(pjsip_resolver_t *resolver, pj_pool_t *pool, const pjsip return; } + if (get_dns_cache_addr) { + ast_debug(2, "Checking DNS cache...(%s)\n", host); + pjsip_server_addresses addresses = { .count = 0, }; + + if (!(*get_dns_cache_addr)(host, &addresses)) { + ast_debug(2, "Addr found in DNS cache\n"); + + cb(PJ_SUCCESS, token, &addresses); + return; + } + ast_debug(2, "No addr found in DNS cache\n"); + } + resolve = ao2_alloc_options(sizeof(*resolve), sip_resolve_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK); if (!resolve) { cb(PJ_ENOMEM, token, NULL); @@ -744,6 +794,8 @@ static void sip_resolve(pjsip_resolver_t *resolver, pj_pool_t *pool, const pjsip pj_memcpy(&resolve->target, target, sizeof(*target)); resolve->callback = cb; resolve->token = token; + strncpy(resolve->host, host, NI_MAXHOST); + resolve->ttl = 0; if (AST_VECTOR_INIT(&resolve->resolving, 4)) { ao2_ref(resolve, -1); @@ -869,6 +921,22 @@ static void sip_check_transport(pj_pool_t *pool, pjsip_transport_type_e transpor } } +/* External API to set DNS cache function pointers */ +void pjsip_resolver_set_dns_cache(dns_cache_get_addr_t get_addr, dns_cache_update_t update_cache) +{ + ast_debug(3, "Setting DNS cache control function pointers\n"); + get_dns_cache_addr = get_addr; + update_dns_cache = update_cache; +} + +/* External API to clear DNS cache function pointers */ +void pjsip_resolver_clear_dns_cache(void) +{ + ast_debug(3, "Clearing DNS cache control function pointers\n"); + get_dns_cache_addr = NULL; + update_dns_cache = NULL; +} + /*! \brief External resolver implementation for PJSIP */ static pjsip_ext_resolver ext_resolver = { .resolve = sip_resolve, diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c index 6ed2141d69f5435a13038d9709fb71b344acee20..9f91bc7eb9ddb33e376f5dfb51d00d8433c2be2b 100644 --- a/res/res_pjsip_outbound_registration.c +++ b/res/res_pjsip_outbound_registration.c @@ -286,6 +286,10 @@ static int set_outbound_initial_authentication_credentials(pjsip_regc *regc, /*! \brief Some thread local storage used to determine if the running thread invoked the callback */ AST_THREADSTORAGE(register_callback_invoked); +/*! DNS cache function pointer typedefs */ +typedef void (*dns_cache_update_addr_t)(void); +typedef void (*dns_cache_clear_t)(void); + /*! \brief Amount of buffer time (in seconds) before expiration that we re-register at */ #define REREGISTER_BUFFER_TIME 10 @@ -505,6 +509,10 @@ static AO2_GLOBAL_OBJ_STATIC(current_states); /*! subscription id for network change events */ static struct stasis_subscription *network_change_sub; +/*! DNS cache function pointers */ +dns_cache_update_addr_t update_dns_cache_addr = NULL; +dns_cache_clear_t clear_dns_cache = NULL; + /*! \brief hashing function for state objects */ static int registration_state_hash(const void *obj, const int flags) { @@ -3142,6 +3150,23 @@ static void network_change_stasis_cb(void *data, struct stasis_subscription *sub reregister_all(); } +/* External API to set DNS cache control function pointers */ +void pjsip_outbound_registration_set_dns_cache_control(dns_cache_update_addr_t update_addr, + dns_cache_clear_t clear_cache) +{ + ast_debug(3, "Setting DNS cache control function pointers\n"); + update_dns_cache_addr = update_addr; + clear_dns_cache = clear_cache; +} + +/* External API to clear DNS cache control function pointers */ +void pjsip_outbound_registration_clear_dns_cache_control(void) +{ + ast_debug(3, "Clearing DNS cache control function pointers\n"); + update_dns_cache_addr = NULL; + clear_dns_cache = NULL; +} + static int unload_module(void) { int remaining;