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