diff --git a/include/asterisk/acl.h b/include/asterisk/acl.h index 14a9c6524f..fda4dc3138 100644 --- a/include/asterisk/acl.h +++ b/include/asterisk/acl.h @@ -287,6 +287,33 @@ int ast_get_ip(struct ast_sockaddr *addr, const char *hostname); */ int ast_get_ip_or_srv(struct ast_sockaddr *addr, const char *hostname, const char *service); +/*! + * \brief Get the IP address given a hostname and optional service with a preferred result + * + * \details + * If the service parameter is non-NULL, then an SRV lookup will be made by + * prepending the service to the hostname parameter, separated by a '.' + * For example, if hostname is "example.com" and service is "_sip._udp" then + * an SRV lookup will be done for "_sip._udp.example.com". If service is NULL, + * then this function acts exactly like a call to ast_get_ip. + * + * \param addr The IP address found. The address family is used as an input + * parameter to filter the returned addresses. If it is 0, both IPv4 and + * IPv6 addresses can be returned. + * + * \param hostname The hostname to look up + * \param service A specific service provided by the host. A NULL service results + * in an A-record lookup instead of an SRV lookup + * \param preference The preferred IP address to return. If multiple results are + * available and this IP address is in the list then it will be returned. If NULL, + * or if the none of the returned IP addresses match, then the first IP address + * will be returned. + * \retval 0 Success + * \retval -1 Failure + */ +int ast_get_ip_or_srv_with_preference(struct ast_sockaddr *addr, const char *hostname, + const char *service, struct ast_sockaddr *preference); + /*! * \brief Get our local IP address when contacting a remote host * diff --git a/main/acl.c b/main/acl.c index fe388da538..772b0fb0df 100644 --- a/main/acl.c +++ b/main/acl.c @@ -872,18 +872,29 @@ enum ast_acl_sense ast_apply_ha(const struct ast_ha *ha, const struct ast_sockad return res; } -static int resolve_first(struct ast_sockaddr *addr, const char *name, int flag, - int family) +static int resolve_match_or_first(struct ast_sockaddr *addr, const char *name, int flag, + int family, struct ast_sockaddr *preference) { struct ast_sockaddr *addrs; int addrs_cnt; addrs_cnt = ast_sockaddr_resolve(&addrs, name, flag, family); if (addrs_cnt > 0) { - if (addrs_cnt > 1) { + struct ast_sockaddr *resolved = &addrs[0]; + + if (preference) { + int i; + + for (i = 0; i < addrs_cnt; i++) { + if (!ast_sockaddr_cmp_addr(preference, &addrs[i])) { + resolved = &addrs[i]; + break; + } + } + } else if (addrs_cnt > 1) { ast_debug(1, "Multiple addresses. Using the first only\n"); } - ast_sockaddr_copy(addr, &addrs[0]); + ast_sockaddr_copy(addr, resolved); ast_free(addrs); } else { ast_log(LOG_WARNING, "Unable to lookup '%s'\n", name); @@ -893,7 +904,8 @@ static int resolve_first(struct ast_sockaddr *addr, const char *name, int flag, return 0; } -int ast_get_ip_or_srv(struct ast_sockaddr *addr, const char *hostname, const char *service) +int ast_get_ip_or_srv_with_preference(struct ast_sockaddr *addr, const char *hostname, + const char *service, struct ast_sockaddr *preference) { char srv[256]; char host[256]; @@ -907,7 +919,7 @@ int ast_get_ip_or_srv(struct ast_sockaddr *addr, const char *hostname, const cha } } - if (resolve_first(addr, hostname, PARSE_PORT_FORBID, addr->ss.ss_family) != 0) { + if (resolve_match_or_first(addr, hostname, PARSE_PORT_FORBID, addr->ss.ss_family, preference) != 0) { return -1; } @@ -918,6 +930,11 @@ int ast_get_ip_or_srv(struct ast_sockaddr *addr, const char *hostname, const cha return 0; } +int ast_get_ip_or_srv(struct ast_sockaddr *addr, const char *hostname, const char *service) +{ + return ast_get_ip_or_srv_with_preference(addr, hostname, service, NULL); +} + struct dscp_codepoint { char *name; unsigned int space; @@ -1064,17 +1081,17 @@ int ast_find_ourip(struct ast_sockaddr *ourip, const struct ast_sockaddr *bindad if (gethostname(ourhost, sizeof(ourhost) - 1)) { ast_log(LOG_WARNING, "Unable to get hostname\n"); } else { - if (resolve_first(ourip, ourhost, PARSE_PORT_FORBID, family) == 0) { - /* reset port since resolve_first wipes this out */ + if (resolve_match_or_first(ourip, ourhost, PARSE_PORT_FORBID, family, NULL) == 0) { + /* reset port since resolve_match_or_first wipes this out */ ast_sockaddr_set_port(ourip, port); return 0; } } ast_debug(3, "Trying to check A.ROOT-SERVERS.NET and get our IP address for that connection\n"); /* A.ROOT-SERVERS.NET. */ - if (!resolve_first(&root, "A.ROOT-SERVERS.NET", PARSE_PORT_FORBID, 0) && + if (!resolve_match_or_first(&root, "A.ROOT-SERVERS.NET", PARSE_PORT_FORBID, 0, NULL) && !ast_ouraddrfor(&root, ourip)) { - /* reset port since resolve_first wipes this out */ + /* reset port since resolve_match_or_first wipes this out */ ast_sockaddr_set_port(ourip, port); return 0; } diff --git a/main/dnsmgr.c b/main/dnsmgr.c index bcde08251e..1140d47f43 100644 --- a/main/dnsmgr.c +++ b/main/dnsmgr.c @@ -211,7 +211,7 @@ static int dnsmgr_refresh(struct ast_dnsmgr_entry *entry, int verbose) ast_debug(6, "refreshing '%s'\n", entry->name); tmp.ss.ss_family = entry->family; - if (!ast_get_ip_or_srv(&tmp, entry->name, entry->service)) { + if (!ast_get_ip_or_srv_with_preference(&tmp, entry->name, entry->service, entry->result)) { if (!ast_sockaddr_port(&tmp)) { ast_sockaddr_set_port(&tmp, ast_sockaddr_port(entry->result)); }