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>
Fri, 26 Feb 2021 15:54:29 +0000 (16:54 +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

(cherry picked from commit b4badf720ce484001f606011aee7cd216e5ce4e3)
[cf: Changes applied in src/dns.c and structures renamed]
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit 826060e383b34661a91bf3350f6e1137c603e9f5)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit 8c0d185e01060f447bf94949a507a3aaf678a779)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>

include/proto/server.h
src/dns.c
src/server.c

index 83f9b12..eaf4907 100644 (file)
@@ -63,6 +63,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 dns_requester *requester, struct dns_nameserver *nameserver);
+int srvrq_resolution_error_cb(struct dns_requester *requester, int error_code);
 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);
 struct task *srv_cleanup_idle_connections(struct task *task, void *ctx, unsigned short state);
index 97ed87d..ab29178 100644 (file)
--- a/src/dns.c
+++ b/src/dns.c
@@ -1441,7 +1441,7 @@ int dns_link_resolution(void *requester, int requester_type, int requester_locke
                        req = srvrq->dns_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->dns_ctx.dns_requester == NULL) {
index 053e95c..2d27078 100644 (file)
@@ -4167,6 +4167,78 @@ int snr_resolution_cb(struct dns_requester *requester, struct dns_nameserver *na
 }
 
 /*
+ * 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 dns_requester *requester, int error_code)
+{
+       struct server *s;
+       struct dns_srvrq *srvrq;
+       struct dns_resolution *res;
+       struct dns_resolvers *resolvers;
+       int exp;
+
+       /* SRV records */
+       srvrq = objt_dns_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;
+                       dns_unlink_resolution(s->dns_requester);
+               }
+               HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
+       }
+
+       return 1;
+}
+
+/*
  * Server Name Resolution error management callback
  * returns:
  *  0 on error