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