diff --git a/include/haproxy/server.h b/include/haproxy/server.h index 4d41e2771..a38c4144e 100644 --- a/include/haproxy/server.h +++ b/include/haproxy/server.h @@ -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); diff --git a/src/resolvers.c b/src/resolvers.c index a64896eb2..a10600b06 100644 --- a/src/resolvers.c +++ b/src/resolvers.c @@ -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) { diff --git a/src/server.c b/src/server.c index 732643cbc..2582e92a5 100644 --- a/src/server.c +++ b/src/server.c @@ -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: