Line data Source code
1 : /*
2 : SSSD
3 :
4 : LDAP ID backend operation retry logic and connection cache
5 :
6 : Authors:
7 : Eugene Indenbom <eindenbom@gmail.com>
8 :
9 : Copyright (C) 2008-2010 Red Hat
10 :
11 : This program is free software; you can redistribute it and/or modify
12 : it under the terms of the GNU General Public License as published by
13 : the Free Software Foundation; either version 3 of the License, or
14 : (at your option) any later version.
15 :
16 : This program is distributed in the hope that it will be useful,
17 : but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : GNU General Public License for more details.
20 :
21 : You should have received a copy of the GNU General Public License
22 : along with this program. If not, see <http://www.gnu.org/licenses/>.
23 : */
24 :
25 : #include "providers/ldap/ldap_common.h"
26 : #include "providers/ldap/sdap_async.h"
27 : #include "providers/ldap/sdap_id_op.h"
28 :
29 : /* LDAP async connection cache */
30 : struct sdap_id_conn_cache {
31 : struct sdap_id_conn_ctx *id_conn;
32 :
33 : /* list of all open connections */
34 : struct sdap_id_conn_data *connections;
35 : /* cached (current) connection */
36 : struct sdap_id_conn_data *cached_connection;
37 : };
38 :
39 : /* LDAP async operation tracker:
40 : * - keeps track of connection usage
41 : * - keeps track of operation retries */
42 : struct sdap_id_op {
43 : /* ID backend context */
44 : struct sdap_id_conn_cache *conn_cache;
45 : /* double linked list pointers */
46 : struct sdap_id_op *prev, *next;
47 : /* current connection */
48 : struct sdap_id_conn_data *conn_data;
49 : /* number of reconnects for this operation */
50 : int reconnect_retry_count;
51 : /* connection request
52 : * It is required as we need to know which requests to notify
53 : * when shared connection request to sdap_handle completes.
54 : * This member is cleared when sdap_id_op_connect_state
55 : * associated with request is destroyed */
56 : struct tevent_req *connect_req;
57 : };
58 :
59 : /* LDAP connection cache connection attempt/established connection data */
60 : struct sdap_id_conn_data {
61 : /* LDAP connection cache */
62 : struct sdap_id_conn_cache *conn_cache;
63 : /* double linked list pointers */
64 : struct sdap_id_conn_data *prev, *next;
65 : /* sdap handle */
66 : struct sdap_handle *sh;
67 : /* connection request */
68 : struct tevent_req *connect_req;
69 : /* timer for connection expiration */
70 : struct tevent_timer *expire_timer;
71 : /* number of running connection notifies */
72 : int notify_lock;
73 : /* list of operations using connect */
74 : struct sdap_id_op *ops;
75 : /* A flag which is signalizing that this
76 : * connection will be disconnected and should
77 : * not be used any more */
78 : bool disconnecting;
79 : };
80 :
81 : static void sdap_id_conn_cache_be_offline_cb(void *pvt);
82 : static void sdap_id_conn_cache_fo_reconnect_cb(void *pvt);
83 :
84 : static void sdap_id_release_conn_data(struct sdap_id_conn_data *conn_data);
85 : static int sdap_id_conn_data_destroy(struct sdap_id_conn_data *conn_data);
86 : static bool sdap_is_connection_expired(struct sdap_id_conn_data *conn_data, int timeout);
87 : static bool sdap_can_reuse_connection(struct sdap_id_conn_data *conn_data);
88 : static void sdap_id_conn_data_expire_handler(struct tevent_context *ev,
89 : struct tevent_timer *te,
90 : struct timeval current_time,
91 : void *pvt);
92 : static int sdap_id_conn_data_set_expire_timer(struct sdap_id_conn_data *conn_data);
93 :
94 : static void sdap_id_op_hook_conn_data(struct sdap_id_op *op, struct sdap_id_conn_data *conn_data);
95 : static int sdap_id_op_destroy(void *pvt);
96 : static bool sdap_id_op_can_reconnect(struct sdap_id_op *op);
97 :
98 : static void sdap_id_op_connect_req_complete(struct sdap_id_op *op, int dp_error, int ret);
99 : static int sdap_id_op_connect_state_destroy(void *pvt);
100 : static int sdap_id_op_connect_step(struct tevent_req *req);
101 : static void sdap_id_op_connect_done(struct tevent_req *subreq);
102 :
103 : /* Create a connection cache */
104 0 : int sdap_id_conn_cache_create(TALLOC_CTX *memctx,
105 : struct sdap_id_conn_ctx *id_conn,
106 : struct sdap_id_conn_cache** conn_cache_out)
107 : {
108 : int ret;
109 0 : struct sdap_id_conn_cache *conn_cache = talloc_zero(memctx, struct sdap_id_conn_cache);
110 0 : if (!conn_cache) {
111 0 : DEBUG(SSSDBG_CRIT_FAILURE,
112 : "talloc_zero(struct sdap_id_conn_cache) failed.\n");
113 0 : ret = ENOMEM;
114 0 : goto fail;
115 : }
116 :
117 0 : conn_cache->id_conn = id_conn;
118 :
119 0 : ret = be_add_offline_cb(conn_cache, id_conn->id_ctx->be,
120 : sdap_id_conn_cache_be_offline_cb, conn_cache,
121 : NULL);
122 0 : if (ret != EOK) {
123 0 : DEBUG(SSSDBG_CRIT_FAILURE, "be_add_offline_cb failed.\n");
124 0 : goto fail;
125 : }
126 :
127 0 : ret = be_add_reconnect_cb(conn_cache, id_conn->id_ctx->be,
128 : sdap_id_conn_cache_fo_reconnect_cb, conn_cache,
129 : NULL);
130 0 : if (ret != EOK) {
131 0 : DEBUG(SSSDBG_CRIT_FAILURE, "be_add_reconnect_cb failed.\n");
132 0 : goto fail;
133 : }
134 :
135 0 : *conn_cache_out = conn_cache;
136 0 : return EOK;
137 :
138 : fail:
139 0 : talloc_zfree(conn_cache);
140 0 : return ret;
141 : }
142 :
143 : /* Callback on BE going offline */
144 0 : static void sdap_id_conn_cache_be_offline_cb(void *pvt)
145 : {
146 0 : struct sdap_id_conn_cache *conn_cache = talloc_get_type(pvt, struct sdap_id_conn_cache);
147 0 : struct sdap_id_conn_data *cached_connection = conn_cache->cached_connection;
148 :
149 : /* Release any cached connection on going offline */
150 0 : if (cached_connection != NULL) {
151 0 : conn_cache->cached_connection = NULL;
152 0 : sdap_id_release_conn_data(cached_connection);
153 : }
154 0 : }
155 :
156 : /* Callback for attempt to reconnect to primary server */
157 0 : static void sdap_id_conn_cache_fo_reconnect_cb(void *pvt)
158 : {
159 0 : struct sdap_id_conn_cache *conn_cache = talloc_get_type(pvt, struct sdap_id_conn_cache);
160 0 : struct sdap_id_conn_data *cached_connection = conn_cache->cached_connection;
161 :
162 : /* Release any cached connection on going offline */
163 0 : if (cached_connection != NULL) {
164 0 : cached_connection->disconnecting = true;
165 : }
166 0 : }
167 :
168 : /* Release sdap_id_conn_data and destroy it if no longer needed */
169 0 : static void sdap_id_release_conn_data(struct sdap_id_conn_data *conn_data)
170 : {
171 : struct sdap_id_conn_cache *conn_cache;
172 0 : if (!conn_data || conn_data->ops || conn_data->notify_lock) {
173 : /* connection is in use */
174 0 : return;
175 : }
176 :
177 0 : conn_cache = conn_data->conn_cache;
178 0 : if (conn_data == conn_cache->cached_connection) {
179 0 : return;
180 : }
181 :
182 0 : DEBUG(SSSDBG_TRACE_ALL, "releasing unused connection\n");
183 :
184 0 : DLIST_REMOVE(conn_cache->connections, conn_data);
185 0 : talloc_zfree(conn_data);
186 : }
187 :
188 : /* Destructor for struct sdap_id_conn_data */
189 0 : static int sdap_id_conn_data_destroy(struct sdap_id_conn_data *conn_data)
190 : {
191 : struct sdap_id_op *op;
192 :
193 : /* we clean out list of ops to make sure that order of destruction does not matter */
194 0 : while ((op = conn_data->ops) != NULL) {
195 0 : op->conn_data = NULL;
196 0 : DLIST_REMOVE(conn_data->ops, op);
197 : }
198 :
199 0 : return 0;
200 : }
201 :
202 : /* Check whether connection will expire after timeout seconds */
203 0 : static bool sdap_is_connection_expired(struct sdap_id_conn_data *conn_data, int timeout)
204 : {
205 : time_t expire_time;
206 0 : if (!conn_data || !conn_data->sh || !conn_data->sh->connected) {
207 0 : return true;
208 : }
209 :
210 0 : expire_time = conn_data->sh->expire_time;
211 0 : if ((expire_time != 0) && (expire_time < time( NULL ) + timeout) ) {
212 0 : return true;
213 : }
214 :
215 0 : return false;
216 : }
217 :
218 : /* Check whether connection can be reused for next LDAP ID operation */
219 0 : static bool sdap_can_reuse_connection(struct sdap_id_conn_data *conn_data)
220 : {
221 : int timeout;
222 :
223 0 : if (!conn_data || !conn_data->sh ||
224 0 : !conn_data->sh->connected || conn_data->disconnecting) {
225 0 : return false;
226 : }
227 :
228 0 : timeout = dp_opt_get_int(conn_data->conn_cache->id_conn->id_ctx->opts->basic,
229 : SDAP_OPT_TIMEOUT);
230 0 : return !sdap_is_connection_expired(conn_data, timeout);
231 : }
232 :
233 : /* Set expiration timer for connection if needed */
234 0 : static int sdap_id_conn_data_set_expire_timer(struct sdap_id_conn_data *conn_data)
235 : {
236 : int timeout;
237 : struct timeval tv;
238 :
239 0 : memset(&tv, 0, sizeof(tv));
240 :
241 0 : tv.tv_sec = conn_data->sh->expire_time;
242 0 : if (tv.tv_sec <= 0) {
243 0 : return EOK;
244 : }
245 :
246 0 : timeout = dp_opt_get_int(conn_data->conn_cache->id_conn->id_ctx->opts->basic,
247 : SDAP_OPT_TIMEOUT);
248 0 : if (timeout > 0) {
249 0 : tv.tv_sec -= timeout;
250 : }
251 :
252 0 : if (tv.tv_sec <= time(NULL)) {
253 0 : return EOK;
254 : }
255 :
256 0 : talloc_zfree(conn_data->expire_timer);
257 :
258 0 : conn_data->expire_timer =
259 0 : tevent_add_timer(conn_data->conn_cache->id_conn->id_ctx->be->ev,
260 : conn_data, tv,
261 : sdap_id_conn_data_expire_handler,
262 : conn_data);
263 0 : if (!conn_data->expire_timer) {
264 0 : return ENOMEM;
265 : }
266 :
267 0 : return EOK;
268 : }
269 :
270 : /* Handler for connection expiration timer */
271 0 : static void sdap_id_conn_data_expire_handler(struct tevent_context *ev,
272 : struct tevent_timer *te,
273 : struct timeval current_time,
274 : void *pvt)
275 : {
276 0 : struct sdap_id_conn_data *conn_data = talloc_get_type(pvt,
277 : struct sdap_id_conn_data);
278 0 : struct sdap_id_conn_cache *conn_cache = conn_data->conn_cache;
279 :
280 0 : DEBUG(SSSDBG_MINOR_FAILURE,
281 : "connection is about to expire, releasing it\n");
282 :
283 0 : if (conn_cache->cached_connection == conn_data) {
284 0 : conn_cache->cached_connection = NULL;
285 :
286 0 : sdap_id_release_conn_data(conn_data);
287 : }
288 0 : }
289 :
290 : /* Create an operation object */
291 0 : struct sdap_id_op *sdap_id_op_create(TALLOC_CTX *memctx, struct sdap_id_conn_cache *conn_cache)
292 : {
293 0 : struct sdap_id_op *op = talloc_zero(memctx, struct sdap_id_op);
294 0 : if (!op) {
295 0 : return NULL;
296 : }
297 :
298 0 : op->conn_cache = conn_cache;
299 :
300 0 : talloc_set_destructor((void*)op, sdap_id_op_destroy);
301 0 : return op;
302 : }
303 :
304 : /* Attach/detach connection to sdap_id_op */
305 0 : static void sdap_id_op_hook_conn_data(struct sdap_id_op *op, struct sdap_id_conn_data *conn_data)
306 : {
307 0 : if (!op) {
308 0 : DEBUG(SSSDBG_FATAL_FAILURE, "NULL op passed!!!\n");
309 0 : return;
310 : }
311 :
312 0 : struct sdap_id_conn_data *current = op->conn_data;
313 0 : if (conn_data == current) {
314 0 : return;
315 : }
316 :
317 0 : if (current) {
318 0 : DLIST_REMOVE(current->ops, op);
319 : }
320 :
321 0 : op->conn_data = conn_data;
322 :
323 0 : if (conn_data) {
324 0 : DLIST_ADD_END(conn_data->ops, op, struct sdap_id_op*);
325 : }
326 :
327 0 : if (current) {
328 0 : sdap_id_release_conn_data(current);
329 : }
330 : }
331 :
332 : /* Destructor for sdap_id_op */
333 0 : static int sdap_id_op_destroy(void *pvt)
334 : {
335 0 : struct sdap_id_op *op = talloc_get_type(pvt, struct sdap_id_op);
336 :
337 0 : if (op->conn_data) {
338 0 : DEBUG(SSSDBG_TRACE_ALL, "releasing operation connection\n");
339 0 : sdap_id_op_hook_conn_data(op, NULL);
340 : }
341 :
342 0 : return 0;
343 : }
344 :
345 : /* Check whether retry with reconnect can be performed for the operation */
346 0 : static bool sdap_id_op_can_reconnect(struct sdap_id_op *op)
347 : {
348 : /* we allow 2 retries for failover server configured:
349 : * - one for connection broken during request execution
350 : * - one for the following (probably failed) reconnect attempt */
351 : int max_retries;
352 : int count;
353 :
354 0 : count = be_fo_get_server_count(op->conn_cache->id_conn->id_ctx->be,
355 0 : op->conn_cache->id_conn->service->name);
356 0 : max_retries = 2 * count -1;
357 0 : if (max_retries < 1) {
358 0 : max_retries = 1;
359 : }
360 :
361 0 : return op->reconnect_retry_count < max_retries;
362 : }
363 :
364 : /* state of connect request */
365 : struct sdap_id_op_connect_state {
366 : struct sdap_id_conn_ctx *id_conn;
367 : struct tevent_context *ev;
368 : struct sdap_id_op *op;
369 : int dp_error;
370 : int result;
371 : };
372 :
373 : /* Destructor for operation connection request */
374 0 : static int sdap_id_op_connect_state_destroy(void *pvt)
375 : {
376 0 : struct sdap_id_op_connect_state *state = talloc_get_type(pvt,
377 : struct sdap_id_op_connect_state);
378 0 : if (state->op != NULL) {
379 : /* clear destroyed connection request */
380 0 : state->op->connect_req = NULL;
381 : }
382 :
383 0 : return 0;
384 : }
385 :
386 : /* Begin to connect to LDAP server */
387 0 : struct tevent_req *sdap_id_op_connect_send(struct sdap_id_op *op,
388 : TALLOC_CTX *memctx,
389 : int *ret_out)
390 : {
391 0 : struct tevent_req *req = NULL;
392 : struct sdap_id_op_connect_state *state;
393 0 : int ret = EOK;
394 :
395 0 : if (!memctx) {
396 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Bug: no memory context passed.\n");
397 0 : ret = EINVAL;
398 0 : goto done;
399 : }
400 :
401 0 : if (op->connect_req) {
402 : /* Connection already in progress, invalid operation */
403 0 : DEBUG(SSSDBG_CRIT_FAILURE,
404 : "Bug: connection request is already running or completed and leaked.\n");
405 0 : ret = EINVAL;
406 0 : goto done;
407 : }
408 :
409 0 : req = tevent_req_create(memctx, &state, struct sdap_id_op_connect_state);
410 0 : if (!req) {
411 0 : ret = ENOMEM;
412 0 : goto done;
413 : }
414 :
415 0 : talloc_set_destructor((void*)state, sdap_id_op_connect_state_destroy);
416 :
417 0 : state->id_conn = op->conn_cache->id_conn;
418 0 : state->ev = state->id_conn->id_ctx->be->ev;
419 0 : state->op = op;
420 0 : op->connect_req = req;
421 :
422 0 : if (op->conn_data) {
423 : /* If the operation is already connected,
424 : * reuse existing connection regardless of its status */
425 0 : DEBUG(SSSDBG_TRACE_ALL, "reusing operation connection\n");
426 0 : ret = EOK;
427 0 : goto done;
428 : }
429 :
430 0 : ret = sdap_id_op_connect_step(req);
431 0 : if (ret != EOK) {
432 0 : goto done;
433 : }
434 :
435 : done:
436 0 : if (ret != EOK) {
437 0 : talloc_zfree(req);
438 0 : } else if (op->conn_data && !op->conn_data->connect_req) {
439 : /* Connection is already established */
440 0 : tevent_req_done(req);
441 0 : tevent_req_post(req, state->ev);
442 : }
443 :
444 0 : if (ret_out) {
445 0 : *ret_out = ret;
446 : }
447 :
448 0 : return req;
449 : }
450 :
451 : /* Begin a connection retry to LDAP server */
452 0 : static int sdap_id_op_connect_step(struct tevent_req *req)
453 : {
454 0 : struct sdap_id_op_connect_state *state =
455 0 : tevent_req_data(req, struct sdap_id_op_connect_state);
456 0 : struct sdap_id_op *op = state->op;
457 0 : struct sdap_id_conn_cache *conn_cache = op->conn_cache;
458 :
459 0 : int ret = EOK;
460 : struct sdap_id_conn_data *conn_data;
461 0 : struct tevent_req *subreq = NULL;
462 :
463 : /* Try to reuse context cached connection */
464 0 : conn_data = conn_cache->cached_connection;
465 0 : if (conn_data) {
466 0 : if (conn_data->connect_req) {
467 0 : DEBUG(SSSDBG_TRACE_ALL, "waiting for connection to complete\n");
468 0 : sdap_id_op_hook_conn_data(op, conn_data);
469 0 : goto done;
470 : }
471 :
472 0 : if (sdap_can_reuse_connection(conn_data)) {
473 0 : DEBUG(SSSDBG_TRACE_ALL, "reusing cached connection\n");
474 0 : sdap_id_op_hook_conn_data(op, conn_data);
475 0 : goto done;
476 : }
477 :
478 0 : DEBUG(SSSDBG_TRACE_ALL, "releasing expired cached connection\n");
479 0 : conn_cache->cached_connection = NULL;
480 0 : sdap_id_release_conn_data(conn_data);
481 : }
482 :
483 0 : DEBUG(SSSDBG_TRACE_ALL, "beginning to connect\n");
484 :
485 0 : conn_data = talloc_zero(conn_cache, struct sdap_id_conn_data);
486 0 : if (!conn_data) {
487 0 : ret = ENOMEM;
488 0 : goto done;
489 : }
490 :
491 0 : talloc_set_destructor(conn_data, sdap_id_conn_data_destroy);
492 :
493 0 : conn_data->conn_cache = conn_cache;
494 0 : subreq = sdap_cli_connect_send(conn_data, state->ev,
495 0 : state->id_conn->id_ctx->opts,
496 0 : state->id_conn->id_ctx->be,
497 0 : state->id_conn->service, false,
498 : CON_TLS_DFL, false);
499 :
500 0 : if (!subreq) {
501 0 : ret = ENOMEM;
502 0 : goto done;
503 : }
504 :
505 0 : tevent_req_set_callback(subreq, sdap_id_op_connect_done, conn_data);
506 0 : conn_data->connect_req = subreq;
507 :
508 0 : DLIST_ADD(conn_cache->connections, conn_data);
509 0 : conn_cache->cached_connection = conn_data;
510 :
511 0 : sdap_id_op_hook_conn_data(op, conn_data);
512 :
513 : done:
514 0 : if (ret != EOK && conn_data) {
515 0 : sdap_id_release_conn_data(conn_data);
516 : }
517 :
518 0 : if (ret != EOK) {
519 0 : talloc_zfree(subreq);
520 : }
521 :
522 0 : return ret;
523 : }
524 :
525 : static void sdap_id_op_connect_reinit_done(struct tevent_req *req);
526 :
527 : /* Subrequest callback for connection completion */
528 0 : static void sdap_id_op_connect_done(struct tevent_req *subreq)
529 : {
530 0 : struct sdap_id_conn_data *conn_data =
531 0 : tevent_req_callback_data(subreq, struct sdap_id_conn_data);
532 0 : struct sdap_id_conn_cache *conn_cache = conn_data->conn_cache;
533 0 : struct sdap_server_opts *srv_opts = NULL;
534 0 : struct sdap_server_opts *current_srv_opts = NULL;
535 0 : bool can_retry = false;
536 0 : bool is_offline = false;
537 0 : struct tevent_req *reinit_req = NULL;
538 0 : bool reinit = false;
539 : int ret;
540 :
541 0 : ret = sdap_cli_connect_recv(subreq, conn_data, &can_retry,
542 : &conn_data->sh, &srv_opts);
543 0 : conn_data->connect_req = NULL;
544 0 : talloc_zfree(subreq);
545 :
546 0 : conn_data->notify_lock++;
547 :
548 0 : if (ret == ENOTSUP) {
549 0 : DEBUG(SSSDBG_FATAL_FAILURE,
550 : "Authentication mechanism not Supported by server\n");
551 : }
552 :
553 0 : if (ret == EOK && (!conn_data->sh || !conn_data->sh->connected)) {
554 0 : DEBUG(SSSDBG_FATAL_FAILURE,
555 : "sdap_cli_connect_recv returned bogus connection\n");
556 0 : ret = EFAULT;
557 : }
558 :
559 0 : if (ret != EOK && !can_retry) {
560 0 : if (conn_cache->id_conn->ignore_mark_offline) {
561 0 : DEBUG(SSSDBG_TRACE_FUNC,
562 : "Failed to connect to server, but ignore mark offline "
563 : "is enabled.\n");
564 : } else {
565 : /* be is going offline as there is no more servers to try */
566 0 : DEBUG(SSSDBG_CRIT_FAILURE,
567 : "Failed to connect, going offline (%d [%s])\n",
568 : ret, strerror(ret));
569 0 : is_offline = true;
570 0 : be_mark_offline(conn_cache->id_conn->id_ctx->be);
571 : }
572 : }
573 :
574 0 : if (ret == EOK) {
575 0 : current_srv_opts = conn_cache->id_conn->id_ctx->srv_opts;
576 0 : if (current_srv_opts) {
577 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
578 : "Old USN: %lu, New USN: %lu\n", current_srv_opts->last_usn, srv_opts->last_usn);
579 :
580 0 : if (strcmp(srv_opts->server_id, current_srv_opts->server_id) == 0 &&
581 0 : srv_opts->supports_usn &&
582 0 : current_srv_opts->last_usn > srv_opts->last_usn) {
583 0 : DEBUG(SSSDBG_FUNC_DATA, "Server was probably re-initialized\n");
584 :
585 0 : current_srv_opts->max_user_value = 0;
586 0 : current_srv_opts->max_group_value = 0;
587 0 : current_srv_opts->max_service_value = 0;
588 0 : current_srv_opts->max_sudo_value = 0;
589 0 : current_srv_opts->last_usn = srv_opts->last_usn;
590 :
591 0 : reinit = true;
592 : }
593 : }
594 0 : ret = sdap_id_conn_data_set_expire_timer(conn_data);
595 0 : sdap_steal_server_opts(conn_cache->id_conn->id_ctx, &srv_opts);
596 : }
597 :
598 0 : if (can_retry) {
599 0 : switch (ret) {
600 : case EOK:
601 : case ENOTSUP:
602 : case EACCES:
603 : case EIO:
604 : case EFAULT:
605 : case ETIMEDOUT:
606 : case ERR_AUTH_FAILED:
607 0 : break;
608 :
609 : default:
610 : /* do not attempt to retry on errors like ENOMEM */
611 0 : can_retry = false;
612 0 : is_offline = true;
613 0 : be_mark_offline(conn_cache->id_conn->id_ctx->be);
614 0 : break;
615 : }
616 : }
617 :
618 0 : int notify_count = 0;
619 :
620 : /* Notify about connection */
621 : for(;;) {
622 : struct sdap_id_op *op;
623 :
624 0 : if (ret == EOK && !conn_data->sh->connected) {
625 0 : DEBUG(SSSDBG_TRACE_ALL,
626 : "connection was broken after %d notifies\n", notify_count);
627 : }
628 :
629 0 : DLIST_FOR_EACH(op, conn_data->ops) {
630 0 : if (op->connect_req) {
631 0 : break;
632 : }
633 : }
634 :
635 0 : if (!op) {
636 0 : break;
637 : }
638 :
639 : /* another operation to notify */
640 0 : notify_count++;
641 :
642 0 : if (ret != EOK || !conn_data->sh->connected) {
643 : /* failed to connect or connection got broken during notify */
644 0 : bool retry = false;
645 :
646 : /* drop connection from cache now */
647 0 : if (conn_cache->cached_connection == conn_data) {
648 0 : conn_cache->cached_connection = NULL;
649 : }
650 :
651 0 : if (can_retry) {
652 : /* determining whether retry is possible */
653 0 : if (be_is_offline(conn_cache->id_conn->id_ctx->be)) {
654 : /* be is offline, no retry possible */
655 0 : if (ret == EOK) {
656 0 : DEBUG(SSSDBG_TRACE_ALL,
657 : "skipping automatic retry on op #%d as be is offline\n", notify_count);
658 0 : ret = EIO;
659 : }
660 :
661 0 : can_retry = false;
662 0 : is_offline = true;
663 : } else {
664 0 : if (ret == EOK) {
665 0 : DEBUG(SSSDBG_TRACE_ALL,
666 : "attempting automatic retry on op #%d\n", notify_count);
667 0 : retry = true;
668 0 : } else if (sdap_id_op_can_reconnect(op)) {
669 0 : DEBUG(SSSDBG_TRACE_ALL,
670 : "attempting failover retry on op #%d\n", notify_count);
671 0 : op->reconnect_retry_count++;
672 0 : retry = true;
673 : }
674 : }
675 : }
676 :
677 0 : if (retry && op->connect_req) {
678 0 : int retry_ret = sdap_id_op_connect_step(op->connect_req);
679 0 : if (retry_ret != EOK) {
680 0 : can_retry = false;
681 0 : sdap_id_op_connect_req_complete(op, DP_ERR_FATAL, retry_ret);
682 : }
683 :
684 0 : continue;
685 : }
686 : }
687 :
688 0 : if (ret == EOK) {
689 0 : DEBUG(SSSDBG_TRACE_ALL,
690 : "notify connected to op #%d\n", notify_count);
691 0 : sdap_id_op_connect_req_complete(op, DP_ERR_OK, ret);
692 0 : } else if (is_offline) {
693 0 : DEBUG(SSSDBG_TRACE_ALL, "notify offline to op #%d\n", notify_count);
694 0 : sdap_id_op_connect_req_complete(op, DP_ERR_OFFLINE, EAGAIN);
695 : } else {
696 0 : DEBUG(SSSDBG_TRACE_ALL,
697 : "notify error to op #%d: %d [%s]\n", notify_count, ret, strerror(ret));
698 0 : sdap_id_op_connect_req_complete(op, DP_ERR_FATAL, ret);
699 : }
700 0 : }
701 :
702 : /* all operations notified */
703 0 : if (conn_data->notify_lock > 0) {
704 0 : conn_data->notify_lock--;
705 : }
706 :
707 0 : if ((ret == EOK) &&
708 0 : conn_data->sh->connected &&
709 0 : !be_is_offline(conn_cache->id_conn->id_ctx->be)) {
710 0 : DEBUG(SSSDBG_TRACE_ALL,
711 : "caching successful connection after %d notifies\n", notify_count);
712 0 : conn_cache->cached_connection = conn_data;
713 :
714 : /* Run any post-connection routines */
715 0 : be_run_unconditional_online_cb(conn_cache->id_conn->id_ctx->be);
716 0 : be_run_online_cb(conn_cache->id_conn->id_ctx->be);
717 :
718 : } else {
719 0 : if (conn_cache->cached_connection == conn_data) {
720 0 : conn_cache->cached_connection = NULL;
721 : }
722 :
723 0 : sdap_id_release_conn_data(conn_data);
724 : }
725 :
726 0 : if (reinit) {
727 0 : DEBUG(SSSDBG_TRACE_FUNC, "Server reinitialization detected. "
728 : "Cleaning cache.\n");
729 0 : reinit_req = sdap_reinit_cleanup_send(conn_cache->id_conn->id_ctx->be,
730 0 : conn_cache->id_conn->id_ctx->be,
731 0 : conn_cache->id_conn->id_ctx);
732 0 : if (reinit_req == NULL) {
733 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to perform reinitialization "
734 : "clean up.\n");
735 0 : return;
736 : }
737 :
738 0 : tevent_req_set_callback(reinit_req, sdap_id_op_connect_reinit_done,
739 : NULL);
740 : }
741 : }
742 :
743 0 : static void sdap_id_op_connect_reinit_done(struct tevent_req *req)
744 : {
745 : errno_t ret;
746 :
747 0 : ret = sdap_reinit_cleanup_recv(req);
748 0 : talloc_zfree(req);
749 0 : if (ret != EOK) {
750 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to perform reinitialization "
751 : "clean up [%d]: %s\n", ret, strerror(ret));
752 : /* not fatal */
753 0 : return;
754 : }
755 :
756 0 : DEBUG(SSSDBG_TRACE_FUNC, "Reinitialization clean up completed\n");
757 : }
758 :
759 : /* Mark operation connection request as complete */
760 0 : static void sdap_id_op_connect_req_complete(struct sdap_id_op *op, int dp_error, int ret)
761 : {
762 0 : struct tevent_req *req = op->connect_req;
763 : struct sdap_id_op_connect_state *state;
764 :
765 0 : if (!req) {
766 0 : return;
767 : }
768 :
769 0 : op->connect_req = NULL;
770 :
771 0 : state = tevent_req_data(req, struct sdap_id_op_connect_state);
772 0 : state->dp_error = dp_error;
773 0 : state->result = ret;
774 :
775 0 : if (ret == EOK) {
776 0 : tevent_req_done(req);
777 : } else {
778 0 : sdap_id_op_hook_conn_data(op, NULL);
779 0 : tevent_req_error(req, ret);
780 : }
781 : }
782 :
783 : /* Get the result of an asynchronous connect operation on sdap_id_op
784 : *
785 : * In dp_error data provider error code is returned:
786 : * DP_ERR_OK - connection established
787 : * DP_ERR_OFFLINE - backend is offline, operation result is set EAGAIN
788 : * DP_ERR_FATAL - operation failed
789 : */
790 0 : int sdap_id_op_connect_recv(struct tevent_req *req, int *dp_error)
791 : {
792 0 : struct sdap_id_op_connect_state *state = tevent_req_data(req,
793 : struct sdap_id_op_connect_state);
794 :
795 0 : *dp_error = state->dp_error;
796 0 : return state->result;
797 : }
798 :
799 : /* Report completion of LDAP operation and release associated connection.
800 : * Returns operation result (possible updated) passed in ret parameter.
801 : *
802 : * In dp_error data provider error code is returned:
803 : * DP_ERR_OK (operation result = EOK) - operation completed
804 : * DP_ERR_OK (operation result != EOK) - operation can be retried
805 : * DP_ERR_OFFLINE - backend is offline, operation result is set EAGAIN
806 : * DP_ERR_FATAL - operation failed */
807 0 : int sdap_id_op_done(struct sdap_id_op *op, int retval, int *dp_err_out)
808 : {
809 : bool communication_error;
810 0 : struct sdap_id_conn_data *current_conn = op->conn_data;
811 0 : switch (retval) {
812 : case EIO:
813 : case ETIMEDOUT:
814 : /* this currently the only possible communication error after connection is established */
815 0 : communication_error = true;
816 0 : break;
817 :
818 : default:
819 0 : communication_error = false;
820 0 : break;
821 : }
822 :
823 0 : if (communication_error && current_conn != 0
824 0 : && current_conn == op->conn_cache->cached_connection) {
825 : /* do not reuse failed connection */
826 0 : op->conn_cache->cached_connection = NULL;
827 :
828 0 : DEBUG(SSSDBG_FUNC_DATA,
829 : "communication error on cached connection, moving to next server\n");
830 0 : be_fo_try_next_server(op->conn_cache->id_conn->id_ctx->be,
831 0 : op->conn_cache->id_conn->service->name);
832 : }
833 :
834 : int dp_err;
835 0 : if (retval == EOK) {
836 0 : dp_err = DP_ERR_OK;
837 0 : } else if (be_is_offline(op->conn_cache->id_conn->id_ctx->be)) {
838 : /* if backend is already offline, just report offline, do not duplicate errors */
839 0 : dp_err = DP_ERR_OFFLINE;
840 0 : retval = EAGAIN;
841 0 : DEBUG(SSSDBG_TRACE_ALL, "falling back to offline data...\n");
842 0 : } else if (communication_error) {
843 : /* communication error, can try to reconnect */
844 :
845 0 : if (!sdap_id_op_can_reconnect(op)) {
846 0 : dp_err = DP_ERR_FATAL;
847 0 : DEBUG(SSSDBG_TRACE_ALL,
848 : "too many communication failures, giving up...\n");
849 : } else {
850 0 : dp_err = DP_ERR_OK;
851 0 : retval = EAGAIN;
852 : }
853 : } else {
854 0 : dp_err = DP_ERR_FATAL;
855 : }
856 :
857 0 : if (dp_err == DP_ERR_OK && retval != EOK) {
858 : /* reconnect retry */
859 0 : op->reconnect_retry_count++;
860 0 : DEBUG(SSSDBG_TRACE_ALL,
861 : "advising for connection retry #%i\n", op->reconnect_retry_count);
862 : } else {
863 : /* end of request */
864 0 : op->reconnect_retry_count = 0;
865 : }
866 :
867 0 : if (current_conn) {
868 0 : DEBUG(SSSDBG_TRACE_ALL, "releasing operation connection\n");
869 0 : sdap_id_op_hook_conn_data(op, NULL);
870 : }
871 :
872 0 : *dp_err_out = dp_err;
873 0 : return retval;
874 : }
875 :
876 : /* Get SDAP handle associated with operation by sdap_id_op_connect */
877 0 : struct sdap_handle *sdap_id_op_handle(struct sdap_id_op *op)
878 : {
879 0 : return op && op->conn_data ? op->conn_data->sh : NULL;
880 : }
|