From 6b9a398d9a10c705a423f7d9b0c0cbcf23074c05 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Fri, 29 Mar 2013 15:57:20 +0100
Subject: [PATCH 2/8] resolv: add resolv_discover_srv request to resolv utils

---
 src/resolv/async_resolv.h       |  13 +++
 src/resolv/async_resolv_utils.c | 190 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 203 insertions(+)

diff --git a/src/resolv/async_resolv.h b/src/resolv/async_resolv.h
index cba6ca8d2974cbbbebe3c2f3d44997b4adc48af4..3a02558866a078a0f52774a3fd84f21a0709f3a1 100644
--- a/src/resolv/async_resolv.h
+++ b/src/resolv/async_resolv.h
@@ -170,4 +170,17 @@ errno_t resolv_get_domain_recv(TALLOC_CTX *mem_ctx,
                                struct tevent_req *req,
                                char **_dns_domain);
 
+struct tevent_req *
+resolv_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 resolv_discover_srv_recv(TALLOC_CTX *mem_ctx,
+                                 struct tevent_req *req,
+                                 struct ares_srv_reply **_reply_list,
+                                 char **_dns_domain);
+
 #endif /* __ASYNC_RESOLV_H__ */
diff --git a/src/resolv/async_resolv_utils.c b/src/resolv/async_resolv_utils.c
index cf9711b41495061171492240c09ff8f6f7d74ed3..0a85932cdda5fc4d5eab062dfdf0181abb59bc9e 100644
--- a/src/resolv/async_resolv_utils.c
+++ b/src/resolv/async_resolv_utils.c
@@ -158,3 +158,193 @@ errno_t resolv_get_domain_recv(TALLOC_CTX *mem_ctx,
 
     return EOK;
 }
+
+struct resolv_discover_srv_state {
+    struct tevent_context *ev;
+    struct resolv_ctx *resolv_ctx;
+    char *service;
+    char *protocol;
+    const char **discovery_domains;
+    int domain_index;
+
+    struct ares_srv_reply *reply_list;
+};
+
+static errno_t resolv_discover_srv_next_domain(struct tevent_req *req);
+static void resolv_discover_srv_done(struct tevent_req *subreq);
+
+struct tevent_req *resolv_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 resolv_discover_srv_state *state = NULL;
+    struct tevent_req *req = NULL;
+    errno_t ret;
+
+    req = tevent_req_create(mem_ctx, &state,
+                            struct resolv_discover_srv_state);
+    if (req == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("tevent_req_create() failed\n"));
+        return NULL;
+    }
+
+    if (resolv_ctx == NULL || service == NULL || protocol == NULL) {
+        ret = EINVAL;
+        goto immediately;
+    }
+
+    state->ev = ev;
+    state->resolv_ctx = resolv_ctx;
+    state->discovery_domains = discovery_domains;
+
+    state->service = talloc_strdup(state, service);
+    if (state->service == NULL) {
+        ret = ENOMEM;
+        goto immediately;
+    }
+
+    state->protocol = talloc_strdup(state, protocol);
+    if (state->protocol == NULL) {
+        ret = ENOMEM;
+        goto immediately;
+    }
+
+    state->domain_index = 0;
+    ret = resolv_discover_srv_next_domain(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 errno_t resolv_discover_srv_next_domain(struct tevent_req *req)
+{
+    struct resolv_discover_srv_state *state = NULL;
+    struct tevent_req *subreq = NULL;
+    const char *domain = NULL;
+    char *query = NULL;
+    errno_t ret;
+
+    state = tevent_req_data(req, struct resolv_discover_srv_state);
+
+    domain = state->discovery_domains[state->domain_index];
+    if (domain == NULL) {
+        ret = EOK;
+        goto done;
+    }
+
+    query = talloc_asprintf(state, "_%s._%s.%s", state->service,
+                            state->protocol, domain);
+    if (query == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, ("SRV resolution of service '%s'. Will use DNS "
+          "discovery domain '%s'\n", state->service, domain));
+
+    subreq = resolv_getsrv_send(state, state->ev,
+                                state->resolv_ctx, query);
+    if (subreq == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    tevent_req_set_callback(subreq, resolv_discover_srv_done, req);
+
+    state->domain_index++;
+    ret = EAGAIN;
+
+done:
+    if (ret != EAGAIN) {
+        talloc_free(query);
+    }
+
+    return ret;
+}
+
+static void resolv_discover_srv_done(struct tevent_req *subreq)
+{
+    struct resolv_discover_srv_state *state = NULL;
+    struct tevent_req *req = NULL;
+    int status;
+    errno_t ret;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct resolv_discover_srv_state);
+
+    ret = resolv_getsrv_recv(state, subreq, &status, NULL, &state->reply_list);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("SRV query failed [%d]: %s\n",
+                                  status, resolv_strerror(status)));
+
+        if (status == ARES_ENOTFOUND) {
+            /* continue with next discovery domain */
+            ret = resolv_discover_srv_next_domain(req);
+            if (ret == EOK) {
+                /* there are no more domains to try */
+                ret = ENOENT;
+            }
+
+            goto done;
+        }
+
+        /* critical error when fetching SRV record */
+        ret = EIO;
+        goto done;
+    }
+
+done:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else if (ret != EAGAIN) {
+        tevent_req_error(req, ret);
+    }
+
+    return;
+}
+
+errno_t resolv_discover_srv_recv(TALLOC_CTX *mem_ctx,
+                                 struct tevent_req *req,
+                                 struct ares_srv_reply **_reply_list,
+                                 char **_dns_domain)
+{
+    struct resolv_discover_srv_state *state = NULL;
+    char *domain = NULL;
+
+    state = tevent_req_data(req, struct resolv_discover_srv_state);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    if (_dns_domain != NULL) {
+        /* domain_index now points to selected domain + 1 */
+        domain = talloc_strdup(mem_ctx,
+                           state->discovery_domains[state->domain_index - 1]);
+        if (domain == NULL) {
+            return ENOMEM;
+        }
+
+        *_dns_domain = domain;
+    }
+
+    if (_reply_list != NULL) {
+        *_reply_list = talloc_steal(mem_ctx, state->reply_list);
+    }
+
+    return EOK;
+}
-- 
1.7.11.7

