Line data Source code
1 : /*
2 : SSSD
3 :
4 : Data Provider Helpers
5 :
6 : Copyright (C) Simo Sorce <ssorce@redhat.com> 2009
7 :
8 : This program is free software; you can redistribute it and/or modify
9 : it under the terms of the GNU General Public License as published by
10 : the Free Software Foundation; either version 3 of the License, or
11 : (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program. If not, see <http://www.gnu.org/licenses/>.
20 : */
21 :
22 : #include <netdb.h>
23 : #include <arpa/inet.h>
24 : #include "providers/dp_backend.h"
25 : #include "resolv/async_resolv.h"
26 :
27 : struct be_svc_callback {
28 : struct be_svc_callback *prev;
29 : struct be_svc_callback *next;
30 :
31 : struct be_svc_data *svc;
32 :
33 : be_svc_callback_fn_t *fn;
34 : void *private_data;
35 : };
36 :
37 : struct be_svc_data {
38 : struct be_svc_data *prev;
39 : struct be_svc_data *next;
40 :
41 : const char *name;
42 : struct fo_service *fo_service;
43 :
44 : struct fo_server *last_good_srv;
45 : time_t last_status_change;
46 : bool run_callbacks;
47 :
48 : struct be_svc_callback *callbacks;
49 : struct fo_server *first_resolved;
50 : };
51 :
52 : struct be_failover_ctx {
53 : struct fo_ctx *fo_ctx;
54 : struct be_resolv_ctx *be_res;
55 :
56 : struct be_svc_data *svcs;
57 : struct tevent_timer *primary_server_handler;
58 : };
59 :
60 : static const char *proto_table[] = { FO_PROTO_TCP, FO_PROTO_UDP, NULL };
61 :
62 12 : int be_fo_is_srv_identifier(const char *server)
63 : {
64 12 : return server && strcasecmp(server, BE_SRV_IDENTIFIER) == 0;
65 : }
66 :
67 7 : static int be_fo_get_options(struct be_ctx *ctx,
68 : struct fo_options *opts)
69 : {
70 7 : opts->service_resolv_timeout = dp_opt_get_int(ctx->be_res->opts,
71 : DP_RES_OPT_RESOLVER_TIMEOUT);
72 7 : opts->retry_timeout = 30;
73 7 : opts->srv_retry_neg_timeout = 15;
74 7 : opts->family_order = ctx->be_res->family_order;
75 :
76 7 : return EOK;
77 : }
78 :
79 7 : int be_init_failover(struct be_ctx *ctx)
80 : {
81 : int ret;
82 : struct fo_options fopts;
83 :
84 7 : if (ctx->be_fo != NULL) {
85 0 : return EOK;
86 : }
87 :
88 7 : ctx->be_fo = talloc_zero(ctx, struct be_failover_ctx);
89 7 : if (!ctx->be_fo) {
90 0 : return ENOMEM;
91 : }
92 :
93 7 : ret = be_res_init(ctx);
94 7 : if (ret != EOK) {
95 0 : DEBUG(SSSDBG_FATAL_FAILURE,
96 : "fatal error initializing resolver context\n");
97 0 : talloc_zfree(ctx->be_fo);
98 0 : return ret;
99 : }
100 7 : ctx->be_fo->be_res = ctx->be_res;
101 :
102 7 : ret = be_fo_get_options(ctx, &fopts);
103 7 : if (ret != EOK) {
104 0 : talloc_zfree(ctx->be_fo);
105 0 : return ret;
106 : }
107 :
108 7 : ctx->be_fo->fo_ctx = fo_context_init(ctx->be_fo, &fopts);
109 7 : if (!ctx->be_fo->fo_ctx) {
110 0 : talloc_zfree(ctx->be_fo);
111 0 : return ENOMEM;
112 : }
113 :
114 7 : return EOK;
115 : }
116 :
117 24 : static int be_svc_data_destroy(void *memptr)
118 : {
119 : struct be_svc_data *svc;
120 :
121 24 : svc = talloc_get_type(memptr, struct be_svc_data);
122 :
123 48 : while (svc->callbacks) {
124 : /* callbacks removes themselves from the list,
125 : * so this while will freem them all and then terminate */
126 0 : talloc_free(svc->callbacks);
127 : }
128 :
129 24 : return 0;
130 : }
131 :
132 : /*
133 : * Find registered be_svc_data by service name.
134 : */
135 72 : static struct be_svc_data *be_fo_find_svc_data(struct be_ctx *ctx,
136 : const char *service_name)
137 : {
138 : struct be_svc_data *svc;
139 :
140 72 : if (!ctx || !ctx->be_fo) {
141 0 : return 0;
142 : }
143 :
144 132 : DLIST_FOR_EACH(svc, ctx->be_fo->svcs) {
145 108 : if (strcmp(svc->name, service_name) == 0) {
146 48 : return svc;
147 : }
148 : }
149 :
150 24 : return 0;
151 : }
152 :
153 24 : int be_fo_add_service(struct be_ctx *ctx, const char *service_name,
154 : datacmp_fn user_data_cmp)
155 : {
156 : struct fo_service *service;
157 : struct be_svc_data *svc;
158 : int ret;
159 :
160 24 : svc = be_fo_find_svc_data(ctx, service_name);
161 24 : if (svc) {
162 0 : DEBUG(SSSDBG_TRACE_FUNC, "Failover service already initialized!\n");
163 : /* we already have a service up and configured,
164 : * can happen when using both id and auth provider
165 : */
166 0 : return EOK;
167 : }
168 :
169 : /* if not in the be service list, try to create new one */
170 :
171 24 : ret = fo_new_service(ctx->be_fo->fo_ctx, service_name, user_data_cmp,
172 : &service);
173 24 : if (ret != EOK && ret != EEXIST) {
174 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create failover service!\n");
175 0 : return ret;
176 : }
177 :
178 24 : svc = talloc_zero(ctx->be_fo, struct be_svc_data);
179 24 : if (!svc) {
180 0 : return ENOMEM;
181 : }
182 24 : talloc_set_destructor((TALLOC_CTX *)svc, be_svc_data_destroy);
183 :
184 24 : svc->name = talloc_strdup(svc, service_name);
185 24 : if (!svc->name) {
186 0 : talloc_zfree(svc);
187 0 : return ENOMEM;
188 : }
189 24 : svc->fo_service = service;
190 :
191 24 : DLIST_ADD(ctx->be_fo->svcs, svc);
192 :
193 24 : return EOK;
194 : }
195 :
196 24 : static int be_svc_callback_destroy(void *memptr)
197 : {
198 : struct be_svc_callback *callback;
199 :
200 24 : callback = talloc_get_type(memptr, struct be_svc_callback);
201 :
202 24 : if (callback->svc) {
203 24 : DLIST_REMOVE(callback->svc->callbacks, callback);
204 : }
205 :
206 24 : return 0;
207 : }
208 :
209 24 : int be_fo_service_add_callback(TALLOC_CTX *memctx,
210 : struct be_ctx *ctx, const char *service_name,
211 : be_svc_callback_fn_t *fn, void *private_data)
212 : {
213 : struct be_svc_callback *callback;
214 : struct be_svc_data *svc;
215 :
216 24 : svc = be_fo_find_svc_data(ctx, service_name);
217 24 : if (NULL == svc) {
218 0 : return ENOENT;
219 : }
220 :
221 24 : callback = talloc_zero(memctx, struct be_svc_callback);
222 24 : if (!callback) {
223 0 : return ENOMEM;
224 : }
225 24 : talloc_set_destructor((TALLOC_CTX *)callback, be_svc_callback_destroy);
226 :
227 24 : callback->svc = svc;
228 24 : callback->fn = fn;
229 24 : callback->private_data = private_data;
230 :
231 24 : DLIST_ADD(svc->callbacks, callback);
232 :
233 24 : return EOK;
234 : }
235 :
236 12 : void be_fo_set_srv_lookup_plugin(struct be_ctx *ctx,
237 : fo_srv_lookup_plugin_send_t send_fn,
238 : fo_srv_lookup_plugin_recv_t recv_fn,
239 : void *pvt,
240 : const char *plugin_name)
241 : {
242 : bool bret;
243 :
244 12 : DEBUG(SSSDBG_TRACE_FUNC, "Trying to set SRV lookup plugin to %s\n",
245 : plugin_name);
246 :
247 12 : bret = fo_set_srv_lookup_plugin(ctx->be_fo->fo_ctx, send_fn, recv_fn, pvt);
248 12 : if (bret) {
249 6 : DEBUG(SSSDBG_TRACE_FUNC, "SRV lookup plugin is now %s\n",
250 : plugin_name);
251 : } else {
252 6 : DEBUG(SSSDBG_MINOR_FAILURE, "Unable to set SRV lookup plugin, "
253 : "another plugin may be already in place\n");
254 : }
255 12 : }
256 :
257 0 : errno_t be_fo_set_dns_srv_lookup_plugin(struct be_ctx *be_ctx,
258 : const char *hostname)
259 : {
260 0 : struct fo_resolve_srv_dns_ctx *srv_ctx = NULL;
261 : char resolved_hostname[HOST_NAME_MAX + 1];
262 : errno_t ret;
263 :
264 0 : if (hostname == NULL) {
265 0 : ret = gethostname(resolved_hostname, HOST_NAME_MAX);
266 0 : if (ret != EOK) {
267 0 : ret = errno;
268 0 : DEBUG(SSSDBG_CRIT_FAILURE,
269 : "gethostname() failed: [%d]: %s\n", ret, strerror(ret));
270 0 : return ret;
271 : }
272 0 : resolved_hostname[HOST_NAME_MAX] = '\0';
273 0 : hostname = resolved_hostname;
274 : }
275 :
276 0 : srv_ctx = fo_resolve_srv_dns_ctx_init(be_ctx, be_ctx->be_res->resolv,
277 0 : be_ctx->be_res->family_order,
278 : default_host_dbs, hostname,
279 0 : be_ctx->domain->name);
280 0 : if (srv_ctx == NULL) {
281 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory?\n");
282 0 : return ENOMEM;
283 : }
284 :
285 0 : be_fo_set_srv_lookup_plugin(be_ctx, fo_resolve_srv_dns_send,
286 : fo_resolve_srv_dns_recv, srv_ctx, "DNS");
287 :
288 0 : return EOK;
289 : }
290 :
291 24 : int be_fo_add_srv_server(struct be_ctx *ctx,
292 : const char *service_name,
293 : const char *query_service,
294 : const char *default_discovery_domain,
295 : enum be_fo_protocol proto,
296 : bool proto_fallback, void *user_data)
297 : {
298 : struct be_svc_data *svc;
299 : const char *domain;
300 : int ret;
301 : int i;
302 :
303 24 : svc = be_fo_find_svc_data(ctx, service_name);
304 24 : if (NULL == svc) {
305 0 : return ENOENT;
306 : }
307 :
308 24 : domain = dp_opt_get_string(ctx->be_res->opts, DP_RES_OPT_DNS_DOMAIN);
309 24 : if (!domain) {
310 24 : domain = default_discovery_domain;
311 : }
312 :
313 : /* Add the first protocol as the primary lookup */
314 48 : ret = fo_add_srv_server(svc->fo_service, query_service,
315 24 : domain, ctx->domain->name,
316 : proto_table[proto], user_data);
317 24 : if (ret && ret != EEXIST) {
318 0 : DEBUG(SSSDBG_CRIT_FAILURE,
319 : "Failed to add SRV lookup reference to failover service\n");
320 0 : return ret;
321 : }
322 :
323 24 : if (proto_fallback) {
324 0 : i = (proto + 1) % BE_FO_PROTO_SENTINEL;
325 : /* All the rest as fallback */
326 0 : while (i != proto) {
327 0 : ret = fo_add_srv_server(svc->fo_service, query_service,
328 0 : domain, ctx->domain->name,
329 : proto_table[i], user_data);
330 0 : if (ret && ret != EEXIST) {
331 0 : DEBUG(SSSDBG_CRIT_FAILURE,
332 : "Failed to add SRV lookup reference to failover service\n");
333 0 : return ret;
334 : }
335 :
336 0 : i = (i + 1) % BE_FO_PROTO_SENTINEL;
337 : }
338 : }
339 :
340 24 : return EOK;
341 : }
342 :
343 0 : int be_fo_get_server_count(struct be_ctx *ctx, const char *service_name)
344 : {
345 : struct be_svc_data *svc_data;
346 :
347 0 : svc_data = be_fo_find_svc_data(ctx, service_name);
348 0 : if (!svc_data) {
349 0 : return 0;
350 : }
351 :
352 0 : return fo_get_server_count(svc_data->fo_service);
353 : }
354 :
355 0 : int be_fo_add_server(struct be_ctx *ctx, const char *service_name,
356 : const char *server, int port, void *user_data,
357 : bool primary)
358 : {
359 : struct be_svc_data *svc;
360 : int ret;
361 :
362 0 : svc = be_fo_find_svc_data(ctx, service_name);
363 0 : if (NULL == svc) {
364 0 : return ENOENT;
365 : }
366 :
367 0 : ret = fo_add_server(svc->fo_service, server, port,
368 : user_data, primary);
369 0 : if (ret && ret != EEXIST) {
370 0 : DEBUG(SSSDBG_CRIT_FAILURE,
371 : "Failed to add server to failover service\n");
372 0 : return ret;
373 : }
374 :
375 0 : return EOK;
376 : }
377 :
378 : struct be_resolve_server_state {
379 : struct tevent_context *ev;
380 : struct be_ctx *ctx;
381 :
382 : struct be_svc_data *svc;
383 : int attempts;
384 :
385 : struct fo_server *srv;
386 : bool first_try;
387 : };
388 :
389 : struct be_primary_server_ctx {
390 : struct be_ctx *bctx;
391 : struct tevent_context *ev;
392 :
393 : struct be_svc_data *svc;
394 : unsigned long timeout;
395 :
396 : int attempts;
397 : };
398 :
399 : errno_t be_resolve_server_process(struct tevent_req *subreq,
400 : struct be_resolve_server_state *state,
401 : struct tevent_req **new_subreq);
402 : static void be_primary_server_done(struct tevent_req *subreq);
403 : static errno_t
404 : be_primary_server_timeout_activate(TALLOC_CTX *mem_ctx,
405 : struct tevent_context *ev,
406 : struct be_ctx *bctx,
407 : struct be_svc_data *svc,
408 : const unsigned long timeout_seconds);
409 :
410 : static void
411 0 : be_primary_server_timeout(struct tevent_context *ev,
412 : struct tevent_timer *te,
413 : struct timeval tv, void *pvt)
414 : {
415 0 : struct be_primary_server_ctx *ctx = talloc_get_type(pvt, struct be_primary_server_ctx);
416 : struct tevent_req *subreq;
417 :
418 0 : ctx->bctx->be_fo->primary_server_handler = NULL;
419 :
420 0 : DEBUG(SSSDBG_TRACE_FUNC, "Looking for primary server!\n");
421 0 : subreq = fo_resolve_service_send(ctx->bctx, ctx->ev,
422 0 : ctx->bctx->be_fo->be_res->resolv,
423 0 : ctx->bctx->be_fo->fo_ctx,
424 0 : ctx->svc->fo_service);
425 0 : if (subreq == NULL) {
426 0 : return;
427 : }
428 0 : tevent_req_set_callback(subreq, be_primary_server_done, ctx);
429 : }
430 :
431 0 : static void be_primary_server_done(struct tevent_req *subreq)
432 : {
433 : errno_t ret;
434 : struct be_primary_server_ctx *ctx;
435 : struct be_resolve_server_state *resolve_state;
436 : struct tevent_req *new_subreq;
437 :
438 0 : ctx = tevent_req_callback_data(subreq, struct be_primary_server_ctx);
439 :
440 0 : resolve_state = talloc_zero(ctx->bctx, struct be_resolve_server_state);
441 0 : if (resolve_state == NULL) {
442 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero() failed\n");
443 0 : return;
444 : }
445 :
446 0 : resolve_state->attempts = ctx->attempts;
447 0 : resolve_state->ctx = ctx->bctx;
448 0 : resolve_state->ev = ctx->ev;
449 0 : resolve_state->first_try = true;
450 0 : resolve_state->srv = NULL;
451 0 : resolve_state->svc = ctx->svc;
452 :
453 0 : ret = be_resolve_server_process(subreq, resolve_state, &new_subreq);
454 0 : talloc_free(subreq);
455 0 : if (ret == EAGAIN) {
456 0 : ctx->attempts++;
457 0 : tevent_req_set_callback(new_subreq, be_primary_server_done, ctx);
458 0 : return;
459 0 : } else if (ret == EIO || (ret == EOK &&
460 0 : !fo_is_server_primary(resolve_state->srv))) {
461 :
462 : /* Schedule another lookup
463 : * (either no server could be found or it was not primary)
464 : */
465 0 : ret = be_primary_server_timeout_activate(ctx->bctx, ctx->ev, ctx->bctx,
466 : ctx->svc, ctx->timeout);
467 0 : if (ret != EOK) {
468 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Could not schedule primary server lookup\n");
469 : }
470 0 : } else if (ret == EOK) {
471 0 : be_run_reconnect_cb(ctx->bctx);
472 : }
473 0 : talloc_zfree(ctx);
474 :
475 : /* If an error occurred just end the routine */
476 : }
477 :
478 : static errno_t
479 0 : be_primary_server_timeout_activate(TALLOC_CTX *mem_ctx,
480 : struct tevent_context *ev,
481 : struct be_ctx *bctx,
482 : struct be_svc_data *svc,
483 : const unsigned long timeout_seconds)
484 : {
485 : struct timeval tv;
486 : struct be_primary_server_ctx *ctx;
487 0 : struct be_failover_ctx *fo_ctx = bctx->be_fo;
488 :
489 0 : if (fo_ctx->primary_server_handler != NULL) {
490 0 : DEBUG(SSSDBG_TRACE_FUNC, "The primary server reconnection "
491 : "is already scheduled\n");
492 0 : return EOK;
493 : }
494 :
495 0 : ctx = talloc_zero(mem_ctx, struct be_primary_server_ctx);
496 0 : if (ctx == NULL) {
497 0 : return ENOMEM;
498 : }
499 :
500 0 : ctx->bctx = bctx;
501 0 : ctx->ev = ev;
502 0 : ctx->svc = svc;
503 0 : ctx->timeout = timeout_seconds;
504 :
505 0 : tv = tevent_timeval_current();
506 0 : tv = tevent_timeval_add(&tv, timeout_seconds, 0);
507 0 : fo_ctx->primary_server_handler = tevent_add_timer(ev, bctx, tv,
508 : be_primary_server_timeout, ctx);
509 0 : if (fo_ctx->primary_server_handler == NULL) {
510 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_add_timer failed.\n");
511 0 : talloc_free(ctx);
512 0 : return ENOMEM;
513 : }
514 :
515 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "Primary server reactivation timeout set "
516 : "to %lu seconds\n", timeout_seconds);
517 0 : return EOK;
518 : }
519 :
520 :
521 : static void be_resolve_server_done(struct tevent_req *subreq);
522 :
523 0 : struct tevent_req *be_resolve_server_send(TALLOC_CTX *memctx,
524 : struct tevent_context *ev,
525 : struct be_ctx *ctx,
526 : const char *service_name,
527 : bool first_try)
528 : {
529 : struct tevent_req *req, *subreq;
530 : struct be_resolve_server_state *state;
531 : struct be_svc_data *svc;
532 :
533 0 : req = tevent_req_create(memctx, &state, struct be_resolve_server_state);
534 0 : if (!req) return NULL;
535 :
536 0 : state->ev = ev;
537 0 : state->ctx = ctx;
538 :
539 0 : svc = be_fo_find_svc_data(ctx, service_name);
540 0 : if (NULL == svc) {
541 0 : tevent_req_error(req, EINVAL);
542 0 : tevent_req_post(req, ev);
543 0 : return req;
544 : }
545 :
546 0 : state->svc = svc;
547 0 : state->attempts = 0;
548 0 : state->first_try = first_try;
549 :
550 0 : subreq = fo_resolve_service_send(state, ev,
551 0 : ctx->be_fo->be_res->resolv,
552 0 : ctx->be_fo->fo_ctx,
553 : svc->fo_service);
554 0 : if (!subreq) {
555 0 : talloc_zfree(req);
556 0 : return NULL;
557 : }
558 0 : tevent_req_set_callback(subreq, be_resolve_server_done, req);
559 :
560 0 : return req;
561 : }
562 :
563 0 : static void be_resolve_server_done(struct tevent_req *subreq)
564 : {
565 : struct tevent_req *new_subreq;
566 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
567 : struct tevent_req);
568 0 : struct be_resolve_server_state *state = tevent_req_data(req,
569 : struct be_resolve_server_state);
570 0 : time_t timeout = fo_get_service_retry_timeout(state->svc->fo_service) + 1;
571 : int ret;
572 :
573 0 : ret = be_resolve_server_process(subreq, state, &new_subreq);
574 0 : talloc_zfree(subreq);
575 0 : if (ret == EAGAIN) {
576 0 : tevent_req_set_callback(new_subreq, be_resolve_server_done, req);
577 0 : return;
578 0 : } else if (ret != EOK) {
579 0 : goto fail;
580 : }
581 :
582 0 : if (!fo_is_server_primary(state->srv)) {
583 : /* FIXME: make the timeout configurable */
584 0 : ret = be_primary_server_timeout_activate(state->ctx, state->ev,
585 : state->ctx, state->svc,
586 : timeout);
587 0 : if (ret != EOK) {
588 0 : goto fail;
589 : }
590 : }
591 :
592 0 : tevent_req_done(req);
593 0 : return;
594 :
595 : fail:
596 0 : DEBUG(SSSDBG_TRACE_LIBS, "Server resolution failed: %d\n", ret);
597 0 : state->svc->first_resolved = NULL;
598 0 : tevent_req_error(req, ret);
599 : }
600 :
601 0 : errno_t be_resolve_server_process(struct tevent_req *subreq,
602 : struct be_resolve_server_state *state,
603 : struct tevent_req **new_subreq)
604 : {
605 : errno_t ret;
606 : time_t srv_status_change;
607 : struct be_svc_callback *callback;
608 :
609 0 : ret = fo_resolve_service_recv(subreq, &state->srv);
610 0 : switch (ret) {
611 : case EOK:
612 0 : if (!state->srv) {
613 0 : return EFAULT;
614 : }
615 0 : break;
616 :
617 : case ENOENT:
618 : /* all servers have been tried and none
619 : * was found good, go offline */
620 0 : return EIO;
621 :
622 : default:
623 : /* mark server as bad and retry */
624 0 : if (!state->srv) {
625 0 : return EFAULT;
626 : }
627 0 : DEBUG(SSSDBG_MINOR_FAILURE,
628 : "Couldn't resolve server (%s), resolver returned (%d)\n",
629 : fo_get_server_str_name(state->srv), ret);
630 :
631 0 : state->attempts++;
632 0 : if (state->attempts >= 10) {
633 0 : DEBUG(SSSDBG_OP_FAILURE, "Failed to find a server after 10 attempts\n");
634 0 : return EIO;
635 : }
636 :
637 : /* now try next one */
638 0 : DEBUG(SSSDBG_TRACE_LIBS, "Trying with the next one!\n");
639 0 : subreq = fo_resolve_service_send(state, state->ev,
640 0 : state->ctx->be_fo->be_res->resolv,
641 0 : state->ctx->be_fo->fo_ctx,
642 0 : state->svc->fo_service);
643 0 : if (!subreq) {
644 0 : return ENOMEM;
645 : }
646 :
647 0 : if (new_subreq) {
648 0 : *new_subreq = subreq;
649 : }
650 :
651 0 : return EAGAIN;
652 : }
653 :
654 : /* all fine we got the server */
655 0 : if (state->svc->first_resolved == NULL || state->first_try == true) {
656 0 : DEBUG(SSSDBG_TRACE_LIBS, "Saving the first resolved server\n");
657 0 : state->svc->first_resolved = state->srv;
658 0 : } else if (state->svc->first_resolved == state->srv) {
659 0 : DEBUG(SSSDBG_OP_FAILURE,
660 : "The fail over cycled through all available servers\n");
661 0 : return ENOENT;
662 : }
663 :
664 0 : if (DEBUG_IS_SET(SSSDBG_FUNC_DATA) && fo_get_server_name(state->srv)) {
665 : struct resolv_hostent *srvaddr;
666 : char ipaddr[128];
667 0 : srvaddr = fo_get_server_hostent(state->srv);
668 0 : if (!srvaddr) {
669 0 : DEBUG(SSSDBG_CRIT_FAILURE,
670 : "FATAL: No hostent available for server (%s)\n",
671 : fo_get_server_str_name(state->srv));
672 0 : return EFAULT;
673 : }
674 :
675 0 : inet_ntop(srvaddr->family, srvaddr->addr_list[0]->ipaddr,
676 : ipaddr, 128);
677 :
678 0 : DEBUG(SSSDBG_FUNC_DATA, "Found address for server %s: [%s] TTL %d\n",
679 : fo_get_server_str_name(state->srv), ipaddr,
680 : srvaddr->addr_list[0]->ttl);
681 : }
682 :
683 0 : srv_status_change = fo_get_server_hostname_last_change(state->srv);
684 :
685 : /* now call all svc callbacks if server changed or if it is explicitly
686 : * requested or if the server is the same but changed status since last time*/
687 0 : if (state->srv != state->svc->last_good_srv ||
688 0 : state->svc->run_callbacks ||
689 0 : srv_status_change > state->svc->last_status_change) {
690 0 : state->svc->last_good_srv = state->srv;
691 0 : state->svc->last_status_change = srv_status_change;
692 0 : state->svc->run_callbacks = false;
693 :
694 0 : DLIST_FOR_EACH(callback, state->svc->callbacks) {
695 0 : callback->fn(callback->private_data, state->srv);
696 : }
697 : }
698 :
699 0 : return EOK;
700 : }
701 :
702 0 : int be_resolve_server_recv(struct tevent_req *req, struct fo_server **srv)
703 : {
704 0 : struct be_resolve_server_state *state = tevent_req_data(req,
705 : struct be_resolve_server_state);
706 :
707 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
708 :
709 0 : if (srv) {
710 0 : *srv = state->srv;
711 : }
712 :
713 0 : return EOK;
714 : }
715 :
716 0 : void be_fo_try_next_server(struct be_ctx *ctx, const char *service_name)
717 : {
718 : struct be_svc_data *svc;
719 :
720 0 : svc = be_fo_find_svc_data(ctx, service_name);
721 0 : if (svc) {
722 0 : fo_try_next_server(svc->fo_service);
723 : }
724 0 : }
725 :
726 0 : int be_fo_run_callbacks_at_next_request(struct be_ctx *ctx,
727 : const char *service_name)
728 : {
729 : struct be_svc_data *svc;
730 :
731 0 : svc = be_fo_find_svc_data(ctx, service_name);
732 0 : if (NULL == svc) {
733 0 : return ENOENT;
734 : }
735 :
736 0 : svc->run_callbacks = true;
737 :
738 0 : return EOK;
739 : }
740 :
741 0 : void reset_fo(struct be_ctx *be_ctx)
742 : {
743 0 : fo_reset_services(be_ctx->be_fo->fo_ctx);
744 0 : }
745 :
746 0 : void be_fo_reset_svc(struct be_ctx *be_ctx,
747 : const char *svc_name)
748 : {
749 : struct fo_service *service;
750 : int ret;
751 :
752 0 : DEBUG(SSSDBG_TRACE_LIBS,
753 : "Resetting all servers in service %s\n", svc_name);
754 :
755 0 : ret = fo_get_service(be_ctx->be_fo->fo_ctx, svc_name, &service);
756 0 : if (ret != EOK) {
757 0 : DEBUG(SSSDBG_MINOR_FAILURE,
758 : "Cannot retrieve service [%s]\n", svc_name);
759 0 : return;
760 : }
761 :
762 0 : fo_reset_servers(service);
763 : }
764 :
765 0 : void _be_fo_set_port_status(struct be_ctx *ctx,
766 : const char *service_name,
767 : struct fo_server *server,
768 : enum port_status status,
769 : int line,
770 : const char *file,
771 : const char *function)
772 : {
773 : struct be_svc_data *be_svc;
774 :
775 : /* Print debug info */
776 0 : switch (status) {
777 : case PORT_NEUTRAL:
778 0 : DEBUG(SSSDBG_BE_FO,
779 : "Setting status: PORT_NEUTRAL. Called from: %s: %s: %d\n",
780 : file, function, line);
781 0 : break;
782 : case PORT_WORKING:
783 0 : DEBUG(SSSDBG_BE_FO,
784 : "Setting status: PORT_WORKING. Called from: %s: %s: %d\n",
785 : file, function, line);
786 0 : break;
787 : case PORT_NOT_WORKING:
788 0 : DEBUG(SSSDBG_BE_FO,
789 : "Setting status: PORT_NOT_WORKING. Called from: %s: %s: %d\n",
790 : file, function, line);
791 0 : break;
792 : }
793 :
794 0 : be_svc = be_fo_find_svc_data(ctx, service_name);
795 0 : if (be_svc == NULL) {
796 0 : DEBUG(SSSDBG_OP_FAILURE,
797 : "No service associated with name %s\n", service_name);
798 0 : return;
799 : }
800 :
801 0 : if (!fo_svc_has_server(be_svc->fo_service, server)) {
802 0 : DEBUG(SSSDBG_OP_FAILURE,
803 : "The server %p is not valid anymore, cannot set its status\n",
804 : server);
805 0 : return;
806 : }
807 :
808 : /* Now we know that the server is valid */
809 0 : fo_set_port_status(server, status);
810 :
811 0 : if (status == PORT_WORKING) {
812 : /* We were successful in connecting to the server. Cycle through all
813 : * available servers next time */
814 0 : be_svc->first_resolved = NULL;
815 : }
816 : }
817 :
818 : /* Resolver back end interface */
819 : static struct dp_option dp_res_default_opts[] = {
820 : { "lookup_family_order", DP_OPT_STRING, { "ipv4_first" }, NULL_STRING },
821 : { "dns_resolver_timeout", DP_OPT_NUMBER, { .number = 6 }, NULL_NUMBER },
822 : { "dns_resolver_op_timeout", DP_OPT_NUMBER, { .number = 6 }, NULL_NUMBER },
823 : { "dns_discovery_domain", DP_OPT_STRING, NULL_STRING, NULL_STRING },
824 : DP_OPTION_TERMINATOR
825 : };
826 :
827 7 : static errno_t be_res_get_opts(struct be_resolv_ctx *res_ctx,
828 : struct confdb_ctx *cdb,
829 : const char *conf_path)
830 : {
831 : errno_t ret;
832 : const char *str_family;
833 :
834 7 : ret = dp_get_options(res_ctx, cdb, conf_path,
835 : dp_res_default_opts,
836 : DP_RES_OPTS,
837 : &res_ctx->opts);
838 7 : if (ret != EOK) {
839 0 : return ret;
840 : }
841 :
842 7 : str_family = dp_opt_get_string(res_ctx->opts, DP_RES_OPT_FAMILY_ORDER);
843 7 : DEBUG(SSSDBG_CONF_SETTINGS, "Lookup order: %s\n", str_family);
844 :
845 7 : if (strcasecmp(str_family, "ipv4_first") == 0) {
846 7 : res_ctx->family_order = IPV4_FIRST;
847 0 : } else if (strcasecmp(str_family, "ipv4_only") == 0) {
848 0 : res_ctx->family_order = IPV4_ONLY;
849 0 : } else if (strcasecmp(str_family, "ipv6_first") == 0) {
850 0 : res_ctx->family_order = IPV6_FIRST;
851 0 : } else if (strcasecmp(str_family, "ipv6_only") == 0) {
852 0 : res_ctx->family_order = IPV6_ONLY;
853 : } else {
854 0 : DEBUG(SSSDBG_OP_FAILURE, "Unknown value for option %s: %s\n",
855 : dp_res_default_opts[DP_RES_OPT_FAMILY_ORDER].opt_name, str_family);
856 0 : return EINVAL;
857 : }
858 :
859 7 : return EOK;
860 : }
861 :
862 7 : errno_t be_res_init(struct be_ctx *ctx)
863 : {
864 : errno_t ret;
865 :
866 7 : if (ctx->be_res != NULL) {
867 0 : return EOK;
868 : }
869 :
870 7 : ctx->be_res = talloc_zero(ctx, struct be_resolv_ctx);
871 7 : if (!ctx->be_res) {
872 0 : return ENOMEM;
873 : }
874 :
875 7 : ret = be_res_get_opts(ctx->be_res, ctx->cdb, ctx->conf_path);
876 7 : if (ret != EOK) {
877 0 : talloc_zfree(ctx->be_res);
878 0 : return ret;
879 : }
880 :
881 14 : ret = resolv_init(ctx, ctx->ev,
882 7 : dp_opt_get_int(ctx->be_res->opts,
883 : DP_RES_OPT_RESOLVER_OP_TIMEOUT),
884 7 : &ctx->be_res->resolv);
885 7 : if (ret != EOK) {
886 0 : talloc_zfree(ctx->be_res);
887 0 : return ret;
888 : }
889 :
890 7 : return EOK;
891 : }
|