MINOR: dns: Cache previous DNS answers.

As DNS servers may not return all IPs in one answer, we want to cache the
previous entries. Those entries are removed when considered obsolete, which
happens when the IP hasn't been returned by the DNS server for a time
defined in the "hold obsolete" parameter of the resolver section. The default
is 30s.
This commit is contained in:
Olivier Houchard 2017-07-06 18:46:47 +02:00 committed by Willy Tarreau
parent aa0d637292
commit a8c6db8d2d
6 changed files with 175 additions and 123 deletions

View File

@ -11693,6 +11693,10 @@ For example, with 2 name servers configured in a resolvers section:
- first response is truncated and second one is a NX Domain, then HAProxy
stops resolution.
As a DNS server may not answer all the IPs in one DNS request, haproxy keeps
a cache of previous answers, an answer will be considered obsolete after
"hold obsolete" seconds without the IP returned.
resolvers <resolvers id>
Creates a new name server list labelled <resolvers id>
@ -11709,7 +11713,7 @@ hold <status> <period>
Defines <period> during which the last name resolution should be kept based
on last resolution <status>
<status> : last name resolution status. Acceptable values are "nx",
"other", "refused", "timeout", "valid".
"other", "refused", "timeout", "valid", "obsolete".
<period> : interval between two successive name resolution when the last
answer was in <status>. It follows the HAProxy time format.
<period> is in milliseconds by default.
@ -11756,6 +11760,7 @@ timeout <event> <time>
hold nx 30s
hold timeout 30s
hold valid 10s
hold obsolete 30s
6. HTTP header manipulation

View File

@ -52,7 +52,7 @@ int srv_init_addr(void);
struct server *cli_find_server(struct appctx *appctx, char *arg);
/* functions related to server name resolution */
int snr_update_srv_status(struct server *s);
int snr_update_srv_status(struct server *s, int has_no_ip);
int snr_resolution_cb(struct dns_requester *requester, struct dns_nameserver *nameserver);
int snr_resolution_error_cb(struct dns_requester *requester, int error_code);
struct server *snr_check_ip_callback(struct server *srv, void *ip, unsigned char *ip_family);

View File

@ -113,7 +113,7 @@ struct dns_query_item {
/* NOTE: big endian structure */
struct dns_answer_item {
struct list list;
char *name; /* answer name
char name[DNS_MAX_NAME_SIZE]; /* answer name
* For SRV type, name also includes service
* and protocol value */
int16_t type; /* question type */
@ -124,7 +124,8 @@ struct dns_answer_item {
int16_t port; /* SRV type port */
int16_t data_len; /* number of bytes in target below */
struct sockaddr address; /* IPv4 or IPv6, network format */
char *target; /* Response data: SRV or CNAME type target */
char target[DNS_MAX_NAME_SIZE]; /* Response data: SRV or CNAME type target */
time_t last_seen; /* When was the answer was last seen */
};
struct dns_response_packet {
@ -158,6 +159,7 @@ struct dns_resolvers {
int timeout; /* no answer was delivered */
int refused; /* dns server refused to answer */
int other; /* other dns response errors */
int obsolete; /* an answer hasn't been seen */
} hold;
struct task *t; /* timeout management */
int resolution_pool_size; /* size of the resolution pool associated to this resolvers section */
@ -252,8 +254,6 @@ struct dns_resolution {
unsigned long long revision; /* updated for each update */
struct dns_response_packet response; /* structure hosting the DNS response */
struct dns_query_item response_query_records[DNS_MAX_QUERY_RECORDS]; /* <response> query records */
struct dns_answer_item response_answer_records[DNS_MAX_ANSWER_RECORDS]; /* <response> answer records */
struct chunk response_buffer; /* buffer used as a data store for <response> above TODO: optimize the size (might be smaller) */
};
/*
@ -315,6 +315,7 @@ enum {
DNS_UPD_CNAME, /* CNAME without any IP provided in the response */
DNS_UPD_NAME_ERROR, /* name in the response did not match the query */
DNS_UPD_NO_IP_FOUND, /* no IP could be found in the response */
DNS_UPD_OBSOLETE_IP, /* The server IP was obsolete, and no other IP was found */
};
#endif /* _TYPES_DNS_H */

View File

@ -2167,6 +2167,7 @@ int cfg_parse_resolvers(const char *file, int linenum, char **args, int kwm)
curr_resolvers->hold.other = 30000;
curr_resolvers->hold.refused = 30000;
curr_resolvers->hold.timeout = 30000;
curr_resolvers->hold.obsolete = 30000;
/* default hold period for valid is 10s */
curr_resolvers->hold.valid = 10000;
curr_resolvers->timeout.retry = 1000;
@ -2280,8 +2281,10 @@ int cfg_parse_resolvers(const char *file, int linenum, char **args, int kwm)
curr_resolvers->hold.timeout = time;
else if (strcmp(args[1], "valid") == 0)
curr_resolvers->hold.valid = time;
else if (strcmp(args[1], "obsolete") == 0)
curr_resolvers->hold.obsolete = time;
else {
Alert("parsing [%s:%d] : '%s' unknown <event>: '%s', expects either 'nx', 'timeout', 'valid', or 'other'.\n",
Alert("parsing [%s:%d] : '%s' unknown <event>: '%s', expects either 'nx', 'timeout', 'valid', 'obsolete' or 'other'.\n",
file, linenum, args[0], args[1]);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;

247
src/dns.c
View File

@ -51,6 +51,8 @@ static int64_t dns_query_id_seed; /* random seed */
static struct lru64_head *dns_lru_tree;
static int dns_cache_size = 1024; /* arbitrary DNS cache size */
static struct pool_head *dns_answer_item_pool;
/* proto_udp callback functions for a DNS resolution */
struct dgram_data_cb resolve_dgram_cb = {
.recv = dns_resolve_recv,
@ -169,6 +171,7 @@ int dns_trigger_resolution(struct dns_resolution *resolution)
}
requester->requester_cb(requester, NULL);
resolvers = NULL;
}
else {
LIST_DEL(&requester->list);
@ -306,6 +309,12 @@ void dns_reset_resolution(struct dns_resolution *resolution)
resolution->qid.key = 0;
}
static inline void free_dns_answer_item(struct dns_answer_item *item)
{
pool_free2(dns_answer_item_pool, item);
}
/*
* function called when a network IO is generated on a name server socket for an incoming packet
* It performs the following actions:
@ -327,6 +336,7 @@ void dns_resolve_recv(struct dgram_conn *dgram)
struct eb32_node *eb;
struct lru64 *lru = NULL;
struct dns_requester *requester = NULL, *tmprequester = NULL;
struct dns_answer_item *item1, *item2 = NULL;
fd = dgram->t.sock.fd;
@ -468,6 +478,15 @@ void dns_resolve_recv(struct dgram_conn *dgram)
break;
}
/* Check for any obsolete record */
list_for_each_entry_safe(item1, item2, &resolution->response.answer_list,
list) {
if (item1->last_seen + nameserver->resolvers->hold.obsolete / 1000 < now.tv_sec) {
LIST_DEL(&item1->list);
free_dns_answer_item(item1);
}
}
/* some error codes trigger a re-send of the query, but switching the
* query type.
* This is the case for the following error codes:
@ -885,13 +904,13 @@ int dns_validate_dns_response(unsigned char *resp, unsigned char *bufend, struct
{
unsigned char *reader;
char *previous_dname, tmpname[DNS_MAX_NAME_SIZE];
int len, flags, offset, ret;
int dns_query_record_id, dns_answer_record_id;
int len, flags, offset;
int dns_query_record_id;
int nb_saved_records;
struct dns_query_item *dns_query;
struct dns_answer_item *dns_answer_record;
struct dns_answer_item *dns_answer_record, *tmp_record;
struct dns_response_packet *dns_p;
struct chunk *dns_response_buffer;
int found = 0;
reader = resp;
len = 0;
@ -899,9 +918,6 @@ int dns_validate_dns_response(unsigned char *resp, unsigned char *bufend, struct
/* initialization of response buffer and structure */
dns_p = &resolution->response;
dns_response_buffer = &resolution->response_buffer;
memset(dns_p, '\0', sizeof(struct dns_response_packet));
chunk_reset(dns_response_buffer);
/* query id */
if (reader + 2 >= bufend)
@ -1011,30 +1027,29 @@ int dns_validate_dns_response(unsigned char *resp, unsigned char *bufend, struct
}
/* now parsing response records */
LIST_INIT(&dns_p->answer_list);
nb_saved_records = 0;
for (dns_answer_record_id = 0; dns_answer_record_id < dns_p->header.ancount; dns_answer_record_id++) {
for (int i = 0; i < dns_p->header.ancount; i++) {
if (reader >= bufend)
return DNS_RESP_INVALID;
/* pull next response record from the list, if still one available, then add it
* to the record list */
if (dns_answer_record_id > DNS_MAX_ANSWER_RECORDS)
return DNS_RESP_INVALID;
dns_answer_record = &resolution->response_answer_records[dns_answer_record_id];
LIST_ADDQ(&dns_p->answer_list, &dns_answer_record->list);
dns_answer_record = pool_alloc2(dns_answer_item_pool);
if (dns_answer_record == NULL)
return (DNS_RESP_INVALID);
offset = 0;
len = dns_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE, &offset);
if (len == 0)
if (len == 0) {
free_dns_answer_item(dns_answer_record);
return DNS_RESP_INVALID;
}
/* check if the current record dname is valid.
* previous_dname points either to queried dname or last CNAME target
*/
if (memcmp(previous_dname, tmpname, len) != 0) {
if (dns_answer_record_id == 0) {
free_dns_answer_item(dns_answer_record);
if (i == 0) {
/* first record, means a mismatch issue between queried dname
* and dname found in the first record */
return DNS_RESP_INVALID;
@ -1046,43 +1061,50 @@ int dns_validate_dns_response(unsigned char *resp, unsigned char *bufend, struct
}
dns_answer_record->name = chunk_newstr(dns_response_buffer);
if (dns_answer_record->name == NULL)
return DNS_RESP_INVALID;
ret = chunk_strncat(dns_response_buffer, tmpname, len);
if (ret == 0)
return DNS_RESP_INVALID;
memcpy(dns_answer_record->name, tmpname, len);
dns_answer_record->name[len] = 0;
reader += offset;
if (reader >= bufend)
if (reader >= bufend) {
free_dns_answer_item(dns_answer_record);
return DNS_RESP_INVALID;
}
if (reader >= bufend)
if (reader >= bufend) {
free_dns_answer_item(dns_answer_record);
return DNS_RESP_INVALID;
}
/* 2 bytes for record type (A, AAAA, CNAME, etc...) */
if (reader + 2 > bufend)
if (reader + 2 > bufend) {
free_dns_answer_item(dns_answer_record);
return DNS_RESP_INVALID;
}
dns_answer_record->type = reader[0] * 256 + reader[1];
reader += 2;
/* 2 bytes for class (2) */
if (reader + 2 > bufend)
if (reader + 2 > bufend) {
free_dns_answer_item(dns_answer_record);
return DNS_RESP_INVALID;
}
dns_answer_record->class = reader[0] * 256 + reader[1];
reader += 2;
/* 4 bytes for ttl (4) */
if (reader + 4 > bufend)
if (reader + 4 > bufend) {
free_dns_answer_item(dns_answer_record);
return DNS_RESP_INVALID;
}
dns_answer_record->ttl = reader[0] * 16777216 + reader[1] * 65536
+ reader[2] * 256 + reader[3];
reader += 4;
/* now reading data len */
if (reader + 2 > bufend)
if (reader + 2 > bufend) {
free_dns_answer_item(dns_answer_record);
return DNS_RESP_INVALID;
}
dns_answer_record->data_len = reader[0] * 256 + reader[1];
/* move forward 2 bytes for data len */
@ -1092,8 +1114,10 @@ int dns_validate_dns_response(unsigned char *resp, unsigned char *bufend, struct
switch (dns_answer_record->type) {
case DNS_RTYPE_A:
/* ipv4 is stored on 4 bytes */
if (dns_answer_record->data_len != 4)
if (dns_answer_record->data_len != 4) {
free_dns_answer_item(dns_answer_record);
return DNS_RESP_INVALID;
}
dns_answer_record->address.sa_family = AF_INET;
memcpy(&(((struct sockaddr_in *)&dns_answer_record->address)->sin_addr),
reader, dns_answer_record->data_len);
@ -1107,22 +1131,21 @@ int dns_validate_dns_response(unsigned char *resp, unsigned char *bufend, struct
* + 1 because dns_answer_record_id starts at 0 while number of answers
* is an integer and starts at 1.
*/
if (dns_answer_record_id + 1 == dns_p->header.ancount)
if (i + 1 == dns_p->header.ancount) {
free_dns_answer_item(dns_answer_record);
return DNS_RESP_CNAME_ERROR;
}
offset = 0;
len = dns_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE, &offset);
if (len == 0)
if (len == 0) {
free_dns_answer_item(dns_answer_record);
return DNS_RESP_INVALID;
}
dns_answer_record->target = chunk_newstr(dns_response_buffer);
if (dns_answer_record->target == NULL)
return DNS_RESP_INVALID;
ret = chunk_strncat(dns_response_buffer, tmpname, len);
if (ret == 0)
return DNS_RESP_INVALID;
memcpy(dns_answer_record->target, tmpname, len);
dns_answer_record->target[len] = 0;
previous_dname = dns_answer_record->target;
@ -1130,8 +1153,10 @@ int dns_validate_dns_response(unsigned char *resp, unsigned char *bufend, struct
case DNS_RTYPE_AAAA:
/* ipv6 is stored on 16 bytes */
if (dns_answer_record->data_len != 16)
if (dns_answer_record->data_len != 16) {
free_dns_answer_item(dns_answer_record);
return DNS_RESP_INVALID;
}
dns_answer_record->address.sa_family = AF_INET6;
memcpy(&(((struct sockaddr_in6 *)&dns_answer_record->address)->sin6_addr),
reader, dns_answer_record->data_len);
@ -1144,12 +1169,39 @@ int dns_validate_dns_response(unsigned char *resp, unsigned char *bufend, struct
/* move forward dns_answer_record->data_len for analyzing next record in the response */
reader += dns_answer_record->data_len;
/* Lookup to see if we already had this entry */
list_for_each_entry(tmp_record, &dns_p->answer_list, list) {
if (tmp_record->type != dns_answer_record->type)
continue;
switch (tmp_record->type) {
case DNS_RTYPE_A:
if (!memcmp(&((struct sockaddr_in *)&dns_answer_record->address)->sin_addr,
&((struct sockaddr_in *)&tmp_record->address)->sin_addr, sizeof(in_addr_t)))
found = 1;
break;
case DNS_RTYPE_AAAA:
if (!memcmp(&((struct sockaddr_in6 *)&dns_answer_record->address)->sin6_addr,
&((struct sockaddr_in6 *)&tmp_record->address)->sin6_addr, sizeof(struct in6_addr)))
found = 1;
break;
default:
break;
}
if (found == 1)
break;
}
if (found == 1) {
tmp_record->last_seen = now.tv_sec;
free_dns_answer_item(dns_answer_record);
} else {
dns_answer_record->last_seen = now.tv_sec;
LIST_ADDQ(&dns_p->answer_list, &dns_answer_record->list);
}
} /* for i 0 to ancount */
/* let's add a last \0 to close our last string */
ret = chunk_strncat(dns_response_buffer, "\0", 1);
if (ret == 0)
return DNS_RESP_INVALID;
/* save the number of records we really own */
dns_p->header.ancount = nb_saved_records;
@ -1175,51 +1227,17 @@ int dns_get_ip_from_response(struct dns_response_packet *dns_p,
{
struct dns_answer_item *record;
int family_priority;
int i, currentip_found;
int currentip_found;
unsigned char *newip4, *newip6;
struct {
void *ip;
unsigned char type;
} rec[DNS_MAX_IP_REC];
int currentip_sel;
int j;
int rec_nb = 0;
int score, max_score;
family_priority = dns_opts->family_prio;
*newip = newip4 = newip6 = NULL;
currentip_found = 0;
*newip_sin_family = AF_UNSPEC;
/* now parsing response records */
list_for_each_entry(record, &dns_p->answer_list, list) {
/* analyzing record content */
switch (record->type) {
case DNS_RTYPE_A:
/* Store IPv4, only if some room is avalaible. */
if (rec_nb < DNS_MAX_IP_REC) {
rec[rec_nb].ip = &(((struct sockaddr_in *)&record->address)->sin_addr);
rec[rec_nb].type = AF_INET;
rec_nb++;
}
break;
/* we're looking for IPs only. CNAME validation is done when
* parsing the response buffer for the first time */
case DNS_RTYPE_CNAME:
break;
case DNS_RTYPE_AAAA:
/* Store IPv6, only if some room is avalaible. */
if (rec_nb < DNS_MAX_IP_REC) {
rec[rec_nb].ip = &(((struct sockaddr_in6 *)&record->address)->sin6_addr);
rec[rec_nb].type = AF_INET6;
rec_nb++;
}
break;
} /* switch (record type) */
} /* list for each record entries */
max_score = -1;
/* Select an IP regarding configuration preference.
* Top priority is the prefered network ip version,
@ -1234,29 +1252,38 @@ int dns_get_ip_from_response(struct dns_response_packet *dns_p,
* 1 - current ip.
* The result with the biggest score is returned.
*/
max_score = -1;
for (i = 0; i < rec_nb; i++) {
int record_ip_already_affected = 0;
list_for_each_entry(record, &dns_p->answer_list, list) {
void *ip;
unsigned char ip_type;
if (record->type == DNS_RTYPE_A) {
ip = &(((struct sockaddr_in *)&record->address)->sin_addr);
ip_type = AF_INET;
} else if (record->type == DNS_RTYPE_AAAA) {
ip_type = AF_INET6;
ip = &(((struct sockaddr_in6 *)&record->address)->sin6_addr);
} else
continue;
score = 0;
/* Check for prefered ip protocol. */
if (rec[i].type == family_priority)
if (ip_type == family_priority)
score += 8;
/* Check for prefered network. */
for (j = 0; j < dns_opts->pref_net_nb; j++) {
/* Compare only the same adresses class. */
if (dns_opts->pref_net[j].family != rec[i].type)
if (dns_opts->pref_net[j].family != ip_type)
continue;
if ((rec[i].type == AF_INET &&
in_net_ipv4(rec[i].ip,
if ((ip_type == AF_INET &&
in_net_ipv4(ip,
&dns_opts->pref_net[j].mask.in4,
&dns_opts->pref_net[j].addr.in4)) ||
(rec[i].type == AF_INET6 &&
in_net_ipv6(rec[i].ip,
(ip_type == AF_INET6 &&
in_net_ipv6(ip,
&dns_opts->pref_net[j].mask.in6,
&dns_opts->pref_net[j].addr.in6))) {
score += 4;
@ -1268,18 +1295,17 @@ int dns_get_ip_from_response(struct dns_response_packet *dns_p,
* If yes, the score should be incremented by 2.
*/
if (owner) {
if (snr_check_ip_callback(owner, rec[i].ip, &rec[i].type))
record_ip_already_affected = 1;
if (snr_check_ip_callback(owner, ip, &ip_type))
{
continue;
}
}
if (record_ip_already_affected == 0)
score += 2;
/* Check for current ip matching. */
if (rec[i].type == currentip_sin_family &&
if (ip_type == currentip_sin_family &&
((currentip_sin_family == AF_INET &&
memcmp(rec[i].ip, currentip, 4) == 0) ||
memcmp(ip, currentip, 4) == 0) ||
(currentip_sin_family == AF_INET6 &&
memcmp(rec[i].ip, currentip, 16) == 0))) {
memcmp(ip, currentip, 16) == 0))) {
score += 1;
currentip_sel = 1;
} else
@ -1292,21 +1318,22 @@ int dns_get_ip_from_response(struct dns_response_packet *dns_p,
* the ip selected is the current ip.
*/
if (score > max_score) {
if (rec[i].type == AF_INET)
newip4 = rec[i].ip;
if (ip_type == AF_INET)
newip4 = ip;
else
newip6 = rec[i].ip;
newip6 = ip;
currentip_found = currentip_sel;
if (score == 15)
return DNS_UPD_NO;
max_score = score;
}
}
} /* list for each record entries */
/* no IP found in the response */
if (!newip4 && !newip6) {
if (!newip4 && !newip6)
return DNS_UPD_NO_IP_FOUND;
}
/* case when the caller looks first for an IPv4 address */
if (family_priority == AF_INET) {
@ -1410,6 +1437,14 @@ int dns_init_resolvers(int close_socket)
/* give a first random value to our dns query_id seed */
dns_query_id_seed = random();
/* Initialize the answer items pool */
dns_answer_item_pool = create_pool("dns_answer_item",
sizeof(struct dns_answer_item), MEM_F_SHARED);
if (dns_answer_item_pool == NULL) {
Alert("Failed to create the dns answer items pool");
return 0;
}
/* run through the resolvers section list */
list_for_each_entry(curr_resolvers, &dns_resolvers, list) {
/* create the task associated to the resolvers section */
@ -1442,6 +1477,7 @@ int dns_init_resolvers(int close_socket)
/* allocate memory only if it has not already been allocated
* by a previous call to this function */
if (!dgram && (dgram = calloc(1, sizeof(*dgram))) == NULL) {
Alert("Starting [%s/%s] nameserver: out of memory.\n", curr_resolvers->id,
curnameserver->id);
@ -2081,6 +2117,7 @@ int dns_link_resolution(void *requester, int requester_type, struct dns_resoluti
tmpresolution->status = RSLV_STATUS_NONE;
tmpresolution->step = RSLV_STEP_NONE;
tmpresolution->revision = 1;
LIST_INIT(&tmpresolution->response.answer_list);
}
/* add the requester to the resolution's wait queue */
@ -2170,7 +2207,6 @@ struct dns_resolution *dns_alloc_resolution(void)
return NULL;
}
chunk_init(&resolution->response_buffer, buffer, global.tune.bufsize);
LIST_INIT(&resolution->requester.wait);
LIST_INIT(&resolution->requester.curr);
@ -2180,7 +2216,6 @@ struct dns_resolution *dns_alloc_resolution(void)
/* This function free the memory allocated to a DNS resolution */
void dns_free_resolution(struct dns_resolution *resolution)
{
chunk_destroy(&resolution->response_buffer);
free(resolution);
return;

View File

@ -3756,7 +3756,7 @@ out:
* 0 if server status is updated
* 1 if server status has not changed
*/
int snr_update_srv_status(struct server *s)
int snr_update_srv_status(struct server *s, int has_no_ip)
{
struct dns_resolution *resolution = s->resolution;
struct dns_resolvers *resolvers = s->resolvers;
@ -3772,6 +3772,13 @@ int snr_update_srv_status(struct server *s)
* resume health checks
* server will be turned back on if health check is safe
*/
if (has_no_ip) {
if (s->admin & SRV_ADMF_RMAINT)
return 1;
srv_set_admin_flag(s, SRV_ADMF_RMAINT,
"No IP for server ");
return (0);
}
if (!(s->admin & SRV_ADMF_RMAINT))
return 1;
srv_clr_admin_flag(s, SRV_ADMF_RMAINT);
@ -3847,6 +3854,7 @@ int snr_resolution_cb(struct dns_requester *requester, struct dns_nameserver *na
short server_sin_family, firstip_sin_family;
int ret;
struct chunk *chk = get_trash_chunk();
int has_no_ip = 0;
s = objt_server(requester->requester);
if (!s)
@ -3893,10 +3901,7 @@ int snr_resolution_cb(struct dns_requester *requester, struct dns_nameserver *na
goto invalid;
case DNS_UPD_NO_IP_FOUND:
if (resolution->status != RSLV_STATUS_OTHER) {
resolution->status = RSLV_STATUS_OTHER;
resolution->last_status_change = now_ms;
}
has_no_ip = 1;
goto update_status;
case DNS_UPD_NAME_ERROR:
@ -3927,7 +3932,7 @@ int snr_resolution_cb(struct dns_requester *requester, struct dns_nameserver *na
update_server_addr(s, firstip, firstip_sin_family, (char *)chk->str);
update_status:
snr_update_srv_status(s);
snr_update_srv_status(s, has_no_ip);
return 1;
invalid:
@ -3936,7 +3941,7 @@ int snr_resolution_cb(struct dns_requester *requester, struct dns_nameserver *na
if (resolution->nb_responses >= nameserver->resolvers->count_nameservers)
goto update_status;
snr_update_srv_status(s);
snr_update_srv_status(s, has_no_ip);
return 0;
}
@ -3964,16 +3969,15 @@ int snr_resolution_error_cb(struct dns_requester *requester, int error_code)
return 1;
}
snr_update_srv_status(s);
snr_update_srv_status(s, 0);
return 1;
}
/*
* Function to check if <ip> is already affected to a server in the backend
* which owns <srv>.
* which owns <srv> and is up.
* It returns a pointer to the first server found or NULL if <ip> is not yet
* assigned.
* NOTE: <ip> and <ip_family> are provided by a 'struct rec' available in dns.c.
*/
struct server *snr_check_ip_callback(struct server *srv, void *ip, unsigned char *ip_family)
{
@ -3998,6 +4002,10 @@ struct server *snr_check_ip_callback(struct server *srv, void *ip, unsigned char
(srv->puid == tmpsrv->puid))
continue;
/* If the server has been taken down, don't consider it */
if (tmpsrv->admin & SRV_ADMF_RMAINT)
continue;
/* At this point, we have 2 different servers using the same DNS hostname
* for their respective resolution.
*/