]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MINOR: resolvers: new callback to properly handle SRV record errors
authorBaptiste Assmann <bedis9@gmail.com>
Thu, 19 Nov 2020 21:38:33 +0000 (22:38 +0100)
committerChristopher Faulet <cfaulet@haproxy.com>
Wed, 24 Feb 2021 20:58:45 +0000 (21:58 +0100)
When a SRV record was created, it used to register the regular server name
resolution callbacks. That said, SRV records and regular server name
resolution don't work the same way, furthermore on error management.

This patch introduces a new call back to manage DNS errors related to
the SRV queries.

this fixes github issue #50.

Backport status: 2.3, 2.2, 2.1, 2.0

include/haproxy/server.h
src/resolvers.c
src/server.c

index 4d41e2771615895a0f904fd1f81570169ab72778..a38c4144e8241dc4072f42a81451e4c709f8b6ac 100644 (file)
@@ -61,6 +61,7 @@ struct server *new_server(struct proxy *proxy);
 int snr_update_srv_status(struct server *s, int has_no_ip);
 const char *update_server_fqdn(struct server *server, const char *fqdn, const char *updater, int dns_locked);
 int snr_resolution_cb(struct resolv_requester *requester, struct dns_counters *counters);
+int srvrq_resolution_error_cb(struct resolv_requester *requester, int error_code);
 int snr_resolution_error_cb(struct resolv_requester *requester, int error_code);
 struct server *snr_check_ip_callback(struct server *srv, void *ip, unsigned char *ip_family);
 struct task *srv_cleanup_idle_connections(struct task *task, void *ctx, unsigned short state);
index a64896eb251b258a63677d87c237a8351437733b..a10600b06ec62151194a769f7c236d858d4c9ff2 100644 (file)
@@ -1739,7 +1739,7 @@ int resolv_link_resolution(void *requester, int requester_type, int requester_lo
                        req = srvrq->requester;
 
                req->requester_cb       = snr_resolution_cb;
-               req->requester_error_cb = snr_resolution_error_cb;
+               req->requester_error_cb = srvrq_resolution_error_cb;
        }
        else if (stream) {
                if (stream->resolv_ctx.requester == NULL) {
index 732643cbc4e15a122c6bd432b7229bf76466c868..2582e92a5d89bf0ad46df7a033ef62df723fe985 100644 (file)
@@ -4071,6 +4071,78 @@ int snr_resolution_cb(struct resolv_requester *requester, struct dns_counters *c
        return 0;
 }
 
+/*
+ * SRV record error management callback
+ * returns:
+ *  0 on error
+ *  1 when no error or safe ignore
+ *
+ * Grabs the server's lock.
+ */
+int srvrq_resolution_error_cb(struct resolv_requester *requester, int error_code)
+{
+       struct server *s;
+       struct resolv_srvrq *srvrq;
+       struct resolv_resolution *res;
+       struct resolvers *resolvers;
+       int exp;
+
+       /* SRV records */
+       srvrq = objt_resolv_srvrq(requester->owner);
+       if (!srvrq)
+               return 1;
+
+       resolvers = srvrq->resolvers;
+       res = requester->resolution;
+
+       switch (res->status) {
+
+               case RSLV_STATUS_NX:
+                       /* stop server if resolution is NX for a long enough period */
+                       exp = tick_add(res->last_valid, resolvers->hold.nx);
+                       if (!tick_is_expired(exp, now_ms))
+                               return 1;
+                       break;
+
+               case RSLV_STATUS_TIMEOUT:
+                       /* stop server if resolution is TIMEOUT for a long enough period */
+                       exp = tick_add(res->last_valid, resolvers->hold.timeout);
+                       if (!tick_is_expired(exp, now_ms))
+                               return 1;
+                       break;
+
+               case RSLV_STATUS_REFUSED:
+                       /* stop server if resolution is REFUSED for a long enough period */
+                       exp = tick_add(res->last_valid, resolvers->hold.refused);
+                       if (!tick_is_expired(exp, now_ms))
+                               return 1;
+                       break;
+
+               default:
+                       /* stop server if resolution failed for a long enough period */
+                       exp = tick_add(res->last_valid, resolvers->hold.other);
+                       if (!tick_is_expired(exp, now_ms))
+                               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) {
+                       snr_update_srv_status(s, 1);
+                       free(s->hostname);
+                       free(s->hostname_dn);
+                       s->hostname        = NULL;
+                       s->hostname_dn     = NULL;
+                       s->hostname_dn_len = 0;
+                       resolv_unlink_resolution(s->resolv_requester);
+               }
+               HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
+       }
+
+       return 1;
+}
+
 /*
  * Server Name Resolution error management callback
  * returns: