From b41f69f10acfeb2ec4179b519e663cd4b7c960a7 Mon Sep 17 00:00:00 2001 From: Pavel Březina Date: Fri, 2 Aug 2013 23:01:57 +0200 Subject: ad srv: prefer servers that are in the same domain as client https://fedorahosted.org/sssd/ticket/2001 --- src/providers/ad/ad_srv.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/src/providers/ad/ad_srv.c b/src/providers/ad/ad_srv.c index dfb15b30..01660c48 100644 --- a/src/providers/ad/ad_srv.c +++ b/src/providers/ad/ad_srv.c @@ -26,6 +26,7 @@ #include "util/util.h" #include "util/sss_ldap.h" +#include "util/slinklist.h" #include "resolv/async_resolv.h" #include "providers/dp_backend.h" #include "providers/fail_over.h" @@ -548,6 +549,75 @@ static void ad_srv_plugin_dcs_done(struct tevent_req *subreq); static void ad_srv_plugin_site_done(struct tevent_req *subreq); static void ad_srv_plugin_servers_done(struct tevent_req *subreq); +static errno_t ad_sort_servers(struct ares_srv_reply **list, void *pvt) +{ + struct ad_srv_plugin_state *state = NULL; + struct ares_srv_reply *final_list = NULL; + struct ares_srv_reply *inside_list = NULL; + struct ares_srv_reply *outside_list = NULL; + struct ares_srv_reply *cur = NULL; + struct ares_srv_reply *next = NULL; + const char *domain = NULL; + errno_t ret; + + if (list == NULL) { + ret = EINVAL; + goto done; + } + + if (*list == NULL || (*list)->next == NULL) { + ret = EOK; + goto done; + } + + state = talloc_get_type(pvt, struct ad_srv_plugin_state); + domain = state->discovery_domain; + + /* first sort by rfc2782 */ + ret = resolv_sort_srv_reply(list); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, ("resolv_sort_srv_reply() failed " + "[%d]: %s\n", ret, strerror(ret))); + goto done; + } + + /* when several servers share priority, prefer the one that is located + * in the same domain as client (e.g. child domain instead of forest + * root) but obey their weight */ + + next = *list; + do { + cur = next; + next = cur->next; + + /* split servers into two lists - inside and outside of the preferred + * domain */ + if (is_host_in_domain(cur->host, domain)) { + SLIST_ADD_END(inside_list, cur, struct ares_srv_reply); + } else { + SLIST_ADD_END(outside_list, cur, struct ares_srv_reply); + } + cur->next = NULL; + + if (next == NULL || cur->priority != next->priority) { + /* priority has changed or we have reached the end of the srv list, + * we will merge the list into final list and start over with + * next priority */ + SLIST_CONCATENATE(final_list, inside_list, struct ares_srv_reply); + SLIST_CONCATENATE(final_list, outside_list, struct ares_srv_reply); + inside_list = NULL; + outside_list = NULL; + } + } while (next != NULL); + + *list = final_list; + + ret = EOK; + +done: + return ret; +} + /* 1. Do a DNS lookup to find any DC in domain * _ldap._tcp.domain.name * 2. Send a CLDAP ping to the found DC to get the desirable site @@ -720,7 +790,7 @@ static void ad_srv_plugin_site_done(struct tevent_req *subreq) state->ctx->be_res->resolv, state->service, state->protocol, primary_domain, backup_domain, - NULL, NULL); + ad_sort_servers, state); if (subreq == NULL) { ret = ENOMEM; goto done; -- cgit