diff options
-rw-r--r-- | src/resolv/async_resolv.h | 13 | ||||
-rw-r--r-- | src/resolv/async_resolv_utils.c | 181 |
2 files changed, 194 insertions, 0 deletions
diff --git a/src/resolv/async_resolv.h b/src/resolv/async_resolv.h index cba6ca8d..3a025588 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 9f55e01b..0cb373ca 100644 --- a/src/resolv/async_resolv_utils.c +++ b/src/resolv/async_resolv_utils.c @@ -152,3 +152,184 @@ 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; + const char *service; + const 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 + || discovery_domains == NULL) { + ret = EINVAL; + goto immediately; + } + + state->ev = ev; + state->resolv_ctx = resolv_ctx; + state->discovery_domains = discovery_domains; + state->service = service; + state->protocol = protocol; + 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; +} |