dnsmgr.c: dnsmgr_refresh() incorrectly flags change with DNS round-robin

The dnsmgr_refresh() function checks to see if the IP address associated
with a name/service has changed. The gotcha is that the ast_get_ip_or_srv()
function only returns the first IP address returned by the DNS query. If
there are multiple IPs associated with the name and the returned order is
not consistent (e.g. with DNS round-robin) then the other IP addresses are
not included in the comparison and the entry is flagged as changed even
though the IP is still valid.

Updated the code to check all IP addresses and flag a change only if the
original IP is no longer valid.

Resolves: #924
(cherry picked from commit 338e191539)
releases/22
Allan Nathanson 7 months ago committed by Asterisk Development Team
parent 2425d2218c
commit 039baa0317

@ -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
*

@ -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;
}

@ -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));
}

Loading…
Cancel
Save