summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPavel Březina <pbrezina@redhat.com>2013-03-13 15:13:46 +0100
committerJakub Hrozek <jhrozek@redhat.com>2013-04-10 15:36:55 +0200
commit77d165f0629966db65753a3aee84a8b4971673af (patch)
tree19e9d6b94426dec048b02d56663da2a981516195
parentf9961e5f82e0ef474d6492371bfdf9e74e208a99 (diff)
downloadsssd-77d165f0629966db65753a3aee84a8b4971673af.tar.gz
sssd-77d165f0629966db65753a3aee84a8b4971673af.tar.bz2
sssd-77d165f0629966db65753a3aee84a8b4971673af.zip
DNS sites support - SRV DNS lookup plugin
https://fedorahosted.org/sssd/ticket/1032 This plugin mimics the current behaviour. If discovery_domain is set it is the only domain that is tried. If discovery_domain is not set, we try to autodetect domain first and if that fails or SRV lookup on this domain fails, we fallback to SSSD domain name.
-rw-r--r--Makefile.am1
-rw-r--r--src/providers/data_provider_fo.c34
-rw-r--r--src/providers/dp_backend.h3
-rw-r--r--src/providers/fail_over_srv.c444
-rw-r--r--src/providers/fail_over_srv.h40
5 files changed, 522 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am
index 4e331098..7a352333 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -325,6 +325,7 @@ endif
SSSD_FAILOVER_OBJ = \
src/providers/fail_over.c \
+ src/providers/fail_over_srv.c \
$(SSSD_RESOLV_OBJ)
SSSD_LIBS = \
diff --git a/src/providers/data_provider_fo.c b/src/providers/data_provider_fo.c
index 232717e7..83294b59 100644
--- a/src/providers/data_provider_fo.c
+++ b/src/providers/data_provider_fo.c
@@ -254,6 +254,40 @@ void be_fo_set_srv_lookup_plugin(struct be_ctx *ctx,
}
}
+errno_t be_fo_set_dns_srv_lookup_plugin(struct be_ctx *be_ctx,
+ const char *hostname)
+{
+ struct fo_resolve_srv_dns_ctx *srv_ctx = NULL;
+ char resolved_hostname[HOST_NAME_MAX];
+ errno_t ret;
+
+ if (hostname == NULL) {
+ ret = gethostname(resolved_hostname, HOST_NAME_MAX);
+ if (ret != EOK) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ ("gethostname() failed: [%d]: %s\n", ret, strerror(ret)));
+ return ret;
+ }
+ resolved_hostname[HOST_NAME_MAX-1] = '\0';
+ hostname = resolved_hostname;
+ }
+
+ srv_ctx = fo_resolve_srv_dns_ctx_init(be_ctx, be_ctx->be_res->resolv,
+ be_ctx->be_res->family_order,
+ default_host_dbs, hostname,
+ be_ctx->domain->name);
+ if (srv_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, ("Out of memory?\n"));
+ return ENOMEM;
+ }
+
+ be_fo_set_srv_lookup_plugin(be_ctx, fo_resolve_srv_dns_send,
+ fo_resolve_srv_dns_recv, srv_ctx, "DNS");
+
+ return EOK;
+}
+
int be_fo_add_srv_server(struct be_ctx *ctx,
const char *service_name,
const char *query_service,
diff --git a/src/providers/dp_backend.h b/src/providers/dp_backend.h
index 1b8a59e6..e0e2210c 100644
--- a/src/providers/dp_backend.h
+++ b/src/providers/dp_backend.h
@@ -218,6 +218,9 @@ void be_fo_set_srv_lookup_plugin(struct be_ctx *ctx,
void *pvt,
const char *plugin_name);
+errno_t be_fo_set_dns_srv_lookup_plugin(struct be_ctx *be_ctx,
+ const char *hostname);
+
int be_fo_add_srv_server(struct be_ctx *ctx,
const char *service_name,
const char *query_service,
diff --git a/src/providers/fail_over_srv.c b/src/providers/fail_over_srv.c
new file mode 100644
index 00000000..aa0bccb2
--- /dev/null
+++ b/src/providers/fail_over_srv.c
@@ -0,0 +1,444 @@
+/*
+ Authors:
+ Pavel B??ezina <pbrezina@redhat.com>
+
+ Copyright (C) 2013 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <string.h>
+#include <talloc.h>
+#include <tevent.h>
+
+#include "util/util.h"
+#include "resolv/async_resolv.h"
+#include "providers/fail_over_srv.h"
+
+struct fo_discover_srv_state {
+ char *dns_domain;
+ struct fo_server_info *servers;
+ size_t num_servers;
+};
+
+static void fo_discover_srv_done(struct tevent_req *subreq);
+
+struct tevent_req *fo_discover_srv_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resolv_ctx *resolv_ctx,
+ const char *service,
+ const char *protocol,
+ const char **discovery_domains)
+{
+ struct fo_discover_srv_state *state = NULL;
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct fo_discover_srv_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, ("tevent_req_create() failed\n"));
+ return NULL;
+ }
+
+ subreq = resolv_discover_srv_send(state, ev, resolv_ctx, service,
+ protocol, discovery_domains);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediately;
+ }
+
+ tevent_req_set_callback(subreq, fo_discover_srv_done, req);
+
+ return req;
+
+immediately:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+
+ return req;
+}
+
+static void fo_discover_srv_done(struct tevent_req *subreq)
+{
+ struct fo_discover_srv_state *state = NULL;
+ struct tevent_req *req = NULL;
+ struct ares_srv_reply *reply_list = NULL;
+ struct ares_srv_reply *record = NULL;
+ int i;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct fo_discover_srv_state);
+
+ ret = resolv_discover_srv_recv(state, subreq,
+ &reply_list, &state->dns_domain);
+ talloc_zfree(subreq);
+ if (ret == ENOENT) {
+ ret = ERR_SRV_NOT_FOUND;
+ goto done;
+ } else if (ret == EIO) {
+ ret = ERR_SRV_LOOKUP_ERROR;
+ goto done;
+ } else if (ret != EOK) {
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, ("Got answer. Processing...\n"));
+
+ /* sort and store the answer */
+ ret = resolv_sort_srv_reply(state, &reply_list);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, ("Could not sort the answers from DNS "
+ "[%d]: %s\n", ret, strerror(ret)));
+ goto done;
+ }
+
+ state->num_servers = 0;
+ for (record = reply_list; record != NULL; record = record->next) {
+ state->num_servers++;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, ("Got %lu servers\n", state->num_servers));
+
+ state->servers = talloc_array(state, struct fo_server_info,
+ state->num_servers);
+ if (state->servers == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (record = reply_list, i = 0;
+ record != NULL;
+ record = record->next, i++) {
+ state->servers[i].host = talloc_steal(state->servers, record->host);
+ state->servers[i].port = record->port;
+ }
+
+ talloc_zfree(reply_list);
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+errno_t fo_discover_srv_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ char **_dns_domain,
+ struct fo_server_info **_servers,
+ size_t *_num_servers)
+{
+ struct fo_discover_srv_state *state = NULL;
+ state = tevent_req_data(req, struct fo_discover_srv_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ if (_dns_domain != NULL) {
+ *_dns_domain = talloc_steal(mem_ctx, state->dns_domain);
+ }
+
+ if (_servers != NULL) {
+ *_servers = talloc_steal(mem_ctx, state->servers);
+ }
+
+ if (_num_servers != NULL) {
+ *_num_servers = state->num_servers;
+ }
+
+ return EOK;
+}
+
+struct fo_resolve_srv_dns_ctx {
+ struct resolv_ctx *resolv_ctx;
+ enum restrict_family family_order;
+ enum host_database *host_dbs;
+ char *hostname;
+ char *sssd_domain;
+ char *detected_domain;
+};
+
+struct fo_resolve_srv_dns_state {
+ struct tevent_context *ev;
+ struct fo_resolve_srv_dns_ctx *ctx;
+ const char *service;
+ const char *protocol;
+ const char *discovery_domain;
+
+ char *dns_domain;
+ struct fo_server_info *servers;
+ size_t num_servers;
+};
+
+static void fo_resolve_srv_dns_domain_done(struct tevent_req *subreq);
+static errno_t fo_resolve_srv_dns_discover(struct tevent_req *req);
+static void fo_resolve_srv_dns_done(struct tevent_req *subreq);
+
+struct fo_resolve_srv_dns_ctx *
+fo_resolve_srv_dns_ctx_init(TALLOC_CTX *mem_ctx,
+ struct resolv_ctx *resolv_ctx,
+ enum restrict_family family_order,
+ enum host_database *host_dbs,
+ const char *hostname,
+ const char *sssd_domain)
+{
+ struct fo_resolve_srv_dns_ctx *ctx = NULL;
+
+ ctx = talloc_zero(mem_ctx, struct fo_resolve_srv_dns_ctx);
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ ctx->resolv_ctx = resolv_ctx;
+ ctx->family_order = family_order;
+ ctx->host_dbs = host_dbs;
+
+ ctx->hostname = talloc_strdup(ctx, hostname);
+ if (ctx->hostname == NULL) {
+ goto fail;
+ }
+
+ ctx->sssd_domain = talloc_strdup(ctx, sssd_domain);
+ if (ctx->sssd_domain == NULL) {
+ goto fail;
+ }
+
+ return ctx;
+
+fail:
+ talloc_free(ctx);
+ return NULL;
+}
+
+struct tevent_req *fo_resolve_srv_dns_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *service,
+ const char *protocol,
+ const char *discovery_domain,
+ void *pvt)
+{
+ struct fo_resolve_srv_dns_state *state = NULL;
+ struct fo_resolve_srv_dns_ctx *ctx = NULL;
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct fo_resolve_srv_dns_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, ("tevent_req_create() failed\n"));
+ return NULL;
+ }
+
+ ctx = talloc_get_type(pvt, struct fo_resolve_srv_dns_ctx);
+ if (ctx == NULL) {
+ ret = EINVAL;
+ goto immediately;
+ }
+
+ state->ev = ev;
+ state->ctx = ctx;
+ state->service = service;
+ state->protocol = protocol;
+
+ if (discovery_domain == NULL) {
+ state->discovery_domain = NULL;
+ } else {
+ state->discovery_domain = discovery_domain;
+ }
+
+ if (discovery_domain == NULL && ctx->detected_domain == NULL) {
+ /* we will try to detect proper discovery domain */
+ subreq = resolv_get_domain_send(state, state->ev, ctx->resolv_ctx,
+ ctx->hostname, ctx->host_dbs,
+ ctx->family_order);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediately;
+ }
+
+ tevent_req_set_callback(subreq, fo_resolve_srv_dns_domain_done, req);
+ } else {
+ /* we will use either provided or previously detected
+ * discovery domain */
+ ret = fo_resolve_srv_dns_discover(req);
+ if (ret != EAGAIN) {
+ goto immediately;
+ }
+ }
+
+ return req;
+
+immediately:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+
+ return req;
+}
+
+static void fo_resolve_srv_dns_domain_done(struct tevent_req *subreq)
+{
+ struct fo_resolve_srv_dns_state *state = NULL;
+ struct tevent_req *req = NULL;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct fo_resolve_srv_dns_state);
+
+ ret = resolv_get_domain_recv(state->ctx, subreq,
+ &state->ctx->detected_domain);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = fo_resolve_srv_dns_discover(req);
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ }
+
+ return;
+}
+
+static errno_t fo_resolve_srv_dns_discover(struct tevent_req *req)
+{
+ struct fo_resolve_srv_dns_state *state = NULL;
+ struct fo_resolve_srv_dns_ctx *ctx = NULL;
+ struct tevent_req *subreq = NULL;
+ const char **domains = NULL;
+ errno_t ret;
+
+ state = tevent_req_data(req, struct fo_resolve_srv_dns_state);
+ ctx = state->ctx;
+
+ domains = talloc_zero_array(state, const char *, 3);
+ if (domains == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (state->discovery_domain == NULL) {
+ /* we will use detected domain with SSSD domain as fallback */
+ domains[0] = talloc_strdup(domains, ctx->detected_domain);
+ if (domains[0] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (strcmp(ctx->detected_domain, ctx->sssd_domain) != 0) {
+ domains[1] = talloc_strdup(domains, ctx->sssd_domain);
+ if (domains[1] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ } else {
+ /* We will use only discovery domain that was provided via plugin
+ * interface. We don't have to dup here because it is already on
+ * state. */
+ domains[0] = state->discovery_domain;
+ }
+
+ subreq = fo_discover_srv_send(state, state->ev, ctx->resolv_ctx,
+ state->service, state->protocol, domains);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, fo_resolve_srv_dns_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ talloc_free(domains);
+ }
+
+ return ret;
+}
+
+static void fo_resolve_srv_dns_done(struct tevent_req *subreq)
+{
+ struct fo_resolve_srv_dns_state *state = NULL;
+ struct tevent_req *req = NULL;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct fo_resolve_srv_dns_state);
+
+ ret = fo_discover_srv_recv(state, subreq, &state->dns_domain,
+ &state->servers, &state->num_servers);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ goto done;
+ }
+
+done:
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+errno_t fo_resolve_srv_dns_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ char **_dns_domain,
+ struct fo_server_info **_primary_servers,
+ size_t *_num_primary_servers,
+ struct fo_server_info **_backup_servers,
+ size_t *_num_backup_servers)
+{
+ struct fo_resolve_srv_dns_state *state = NULL;
+ state = tevent_req_data(req, struct fo_resolve_srv_dns_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ if (_primary_servers) {
+ *_primary_servers = talloc_steal(mem_ctx, state->servers);
+ }
+
+ if (_num_primary_servers) {
+ *_num_primary_servers = state->num_servers;
+ }
+
+ /* backup servers are not supported by simple srv lookup */
+ if (_backup_servers) {
+ *_backup_servers = NULL;
+ }
+
+ if (_num_backup_servers) {
+ *_num_backup_servers = 0;
+ }
+
+ if (_dns_domain) {
+ *_dns_domain = talloc_steal(mem_ctx, state->dns_domain);
+ }
+
+ return EOK;
+}
diff --git a/src/providers/fail_over_srv.h b/src/providers/fail_over_srv.h
index 0796ad5a..4550b09d 100644
--- a/src/providers/fail_over_srv.h
+++ b/src/providers/fail_over_srv.h
@@ -69,4 +69,44 @@ typedef errno_t
struct fo_server_info **_backup_servers,
size_t *_num_backup_servers);
+struct tevent_req *fo_discover_srv_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resolv_ctx *resolv_ctx,
+ const char *service,
+ const char *protocol,
+ const char **discovery_domains);
+
+errno_t fo_discover_srv_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ char **_dns_domain,
+ struct fo_server_info **_servers,
+ size_t *_num_servers);
+
+/* Simple SRV lookup plugin */
+
+struct fo_resolve_srv_dns_ctx;
+
+struct fo_resolve_srv_dns_ctx *
+fo_resolve_srv_dns_ctx_init(TALLOC_CTX *mem_ctx,
+ struct resolv_ctx *resolv_ctx,
+ enum restrict_family family_order,
+ enum host_database *host_dbs,
+ const char *hostname,
+ const char *sssd_domain);
+
+struct tevent_req *fo_resolve_srv_dns_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *service,
+ const char *protocol,
+ const char *discovery_domain,
+ void *pvt);
+
+errno_t fo_resolve_srv_dns_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ char **_dns_domain,
+ struct fo_server_info **_primary_servers,
+ size_t *_num_primary_servers,
+ struct fo_server_info **_backup_servers,
+ size_t *_num_backup_servers);
+
#endif /* __FAIL_OVER_SRV_H__ */