Line data Source code
1 : /*
2 : Authors:
3 : Pavel Březina <pbrezina@redhat.com>
4 :
5 : Copyright (C) 2013 Red Hat
6 :
7 : This program is free software; you can redistribute it and/or modify
8 : it under the terms of the GNU General Public License as published by
9 : the Free Software Foundation; either version 3 of the License, or
10 : (at your option) any later version.
11 :
12 : This program is distributed in the hope that it will be useful,
13 : but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : GNU General Public License for more details.
16 :
17 : You should have received a copy of the GNU General Public License
18 : along with this program. If not, see <http://www.gnu.org/licenses/>.
19 : */
20 :
21 : #include <string.h>
22 : #include <talloc.h>
23 : #include <tevent.h>
24 : #include <unistd.h>
25 :
26 : #include "util/util.h"
27 : #include "resolv/async_resolv.h"
28 :
29 : struct resolv_get_domain_state {
30 : char *fqdn;
31 : char *hostname;
32 : };
33 :
34 : static void resolv_get_domain_done(struct tevent_req *subreq);
35 :
36 : struct tevent_req *
37 0 : resolv_get_domain_send(TALLOC_CTX *mem_ctx,
38 : struct tevent_context *ev,
39 : struct resolv_ctx *resolv_ctx,
40 : const char *hostname,
41 : enum host_database *host_dbs,
42 : enum restrict_family family_order)
43 : {
44 0 : struct resolv_get_domain_state *state = NULL;
45 0 : struct tevent_req *req = NULL;
46 0 : struct tevent_req *subreq = NULL;
47 : char system_hostname[HOST_NAME_MAX + 1];
48 : errno_t ret;
49 :
50 0 : req = tevent_req_create(mem_ctx, &state,
51 : struct resolv_get_domain_state);
52 0 : if (req == NULL) {
53 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
54 0 : return NULL;
55 : }
56 :
57 0 : if (hostname == NULL) {
58 : /* use system hostname */
59 0 : ret = gethostname(system_hostname, HOST_NAME_MAX);
60 0 : if (ret) {
61 0 : ret = errno;
62 0 : DEBUG(SSSDBG_CRIT_FAILURE, "gethostname() failed: [%d]: %s\n",
63 : ret, strerror(ret));
64 0 : goto immediately;
65 : }
66 0 : system_hostname[HOST_NAME_MAX] = '\0';
67 0 : hostname = system_hostname;
68 : }
69 :
70 0 : state->fqdn = NULL;
71 0 : state->hostname = talloc_strdup(state, hostname);
72 0 : if (state->hostname == NULL) {
73 0 : ret = ENOMEM;
74 0 : goto immediately;
75 : }
76 :
77 0 : DEBUG(SSSDBG_TRACE_LIBS, "Host name is: %s\n", state->hostname);
78 :
79 0 : subreq = resolv_gethostbyname_send(state, ev, resolv_ctx, state->hostname,
80 : family_order, host_dbs);
81 0 : if (subreq == NULL) {
82 0 : talloc_zfree(req);
83 0 : return NULL;
84 : }
85 :
86 0 : tevent_req_set_callback(subreq, resolv_get_domain_done, req);
87 :
88 0 : return req;
89 :
90 : immediately:
91 0 : tevent_req_error(req, ret);
92 0 : tevent_req_post(req, ev);
93 :
94 0 : return req;
95 : }
96 :
97 0 : static void resolv_get_domain_done(struct tevent_req *subreq)
98 : {
99 0 : struct resolv_get_domain_state *state = NULL;
100 0 : struct tevent_req *req = NULL;
101 : struct resolv_hostent *rhostent;
102 : int resolv_status;
103 : errno_t ret;
104 :
105 0 : req = tevent_req_callback_data(subreq, struct tevent_req);
106 0 : state = tevent_req_data(req, struct resolv_get_domain_state);
107 :
108 0 : ret = resolv_gethostbyname_recv(subreq, req, &resolv_status,
109 : NULL, &rhostent);
110 0 : talloc_zfree(subreq);
111 0 : if (ret != EOK) {
112 0 : DEBUG(SSSDBG_OP_FAILURE,
113 : "Could not get fully qualified name for host name %s "
114 : "error [%d]: %s, resolver returned: [%d]: %s\n",
115 : state->hostname, ret, strerror(ret), resolv_status,
116 : resolv_strerror(resolv_status));
117 0 : state->fqdn = state->hostname;
118 : } else {
119 0 : DEBUG(SSSDBG_TRACE_LIBS, "The FQDN is: %s\n", rhostent->name);
120 0 : state->fqdn = talloc_steal(state, rhostent->name);
121 0 : talloc_zfree(rhostent);
122 : }
123 :
124 0 : tevent_req_done(req);
125 0 : }
126 :
127 0 : errno_t resolv_get_domain_recv(TALLOC_CTX *mem_ctx,
128 : struct tevent_req *req,
129 : char **_dns_domain)
130 : {
131 0 : struct resolv_get_domain_state *state = NULL;
132 0 : char *dns_domain = NULL;
133 0 : char *domptr = NULL;
134 :
135 0 : state = tevent_req_data(req, struct resolv_get_domain_state);
136 :
137 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
138 :
139 0 : domptr = strchr(state->fqdn, '.');
140 0 : if (domptr == NULL || (*(domptr+1) == '\0')) {
141 : /* If the FQDN did not contain a dot or the dot was the last character
142 : * (broken DNS server perhaps) */
143 0 : dns_domain = state->fqdn;
144 : } else {
145 0 : dns_domain = domptr + 1;
146 : }
147 :
148 0 : *_dns_domain = talloc_strdup(mem_ctx, dns_domain);
149 0 : if (*_dns_domain == NULL) {
150 0 : return ENOMEM;
151 : }
152 :
153 0 : return EOK;
154 : }
155 :
156 : struct resolv_discover_srv_state {
157 : struct tevent_context *ev;
158 : struct resolv_ctx *resolv_ctx;
159 : const char *service;
160 : const char *protocol;
161 : const char **discovery_domains;
162 : int domain_index;
163 :
164 : struct ares_srv_reply *reply_list;
165 : uint32_t ttl;
166 : };
167 :
168 : static errno_t resolv_discover_srv_next_domain(struct tevent_req *req);
169 : static void resolv_discover_srv_done(struct tevent_req *subreq);
170 :
171 0 : struct tevent_req *resolv_discover_srv_send(TALLOC_CTX *mem_ctx,
172 : struct tevent_context *ev,
173 : struct resolv_ctx *resolv_ctx,
174 : const char *service,
175 : const char *protocol,
176 : const char **discovery_domains)
177 : {
178 0 : struct resolv_discover_srv_state *state = NULL;
179 0 : struct tevent_req *req = NULL;
180 : errno_t ret;
181 :
182 0 : req = tevent_req_create(mem_ctx, &state,
183 : struct resolv_discover_srv_state);
184 0 : if (req == NULL) {
185 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
186 0 : return NULL;
187 : }
188 :
189 0 : if (resolv_ctx == NULL || service == NULL || protocol == NULL
190 0 : || discovery_domains == NULL) {
191 0 : ret = EINVAL;
192 0 : goto immediately;
193 : }
194 :
195 0 : state->ev = ev;
196 0 : state->resolv_ctx = resolv_ctx;
197 0 : state->discovery_domains = discovery_domains;
198 0 : state->service = service;
199 0 : state->protocol = protocol;
200 0 : state->domain_index = 0;
201 :
202 0 : ret = resolv_discover_srv_next_domain(req);
203 0 : if (ret != EAGAIN) {
204 0 : goto immediately;
205 : }
206 :
207 0 : return req;
208 :
209 : immediately:
210 0 : if (ret == EOK) {
211 0 : tevent_req_done(req);
212 : } else {
213 0 : tevent_req_error(req, ret);
214 : }
215 0 : tevent_req_post(req, ev);
216 :
217 0 : return req;
218 : }
219 :
220 0 : static errno_t resolv_discover_srv_next_domain(struct tevent_req *req)
221 : {
222 0 : struct resolv_discover_srv_state *state = NULL;
223 0 : struct tevent_req *subreq = NULL;
224 0 : const char *domain = NULL;
225 0 : char *query = NULL;
226 : errno_t ret;
227 :
228 0 : state = tevent_req_data(req, struct resolv_discover_srv_state);
229 :
230 0 : domain = state->discovery_domains[state->domain_index];
231 0 : if (domain == NULL) {
232 0 : ret = EOK;
233 0 : goto done;
234 : }
235 :
236 0 : query = talloc_asprintf(state, "_%s._%s.%s", state->service,
237 : state->protocol, domain);
238 0 : if (query == NULL) {
239 0 : ret = ENOMEM;
240 0 : goto done;
241 : }
242 :
243 0 : DEBUG(SSSDBG_TRACE_FUNC, "SRV resolution of service '%s'. Will use DNS "
244 : "discovery domain '%s'\n", state->service, domain);
245 :
246 0 : subreq = resolv_getsrv_send(state, state->ev,
247 : state->resolv_ctx, query);
248 0 : if (subreq == NULL) {
249 0 : ret = ENOMEM;
250 0 : goto done;
251 : }
252 :
253 0 : tevent_req_set_callback(subreq, resolv_discover_srv_done, req);
254 :
255 0 : state->domain_index++;
256 0 : ret = EAGAIN;
257 :
258 : done:
259 0 : if (ret != EAGAIN) {
260 0 : talloc_free(query);
261 : }
262 :
263 0 : return ret;
264 : }
265 :
266 0 : static void resolv_discover_srv_done(struct tevent_req *subreq)
267 : {
268 0 : struct resolv_discover_srv_state *state = NULL;
269 0 : struct tevent_req *req = NULL;
270 : int status;
271 : errno_t ret;
272 :
273 0 : req = tevent_req_callback_data(subreq, struct tevent_req);
274 0 : state = tevent_req_data(req, struct resolv_discover_srv_state);
275 :
276 0 : ret = resolv_getsrv_recv(state, subreq, &status, NULL,
277 : &state->reply_list, &state->ttl);
278 0 : talloc_zfree(subreq);
279 0 : if (ret != EOK) {
280 0 : DEBUG(SSSDBG_OP_FAILURE, "SRV query failed [%d]: %s\n",
281 : status, resolv_strerror(status));
282 :
283 0 : if (status == ARES_ENOTFOUND) {
284 : /* continue with next discovery domain */
285 0 : ret = resolv_discover_srv_next_domain(req);
286 0 : if (ret == EOK) {
287 : /* there are no more domains to try */
288 0 : ret = ENOENT;
289 : }
290 :
291 0 : goto done;
292 : }
293 :
294 : /* critical error when fetching SRV record */
295 0 : ret = EIO;
296 0 : goto done;
297 : }
298 :
299 : done:
300 0 : if (ret == EOK) {
301 0 : tevent_req_done(req);
302 0 : } else if (ret != EAGAIN) {
303 0 : tevent_req_error(req, ret);
304 : }
305 :
306 0 : return;
307 : }
308 :
309 0 : errno_t resolv_discover_srv_recv(TALLOC_CTX *mem_ctx,
310 : struct tevent_req *req,
311 : struct ares_srv_reply **_reply_list,
312 : uint32_t *_ttl,
313 : char **_dns_domain)
314 : {
315 0 : struct resolv_discover_srv_state *state = NULL;
316 0 : char *domain = NULL;
317 :
318 0 : state = tevent_req_data(req, struct resolv_discover_srv_state);
319 :
320 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
321 :
322 0 : if (_dns_domain != NULL) {
323 : /* domain_index now points to selected domain + 1 */
324 0 : domain = talloc_strdup(mem_ctx,
325 0 : state->discovery_domains[state->domain_index - 1]);
326 0 : if (domain == NULL) {
327 0 : return ENOMEM;
328 : }
329 :
330 0 : *_dns_domain = domain;
331 : }
332 :
333 0 : if (_reply_list != NULL) {
334 0 : *_reply_list = talloc_steal(mem_ctx, state->reply_list);
335 : }
336 :
337 0 : if (_ttl != NULL) {
338 0 : *_ttl = state->ttl;
339 : }
340 :
341 0 : return EOK;
342 : }
|