#include <common/time.h>
#include <common/ticks.h>
+#include <common/net_helper.h>
#include <import/lru.h>
#include <import/xxhash.h>
inter = objt_server(requester->requester)->check.inter;
resolvers = objt_server(requester->requester)->resolvers;
break;
+ case OBJ_TYPE_SRVRQ:
+ inter = objt_dns_srvrq(requester->requester)->inter;
+ resolvers = objt_dns_srvrq(requester->requester)->resolvers;
+ break;
case OBJ_TYPE_NONE:
default:
return -1;
proxy = objt_server(requester->requester)->proxy;
query_type = requester->prefered_query_type;
break;
+ case OBJ_TYPE_SRVRQ:
+ resolution = objt_dns_srvrq(requester->requester)->resolution;
+ resolvers = objt_dns_srvrq(requester->requester)->resolvers;
+ proxy = objt_dns_srvrq(requester->requester)->proxy;
+ query_type = DNS_RTYPE_SRV;
+ break;
case OBJ_TYPE_NONE:
default:
return -1;
}
+ /*
+ * Avoid sending requests for resolutions that don't yet have
+ * an hostname, ie resolutions linked to servers that do not yet
+ * have an fqdn
+ */
+ if (!resolution->hostname_dn)
+ return 0;
+
/*
* check if a resolution has already been started for this server
* return directly to avoid resolution pill up.
/* process all pending input messages */
while (1) {
+ int removed_reso = 0;
/* read message received */
memset(buf, '\0', DNS_MAX_UDP_MESSAGE + 1);
if ((buflen = recv(fd, (char*)buf , DNS_MAX_UDP_MESSAGE, 0)) < 0) {
break;
}
- /* Check for any obsolete record */
+ /* Check for any obsolete record, also identify any SRV request, and try to find a corresponding server */
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);
+ if (item1->type == DNS_RTYPE_SRV && !LIST_ISEMPTY(&resolution->requester.curr)) {
+ struct dns_srvrq *srvrq;
+
+ requester = LIST_NEXT(&resolution->requester.curr, struct dns_requester *, list);
+
+ srvrq = objt_dns_srvrq(requester->requester);
+ /* We're removing an obsolete entry, remove any associated server */
+ if (srvrq) {
+ struct server *srv;
+
+ for (srv = srvrq->proxy->srv; srv != NULL; srv = srv->next) {
+ if (srv->srvrq == srvrq &&
+ item1->data_len ==
+ srv->hostname_dn_len &&
+ !memcmp(srv->hostname_dn, item1->target, item1->data_len) &&
+ srv->svc_port == item1->port) {
+ snr_update_srv_status(srv, 1);
+ free(srv->hostname);
+ srv->hostname = NULL;
+ srv->hostname_dn_len = 0;
+ free(srv->hostname_dn);
+ srv->hostname_dn = NULL;
+ dns_resolution_free(srv->resolvers, srv->resolution);
+ srv->resolution = dns_resolution_list_get(srv->resolvers, NULL, srv->dns_requester->prefered_query_type);
+ if (resolution == srv->resolution)
+ removed_reso = 1;
+ }
+ }
+ }
+ }
free_dns_answer_item(item1);
+ continue;
+ }
+ if (item1->type == DNS_RTYPE_SRV) {
+ struct server *srv;
+ struct dns_srvrq *srvrq;
+
+ if (LIST_ISEMPTY(&resolution->requester.curr))
+ continue;
+
+ requester = LIST_NEXT(&resolution->requester.curr, struct dns_requester *, list);
+ srvrq = objt_dns_srvrq(requester->requester);
+ if (!srvrq)
+ continue;
+ /* Check if a server already uses that hostname */
+ for (srv = srvrq->proxy->srv; srv != NULL; srv = srv->next) {
+ if (srv->srvrq == srvrq &&
+ item1->data_len == srv->hostname_dn_len &&
+ !memcmp(srv->hostname_dn, item1->target, item1->data_len) &&
+ srv->svc_port == item1->port) {
+ if (srv->uweight != item1->weight) {
+ char weight[9];
+
+ snprintf(weight, sizeof(weight),
+ "%d", item1->weight);
+ server_parse_weight_change_request(srv, weight);
+
+ }
+
+ break;
+ }
+ }
+ /* If not, try to find a server that is down */
+ if (!srv) {
+ for (srv = srvrq->proxy->srv; srv != NULL; srv = srv->next) {
+
+ if (srv->srvrq == srvrq &&
+ !srv->hostname_dn)
+ break;
+ }
+ if (srv) {
+ char weight[9];
+
+ char hostname[DNS_MAX_NAME_SIZE];
+
+ if (item1->data_len > DNS_MAX_NAME_SIZE)
+ continue;
+ dns_dn_label_to_str(item1->target, hostname, item1->data_len);
+ update_server_fqdn(srv, hostname, "SRV record");
+ srv->svc_port = item1->port;
+ srv->flags &= ~SRV_F_MAPPORTS;
+ if ((srv->check.state & CHK_ST_CONFIGURED) && !(srv->flags & SRV_F_CHECKPORT))
+ srv->check.port = item1->port;
+ snprintf(weight, sizeof(weight),
+ "%d", item1->weight);
+ server_parse_weight_change_request(srv, weight);
+ }
+
+ }
}
}
+ if (removed_reso)
+ goto next_packet;
/* some error codes trigger a re-send of the query, but switching the
* query type.
* We can check only the first query of the list. We send one query at a time
* so we get one query in the response */
query = LIST_NEXT(&resolution->response.query_list, struct dns_query_item *, list);
+ if (!resolution->hostname_dn)
+ abort();
if (query && memcmp(query->name, resolution->hostname_dn, resolution->hostname_dn_len) != 0) {
nameserver->counters.other += 1;
/* now parse list of requesters currently waiting for this resolution */
case OBJ_TYPE_SERVER:
resolvers = objt_server(requester->requester)->resolvers;
break;
+ case OBJ_TYPE_SRVRQ:
+ resolvers = objt_dns_srvrq(requester->requester)->resolvers;
+ break;
case OBJ_TYPE_NONE:
default:
return 0;
case OBJ_TYPE_SERVER:
valid_period = objt_server(requester->requester)->check.inter;
break;
+ case OBJ_TYPE_SRVRQ:
+ valid_period = objt_dns_srvrq(requester->requester)->inter;
+ break;
case OBJ_TYPE_NONE:
default:
continue;
case OBJ_TYPE_SERVER:
dns_trigger_resolution(objt_server(requester->requester)->resolution);
break;
+ case OBJ_TYPE_SRVRQ:
+ dns_trigger_resolution(objt_dns_srvrq(requester->requester)->resolution);
+ break;
case OBJ_TYPE_NONE:
default:
;;
struct dns_answer_item *dns_answer_record, *tmp_record;
struct dns_response_packet *dns_p;
int found = 0;
+ int i;
reader = resp;
len = 0;
/* now parsing response records */
nb_saved_records = 0;
- for (int i = 0; i < dns_p->header.ancount; i++) {
+ for (i = 0; i < dns_p->header.ancount; i++) {
if (reader >= bufend)
return DNS_RESP_INVALID;
break;
+
+ case DNS_RTYPE_SRV:
+ /*
+ * Answer must contain :
+ * - 2 bytes for the priority
+ * - 2 bytes for the weight
+ * - 2 bytes for the port
+ * - the target hostname
+ */
+ if (dns_answer_record->data_len <= 6) {
+ free_dns_answer_item(dns_answer_record);
+ return DNS_RESP_INVALID;
+ }
+ dns_answer_record->priority = readn16(reader);
+ reader += sizeof(uint16_t);
+ dns_answer_record->weight = readn16(reader);
+ reader += sizeof(uint16_t);
+ dns_answer_record->port = readn16(reader);
+ reader += sizeof(uint16_t);
+ offset = 0;
+ len = dns_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE, &offset);
+ if (len == 0) {
+ free_dns_answer_item(dns_answer_record);
+ return DNS_RESP_INVALID;
+ }
+ reader++;
+ dns_answer_record->data_len = len;
+ memcpy(dns_answer_record->target, tmpname, len);
+ dns_answer_record->target[len] = 0;
+ break;
case DNS_RTYPE_AAAA:
/* ipv6 is stored on 16 bytes */
if (dns_answer_record->data_len != 16) {
/* Lookup to see if we already had this entry */
+ found = 0;
list_for_each_entry(tmp_record, &dns_p->answer_list, list) {
if (tmp_record->type != dns_answer_record->type)
continue;
&((struct sockaddr_in6 *)&tmp_record->address)->sin6_addr, sizeof(struct in6_addr)))
found = 1;
break;
+ case DNS_RTYPE_SRV:
+ if (dns_answer_record->data_len == tmp_record->data_len &&
+ !memcmp(dns_answer_record->target,
+ tmp_record->target, dns_answer_record->data_len) &&
+ dns_answer_record->port == tmp_record->port) {
+ tmp_record->weight = dns_answer_record->weight;
+ found = 1;
+ }
+ break;
default:
break;
}
return ptr - buf;
}
+/* Turn a domain name label into a string */
+void dns_dn_label_to_str(char *dn, char *str, int dn_len)
+{
+ int remain_size = 0;
+ int i;
+
+ for (i = 0; i < dn_len; i++) {
+ if (remain_size == 0) {
+ remain_size = dn[i];
+ if (i != 0) {
+ str[i - 1] = '.';
+
+ }
+ } else {
+ str[i - 1] = dn[i];
+ remain_size--;
+ }
+ }
+ str[dn_len - 1] = 0;
+
+}
+
/*
* turn a string into domain name label:
* www.haproxy.org into 3www7haproxy3org
switch (obj_type(requester->requester)) {
case OBJ_TYPE_SERVER:
dns_opts = &(objt_server(requester->requester)->dns_opts);
+ res_preferred_afinet = dns_opts->family_prio == AF_INET && resolution->query_type == DNS_RTYPE_A;
+ res_preferred_afinet6 = dns_opts->family_prio == AF_INET6 && resolution->query_type == DNS_RTYPE_AAAA;
+
+ /* let's change the query type if needed */
+ if (res_preferred_afinet6) {
+ /* fallback from AAAA to A */
+ resolution->query_type = DNS_RTYPE_A;
+ }
+ else if (res_preferred_afinet) {
+ /* fallback from A to AAAA */
+ resolution->query_type = DNS_RTYPE_AAAA;
+ }
+
break;
+ case OBJ_TYPE_SRVRQ:
+ break;
case OBJ_TYPE_NONE:
default:
/* clean up resolution information and remove from the list */
goto out;
}
- res_preferred_afinet = dns_opts->family_prio == AF_INET && resolution->query_type == DNS_RTYPE_A;
- res_preferred_afinet6 = dns_opts->family_prio == AF_INET6 && resolution->query_type == DNS_RTYPE_AAAA;
-
- /* let's change the query type if needed */
- if (res_preferred_afinet6) {
- /* fallback from AAAA to A */
- resolution->query_type = DNS_RTYPE_A;
- }
- else if (res_preferred_afinet) {
- /* fallback from A to AAAA */
- resolution->query_type = DNS_RTYPE_AAAA;
- }
-
/* resend the DNS query */
dns_send_query(resolution);
case OBJ_TYPE_SERVER:
resolvers = objt_server(requester->requester)->resolvers;
break;
+ case OBJ_TYPE_SRVRQ:
+ resolvers = objt_dns_srvrq(requester->requester)->resolvers;
+ break;
case OBJ_TYPE_NONE:
default:
return NULL;
char *hostname_dn = NULL;
int new_resolution;
+
if (!resolution) {
tmprequester = calloc(1, sizeof(*tmprequester));
if (!tmprequester)
tmprequester->prefered_query_type = DNS_RTYPE_AAAA;
}
+ break;
+ case OBJ_TYPE_SRVRQ:
+ tmprequester->requester = &((struct dns_srvrq *)requester)->obj_type;
+ hostname_dn = objt_dns_srvrq(requester)->hostname_dn;
+ resolvers = objt_dns_srvrq(requester)->resolvers;
break;
case OBJ_TYPE_NONE:
default:
tmprequester = ((struct server *)requester)->dns_requester;
resolvers = ((struct server *)requester)->resolvers;
break;
+ case OBJ_TYPE_SRVRQ:
+ tmprequester = objt_dns_srvrq(requester)->dns_requester;
+ resolvers = objt_dns_srvrq(requester)->resolvers;
+ break;
case OBJ_TYPE_NONE:
default:
return -1;
objt_server(tmprequester->requester)->dns_requester = tmprequester;
}
break;
+ case OBJ_TYPE_SRVRQ:
+ /* some parameters should be set only if the resolution is brand new */
+ if (new_resolution) {
+ tmpresolution->query_type = DNS_RTYPE_SRV;
+ tmpresolution->hostname_dn = objt_dns_srvrq(tmprequester->requester)->hostname_dn;
+ tmpresolution->hostname_dn_len = objt_dns_srvrq(tmprequester->requester)->hostname_dn_len;
+ }
+
+ /* update requester as well, only if we just allocated it */
+ objt_dns_srvrq(tmprequester->requester)->resolution = tmpresolution;
+ if (!resolution) {
+ tmprequester->requester_cb = snr_resolution_cb;
+ tmprequester->requester_error_cb = snr_resolution_error_cb;
+ objt_dns_srvrq(tmprequester->requester)->dns_requester = tmprequester;
+ }
+ break;
+
case OBJ_TYPE_NONE:
default:
free(tmprequester);
struct dns_resolution *resolution, *tmpresolution;
struct dns_requester *requester;
- /* search for same hostname and query type in resolution.curr */
- list_for_each_entry_safe(resolution, tmpresolution, &resolvers->resolution.curr, list) {
- requester = NULL;
+ if (hostname_dn) {
+ /* search for same hostname and query type in resolution.curr */
+ list_for_each_entry_safe(resolution, tmpresolution, &resolvers->resolution.curr, list) {
+ requester = NULL;
- if (!LIST_ISEMPTY(&resolution->requester.wait))
- requester = LIST_NEXT(&resolution->requester.wait, struct dns_requester *, list);
- else if (!LIST_ISEMPTY(&resolution->requester.curr))
- requester = LIST_NEXT(&resolution->requester.curr, struct dns_requester *, list);
+ if (!LIST_ISEMPTY(&resolution->requester.wait))
+ requester = LIST_NEXT(&resolution->requester.wait, struct dns_requester *, list);
+ else if (!LIST_ISEMPTY(&resolution->requester.curr))
+ requester = LIST_NEXT(&resolution->requester.curr, struct dns_requester *, list);
- if (!requester)
- continue;
+ if (!requester)
+ continue;
- if ((query_type == requester->prefered_query_type) &&
- (strcmp(hostname_dn, resolution->hostname_dn) == 0)) {
- return resolution;
+ if ((query_type == requester->prefered_query_type) &&
+ (resolution->hostname_dn &&
+ strcmp(hostname_dn, resolution->hostname_dn) == 0)) {
+ return resolution;
+ }
}
- }
- /* search for same hostname and query type in resolution.wait */
- list_for_each_entry_safe(resolution, tmpresolution, &resolvers->resolution.wait, list) {
- requester = NULL;
+ /* search for same hostname and query type in resolution.wait */
+ list_for_each_entry_safe(resolution, tmpresolution, &resolvers->resolution.wait, list) {
+ requester = NULL;
- if (!LIST_ISEMPTY(&resolution->requester.wait))
- requester = LIST_NEXT(&resolution->requester.wait, struct dns_requester *, list);
- else if (!LIST_ISEMPTY(&resolution->requester.curr))
- requester = LIST_NEXT(&resolution->requester.curr, struct dns_requester *, list);
+ if (!LIST_ISEMPTY(&resolution->requester.wait))
+ requester = LIST_NEXT(&resolution->requester.wait, struct dns_requester *, list);
+ else if (!LIST_ISEMPTY(&resolution->requester.curr))
+ requester = LIST_NEXT(&resolution->requester.curr, struct dns_requester *, list);
- if (!requester)
- continue;
+ if (!requester)
+ continue;
- if ((query_type == requester->prefered_query_type) &&
- (strcmp(hostname_dn, resolution->hostname_dn) == 0)) {
- return resolution;
+ if ((query_type == requester->prefered_query_type) &&
+ (resolution->hostname_dn &&
+ strcmp(hostname_dn, resolution->hostname_dn) == 0)) {
+ return resolution;
+ }
}
}
-
/* take the first one (hopefully) from the pool */
list_for_each_entry_safe(resolution, tmpresolution, &resolvers->resolution.pool, list) {
if (LIST_ISEMPTY(&resolution->requester.wait)) {
case OBJ_TYPE_SERVER:
hostname_dn = objt_server(requester->requester)->hostname_dn;
break;
+ case OBJ_TYPE_SRVRQ:
+ hostname_dn = objt_dns_srvrq(requester->requester)->hostname_dn;
+ break;
case OBJ_TYPE_NONE:
default:
hostname_dn = NULL;
resolution->hostname_dn = objt_server(tmprequester->requester)->hostname_dn;
resolution->hostname_dn_len = objt_server(tmprequester->requester)->hostname_dn_len;
break;
+ case OBJ_TYPE_SRVRQ:
+ resolution->hostname_dn = objt_dns_srvrq(tmprequester->requester)->hostname_dn;
+ resolution->hostname_dn_len = objt_dns_srvrq(tmprequester->requester)->hostname_dn_len;
+ break;
+
case OBJ_TYPE_NONE:
default:
;;
case OBJ_TYPE_SERVER:
objt_server(requester->requester)->resolution = NULL;
break;
+ case OBJ_TYPE_SRVRQ:
+ objt_dns_srvrq(requester->requester)->resolution = NULL;
+ break;
case OBJ_TYPE_NONE:
default:
;;
#ifdef TCP_USER_TIMEOUT
srv->tcp_ut = src->tcp_ut;
#endif
+ if (srv_tmpl)
+ srv->srvrq = src->srvrq;
}
static struct server *new_server(struct proxy *proxy)
/* save hostname and create associated name resolution */
if (fqdn) {
- if (srv_prepare_for_resolution(newsrv, fqdn) == -1) {
- Alert("parsing [%s:%d] : Can't create DNS resolution for server '%s'\n",
- file, linenum, newsrv->id);
- err_code |= ERR_ALERT | ERR_FATAL;
- goto out;
+ if (fqdn[0] == '_') {
+ struct dns_srvrq *srvrq = NULL;
+ int found = 0;
+ /* SRV record */
+ /* Check if a SRV request already exists, and if not, create it */
+ list_for_each_entry(srvrq, &curproxy->srvrq_list, list) {
+ if (!strcmp(srvrq->name, fqdn)) {
+ found = 1;
+ break;
+ }
+ }
+ if (found == 0) {
+ int hostname_dn_len;
+
+ srvrq = calloc(1, sizeof(*srvrq));
+ if (!srvrq) {
+ Alert("Failed to allocate memory");
+ err_code = ERR_ALERT | ERR_FATAL;
+ goto out;
+ }
+ srvrq->obj_type = OBJ_TYPE_SRVRQ;
+ srvrq->proxy = proxy;
+ srvrq->name = strdup(fqdn);
+ srvrq->inter = 2000;
+ hostname_dn_len = dns_str_to_dn_label_len(fqdn);
+ if (hostname_dn_len == -1) {
+ Alert("Failed to parse domaine name '%s'", fqdn);
+ err_code = ERR_ALERT | ERR_FATAL;
+ goto out;
+ }
+ srvrq->hostname_dn = malloc(hostname_dn_len + 1);
+ srvrq->hostname_dn_len = hostname_dn_len;
+ if (!srvrq->hostname_dn) {
+ Alert("Failed to alloc memory");
+ err_code = ERR_ALERT | ERR_FATAL;
+ goto out;
+ }
+ if (!dns_str_to_dn_label(fqdn,
+ srvrq->hostname_dn,
+ hostname_dn_len + 1)) {
+ Alert("Failed to parse domain name '%s'", fqdn);
+ err_code = ERR_ALERT | ERR_FATAL;
+ goto out;
+ }
+ LIST_ADDQ(&proxy->srvrq_list, &srvrq->list);
+
+ }
+ newsrv->srvrq = srvrq;
+
+
+ } else if (srv_prepare_for_resolution(newsrv, fqdn) == -1) {
+ Alert("parsing [%s:%d] : Can't create DNS resolution for server '%s'\n",
+ file, linenum, newsrv->id);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto out;
}
}
newsrv->addr = *sk;
newsrv->svc_port = port;
- if (!newsrv->hostname && !protocol_by_family(newsrv->addr.ss_family)) {
+ if (!newsrv->srvrq && !newsrv->hostname && !protocol_by_family(newsrv->addr.ss_family)) {
Alert("parsing [%s:%d] : Unknown protocol family %d '%s'\n",
file, linenum, newsrv->addr.ss_family, args[cur_arg]);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
newsrv->check.inter = val;
+ if (newsrv->srvrq)
+ newsrv->srvrq->inter = val;
cur_arg += 2;
}
else if (!strcmp(args[cur_arg], "fastinter")) {
{
struct dns_resolution *resolution;
int hostname_dn_len;
+ int did_set_reso = 0;
/* run time DNS resolution was not active for this server
* and we can't enable it at run time for now.
return -1;
- /* get a resolution from the curr or wait queues, or a brand new one from the pool */
- resolution = dns_resolution_list_get(srv->resolvers, trash.str, srv->dns_requester->prefered_query_type);
- if (!resolution)
- return -1;
+ if (srv->resolution->hostname_dn) {
+ /* get a resolution from the curr or wait queues, or a brand new one from the pool */
+ resolution = dns_resolution_list_get(srv->resolvers, trash.str, srv->dns_requester->prefered_query_type);
+ if (!resolution)
+ return -1;
- /* in this case, the new hostanme is the same than the old one */
- if (srv->resolution == resolution)
- return 0;
+ /* in this case, the new hostanme is the same than the old one */
+ if (srv->resolution == resolution && srv->hostname)
+ return 0;
- /* first, we need to unlink our server from its current resolution */
- srv_free_from_resolution(srv);
+ /* first, we need to unlink our server from its current resolution */
+ srv_free_from_resolution(srv);
+ } else {
+ resolution = srv->resolution;
+ resolution->last_resolution = now_ms;
+ did_set_reso = 1;
+ }
/* now we update server's parameters */
free(srv->hostname);
srv->hostname_dn_len = hostname_dn_len;
if (!srv->hostname || !srv->hostname_dn)
return -1;
+ if (did_set_reso) {
+ resolution->query_type = srv->dns_requester->prefered_query_type;
+ resolution->hostname_dn = srv->hostname_dn;
+ resolution->hostname_dn_len = hostname_dn_len;
+ }
/* then we can link srv to its new resolution */
dns_link_resolution(srv, OBJ_TYPE_SERVER, resolution);
msg = get_trash_chunk();
chunk_reset(msg);
- if (!strcmp(fqdn, server->hostname)) {
+ if (server->hostname && !strcmp(fqdn, server->hostname)) {
chunk_appendf(msg, "no need to change the FDQN");
goto out;
}