Branch data Line data Source code
1 : : /*
2 : : SSSD
3 : :
4 : : Fail over helper functions.
5 : :
6 : : Authors:
7 : : Martin Nagy <mnagy@redhat.com>
8 : : Jakub Hrozek <jhrozek@redhat.com>
9 : :
10 : : Copyright (C) Red Hat, Inc 2010
11 : :
12 : : This program is free software; you can redistribute it and/or modify
13 : : it under the terms of the GNU General Public License as published by
14 : : the Free Software Foundation; either version 3 of the License, or
15 : : (at your option) any later version.
16 : :
17 : : This program is distributed in the hope that it will be useful,
18 : : but WITHOUT ANY WARRANTY; without even the implied warranty of
19 : : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 : : GNU General Public License for more details.
21 : :
22 : : You should have received a copy of the GNU General Public License
23 : : along with this program. If not, see <http://www.gnu.org/licenses/>.
24 : : */
25 : :
26 : : #include <sys/time.h>
27 : :
28 : : #include <errno.h>
29 : : #include <stdbool.h>
30 : : #include <strings.h>
31 : : #include <talloc.h>
32 : :
33 : : #include "util/dlinklist.h"
34 : : #include "util/refcount.h"
35 : : #include "util/util.h"
36 : : #include "providers/fail_over.h"
37 : : #include "resolv/async_resolv.h"
38 : :
39 : : #define STATUS_DIFF(p, now) ((now).tv_sec - (p)->last_status_change.tv_sec)
40 : : #define SERVER_NAME(s) ((s)->common ? (s)->common->name : "(no name)")
41 : :
42 : : #define DEFAULT_PORT_STATUS PORT_NEUTRAL
43 : : #define DEFAULT_SERVER_STATUS SERVER_NAME_NOT_RESOLVED
44 : : #define DEFAULT_SRV_STATUS SRV_NEUTRAL
45 : :
46 : : enum srv_lookup_status {
47 : : SRV_NEUTRAL, /* We didn't try this SRV lookup yet */
48 : : SRV_RESOLVED, /* This SRV lookup is resolved */
49 : : SRV_RESOLVE_ERROR, /* Could not resolve this SRV lookup */
50 : : SRV_EXPIRED /* Need to refresh the SRV query */
51 : : };
52 : :
53 : : struct fo_ctx {
54 : : struct fo_service *service_list;
55 : : struct server_common *server_common_list;
56 : :
57 : : struct fo_options *opts;
58 : : };
59 : :
60 : : struct fo_service {
61 : : struct fo_service *prev;
62 : : struct fo_service *next;
63 : :
64 : : struct fo_ctx *ctx;
65 : : char *name;
66 : : struct fo_server *active_server;
67 : : struct fo_server *last_tried_server;
68 : : struct fo_server *server_list;
69 : :
70 : : /* Function pointed by user_data_cmp returns 0 if user_data is equal
71 : : * or nonzero value if not. Set to NULL if no user data comparison
72 : : * is needed in fail over duplicate servers detection.
73 : : */
74 : : datacmp_fn user_data_cmp;
75 : : };
76 : :
77 : : struct fo_server {
78 : : struct fo_server *prev;
79 : : struct fo_server *next;
80 : :
81 : : bool primary;
82 : : void *user_data;
83 : : int port;
84 : : enum port_status port_status;
85 : : struct srv_data *srv_data;
86 : : struct fo_service *service;
87 : : struct timeval last_status_change;
88 : : struct server_common *common;
89 : : };
90 : :
91 : : struct server_common {
92 : : REFCOUNT_COMMON;
93 : :
94 : : struct fo_ctx *ctx;
95 : :
96 : : struct server_common *prev;
97 : : struct server_common *next;
98 : :
99 : : char *name;
100 : : struct resolv_hostent *rhostent;
101 : : struct resolve_service_request *request_list;
102 : : enum server_status server_status;
103 : : struct timeval last_status_change;
104 : : };
105 : :
106 : : struct srv_data {
107 : : char *dns_domain;
108 : : char *discovery_domain;
109 : : char *sssd_domain;
110 : : char *proto;
111 : : char *srv;
112 : :
113 : : struct fo_server *meta;
114 : :
115 : : int srv_lookup_status;
116 : : struct timeval last_status_change;
117 : : };
118 : :
119 : : struct resolve_service_request {
120 : : struct resolve_service_request *prev;
121 : : struct resolve_service_request *next;
122 : :
123 : : struct server_common *server_common;
124 : : struct tevent_req *req;
125 : : };
126 : :
127 : : struct status {
128 : : int value;
129 : : struct timeval last_change;
130 : : };
131 : :
132 : : struct fo_ctx *
133 : 2 : fo_context_init(TALLOC_CTX *mem_ctx, struct fo_options *opts)
134 : : {
135 : : struct fo_ctx *ctx;
136 : :
137 : 2 : ctx = talloc_zero(mem_ctx, struct fo_ctx);
138 [ - + ]: 2 : if (ctx == NULL) {
139 [ # # ][ # # ]: 0 : DEBUG(1, ("No memory\n"));
[ # # ][ # # ]
[ # # ]
140 : : return NULL;
141 : : }
142 : 2 : ctx->opts = talloc_zero(ctx, struct fo_options);
143 [ - + ]: 2 : if (ctx->opts == NULL) {
144 [ # # ][ # # ]: 0 : DEBUG(1, ("No memory\n"));
[ # # ][ # # ]
[ # # ]
145 : : return NULL;
146 : : }
147 : :
148 : 2 : ctx->opts->srv_retry_timeout = opts->srv_retry_timeout;
149 : 2 : ctx->opts->retry_timeout = opts->retry_timeout;
150 : 2 : ctx->opts->family_order = opts->family_order;
151 : 2 : ctx->opts->service_resolv_timeout = opts->service_resolv_timeout;
152 : :
153 [ + - ][ + - ]: 2 : DEBUG(SSSDBG_TRACE_FUNC, ("Created new fail over context, retry timeout is %d\n",
[ - + ][ # # ]
[ # # ]
154 : : ctx->opts->retry_timeout));
155 : 2 : return ctx;
156 : : }
157 : :
158 : : static const char *
159 : : str_port_status(enum port_status status)
160 : : {
161 : : switch (status) {
162 : : case PORT_NEUTRAL:
163 : : return "neutral";
164 : : case PORT_WORKING:
165 : : return "working";
166 : : case PORT_NOT_WORKING:
167 : : return "not working";
168 : : }
169 : :
170 : : return "unknown port status";
171 : : }
172 : :
173 : : static const char *
174 : : str_srv_data_status(enum srv_lookup_status status)
175 : : {
176 : : switch (status) {
177 : : case SRV_NEUTRAL:
178 : : return "neutral";
179 : : case SRV_RESOLVED:
180 : : return "resolved";
181 : : case SRV_RESOLVE_ERROR:
182 : : return "not resolved";
183 : : case SRV_EXPIRED:
184 : : return "expired";
185 : : }
186 : :
187 : : return "unknown SRV lookup status";
188 : : }
189 : :
190 : : static const char *
191 : : str_server_status(enum server_status status)
192 : : {
193 : : switch (status) {
194 : : case SERVER_NAME_NOT_RESOLVED:
195 : : return "name not resolved";
196 : : case SERVER_RESOLVING_NAME:
197 : : return "resolving name";
198 : : case SERVER_NAME_RESOLVED:
199 : : return "name resolved";
200 : : case SERVER_WORKING:
201 : : return "working";
202 : : case SERVER_NOT_WORKING:
203 : : return "not working";
204 : : }
205 : :
206 : : return "unknown server status";
207 : : }
208 : :
209 : 0 : int fo_is_srv_lookup(struct fo_server *s)
210 : : {
211 [ # # ][ # # ]: 7 : return s && s->srv_data;
[ + - ][ + - ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
212 : : }
213 : :
214 : : static char *
215 : 0 : get_srv_query(TALLOC_CTX *mem_ctx, struct fo_server *server)
216 : : {
217 : : char *query;
218 : :
219 [ # # ]: 0 : if (!fo_is_srv_lookup(server)) {
220 : : return NULL;
221 : : }
222 : :
223 : 0 : query = talloc_asprintf(mem_ctx, "_%s._%s.%s", server->srv_data->srv,
224 : : server->srv_data->proto,
225 : 0 : server->srv_data->dns_domain);
226 : 0 : return query;
227 : : }
228 : :
229 : : static struct fo_server *
230 : 0 : collapse_srv_lookup(struct fo_server *server)
231 : : {
232 : : struct fo_server *tmp, *meta;
233 : :
234 : 0 : meta = server->srv_data->meta;
235 [ # # ][ # # ]: 0 : DEBUG(4, ("Need to refresh SRV lookup for domain %s\n",
[ # # ][ # # ]
[ # # ]
236 : : meta->srv_data->dns_domain));
237 : :
238 [ # # ]: 0 : if (server != meta) {
239 [ # # ][ # # ]: 0 : while (server->prev && server->prev->srv_data == meta->srv_data) {
240 : 0 : tmp = server->prev;
241 [ # # ][ # # ]: 0 : DLIST_REMOVE(server->service->server_list, tmp);
[ # # ][ # # ]
[ # # ]
242 : 0 : talloc_zfree(tmp);
243 : : }
244 [ # # ][ # # ]: 0 : while (server->next && server->next->srv_data == meta->srv_data) {
245 : 0 : tmp = server->next;
246 [ # # ][ # # ]: 0 : DLIST_REMOVE(server->service->server_list, tmp);
[ # # ][ # # ]
[ # # ]
247 : 0 : talloc_zfree(tmp);
248 : : }
249 : :
250 [ # # ]: 0 : if (server == server->service->active_server) {
251 : 0 : server->service->active_server = NULL;
252 : : }
253 [ # # ]: 0 : if (server == server->service->last_tried_server) {
254 : 0 : server->service->last_tried_server = meta;
255 : : }
256 : :
257 : : /* add back the meta server to denote SRV lookup */
258 [ # # ][ # # ]: 0 : DLIST_ADD_AFTER(server->service->server_list, meta, server);
[ # # ][ # # ]
259 [ # # ][ # # ]: 0 : DLIST_REMOVE(server->service->server_list, server);
[ # # ][ # # ]
[ # # ]
260 : 0 : talloc_zfree(server);
261 : : }
262 : :
263 : 0 : meta->srv_data->srv_lookup_status = SRV_NEUTRAL;
264 : 0 : meta->srv_data->last_status_change.tv_sec = 0;
265 : :
266 : 0 : return meta;
267 : : }
268 : :
269 : : static enum srv_lookup_status
270 : 0 : get_srv_data_status(struct srv_data *data)
271 : : {
272 : : struct timeval tv;
273 : : time_t timeout;
274 : :
275 : 0 : timeout = data->meta->service->ctx->opts->srv_retry_timeout;
276 : 0 : gettimeofday(&tv, NULL);
277 : :
278 [ # # ][ # # ]: 0 : if (timeout && STATUS_DIFF(data, tv) > timeout) {
279 [ # # # # ]: 0 : switch(data->srv_lookup_status) {
280 : : case SRV_EXPIRED:
281 : : case SRV_NEUTRAL:
282 : : break;
283 : : case SRV_RESOLVED:
284 : 0 : data->srv_lookup_status = SRV_EXPIRED;
285 : 0 : data->last_status_change.tv_sec = 0;
286 : 0 : break;
287 : : case SRV_RESOLVE_ERROR:
288 : 0 : data->srv_lookup_status = SRV_NEUTRAL;
289 : 0 : data->last_status_change.tv_sec = 0;
290 : 0 : break;
291 : : default:
292 [ # # ][ # # ]: 0 : DEBUG(1, ("Unknown state for SRV server!\n"));
[ # # ][ # # ]
[ # # ]
293 : : }
294 : : }
295 : :
296 : 0 : return data->srv_lookup_status;
297 : : }
298 : :
299 : : static void
300 : 0 : set_srv_data_status(struct srv_data *data, enum srv_lookup_status status)
301 : : {
302 [ # # ][ # # ]: 0 : DEBUG(4, ("Marking SRV lookup of service '%s' as '%s'\n",
[ # # ][ # # ]
[ # # ][ # # ]
303 : : data->meta->service->name, str_srv_data_status(status)));
304 : :
305 : 0 : gettimeofday(&data->last_status_change, NULL);
306 : 0 : data->srv_lookup_status = status;
307 : 0 : }
308 : :
309 : : /*
310 : : * This function will return the status of the server. If the status was
311 : : * last updated a long time ago, we will first reset the status.
312 : : */
313 : : static enum server_status
314 : 23 : get_server_status(struct fo_server *server)
315 : : {
316 : : struct timeval tv;
317 : : time_t timeout;
318 : :
319 [ + + ]: 23 : if (server->common == NULL)
320 : : return SERVER_NAME_RESOLVED;
321 : :
322 [ + - ][ + - ]: 21 : DEBUG(7, ("Status of server '%s' is '%s'\n", SERVER_NAME(server),
[ - + ][ # # ]
[ # # ][ # # ]
[ # # ]
323 : : str_server_status(server->common->server_status)));
324 : :
325 : 21 : timeout = server->service->ctx->opts->retry_timeout;
326 : 21 : gettimeofday(&tv, NULL);
327 [ + - ][ + + ]: 21 : if (timeout != 0 && server->common->server_status == SERVER_NOT_WORKING) {
328 [ - + ]: 7 : if (STATUS_DIFF(server->common, tv) > timeout) {
329 [ # # ][ # # ]: 0 : DEBUG(4, ("Reseting the server status of '%s'\n",
[ # # ][ # # ]
[ # # ][ # # ]
330 : : SERVER_NAME(server)));
331 : 0 : server->common->server_status = SERVER_NAME_NOT_RESOLVED;
332 : 0 : server->common->last_status_change.tv_sec = tv.tv_sec;
333 : : }
334 : : }
335 : :
336 [ + + ][ - + ]: 21 : if (server->common->rhostent && STATUS_DIFF(server->common, tv) >
337 : 17 : server->common->rhostent->addr_list[0]->ttl) {
338 [ # # ][ # # ]: 0 : DEBUG(4, ("Hostname resolution expired, resetting the server "
[ # # ][ # # ]
[ # # ][ # # ]
339 : : "status of '%s'\n", SERVER_NAME(server)));
340 : 0 : fo_set_server_status(server, SERVER_NAME_NOT_RESOLVED);
341 : : }
342 : :
343 : 23 : return server->common->server_status;
344 : : }
345 : :
346 : : /*
347 : : * This function will return the status of the service. If the status was
348 : : * last updated a long time ago, we will first reset the status.
349 : : */
350 : : static enum port_status
351 : 9 : get_port_status(struct fo_server *server)
352 : : {
353 : : struct timeval tv;
354 : : time_t timeout;
355 : :
356 [ + - ][ + - ]: 9 : DEBUG(7, ("Port status of port %d for server '%s' is '%s'\n", server->port,
[ - + ][ # # ]
[ # # ][ # # ]
[ # # ]
357 : : SERVER_NAME(server), str_port_status(server->port_status)));
358 : :
359 : 9 : timeout = server->service->ctx->opts->retry_timeout;
360 [ + - ][ + + ]: 9 : if (timeout != 0 && server->port_status == PORT_NOT_WORKING) {
361 : 1 : gettimeofday(&tv, NULL);
362 [ - + ]: 1 : if (STATUS_DIFF(server, tv) > timeout) {
363 [ # # ][ # # ]: 0 : DEBUG(4, ("Reseting the status of port %d for server '%s'\n",
[ # # ][ # # ]
[ # # ][ # # ]
364 : : server->port, SERVER_NAME(server)));
365 : 0 : server->port_status = PORT_NEUTRAL;
366 : 0 : server->last_status_change.tv_sec = tv.tv_sec;
367 : : }
368 : : }
369 : :
370 : 9 : return server->port_status;
371 : : }
372 : :
373 : : static int
374 : 16 : server_works(struct fo_server *server)
375 : : {
376 [ + + ]: 16 : if (get_server_status(server) == SERVER_NOT_WORKING)
377 : : return 0;
378 : :
379 : 16 : return 1;
380 : : }
381 : :
382 : : static int
383 : 16 : service_works(struct fo_server *server)
384 : : {
385 [ + + ]: 16 : if (!server_works(server))
386 : : return 0;
387 [ + + ]: 9 : if (get_port_status(server) == PORT_NOT_WORKING)
388 : : return 0;
389 : :
390 : 16 : return 1;
391 : : }
392 : :
393 : : static int
394 : 13 : service_destructor(struct fo_service *service)
395 : : {
396 [ + - ][ + + ]: 13 : DLIST_REMOVE(service->ctx->service_list, service);
[ # # ][ # # ]
[ + - ]
397 : 13 : return 0;
398 : : }
399 : :
400 : : int
401 : 14 : fo_new_service(struct fo_ctx *ctx, const char *name,
402 : : datacmp_fn user_data_cmp,
403 : : struct fo_service **_service)
404 : : {
405 : : struct fo_service *service;
406 : : int ret;
407 : :
408 [ + - ][ + - ]: 14 : DEBUG(SSSDBG_TRACE_FUNC, ("Creating new service '%s'\n", name));
[ - + ][ # # ]
[ # # ]
409 : 14 : ret = fo_get_service(ctx, name, &service);
410 [ + + ]: 14 : if (ret == EOK) {
411 [ + - ][ + - ]: 1 : DEBUG(5, ("Service '%s' already exists\n", name));
[ - + ][ # # ]
[ # # ]
412 [ + - ]: 1 : if (_service) {
413 : 1 : *_service = service;
414 : : }
415 : : return EEXIST;
416 [ + - ]: 13 : } else if (ret != ENOENT) {
417 : : return ret;
418 : : }
419 : :
420 : 13 : service = talloc_zero(ctx, struct fo_service);
421 [ + - ]: 13 : if (service == NULL)
422 : : return ENOMEM;
423 : :
424 : 13 : service->name = talloc_strdup(service, name);
425 [ - + ]: 13 : if (service->name == NULL) {
426 : 0 : talloc_free(service);
427 : : return ENOMEM;
428 : : }
429 : :
430 : 13 : service->user_data_cmp = user_data_cmp;
431 : :
432 : 13 : service->ctx = ctx;
433 [ + + ]: 13 : DLIST_ADD(ctx->service_list, service);
434 : :
435 : 13 : talloc_set_destructor(service, service_destructor);
436 [ + - ]: 13 : if (_service) {
437 : 14 : *_service = service;
438 : : }
439 : :
440 : : return EOK;
441 : : }
442 : :
443 : : int
444 : 34 : fo_get_service(struct fo_ctx *ctx, const char *name,
445 : : struct fo_service **_service)
446 : : {
447 : : struct fo_service *service;
448 : :
449 [ + + ]: 144 : DLIST_FOR_EACH(service, ctx->service_list) {
450 [ + + ]: 110 : if (!strcmp(name, service->name)) {
451 : 11 : *_service = service;
452 : 11 : return EOK;
453 : : }
454 : : }
455 : :
456 : : return ENOENT;
457 : : }
458 : :
459 : : static int
460 : 4 : get_server_common(TALLOC_CTX *mem_ctx, struct fo_ctx *ctx, const char *name,
461 : : struct server_common **_common)
462 : : {
463 : : struct server_common *common;
464 : :
465 [ + + ]: 8 : DLIST_FOR_EACH(common, ctx->server_common_list) {
466 [ + + ]: 4 : if (!strcasecmp(name, common->name)) {
467 : 2 : *_common = rc_reference(mem_ctx, struct server_common, common);
468 [ + - ]: 2 : if (*_common == NULL)
469 : : return ENOMEM;
470 : : return EOK;
471 : : }
472 : : }
473 : :
474 : : return ENOENT;
475 : : }
476 : :
477 : 2 : static int server_common_destructor(void *memptr)
478 : : {
479 : : struct server_common *common;
480 : :
481 : 2 : common = talloc_get_type(memptr, struct server_common);
482 [ - + ]: 2 : if (common->request_list) {
483 [ # # ][ # # ]: 0 : DEBUG(1, ("BUG: pending requests still associated with this server\n"));
[ # # ][ # # ]
[ # # ]
484 : : return -1;
485 : : }
486 [ + + ][ - + ]: 2 : DLIST_REMOVE(common->ctx->server_common_list, common);
[ + - ][ - + ]
[ + - ]
487 : :
488 : : return 0;
489 : : }
490 : :
491 : : static struct server_common *
492 : 2 : create_server_common(TALLOC_CTX *mem_ctx, struct fo_ctx *ctx, const char *name)
493 : : {
494 : : struct server_common *common;
495 : :
496 : 2 : common = rc_alloc(mem_ctx, struct server_common);
497 [ + - ]: 2 : if (common == NULL)
498 : : return NULL;
499 : :
500 : 2 : common->name = talloc_strdup(common, name);
501 [ - + ]: 2 : if (common->name == NULL) {
502 : 0 : talloc_free(common);
503 : 0 : return NULL;
504 : : }
505 : :
506 : 2 : common->ctx = ctx;
507 : 2 : common->prev = NULL;
508 : 2 : common->next = NULL;
509 : 2 : common->rhostent = NULL;
510 : 2 : common->request_list = NULL;
511 : 2 : common->server_status = DEFAULT_SERVER_STATUS;
512 : 2 : common->last_status_change.tv_sec = 0;
513 : 2 : common->last_status_change.tv_usec = 0;
514 : :
515 : 2 : talloc_set_destructor((TALLOC_CTX *) common, server_common_destructor);
516 [ + + ][ - + ]: 2 : DLIST_ADD_END(ctx->server_common_list, common, struct server_common *);
517 : 2 : return common;
518 : : }
519 : :
520 : : int
521 : 0 : fo_add_srv_server(struct fo_service *service, const char *srv,
522 : : const char *discovery_domain, const char *sssd_domain,
523 : : const char *proto, void *user_data)
524 : : {
525 : : struct fo_server *server;
526 : :
527 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_TRACE_FUNC,
[ # # ][ # # ]
[ # # ]
528 : : ("Adding new SRV server to service '%s' using '%s'.\n",
529 : : service->name, proto));
530 : :
531 [ # # ]: 0 : DLIST_FOR_EACH(server, service->server_list) {
532 : : /* Compare user data only if user_data_cmp and both arguments
533 : : * are not NULL.
534 : : */
535 [ # # ][ # # ]: 0 : if (server->service->user_data_cmp && user_data && server->user_data) {
[ # # ]
536 [ # # ]: 0 : if (server->service->user_data_cmp(server->user_data, user_data)) {
537 : 0 : continue;
538 : : }
539 : : }
540 : :
541 [ # # ]: 0 : if (fo_is_srv_lookup(server)) {
542 [ # # ][ # # ]: 0 : if (((discovery_domain == NULL &&
543 [ # # ]: 0 : server->srv_data->dns_domain == NULL) ||
544 [ # # ]: 0 : (discovery_domain != NULL &&
545 [ # # ]: 0 : server->srv_data->dns_domain != NULL &&
546 [ # # ]: 0 : strcasecmp(server->srv_data->dns_domain, discovery_domain) == 0)) &&
547 : 0 : strcasecmp(server->srv_data->proto, proto) == 0) {
548 : : return EEXIST;
549 : : }
550 : : }
551 : : }
552 : :
553 : 0 : server = talloc_zero(service, struct fo_server);
554 [ # # ]: 0 : if (server == NULL)
555 : : return ENOMEM;
556 : :
557 : 0 : server->user_data = user_data;
558 : 0 : server->service = service;
559 : 0 : server->port_status = DEFAULT_PORT_STATUS;
560 : 0 : server->primary = true; /* SRV servers are never back up */
561 : :
562 : : /* add the SRV-specific data */
563 : 0 : server->srv_data = talloc_zero(service, struct srv_data);
564 [ # # ]: 0 : if (server->srv_data == NULL)
565 : : return ENOMEM;
566 : :
567 : 0 : server->srv_data->proto = talloc_strdup(server->srv_data, proto);
568 : 0 : server->srv_data->srv = talloc_strdup(server->srv_data, srv);
569 [ # # ][ # # ]: 0 : if (server->srv_data->proto == NULL ||
570 : 0 : server->srv_data->srv == NULL)
571 : : return ENOMEM;
572 : :
573 [ # # ]: 0 : if (discovery_domain) {
574 : 0 : server->srv_data->discovery_domain = talloc_strdup(server->srv_data,
575 : : discovery_domain);
576 [ # # ]: 0 : if (server->srv_data->discovery_domain == NULL)
577 : : return ENOMEM;
578 : 0 : server->srv_data->dns_domain =
579 : : server->srv_data->discovery_domain;
580 : : }
581 : :
582 : 0 : server->srv_data->sssd_domain =
583 : 0 : talloc_strdup(server->srv_data, sssd_domain);
584 [ # # ]: 0 : if (server->srv_data->sssd_domain == NULL)
585 : : return ENOMEM;
586 : :
587 : 0 : server->srv_data->meta = server;
588 : 0 : server->srv_data->srv_lookup_status = DEFAULT_SRV_STATUS;
589 : 0 : server->srv_data->last_status_change.tv_sec = 0;
590 : :
591 [ # # ][ # # ]: 0 : DLIST_ADD_END(service->server_list, server, struct fo_server *);
592 : : return EOK;
593 : : }
594 : :
595 : : static struct fo_server *
596 : 5 : create_fo_server(struct fo_service *service, const char *name,
597 : : int port, void *user_data, bool primary)
598 : : {
599 : : struct fo_server *server;
600 : : int ret;
601 : :
602 : 5 : server = talloc_zero(service, struct fo_server);
603 [ + - ]: 5 : if (server == NULL)
604 : : return NULL;
605 : :
606 : 5 : server->port = port;
607 : 5 : server->user_data = user_data;
608 : 5 : server->service = service;
609 : 5 : server->port_status = DEFAULT_PORT_STATUS;
610 : 5 : server->primary = primary;
611 : :
612 [ + + ]: 5 : if (name != NULL) {
613 : 4 : ret = get_server_common(server, service->ctx, name, &server->common);
614 [ + + ]: 4 : if (ret == ENOENT) {
615 : 2 : server->common = create_server_common(server, service->ctx, name);
616 [ - + ]: 2 : if (server->common == NULL) {
617 : 0 : talloc_free(server);
618 : 0 : return NULL;
619 : : }
620 [ - + ]: 2 : } else if (ret != EOK) {
621 : 0 : talloc_free(server);
622 : 0 : return NULL;
623 : : }
624 : : }
625 : :
626 : 5 : return server;
627 : : }
628 : :
629 : : int
630 : 0 : fo_get_server_count(struct fo_service *service)
631 : : {
632 : : struct fo_server *server;
633 : 0 : int count = 0;
634 : :
635 [ # # ]: 0 : DLIST_FOR_EACH(server, service->server_list) {
636 : 0 : count++;
637 : : }
638 : :
639 : 0 : return count;
640 : : }
641 : :
642 : 6 : static bool fo_server_match(struct fo_server *server,
643 : : const char *name,
644 : : int port,
645 : : void *user_data)
646 : : {
647 [ + + ]: 6 : if (server->port != port) {
648 : : return false;
649 : : }
650 : :
651 : : /* Compare user data only if user_data_cmp and both arguments
652 : : * are not NULL.
653 : : */
654 [ - + ][ # # ]: 2 : if (server->service->user_data_cmp && server->user_data && user_data) {
[ # # ]
655 [ # # ]: 0 : if (server->service->user_data_cmp(server->user_data, user_data)) {
656 : : return false;
657 : : }
658 : : }
659 : :
660 [ - + ][ # # ]: 2 : if (name == NULL && server->common == NULL) {
661 : : return true;
662 : : }
663 : :
664 [ + - ][ + - ]: 2 : if (name != NULL && server->common != NULL) {
665 [ - + ]: 2 : if (!strcasecmp(name, server->common->name))
666 : : return true;
667 : : }
668 : :
669 : 6 : return false;
670 : : }
671 : :
672 : : int
673 : 7 : fo_add_server(struct fo_service *service, const char *name, int port,
674 : : void *user_data, bool primary)
675 : : {
676 : : struct fo_server *server;
677 : :
678 [ + - ][ + - ]: 7 : DEBUG(3, ("Adding new server '%s', to service '%s'\n",
[ - + ][ # # ]
[ # # ][ # # ]
679 : : name ? name : "(no name)", service->name));
680 [ + + ]: 11 : DLIST_FOR_EACH(server, service->server_list) {
681 [ + + ]: 6 : if (fo_server_match(server, name, port, user_data)) {
682 : : return EEXIST;
683 : : }
684 : : }
685 : :
686 : 5 : server = create_fo_server(service, name, port, user_data, primary);
687 [ + - ]: 5 : if (!server) {
688 : : return ENOMEM;
689 : : }
690 : :
691 [ + + ][ - + ]: 7 : DLIST_ADD_END(service->server_list, server, struct fo_server *);
692 : :
693 : : return EOK;
694 : : }
695 : :
696 : : static int
697 : 9 : get_first_server_entity(struct fo_service *service, struct fo_server **_server)
698 : : {
699 : : struct fo_server *server;
700 : :
701 : : /* If we already have a working server, use that one. */
702 : 9 : server = service->active_server;
703 [ + + ]: 9 : if (server != NULL) {
704 [ + + ][ + + ]: 5 : if (service_works(server) && fo_is_server_primary(server)) {
705 : : goto done;
706 : : }
707 : 3 : service->active_server = NULL;
708 : : }
709 : :
710 : : /*
711 : : * Otherwise iterate through the server list.
712 : : */
713 : :
714 : : /* First, try primary servers after the last one we tried.
715 : : * (only if the last one was primary as well)
716 : : */
717 [ + + ][ + + ]: 7 : if (service->last_tried_server != NULL &&
718 : 4 : service->last_tried_server->primary) {
719 [ - + # # ]: 2 : if (service->last_tried_server->port_status == PORT_NEUTRAL &&
720 : 0 : server_works(service->last_tried_server)) {
721 : 0 : server = service->last_tried_server;
722 : 0 : goto done;
723 : : }
724 : :
725 [ + + ]: 3 : DLIST_FOR_EACH(server, service->last_tried_server->next) {
726 : : /* Go only through primary servers */
727 [ + - ]: 1 : if (!server->primary) continue;
728 : :
729 [ # # ]: 0 : if (service_works(server)) {
730 : : goto done;
731 : : }
732 : : }
733 : : }
734 : :
735 : : /* If none were found, try at the start, primary first */
736 [ + + ]: 13 : DLIST_FOR_EACH(server, service->server_list) {
737 : : /* First iterate only over primary servers */
738 [ + + ]: 11 : if (!server->primary) continue;
739 : :
740 [ + + ]: 7 : if (service_works(server)) {
741 : : goto done;
742 : : }
743 [ + + ]: 4 : if (server == service->last_tried_server) {
744 : : break;
745 : : }
746 : : }
747 : :
748 [ + + ]: 10 : DLIST_FOR_EACH(server, service->server_list) {
749 : : /* Now iterate only over backup servers */
750 [ + + ]: 8 : if (server->primary) continue;
751 : :
752 [ + + ]: 4 : if (service_works(server)) {
753 : : goto done;
754 : : }
755 : : }
756 : :
757 : 2 : service->last_tried_server = NULL;
758 : 2 : return ENOENT;
759 : :
760 : : done:
761 : 7 : service->last_tried_server = server;
762 : 7 : *_server = server;
763 : 9 : return EOK;
764 : : }
765 : :
766 : : static int
767 : 2 : resolve_service_request_destructor(struct resolve_service_request *request)
768 : : {
769 [ - + ][ # # ]: 2 : DLIST_REMOVE(request->server_common->request_list, request);
[ - + ][ - + ]
[ + - ]
770 : 2 : return 0;
771 : : }
772 : :
773 : : static int
774 : 2 : set_lookup_hook(struct fo_server *server, struct tevent_req *req)
775 : : {
776 : : struct resolve_service_request *request;
777 : :
778 : 2 : request = talloc(req, struct resolve_service_request);
779 [ - + ]: 2 : if (request == NULL) {
780 [ # # ][ # # ]: 0 : DEBUG(1, ("No memory\n"));
[ # # ][ # # ]
[ # # ]
781 : 0 : talloc_free(request);
782 : : return ENOMEM;
783 : : }
784 : 2 : request->server_common = rc_reference(request, struct server_common,
785 : : server->common);
786 [ - + ]: 2 : if (request->server_common == NULL) {
787 : 0 : talloc_free(request);
788 : : return ENOMEM;
789 : : }
790 : 2 : request->req = req;
791 [ + - ]: 2 : DLIST_ADD(server->common->request_list, request);
792 : 2 : talloc_set_destructor(request, resolve_service_request_destructor);
793 : :
794 : : return EOK;
795 : : }
796 : :
797 : :
798 : :
799 : : /*******************************************************************
800 : : * Get server to connect to. *
801 : : *******************************************************************/
802 : :
803 : : struct resolve_service_state {
804 : : struct fo_server *server;
805 : :
806 : : struct resolv_ctx *resolv;
807 : : struct tevent_context *ev;
808 : : struct tevent_timer *timeout_handler;
809 : : struct fo_ctx *fo_ctx;
810 : : };
811 : :
812 : : static errno_t fo_resolve_service_activate_timeout(struct tevent_req *req,
813 : : struct tevent_context *ev, const unsigned long timeout_seconds);
814 : : static void fo_resolve_service_cont(struct tevent_req *subreq);
815 : : static void fo_resolve_service_done(struct tevent_req *subreq);
816 : : static bool fo_resolve_service_server(struct tevent_req *req);
817 : :
818 : : /* Forward declarations for SRV resolving */
819 : : static struct tevent_req *
820 : : resolve_srv_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
821 : : struct resolv_ctx *resolv, struct fo_ctx *ctx,
822 : : struct fo_server *server);
823 : : static int
824 : : resolve_srv_recv(struct tevent_req *req, struct fo_server **server);
825 : :
826 : : struct tevent_req *
827 : 9 : fo_resolve_service_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
828 : : struct resolv_ctx *resolv, struct fo_ctx *ctx,
829 : : struct fo_service *service)
830 : : {
831 : : int ret;
832 : : struct fo_server *server;
833 : : struct tevent_req *req;
834 : : struct tevent_req *subreq;
835 : : struct resolve_service_state *state;
836 : :
837 [ + - ][ + - ]: 9 : DEBUG(4, ("Trying to resolve service '%s'\n", service->name));
[ - + ][ # # ]
[ # # ]
838 : 9 : req = tevent_req_create(mem_ctx, &state, struct resolve_service_state);
839 [ + - ]: 9 : if (req == NULL)
840 : : return NULL;
841 : :
842 : 9 : state->resolv = resolv;
843 : 9 : state->ev = ev;
844 : 9 : state->fo_ctx = ctx;
845 : :
846 : 9 : ret = get_first_server_entity(service, &server);
847 [ + + ]: 9 : if (ret != EOK) {
848 [ + - ][ + - ]: 2 : DEBUG(1, ("No available servers for service '%s'\n", service->name));
[ + - ][ + - ]
[ + - ]
849 : : goto done;
850 : : }
851 : :
852 : : /* Activate per-service timeout handler */
853 : 7 : ret = fo_resolve_service_activate_timeout(req, ev,
854 : 7 : ctx->opts->service_resolv_timeout);
855 [ - + ]: 7 : if (ret != EOK) {
856 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_OP_FAILURE, ("Could not set service timeout\n"));
[ # # ][ # # ]
[ # # ]
857 : : goto done;
858 : : }
859 : :
860 [ - + ]: 7 : if (fo_is_srv_lookup(server)) {
861 : : /* Don't know the server yet, must do a SRV lookup */
862 : 0 : subreq = resolve_srv_send(state, ev, resolv,
863 : : ctx, server);
864 [ # # ]: 0 : if (subreq == NULL) {
865 : : ret = ENOMEM;
866 : : goto done;
867 : : }
868 : :
869 : 0 : tevent_req_set_callback(subreq,
870 : : fo_resolve_service_cont,
871 : : req);
872 : : return req;
873 : : }
874 : :
875 : : /* This is a regular server, just do hostname lookup */
876 : 7 : state->server = server;
877 [ + + ]: 7 : if (fo_resolve_service_server(req)) {
878 : 5 : tevent_req_post(req, ev);
879 : : }
880 : :
881 : : ret = EOK;
882 : : done:
883 [ + + ]: 9 : if (ret != EOK) {
884 : 2 : tevent_req_error(req, ret);
885 : 9 : tevent_req_post(req, ev);
886 : : }
887 : : return req;
888 : : }
889 : :
890 : : static void set_server_common_status(struct server_common *common,
891 : : enum server_status status);
892 : :
893 : : static void
894 : 0 : fo_resolve_service_timeout(struct tevent_context *ev,
895 : : struct tevent_timer *te,
896 : : struct timeval tv, void *pvt)
897 : : {
898 : 0 : struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
899 : :
900 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_MINOR_FAILURE, ("Service resolving timeout reached\n"));
[ # # ][ # # ]
[ # # ]
901 : 0 : tevent_req_error(req, ETIMEDOUT);
902 : 0 : }
903 : :
904 : : static errno_t
905 : 7 : fo_resolve_service_activate_timeout(struct tevent_req *req,
906 : : struct tevent_context *ev,
907 : : const unsigned long timeout_seconds)
908 : : {
909 : : struct timeval tv;
910 : 7 : struct resolve_service_state *state = tevent_req_data(req,
911 : : struct resolve_service_state);
912 : :
913 : 7 : tv = tevent_timeval_current();
914 : 7 : tv = tevent_timeval_add(&tv, timeout_seconds, 0);
915 : 7 : state->timeout_handler = tevent_add_timer(ev, state, tv,
916 : : fo_resolve_service_timeout, req);
917 [ - + ]: 7 : if (state->timeout_handler == NULL) {
918 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_CRIT_FAILURE, ("tevent_add_timer failed.\n"));
[ # # ][ # # ]
[ # # ]
919 : : return ENOMEM;
920 : : }
921 : :
922 [ + - ][ + - ]: 7 : DEBUG(SSSDBG_TRACE_INTERNAL, ("Resolve timeout set to %lu seconds\n",
[ - + ][ # # ]
[ # # ]
923 : : timeout_seconds));
924 : : return EOK;
925 : : }
926 : :
927 : : /* SRV resolving finished, see if we got server to work with */
928 : : static void
929 : 0 : fo_resolve_service_cont(struct tevent_req *subreq)
930 : : {
931 : 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
932 : : struct tevent_req);
933 : 0 : struct resolve_service_state *state = tevent_req_data(req,
934 : : struct resolve_service_state);
935 : : int ret;
936 : :
937 : 0 : ret = resolve_srv_recv(subreq, &state->server);
938 : 0 : talloc_zfree(subreq);
939 : :
940 [ # # ]: 0 : if (ret) {
941 : 0 : tevent_req_error(req, ret);
942 : 0 : return;
943 : : }
944 : :
945 : 0 : fo_resolve_service_server(req);
946 : : }
947 : :
948 : : static bool
949 : 7 : fo_resolve_service_server(struct tevent_req *req)
950 : : {
951 : 7 : struct resolve_service_state *state = tevent_req_data(req,
952 : : struct resolve_service_state);
953 : : struct tevent_req *subreq;
954 : : int ret;
955 : :
956 [ + - + ]: 7 : switch (get_server_status(state->server)) {
957 : : case SERVER_NAME_NOT_RESOLVED: /* Request name resolution. */
958 : 2 : subreq = resolv_gethostbyname_send(state->server->common,
959 : : state->ev, state->resolv,
960 : 2 : state->server->common->name,
961 : 2 : state->fo_ctx->opts->family_order,
962 : : default_host_dbs);
963 [ - + ]: 2 : if (subreq == NULL) {
964 : 0 : tevent_req_error(req, ENOMEM);
965 : 0 : return true;
966 : : }
967 : 2 : tevent_req_set_callback(subreq, fo_resolve_service_done,
968 : 2 : state->server->common);
969 : 2 : fo_set_server_status(state->server, SERVER_RESOLVING_NAME);
970 : : /* FALLTHROUGH */
971 : : case SERVER_RESOLVING_NAME:
972 : : /* Name resolution is already under way. Just add ourselves into the
973 : : * waiting queue so we get notified after the operation is finished. */
974 : 2 : ret = set_lookup_hook(state->server, req);
975 [ - + ]: 2 : if (ret != EOK) {
976 : 0 : tevent_req_error(req, ret);
977 : 0 : return true;
978 : : }
979 : : break;
980 : : default: /* The name is already resolved. Return immediately. */
981 : 5 : tevent_req_done(req);
982 : 7 : return true;
983 : : }
984 : :
985 : : return false;
986 : : }
987 : :
988 : : static void
989 : 2 : fo_resolve_service_done(struct tevent_req *subreq)
990 : : {
991 : 2 : struct server_common *common = tevent_req_callback_data(subreq,
992 : : struct server_common);
993 : : int resolv_status;
994 : : struct resolve_service_request *request;
995 : : int ret;
996 : :
997 [ - + ]: 2 : if (common->rhostent != NULL) {
998 : 0 : talloc_zfree(common->rhostent);
999 : : }
1000 : :
1001 : 2 : ret = resolv_gethostbyname_recv(subreq, common,
1002 : : &resolv_status, NULL,
1003 : : &common->rhostent);
1004 : 2 : talloc_zfree(subreq);
1005 [ - + ]: 2 : if (ret != EOK) {
1006 [ # # ][ # # ]: 0 : DEBUG(1, ("Failed to resolve server '%s': %s\n",
[ # # ][ # # ]
[ # # ]
1007 : : common->name,
1008 : : resolv_strerror(resolv_status)));
1009 : : /* If the resolver failed to resolve a hostname but did not
1010 : : * encounter an error, tell the caller to retry another server.
1011 : : *
1012 : : * If there are no more servers to try, the next request would
1013 : : * just shortcut with ENOENT.
1014 : : */
1015 [ # # ]: 0 : if (ret == ENOENT) {
1016 : 0 : ret = EAGAIN;
1017 : : }
1018 : 2 : set_server_common_status(common, SERVER_NOT_WORKING);
1019 : : } else {
1020 : 2 : set_server_common_status(common, SERVER_NAME_RESOLVED);
1021 : : }
1022 : :
1023 : : /* Take care of all requests for this server. */
1024 [ + + ]: 4 : while ((request = common->request_list) != NULL) {
1025 [ - + ][ + - ]: 2 : DLIST_REMOVE(common->request_list, request);
1026 [ - + ]: 2 : if (ret) {
1027 : 0 : tevent_req_error(request->req, ret);
1028 : : } else {
1029 : 2 : tevent_req_done(request->req);
1030 : : }
1031 : : }
1032 : 2 : }
1033 : :
1034 : : int
1035 : 9 : fo_resolve_service_recv(struct tevent_req *req, struct fo_server **server)
1036 : : {
1037 : : struct resolve_service_state *state;
1038 : :
1039 : 9 : state = tevent_req_data(req, struct resolve_service_state);
1040 : :
1041 : : /* always return the server if asked for, otherwise the caller
1042 : : * cannot mark it as faulty in case we return an error */
1043 [ + - ]: 9 : if (server)
1044 : 9 : *server = state->server;
1045 : :
1046 [ + + ][ + - ]: 9 : TEVENT_REQ_RETURN_ON_ERROR(req);
1047 : :
1048 : 9 : return EOK;
1049 : : }
1050 : :
1051 : : /*******************************************************************
1052 : : * Resolve the server to connect to using a SRV query. *
1053 : : *******************************************************************/
1054 : :
1055 : : static void resolve_srv_done(struct tevent_req *subreq);
1056 : : static void resolve_srv_cont(struct tevent_req *req);
1057 : :
1058 : : struct tevent_req *resolve_get_domain_send(TALLOC_CTX *mem_ctx,
1059 : : struct tevent_context *ev,
1060 : : struct fo_ctx *foctx,
1061 : : struct resolv_ctx *resolv);
1062 : :
1063 : : static void resolve_getsrv_domain_done(struct tevent_req *req);
1064 : : int resolve_get_domain_recv(struct tevent_req *req,
1065 : : TALLOC_CTX *mem_ctx,
1066 : : char **dns_domain);
1067 : :
1068 : : struct resolve_srv_state {
1069 : : struct fo_server *meta;
1070 : : struct fo_service *service;
1071 : :
1072 : : struct fo_server *out;
1073 : :
1074 : : struct resolv_ctx *resolv;
1075 : : struct tevent_context *ev;
1076 : : struct fo_ctx *fo_ctx;
1077 : : };
1078 : :
1079 : : static struct tevent_req *
1080 : 0 : resolve_srv_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
1081 : : struct resolv_ctx *resolv, struct fo_ctx *ctx,
1082 : : struct fo_server *server)
1083 : : {
1084 : : int ret;
1085 : : struct tevent_req *req;
1086 : : struct tevent_req *subreq;
1087 : : struct resolve_srv_state *state;
1088 : : int status;
1089 : :
1090 : 0 : req = tevent_req_create(mem_ctx, &state, struct resolve_srv_state);
1091 [ # # ]: 0 : if (req == NULL)
1092 : : return NULL;
1093 : :
1094 : 0 : state->service = server->service;
1095 : 0 : state->ev = ev;
1096 : 0 : state->resolv = resolv;
1097 : 0 : state->fo_ctx = ctx;
1098 : 0 : state->meta = server->srv_data->meta;
1099 : :
1100 : 0 : status = get_srv_data_status(server->srv_data);
1101 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_FUNC_DATA, ("The status of SRV lookup is %s\n",
[ # # ][ # # ]
[ # # ][ # # ]
1102 : : str_srv_data_status(status)));
1103 [ # # # # : 0 : switch(status) {
# ]
1104 : : case SRV_EXPIRED: /* Need a refresh */
1105 : 0 : state->meta = collapse_srv_lookup(server);
1106 : : /* FALLTHROUGH.
1107 : : * "server" might be invalid now if the SRV
1108 : : * query collapsed
1109 : : * */
1110 : : case SRV_NEUTRAL: /* Request SRV lookup */
1111 [ # # ]: 0 : if (state->meta->srv_data->dns_domain == NULL) {
1112 : : /* we need to look up our DNS domain first */
1113 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_TRACE_FUNC,
[ # # ][ # # ]
[ # # ]
1114 : : ("SRV resolution of service '%s'. "
1115 : : "dns_discovery_domain not specified. Need to look it up.\n",
1116 : : server->service->name));
1117 : 0 : subreq = resolve_get_domain_send(state, ev, ctx, resolv);
1118 [ # # ]: 0 : if (subreq == NULL) {
1119 : : ret = ENOMEM;
1120 : : goto done;
1121 : : }
1122 : 0 : tevent_req_set_callback(subreq, resolve_getsrv_domain_done, req);
1123 : : break;
1124 : : }
1125 : : /* we know the DNS domain, just do the lookup */
1126 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_TRACE_FUNC,
[ # # ][ # # ]
[ # # ]
1127 : : ("SRV resolution of service '%s'. "
1128 : : "Will use DNS discovery domain '%s'\n",
1129 : : server->service->name, state->meta->srv_data->dns_domain));
1130 : 0 : resolve_srv_cont(req);
1131 : : break;
1132 : : case SRV_RESOLVE_ERROR: /* query could not be resolved but don't retry yet */
1133 : : ret = EIO;
1134 : : goto done;
1135 : : case SRV_RESOLVED: /* The query is resolved and valid. Return. */
1136 : 0 : state->out = server;
1137 : 0 : tevent_req_done(req);
1138 : 0 : tevent_req_post(req, state->ev);
1139 : : return req;
1140 : : default:
1141 [ # # ][ # # ]: 0 : DEBUG(1, ("Unexpected status %d for a SRV server\n", status));
[ # # ][ # # ]
[ # # ]
1142 : : ret = EIO;
1143 : : goto done;
1144 : : }
1145 : :
1146 : : ret = EOK;
1147 : : done:
1148 [ # # ]: 0 : if (ret != EOK) {
1149 : 0 : tevent_req_error(req, ret);
1150 : 0 : tevent_req_post(req, ev);
1151 : : }
1152 : : return req;
1153 : : }
1154 : :
1155 : : static void
1156 : 0 : resolve_getsrv_domain_done(struct tevent_req *subreq)
1157 : : {
1158 : 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
1159 : : struct tevent_req);
1160 : 0 : struct resolve_srv_state *state = tevent_req_data(req,
1161 : : struct resolve_srv_state);
1162 : : int ret;
1163 : :
1164 : 0 : ret = resolve_get_domain_recv(subreq, state->meta->srv_data,
1165 : 0 : &state->meta->srv_data->dns_domain);
1166 : 0 : talloc_zfree(subreq);
1167 [ # # ]: 0 : if (ret) {
1168 : 0 : tevent_req_error(req, ret);
1169 : 0 : return;
1170 : : }
1171 : :
1172 : 0 : resolve_srv_cont(req);
1173 : : }
1174 : :
1175 : : static void
1176 : 0 : resolve_srv_cont(struct tevent_req *req)
1177 : : {
1178 : 0 : struct resolve_srv_state *state = tevent_req_data(req,
1179 : : struct resolve_srv_state);
1180 : : char *query;
1181 : : struct tevent_req *subreq;
1182 : :
1183 : 0 : query = get_srv_query(state, state->meta);
1184 [ # # ]: 0 : if (!query) {
1185 : 0 : tevent_req_error(req, ENOMEM);
1186 : 0 : return;
1187 : : }
1188 [ # # ][ # # ]: 0 : DEBUG(4, ("Searching for servers via SRV query '%s'\n", query));
[ # # ][ # # ]
[ # # ]
1189 : :
1190 : 0 : subreq = resolv_getsrv_send(state, state->ev, state->resolv, query);
1191 [ # # ]: 0 : if (subreq == NULL) {
1192 : 0 : tevent_req_error(req, ENOMEM);
1193 : 0 : return;
1194 : : }
1195 : 0 : tevent_req_set_callback(subreq, resolve_srv_done, req);
1196 : : }
1197 : :
1198 : : static void
1199 : 0 : resolve_srv_done(struct tevent_req *subreq)
1200 : : {
1201 : 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
1202 : : struct tevent_req);
1203 : 0 : struct resolve_srv_state *state = tevent_req_data(req,
1204 : : struct resolve_srv_state);
1205 : : struct ares_srv_reply *reply_list;
1206 : : struct ares_srv_reply *reply;
1207 : 0 : struct fo_server *server = NULL;
1208 : 0 : struct fo_server *srv_list = NULL;
1209 : : int ret;
1210 : : int resolv_status;
1211 : :
1212 : 0 : ret = resolv_getsrv_recv(state, subreq,
1213 : : &resolv_status, NULL, &reply_list);
1214 : 0 : talloc_free(subreq);
1215 [ # # ]: 0 : if (ret != EOK) {
1216 [ # # ][ # # ]: 0 : DEBUG(1, ("SRV query failed: [%s]\n",
[ # # ][ # # ]
[ # # ]
1217 : : resolv_strerror(resolv_status)));
1218 [ # # ][ # # ]: 0 : if (resolv_status == ARES_ENOTFOUND &&
1219 : 0 : state->meta->srv_data->dns_domain !=
1220 [ # # ]: 0 : state->meta->srv_data->discovery_domain &&
1221 : : state->meta->srv_data->dns_domain !=
1222 : 0 : state->meta->srv_data->sssd_domain) {
1223 : : /* The domain name could not be identified
1224 : : * If the domain wasn't specified in the config
1225 : : * file, also check whether the SSSD domain
1226 : : * works.
1227 : : *
1228 : : * Programming note: It is safe to compare
1229 : : * pointers here, because we're not copying
1230 : : * the data, we're just reassigning the pointer
1231 : : * for the active domain.
1232 : : */
1233 : 0 : talloc_free(state->meta->srv_data->dns_domain);
1234 : 0 : state->meta->srv_data->dns_domain =
1235 : 0 : state->meta->srv_data->sssd_domain;
1236 : 0 : resolve_srv_cont(req);
1237 : : return;
1238 : : }
1239 : :
1240 : : /* We need to make sure we reset this to NULL
1241 : : * so that if we go online later, we re-check
1242 : : * the DNS domain
1243 : : */
1244 [ # # ]: 0 : if (!state->meta->srv_data->discovery_domain) {
1245 : 0 : state->meta->srv_data->dns_domain = NULL;
1246 : : }
1247 : :
1248 : 0 : fo_set_port_status(state->meta, PORT_NOT_WORKING);
1249 : : goto fail;
1250 : : }
1251 : :
1252 : 0 : ret = resolv_sort_srv_reply(state, &reply_list);
1253 [ # # ]: 0 : if (ret != EOK) {
1254 [ # # ][ # # ]: 0 : DEBUG(1, ("Could not sort the answers from DNS [%d]: %s\n",
[ # # ][ # # ]
[ # # ]
1255 : : ret, strerror(ret)));
1256 : 0 : fo_set_port_status(state->meta, PORT_NOT_WORKING);
1257 : : goto fail;
1258 : : }
1259 : :
1260 [ # # ]: 0 : for (reply = reply_list; reply; reply = reply->next) {
1261 : 0 : server = create_fo_server(state->service, reply->host,
1262 : 0 : reply->port, state->meta->user_data,
1263 : : true);
1264 [ # # ]: 0 : if (!server) {
1265 : : ret = ENOMEM;
1266 : : goto fail;
1267 : : }
1268 : 0 : server->srv_data = state->meta->srv_data;
1269 : :
1270 [ # # ][ # # ]: 0 : DLIST_ADD_END(srv_list, server, struct fo_server *);
1271 [ # # ][ # # ]: 0 : DEBUG(6, ("Inserted server '%s:%d' for service %s\n",
[ # # ][ # # ]
[ # # ]
1272 : : server->common->name,
1273 : : server->port,
1274 : : state->service->name));
1275 : : }
1276 : :
1277 [ # # ]: 0 : if (srv_list) {
1278 [ # # ][ # # ]: 0 : DLIST_ADD_LIST_AFTER(state->service->server_list, state->meta,
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
1279 : : srv_list, struct fo_server *);
1280 : :
1281 [ # # ][ # # ]: 0 : DLIST_REMOVE(state->service->server_list, state->meta);
[ # # ][ # # ]
[ # # ]
1282 [ # # ]: 0 : if (state->service->last_tried_server == state->meta) {
1283 : 0 : state->service->last_tried_server = srv_list;
1284 : : }
1285 : :
1286 : 0 : state->out = srv_list;
1287 : 0 : set_srv_data_status(state->meta->srv_data, SRV_RESOLVED);
1288 : 0 : tevent_req_done(req);
1289 : : return;
1290 : : } else {
1291 : : ret = EIO;
1292 : : goto fail;
1293 : : }
1294 : :
1295 : : fail:
1296 : 0 : state->out = state->meta;
1297 : 0 : set_srv_data_status(state->meta->srv_data, SRV_RESOLVE_ERROR);
1298 : 0 : tevent_req_error(req, ret);
1299 : : }
1300 : :
1301 : : static int
1302 : 0 : resolve_srv_recv(struct tevent_req *req, struct fo_server **server)
1303 : : {
1304 : 0 : struct resolve_srv_state *state = tevent_req_data(req,
1305 : : struct resolve_srv_state);
1306 : :
1307 : : /* always return the server if asked for, otherwise the caller
1308 : : * cannot mark it as faulty in case we return an error */
1309 [ # # ]: 0 : if (server) {
1310 : 0 : *server = state->out;
1311 : : }
1312 : :
1313 [ # # ][ # # ]: 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
1314 : :
1315 : 0 : return EOK;
1316 : : }
1317 : :
1318 : : /*******************************************************************
1319 : : * Get Fully Qualified Domain Name of the host machine *
1320 : : *******************************************************************/
1321 : : struct resolve_get_domain_state {
1322 : : char *fqdn;
1323 : : char hostname[HOST_NAME_MAX];
1324 : : };
1325 : :
1326 : : static void resolve_get_domain_done(struct tevent_req *subreq);
1327 : :
1328 : : struct tevent_req *
1329 : 0 : resolve_get_domain_send(TALLOC_CTX *mem_ctx,
1330 : : struct tevent_context *ev,
1331 : : struct fo_ctx *foctx,
1332 : : struct resolv_ctx *resolv)
1333 : : {
1334 : : int ret;
1335 : : struct resolve_get_domain_state *state;
1336 : : struct tevent_req *req, *subreq;
1337 : :
1338 : 0 : req = tevent_req_create(mem_ctx, &state, struct resolve_get_domain_state);
1339 [ # # ]: 0 : if (!req) {
1340 : : return NULL;
1341 : : }
1342 : :
1343 : 0 : ret = gethostname(state->hostname, HOST_NAME_MAX);
1344 [ # # ]: 0 : if (ret) {
1345 : 0 : ret = errno;
1346 [ # # ][ # # ]: 0 : DEBUG(2, ("gethostname() failed: [%d]: %s\n",ret, strerror(ret)));
[ # # ][ # # ]
[ # # ]
1347 : : return NULL;
1348 : : }
1349 : 0 : state->hostname[HOST_NAME_MAX-1] = '\0';
1350 [ # # ][ # # ]: 0 : DEBUG(7, ("Host name is: %s\n", state->hostname));
[ # # ][ # # ]
[ # # ]
1351 : :
1352 : 0 : subreq = resolv_gethostbyname_send(state, ev, resolv,
1353 : 0 : state->hostname,
1354 : 0 : foctx->opts->family_order,
1355 : : default_host_dbs);
1356 [ # # ]: 0 : if (!subreq) {
1357 : 0 : talloc_zfree(req);
1358 : : return NULL;
1359 : : }
1360 : 0 : tevent_req_set_callback(subreq, resolve_get_domain_done, req);
1361 : :
1362 : : return req;
1363 : : }
1364 : :
1365 : 0 : static void resolve_get_domain_done(struct tevent_req *subreq)
1366 : : {
1367 : 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
1368 : : struct tevent_req);
1369 : 0 : struct resolve_get_domain_state *state = tevent_req_data(req,
1370 : : struct resolve_get_domain_state);
1371 : : struct resolv_hostent *rhostent;
1372 : : int ret;
1373 : : int resolv_status;
1374 : :
1375 : 0 : ret = resolv_gethostbyname_recv(subreq, req, &resolv_status,
1376 : : NULL, &rhostent);
1377 : 0 : talloc_zfree(subreq);
1378 [ # # ]: 0 : if (ret) {
1379 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_OP_FAILURE,
[ # # ][ # # ]
[ # # ]
1380 : : ("Could not get fully qualified name for host name %s "
1381 : : "error [%d]: %s, resolver returned: [%d]: %s\n",
1382 : : state->hostname, ret, strerror(ret), resolv_status,
1383 : : resolv_strerror(resolv_status)));
1384 : : /* We'll proceed with hostname in this case */
1385 : : } else {
1386 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_TRACE_LIBS, ("The full FQDN is: %s\n", rhostent->name));
[ # # ][ # # ]
[ # # ]
1387 : 0 : state->fqdn = rhostent->name;
1388 : : }
1389 : 0 : tevent_req_done(req);
1390 : 0 : }
1391 : :
1392 : 0 : int resolve_get_domain_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
1393 : : char **dns_domain)
1394 : : {
1395 : 0 : struct resolve_get_domain_state *state = tevent_req_data(req,
1396 : : struct resolve_get_domain_state);
1397 : : char *fqdn;
1398 : : char *domptr;
1399 : :
1400 [ # # ][ # # ]: 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
1401 : :
1402 [ # # ]: 0 : fqdn = state->fqdn ? state->fqdn : state->hostname;
1403 : 0 : domptr = strchr(fqdn, '.');
1404 : :
1405 [ # # ][ # # ]: 0 : if (!domptr || (*(domptr+1) == '\0')) {
1406 : : /* If the FQDN did not contain a dot or the dot was the last character
1407 : : * (broken DNS server perhaps */
1408 : 0 : *dns_domain = talloc_strdup(mem_ctx, fqdn);
1409 : : } else {
1410 : 0 : domptr++;
1411 : 0 : *dns_domain = talloc_strdup(mem_ctx, domptr);
1412 : : }
1413 : :
1414 [ # # ]: 0 : if (*dns_domain == NULL) {
1415 : : return ENOMEM;
1416 : : }
1417 : :
1418 : 0 : return EOK;
1419 : : }
1420 : :
1421 : : static void
1422 : 9 : set_server_common_status(struct server_common *common,
1423 : : enum server_status status)
1424 : : {
1425 [ + - ][ + - ]: 9 : DEBUG(4, ("Marking server '%s' as '%s'\n", common->name,
[ - + ][ # # ]
[ # # ][ # # ]
1426 : : str_server_status(status)));
1427 : :
1428 : 9 : common->server_status = status;
1429 : 9 : gettimeofday(&common->last_status_change, NULL);
1430 : 9 : }
1431 : :
1432 : : void
1433 : 7 : fo_set_server_status(struct fo_server *server, enum server_status status)
1434 : : {
1435 [ - + ]: 7 : if (server->common == NULL) {
1436 [ # # ][ # # ]: 0 : DEBUG(1, ("Bug: Trying to set server status of a name-less server\n"));
[ # # ][ # # ]
[ # # ]
1437 : 7 : return;
1438 : : }
1439 : :
1440 : 7 : set_server_common_status(server->common, status);
1441 : : }
1442 : :
1443 : : void
1444 : 4 : fo_set_port_status(struct fo_server *server, enum port_status status)
1445 : : {
1446 : : struct fo_server *siter;
1447 : :
1448 [ + - ][ + - ]: 4 : DEBUG(4, ("Marking port %d of server '%s' as '%s'\n", server->port,
[ - + ][ # # ]
[ # # ][ # # ]
[ # # ]
1449 : : SERVER_NAME(server), str_port_status(status)));
1450 : :
1451 : 4 : server->port_status = status;
1452 : 4 : gettimeofday(&server->last_status_change, NULL);
1453 [ + + ]: 4 : if (status == PORT_WORKING) {
1454 : 3 : fo_set_server_status(server, SERVER_WORKING);
1455 : 3 : server->service->active_server = server;
1456 : : }
1457 : :
1458 [ + - ][ + - ]: 8 : if (!server->common || !server->common->name) return;
1459 : :
1460 : : /* It is possible to introduce duplicates when expanding SRV results
1461 : : * into fo_server structures. Find the duplicates and set the same
1462 : : * status */
1463 [ + + ]: 12 : DLIST_FOR_EACH(siter, server->service->server_list) {
1464 [ + + ]: 8 : if (siter == server) continue;
1465 [ + - ][ - + ]: 4 : if (!siter->common || !siter->common->name) continue;
1466 : :
1467 [ - + ][ # # ]: 4 : if (siter->port == server->port &&
1468 : 0 : (strcasecmp(siter->common->name, server->common->name) == 0)) {
1469 [ # # ][ # # ]: 0 : DEBUG(7, ("Marking port %d of duplicate server '%s' as '%s'\n",
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
1470 : : siter->port, SERVER_NAME(siter),
1471 : : str_port_status(status)));
1472 : 0 : siter->port_status = status;
1473 : 0 : gettimeofday(&siter->last_status_change, NULL);
1474 : : }
1475 : : }
1476 : : }
1477 : :
1478 : 0 : void fo_try_next_server(struct fo_service *service)
1479 : : {
1480 : : struct fo_server *server;
1481 : :
1482 [ # # ]: 0 : if (!service) {
1483 [ # # ][ # # ]: 0 : DEBUG(1, ("Bug: No service supplied\n"));
[ # # ][ # # ]
[ # # ]
1484 : : return;
1485 : : }
1486 : :
1487 : 0 : server = service->active_server;
1488 [ # # ]: 0 : if (!server) {
1489 : : return;
1490 : : }
1491 : :
1492 : 0 : service->active_server = 0;
1493 : :
1494 [ # # ]: 0 : if (server->port_status == PORT_WORKING) {
1495 : 0 : server->port_status = PORT_NEUTRAL;
1496 : : }
1497 : : }
1498 : :
1499 : : void *
1500 : 0 : fo_get_server_user_data(struct fo_server *server)
1501 : : {
1502 : 0 : return server->user_data;
1503 : : }
1504 : :
1505 : : int
1506 : 7 : fo_get_server_port(struct fo_server *server)
1507 : : {
1508 : 7 : return server->port;
1509 : : }
1510 : :
1511 : : const char *
1512 : 7 : fo_get_server_name(struct fo_server *server)
1513 : : {
1514 [ + + ]: 7 : if (!server->common) {
1515 : : return NULL;
1516 : : }
1517 : 7 : return server->common->name;
1518 : : }
1519 : :
1520 : 0 : const char *fo_get_server_str_name(struct fo_server *server)
1521 : : {
1522 [ # # ]: 0 : if (!server->common) {
1523 [ # # ]: 0 : if (fo_is_srv_lookup(server)) {
1524 : : return "SRV lookup meta-server";
1525 : : }
1526 : 0 : return "unknown name";
1527 : : }
1528 : :
1529 : 0 : return server->common->name;
1530 : : }
1531 : :
1532 : : struct resolv_hostent *
1533 : 6 : fo_get_server_hostent(struct fo_server *server)
1534 : : {
1535 [ - + ]: 6 : if (server->common == NULL) {
1536 [ # # ][ # # ]: 0 : DEBUG(1, ("Bug: Trying to get hostent from a name-less server\n"));
[ # # ][ # # ]
[ # # ]
1537 : : return NULL;
1538 : : }
1539 : :
1540 : 6 : return server->common->rhostent;
1541 : : }
1542 : :
1543 : : bool
1544 : 0 : fo_is_server_primary(struct fo_server *server)
1545 : : {
1546 : 0 : return server->primary;
1547 : : }
1548 : :
1549 : : time_t
1550 : 0 : fo_get_server_hostname_last_change(struct fo_server *server)
1551 : : {
1552 [ # # ]: 0 : if (server->common == NULL) {
1553 : : return 0;
1554 : : }
1555 : 0 : return server->common->last_status_change.tv_sec;
1556 : : }
1557 : :
1558 : 0 : void fo_reset_services(struct fo_ctx *fo_ctx)
1559 : : {
1560 : : struct fo_service *service;
1561 : : struct fo_server *server;
1562 : :
1563 [ # # ]: 0 : DLIST_FOR_EACH(service, fo_ctx->service_list) {
1564 [ # # ]: 0 : DLIST_FOR_EACH(server, service->server_list) {
1565 [ # # ]: 0 : if (server->srv_data != NULL) {
1566 : 0 : set_srv_data_status(server->srv_data, SRV_NEUTRAL);
1567 : : } else {
1568 : 0 : fo_set_server_status(server, SERVER_NAME_NOT_RESOLVED);
1569 : 0 : fo_set_port_status(server, PORT_NEUTRAL);
1570 : : }
1571 : : }
1572 : : }
1573 : 0 : }
1574 : :
1575 : 0 : bool fo_svc_has_server(struct fo_service *service, struct fo_server *server)
1576 : : {
1577 : : struct fo_server *srv;
1578 : :
1579 [ # # ]: 0 : DLIST_FOR_EACH(srv, service->server_list) {
1580 [ # # ]: 0 : if (srv == server) return true;
1581 : : }
1582 : :
1583 : : return false;
1584 : 6 : }
|