summaryrefslogtreecommitdiff
path: root/src/providers
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 /src/providers
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.
Diffstat (limited to 'src/providers')
-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
4 files changed, 521 insertions, 0 deletions
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__ */