#include <sys/types.h>
+#include <import/ebistree.h>
+
#include <haproxy/action.h>
#include <haproxy/api.h>
#include <haproxy/cfgparse.h>
proxy_type_str(px), px->id, srv->id);
goto err;
}
+ LIST_INIT(&srvrq->attached_servers);
+ srvrq->named_servers = EB_ROOT;
LIST_ADDQ(&dns_srvrq_list, &srvrq->list);
return srvrq;
}
}
else if (item->type == DNS_RTYPE_SRV) {
- list_for_each_entry(req, &res->requesters, list) {
- if ((srvrq = objt_dns_srvrq(req->owner)) == NULL)
- continue;
-
- /* Remove any associated server */
- for (srv = srvrq->proxy->srv; srv != NULL; srv = srv->next) {
- HA_SPIN_LOCK(SERVER_LOCK, &srv->lock);
- if (srv->srvrq == srvrq && srv->svc_port == item->port &&
- item->data_len == srv->hostname_dn_len &&
- !dns_hostname_cmp(srv->hostname_dn, item->target, item->data_len)) {
- dns_unlink_resolution(srv->dns_requester, 0);
- srvrq_update_srv_status(srv, 1);
- free(srv->hostname);
- free(srv->hostname_dn);
- srv->hostname = NULL;
- srv->hostname_dn = NULL;
- srv->hostname_dn_len = 0;
- memset(&srv->addr, 0, sizeof(srv->addr));
- srv->svc_port = 0;
- }
- HA_SPIN_UNLOCK(SERVER_LOCK, &srv->lock);
- }
+ /* Remove any associated server */
+ list_for_each_entry_safe(srv, srvback, &item->attached_servers, srv_rec_item) {
+ dns_unlink_resolution(srv->dns_requester, 0);
+ HA_SPIN_LOCK(SERVER_LOCK, &srv->lock);
+ srvrq_update_srv_status(srv, 1);
+ free(srv->hostname);
+ free(srv->hostname_dn);
+ srv->hostname = NULL;
+ srv->hostname_dn = NULL;
+ srv->hostname_dn_len = 0;
+ memset(&srv->addr, 0, sizeof(srv->addr));
+ srv->svc_port = 0;
+ srv->flags |= SRV_F_NO_RESOLUTION;
+ HA_SPIN_UNLOCK(SERVER_LOCK, &srv->lock);
+ LIST_DEL(&srv->srv_rec_item);
+ LIST_ADDQ(&srv->srvrq->attached_servers, &srv->srv_rec_item);
}
}
/* Now process SRV records */
list_for_each_entry_safe(req, reqback, &res->requesters, list) {
+ struct ebpt_node *node;
+ char target[DNS_MAX_NAME_SIZE+1];
+
+ int i;
if ((srvrq = objt_dns_srvrq(req->owner)) == NULL)
continue;
- /* Check if a server already uses that hostname */
- for (srv = srvrq->proxy->srv; srv != NULL; srv = srv->next) {
- HA_SPIN_LOCK(SERVER_LOCK, &srv->lock);
- if (srv->srvrq == srvrq && srv->svc_port == item->port &&
- item->data_len == srv->hostname_dn_len &&
- !dns_hostname_cmp(srv->hostname_dn, item->target, item->data_len)) {
- break;
+ /* Check if a server already uses that record */
+ srv = NULL;
+ list_for_each_entry(srv, &item->attached_servers, srv_rec_item) {
+ if (srv->srvrq == srvrq) {
+ HA_SPIN_LOCK(SERVER_LOCK, &srv->lock);
+ goto srv_found;
}
- HA_SPIN_UNLOCK(SERVER_LOCK, &srv->lock);
}
- /* If not, try to find a server with undefined hostname */
- if (!srv) {
- for (srv = srvrq->proxy->srv; srv != NULL; srv = srv->next) {
+
+ /* If not empty we try to match a server
+ * in server state file tree with the same hostname
+ */
+ if (!eb_is_empty(&srvrq->named_servers)) {
+ srv = NULL;
+
+ /* convert the key to lookup in lower case */
+ for (i = 0 ; item->target[i] ; i++)
+ target[i] = tolower(item->target[i]);
+
+ node = ebis_lookup(&srvrq->named_servers, target);
+ if (node) {
+ srv = ebpt_entry(node, struct server, host_dn);
HA_SPIN_LOCK(SERVER_LOCK, &srv->lock);
- if (srv->srvrq == srvrq && !srv->hostname_dn)
- break;
- HA_SPIN_UNLOCK(SERVER_LOCK, &srv->lock);
+
+ /* an entry was found with the same hostname
+ * let check this node if the port matches
+ * and try next node if the hostname
+ * is still the same
+ */
+ while (1) {
+ if (srv->svc_port == item->port) {
+ /* server found, we remove it from tree */
+ ebpt_delete(node);
+ free(srv->host_dn.key);
+ goto srv_found;
+ }
+
+ HA_SPIN_UNLOCK(SERVER_LOCK, &srv->lock);
+
+ node = ebpt_next(node);
+ if (!node)
+ break;
+
+ srv = ebpt_entry(node, struct server, host_dn);
+ HA_SPIN_LOCK(SERVER_LOCK, &srv->lock);
+
+ if ((item->data_len != srv->hostname_dn_len)
+ || dns_hostname_cmp(srv->hostname_dn, item->target, item->data_len)) {
+ HA_SPIN_UNLOCK(SERVER_LOCK, &srv->lock);
+ break;
+ }
+ }
}
}
+ /* Pick the first server listed in srvrq (those ones don't
+ * have hostname and are free to use)
+ */
+ srv = NULL;
+ list_for_each_entry(srv, &srvrq->attached_servers, srv_rec_item) {
+ LIST_DEL_INIT(&srv->srv_rec_item);
+ HA_SPIN_LOCK(SERVER_LOCK, &srv->lock);
+ goto srv_found;
+ }
+ srv = NULL;
+
+srv_found:
/* And update this server, if found (srv is locked here) */
if (srv) {
/* re-enable DNS resolution for this server by default */
send_log(srv->proxy, LOG_NOTICE, "%s", msg);
}
+ if (!LIST_ADDED(&srv->srv_rec_item))
+ LIST_ADDQ(&item->attached_servers, &srv->srv_rec_item);
+
if (!(srv->flags & SRV_F_NO_RESOLUTION)) {
/* If there is no AR item responsible of the FQDN resolution,
* trigger a dedicated DNS resolution
return -1;
}
+/* This function removes all server/srvrq references on answer items
+ * if <safe> is set to 1, in case of srvrq, sub server resquesters unlink
+ * is called using safe == 1 to make it usable into callbacks
+ */
+void dns_detach_from_resolution_answer_items(struct dns_resolution *res, struct dns_requester *req, int safe)
+{
+ struct dns_answer_item *item, *itemback;
+ struct server *srv, *srvback;
+ struct dns_srvrq *srvrq;
+
+ if ((srv = objt_server(req->owner)) != NULL) {
+ LIST_DEL_INIT(&srv->ip_rec_item);
+ }
+ else if ((srvrq = objt_dns_srvrq(req->owner)) != NULL) {
+ list_for_each_entry_safe(item, itemback, &res->response.answer_list, list) {
+ if (item->type == DNS_RTYPE_SRV) {
+ list_for_each_entry_safe(srv, srvback, &item->attached_servers, srv_rec_item) {
+ if (srv->srvrq == srvrq) {
+ HA_SPIN_LOCK(SERVER_LOCK, &srv->lock);
+ dns_unlink_resolution(srv->dns_requester, safe);
+ srvrq_update_srv_status(srv, 1);
+ free(srv->hostname);
+ free(srv->hostname_dn);
+ srv->hostname = NULL;
+ srv->hostname_dn = NULL;
+ srv->hostname_dn_len = 0;
+ memset(&srv->addr, 0, sizeof(srv->addr));
+ srv->svc_port = 0;
+ srv->flags |= SRV_F_NO_RESOLUTION;
+ HA_SPIN_UNLOCK(SERVER_LOCK, &srv->lock);
+ LIST_DEL(&srv->srv_rec_item);
+ LIST_ADDQ(&srvrq->attached_servers, &srv->srv_rec_item);
+ }
+ }
+ }
+ }
+ }
+}
+
+
/* Removes a requester from a DNS resolution. It takes takes care of all the
* consequences. It also cleans up some parameters from the requester.
* if <safe> is set to 1, the corresponding resolution is not released.
{
struct dns_resolution *res;
struct dns_requester *req;
- struct server *srv;
/* Nothing to do */
if (!requester || !requester->resolution)
res = requester->resolution;
/* remove ref from the resolution answer item list to the requester */
- if ((srv = objt_server(requester->owner)) != NULL) {
- LIST_DEL_INIT(&srv->ip_rec_item);
- }
+ dns_detach_from_resolution_answer_items(res, requester, safe);
/* Clean up the requester */
LIST_DEL(&requester->list);
for (i = 0; i < MAX_THREADS; i++)
MT_LIST_INIT(&srv->actconns[i]);
srv->pendconns = EB_ROOT;
+ LIST_INIT(&srv->srv_rec_item);
LIST_INIT(&srv->ip_rec_item);
srv->next_state = SRV_ST_RUNNING; /* early server setup */
goto err;
}
#endif
+ /* append to list of servers available to receive an hostname */
+ LIST_ADDQ(&newsrv->srvrq->attached_servers, &newsrv->srv_rec_item);
+
/* Set this new server ID. */
srv_set_id_from_prefix(newsrv, srv->tmpl_info.prefix, i);
*/
else if (fqdn && !srv->hostname && srvrecord) {
int res;
+ int i;
+ char *tmp;
/* we can't apply previous state if SRV record has changed */
if (srv->srvrq && strcmp(srv->srvrq->name, srvrecord) != 0) {
!(srv->flags & SRV_F_CHECKPORT))
srv->check.port = port;
+ /* Remove from available list and insert in tree
+ * since this server has an hostname
+ */
+ LIST_DEL_INIT(&srv->srv_rec_item);
+ srv->host_dn.key = tmp = strdup(srv->hostname_dn);
+
+ /* convert the key in lowercase because tree
+ * lookup is case sensitive but we don't care
+ */
+ for (i = 0; tmp[i]; i++)
+ tmp[i] = tolower(tmp[i]);
+
+ /* insert in tree */
+ ebis_insert(&srv->srvrq->named_servers, &srv->host_dn);
+
/* Unset SRV_F_MAPPORTS for SRV records.
* SRV_F_MAPPORTS is unfortunately set by parse_server()
* because no ports are provided in the configuration file.
return 1;
if (s->srvrq) {
- struct dns_answer_item *srv_item;
-
- /* If DNS resolution is disabled ignore it. */
- if (s->flags & SRV_F_NO_RESOLUTION)
- return 1;
-
- /* The server is based on a SRV record, thus, find the
- * associated answer record. If not found, it means the SRV item
- * has expired and this resolution must be ignored.
+ /* If DNS resolution is disabled ignore it.
+ * This is the case if the server was associated to
+ * a SRV record and this record is now expired.
*/
- srv_item = find_srvrq_answer_record(requester);
- if (!srv_item)
+ if (s->flags & SRV_F_NO_RESOLUTION)
return 1;
}
*/
int srvrq_resolution_error_cb(struct dns_requester *requester, int error_code)
{
- struct server *s;
struct dns_srvrq *srvrq;
struct dns_resolution *res;
struct dns_resolvers *resolvers;
return 1;
}
- /* Remove any associated server */
- for (s = srvrq->proxy->srv; s != NULL; s = s->next) {
- HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
- if (s->srvrq == srvrq) {
- dns_unlink_resolution(s->dns_requester, 1);
- srvrq_update_srv_status(s, 1);
- free(s->hostname);
- free(s->hostname_dn);
- s->hostname = NULL;
- s->hostname_dn = NULL;
- s->hostname_dn_len = 0;
- memset(&s->addr, 0, sizeof(s->addr));
- s->svc_port = 0;
- }
- HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
- }
+ /* Remove any associated server ref */
+ dns_detach_from_resolution_answer_items(res, requester, 1);
return 0;
}
if (!snr_update_srv_status(s, 1)) {
memset(&s->addr, 0, sizeof(s->addr));
HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
- LIST_DEL_INIT(&s->ip_rec_item);
+ dns_detach_from_resolution_answer_items(requester->resolution, requester, 1);
return 0;
}
HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);