MAJOR: server: add DNS-based server name resolution
Relies on the DNS protocol freshly implemented in HAProxy.
It performs a server IP addr resolution based on a server hostname.
diff --git a/src/checks.c b/src/checks.c
index 8d8c31c..2179d4f 100644
--- a/src/checks.c
+++ b/src/checks.c
@@ -38,6 +38,7 @@
#include <types/global.h>
#include <types/mailers.h>
+#include <types/dns.h>
#ifdef USE_OPENSSL
#include <types/ssl_sock.h>
@@ -59,6 +60,9 @@
#include <proto/server.h>
#include <proto/stream_interface.h>
#include <proto/task.h>
+#include <proto/log.h>
+#include <proto/dns.h>
+#include <proto/proto_udp.h>
static int httpchk_expect(struct server *s, int done);
static int tcpcheck_get_step_id(struct check *);
@@ -680,6 +684,14 @@
set_server_check_status(check, HCHK_STATUS_L4CON, err_msg);
else if (expired)
set_server_check_status(check, HCHK_STATUS_L4TOUT, err_msg);
+
+ /*
+ * might be due to a server IP change.
+ * Let's trigger a DNS resolution if none are currently running.
+ */
+ if ((check->server->resolution) && (check->server->resolution->step == RSLV_STEP_NONE))
+ trigger_resolution(check->server);
+
}
else if ((conn->flags & (CO_FL_CONNECTED|CO_FL_WAIT_L6_CONN)) == CO_FL_WAIT_L6_CONN) {
/* L6 not established (yet) */
@@ -2132,10 +2144,93 @@
static struct task *process_chk(struct task *t)
{
struct check *check = t->context;
+ struct server *s = check->server;
+ struct dns_resolution *resolution = s->resolution;
+
+ /* trigger name resolution */
+ if ((s->check.state & CHK_ST_ENABLED) && (resolution)) {
+ /* check if a no resolution is running for this server */
+ if (resolution->step == RSLV_STEP_NONE) {
+ /*
+ * if there has not been any name resolution for a longer period than
+ * hold.valid, let's trigger a new one.
+ */
+ if (tick_is_expired(tick_add(resolution->last_resolution, resolution->resolvers->hold.valid), now_ms)) {
+ trigger_resolution(s);
+ }
+ }
+ }
if (check->type == PR_O2_EXT_CHK)
return process_chk_proc(t);
return process_chk_conn(t);
+
+}
+
+/*
+ * Initiates a new name resolution:
+ * - generates a query id
+ * - configure the resolution structure
+ * - startup the resolvers task if required
+ *
+ * returns:
+ * - 0 in case of error or if resolution already running
+ * - 1 if everything started properly
+ */
+int trigger_resolution(struct server *s)
+{
+ struct dns_resolution *resolution;
+ struct dns_resolvers *resolvers;
+ int query_id;
+ int i;
+
+ resolution = s->resolution;
+ resolvers = resolution->resolvers;
+
+ /*
+ * check if a resolution has already been started for this server
+ * return directly to avoid resolution pill up
+ */
+ if (resolution->step != RSLV_STEP_NONE)
+ return 0;
+
+ /* generates a query id */
+ i = 0;
+ do {
+ query_id = dns_rnd16();
+ /* we do try only 100 times to find a free query id */
+ if (i++ > 100) {
+ chunk_printf(&trash, "could not generate a query id for %s/%s, in resolvers %s",
+ s->proxy->id, s->id, resolvers->id);
+
+ send_log(s->proxy, LOG_NOTICE, "%s.\n", trash.str);
+ return 0;
+ }
+ } while (eb32_lookup(&resolvers->query_ids, query_id));
+
+ LIST_ADDQ(&resolvers->curr_resolution, &resolution->list);
+
+ /* now update resolution parameters */
+ resolution->query_id = query_id;
+ resolution->qid.key = query_id;
+ resolution->step = RSLV_STEP_RUNNING;
+ resolution->query_type = DNS_RTYPE_ANY;
+ resolution->try = 0;
+ resolution->try_cname = 0;
+ resolution->nb_responses = 0;
+ resolution->resolver_family_priority = s->resolver_family_priority;
+ eb32_insert(&resolvers->query_ids, &resolution->qid);
+
+ dns_send_query(resolution);
+
+ /* update wakeup date if this resolution is the only one in the FIFO list */
+ if (dns_check_resolution_queue(resolvers) == 1) {
+ /* update task timeout */
+ dns_update_resolvers_timeout(resolvers);
+ task_queue(resolvers->t);
+ }
+
+ return 1;
}
static int start_check_task(struct check *check, int mininter,