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