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 24 : int sdap_id_conn_cache_create(TALLOC_CTX *memctx,
105 : struct sdap_id_ctx *id_ctx,
106 : struct sdap_id_conn_ctx *id_conn,
107 : struct sdap_id_conn_cache** conn_cache_out)
108 : {
109 : int ret;
110 24 : struct sdap_id_conn_cache *conn_cache = talloc_zero(memctx, struct sdap_id_conn_cache);
111 24 : if (!conn_cache) {
112 0 : DEBUG(SSSDBG_CRIT_FAILURE,
113 : "talloc_zero(struct sdap_id_conn_cache) failed.\n");
114 0 : ret = ENOMEM;
115 0 : goto fail;
116 : }
117 :
118 24 : conn_cache->id_conn = id_conn;
119 :
120 24 : ret = be_add_offline_cb(conn_cache, id_conn->id_ctx->be,
121 : sdap_id_conn_cache_be_offline_cb, conn_cache,
122 : NULL);
123 24 : if (ret != EOK) {
124 0 : DEBUG(SSSDBG_CRIT_FAILURE, "be_add_offline_cb failed.\n");
125 0 : goto fail;
126 : }
127 :
128 24 : ret = be_add_reconnect_cb(conn_cache, id_conn->id_ctx->be,
129 : sdap_id_conn_cache_fo_reconnect_cb, conn_cache,
130 : NULL);
131 24 : if (ret != EOK) {
132 0 : DEBUG(SSSDBG_CRIT_FAILURE, "be_add_reconnect_cb failed.\n");
133 0 : goto fail;
134 : }
135 :
136 24 : *conn_cache_out = conn_cache;
137 24 : return EOK;
138 :
139 : fail:
140 0 : talloc_zfree(conn_cache);
141 0 : return ret;
142 : }
143 :
144 : /* Callback on BE going offline */
145 0 : static void sdap_id_conn_cache_be_offline_cb(void *pvt)
146 : {
147 0 : struct sdap_id_conn_cache *conn_cache = talloc_get_type(pvt, struct sdap_id_conn_cache);
148 0 : struct sdap_id_conn_data *cached_connection = conn_cache->cached_connection;
149 :
150 : /* Release any cached connection on going offline */
151 0 : if (cached_connection != NULL) {
152 0 : conn_cache->cached_connection = NULL;
153 0 : sdap_id_release_conn_data(cached_connection);
154 : }
155 0 : }
156 :
157 : /* Callback for attempt to reconnect to primary server */
158 0 : static void sdap_id_conn_cache_fo_reconnect_cb(void *pvt)
159 : {
160 0 : struct sdap_id_conn_cache *conn_cache = talloc_get_type(pvt, struct sdap_id_conn_cache);
161 0 : struct sdap_id_conn_data *cached_connection = conn_cache->cached_connection;
162 :
163 : /* Release any cached connection on going offline */
164 0 : if (cached_connection != NULL) {
165 0 : cached_connection->disconnecting = true;
166 : }
167 0 : }
168 :
169 : /* Release sdap_id_conn_data and destroy it if no longer needed */
170 0 : static void sdap_id_release_conn_data(struct sdap_id_conn_data *conn_data)
171 : {
172 : struct sdap_id_conn_cache *conn_cache;
173 0 : if (!conn_data || conn_data->ops || conn_data->notify_lock) {
174 : /* connection is in use */
175 0 : return;
176 : }
177 :
178 0 : conn_cache = conn_data->conn_cache;
179 0 : if (conn_data == conn_cache->cached_connection) {
180 0 : return;
181 : }
182 :
183 0 : DEBUG(SSSDBG_TRACE_ALL, "releasing unused connection\n");
184 :
185 0 : DLIST_REMOVE(conn_cache->connections, conn_data);
186 0 : talloc_zfree(conn_data);
187 : }
188 :
189 : /* Destructor for struct sdap_id_conn_data */
190 0 : static int sdap_id_conn_data_destroy(struct sdap_id_conn_data *conn_data)
191 : {
192 : struct sdap_id_op *op;
193 :
194 : /* we clean out list of ops to make sure that order of destruction does not matter */
195 0 : while ((op = conn_data->ops) != NULL) {
196 0 : op->conn_data = NULL;
197 0 : DLIST_REMOVE(conn_data->ops, op);
198 : }
199 :
200 0 : return 0;
201 : }
202 :
203 : /* Check whether connection will expire after timeout seconds */
204 0 : static bool sdap_is_connection_expired(struct sdap_id_conn_data *conn_data, int timeout)
205 : {
206 : time_t expire_time;
207 0 : if (!conn_data || !conn_data->sh || !conn_data->sh->connected) {
208 0 : return true;
209 : }
210 :
211 0 : expire_time = conn_data->sh->expire_time;
212 0 : if ((expire_time != 0) && (expire_time < time( NULL ) + timeout) ) {
213 0 : return true;
214 : }
215 :
216 0 : return false;
217 : }
218 :
219 : /* Check whether connection can be reused for next LDAP ID operation */
220 0 : static bool sdap_can_reuse_connection(struct sdap_id_conn_data *conn_data)
221 : {
222 : int timeout;
223 :
224 0 : if (!conn_data || !conn_data->sh ||
225 0 : !conn_data->sh->connected || conn_data->disconnecting) {
226 0 : return false;
227 : }
228 :
229 0 : timeout = dp_opt_get_int(conn_data->conn_cache->id_conn->id_ctx->opts->basic,
230 : SDAP_OPT_TIMEOUT);
231 0 : return !sdap_is_connection_expired(conn_data, timeout);
232 : }
233 :
234 : /* Set expiration timer for connection if needed */
235 0 : static int sdap_id_conn_data_set_expire_timer(struct sdap_id_conn_data *conn_data)
236 : {
237 : int timeout;
238 : struct timeval tv;
239 :
240 0 : memset(&tv, 0, sizeof(tv));
241 :
242 0 : tv.tv_sec = conn_data->sh->expire_time;
243 0 : if (tv.tv_sec <= 0) {
244 0 : return EOK;
245 : }
246 :
247 0 : timeout = dp_opt_get_int(conn_data->conn_cache->id_conn->id_ctx->opts->basic,
248 : SDAP_OPT_TIMEOUT);
249 0 : if (timeout > 0) {
250 0 : tv.tv_sec -= timeout;
251 : }
252 :
253 0 : if (tv.tv_sec <= time(NULL)) {
254 0 : return EOK;
255 : }
256 :
257 0 : talloc_zfree(conn_data->expire_timer);
258 :
259 0 : conn_data->expire_timer =
260 0 : tevent_add_timer(conn_data->conn_cache->id_conn->id_ctx->be->ev,
261 : conn_data, tv,
262 : sdap_id_conn_data_expire_handler,
263 : conn_data);
264 0 : if (!conn_data->expire_timer) {
265 0 : return ENOMEM;
266 : }
267 :
268 0 : return EOK;
269 : }
270 :
271 : /* Handler for connection expiration timer */
272 0 : static void sdap_id_conn_data_expire_handler(struct tevent_context *ev,
273 : struct tevent_timer *te,
274 : struct timeval current_time,
275 : void *pvt)
276 : {
277 0 : struct sdap_id_conn_data *conn_data = talloc_get_type(pvt,
278 : struct sdap_id_conn_data);
279 0 : struct sdap_id_conn_cache *conn_cache = conn_data->conn_cache;
280 :
281 0 : DEBUG(SSSDBG_MINOR_FAILURE,
282 : "connection is about to expire, releasing it\n");
283 :
284 0 : if (conn_cache->cached_connection == conn_data) {
285 0 : conn_cache->cached_connection = NULL;
286 :
287 0 : sdap_id_release_conn_data(conn_data);
288 : }
289 0 : }
290 :
291 : /* Create an operation object */
292 0 : struct sdap_id_op *sdap_id_op_create(TALLOC_CTX *memctx, struct sdap_id_conn_cache *conn_cache)
293 : {
294 0 : struct sdap_id_op *op = talloc_zero(memctx, struct sdap_id_op);
295 0 : if (!op) {
296 0 : return NULL;
297 : }
298 :
299 0 : op->conn_cache = conn_cache;
300 :
301 0 : talloc_set_destructor((void*)op, sdap_id_op_destroy);
302 0 : return op;
303 : }
304 :
305 : /* Attach/detach connection to sdap_id_op */
306 0 : static void sdap_id_op_hook_conn_data(struct sdap_id_op *op, struct sdap_id_conn_data *conn_data)
307 : {
308 0 : if (!op) {
309 0 : DEBUG(SSSDBG_FATAL_FAILURE, "NULL op passed!!!\n");
310 0 : return;
311 : }
312 :
313 0 : struct sdap_id_conn_data *current = op->conn_data;
314 0 : if (conn_data == current) {
315 0 : return;
316 : }
317 :
318 0 : if (current) {
319 0 : DLIST_REMOVE(current->ops, op);
320 : }
321 :
322 0 : op->conn_data = conn_data;
323 :
324 0 : if (conn_data) {
325 0 : DLIST_ADD_END(conn_data->ops, op, struct sdap_id_op*);
326 : }
327 :
328 0 : if (current) {
329 0 : sdap_id_release_conn_data(current);
330 : }
331 : }
332 :
333 : /* Destructor for sdap_id_op */
334 0 : static int sdap_id_op_destroy(void *pvt)
335 : {
336 0 : struct sdap_id_op *op = talloc_get_type(pvt, struct sdap_id_op);
337 :
338 0 : if (op->conn_data) {
339 0 : DEBUG(SSSDBG_TRACE_ALL, "releasing operation connection\n");
340 0 : sdap_id_op_hook_conn_data(op, NULL);
341 : }
342 :
343 0 : return 0;
344 : }
345 :
346 : /* Check whether retry with reconnect can be performed for the operation */
347 0 : static bool sdap_id_op_can_reconnect(struct sdap_id_op *op)
348 : {
349 : /* we allow 2 retries for failover server configured:
350 : * - one for connection broken during request execution
351 : * - one for the following (probably failed) reconnect attempt */
352 : int max_retries;
353 : int count;
354 :
355 0 : count = be_fo_get_server_count(op->conn_cache->id_conn->id_ctx->be,
356 0 : op->conn_cache->id_conn->service->name);
357 0 : max_retries = 2 * count -1;
358 0 : if (max_retries < 1) {
359 0 : max_retries = 1;
360 : }
361 :
362 0 : return op->reconnect_retry_count < max_retries;
363 : }
364 :
365 : /* state of connect request */
366 : struct sdap_id_op_connect_state {
367 : struct sdap_id_conn_ctx *id_conn;
368 : struct tevent_context *ev;
369 : struct sdap_id_op *op;
370 : int dp_error;
371 : int result;
372 : };
373 :
374 : /* Destructor for operation connection request */
375 0 : static int sdap_id_op_connect_state_destroy(void *pvt)
376 : {
377 0 : struct sdap_id_op_connect_state *state = talloc_get_type(pvt,
378 : struct sdap_id_op_connect_state);
379 0 : if (state->op != NULL) {
380 : /* clear destroyed connection request */
381 0 : state->op->connect_req = NULL;
382 : }
383 :
384 0 : return 0;
385 : }
386 :
387 : /* Begin to connect to LDAP server */
388 0 : struct tevent_req *sdap_id_op_connect_send(struct sdap_id_op *op,
389 : TALLOC_CTX *memctx,
390 : int *ret_out)
391 : {
392 0 : struct tevent_req *req = NULL;
393 : struct sdap_id_op_connect_state *state;
394 0 : int ret = EOK;
395 :
396 0 : if (!memctx) {
397 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Bug: no memory context passed.\n");
398 0 : ret = EINVAL;
399 0 : goto done;
400 : }
401 :
402 0 : if (op->connect_req) {
403 : /* Connection already in progress, invalid operation */
404 0 : DEBUG(SSSDBG_CRIT_FAILURE,
405 : "Bug: connection request is already running or completed and leaked.\n");
406 0 : ret = EINVAL;
407 0 : goto done;
408 : }
409 :
410 0 : req = tevent_req_create(memctx, &state, struct sdap_id_op_connect_state);
411 0 : if (!req) {
412 0 : ret = ENOMEM;
413 0 : goto done;
414 : }
415 :
416 0 : talloc_set_destructor((void*)state, sdap_id_op_connect_state_destroy);
417 :
418 0 : state->id_conn = op->conn_cache->id_conn;
419 0 : state->ev = state->id_conn->id_ctx->be->ev;
420 0 : state->op = op;
421 0 : op->connect_req = req;
422 :
423 0 : if (op->conn_data) {
424 : /* If the operation is already connected,
425 : * reuse existing connection regardless of its status */
426 0 : DEBUG(SSSDBG_TRACE_ALL, "reusing operation connection\n");
427 0 : ret = EOK;
428 0 : goto done;
429 : }
430 :
431 0 : ret = sdap_id_op_connect_step(req);
432 0 : if (ret != EOK) {
433 0 : goto done;
434 : }
435 :
436 : done:
437 0 : if (ret != EOK) {
438 0 : talloc_zfree(req);
439 0 : } else if (op->conn_data && !op->conn_data->connect_req) {
440 : /* Connection is already established */
441 0 : tevent_req_done(req);
442 0 : tevent_req_post(req, state->ev);
443 : }
444 :
445 0 : if (ret_out) {
446 0 : *ret_out = ret;
447 : }
448 :
449 0 : return req;
450 : }
451 :
452 : /* Begin a connection retry to LDAP server */
453 0 : static int sdap_id_op_connect_step(struct tevent_req *req)
454 : {
455 0 : struct sdap_id_op_connect_state *state =
456 0 : tevent_req_data(req, struct sdap_id_op_connect_state);
457 0 : struct sdap_id_op *op = state->op;
458 0 : struct sdap_id_conn_cache *conn_cache = op->conn_cache;
459 :
460 0 : int ret = EOK;
461 : struct sdap_id_conn_data *conn_data;
462 0 : struct tevent_req *subreq = NULL;
463 :
464 : /* Try to reuse context cached connection */
465 0 : conn_data = conn_cache->cached_connection;
466 0 : if (conn_data) {
467 0 : if (conn_data->connect_req) {
468 0 : DEBUG(SSSDBG_TRACE_ALL, "waiting for connection to complete\n");
469 0 : sdap_id_op_hook_conn_data(op, conn_data);
470 0 : goto done;
471 : }
472 :
473 0 : if (sdap_can_reuse_connection(conn_data)) {
474 0 : DEBUG(SSSDBG_TRACE_ALL, "reusing cached connection\n");
475 0 : sdap_id_op_hook_conn_data(op, conn_data);
476 0 : goto done;
477 : }
478 :
479 0 : DEBUG(SSSDBG_TRACE_ALL, "releasing expired cached connection\n");
480 0 : conn_cache->cached_connection = NULL;
481 0 : sdap_id_release_conn_data(conn_data);
482 : }
483 :
484 0 : DEBUG(SSSDBG_TRACE_ALL, "beginning to connect\n");
485 :
486 0 : conn_data = talloc_zero(conn_cache, struct sdap_id_conn_data);
487 0 : if (!conn_data) {
488 0 : ret = ENOMEM;
489 0 : goto done;
490 : }
491 :
492 0 : talloc_set_destructor(conn_data, sdap_id_conn_data_destroy);
493 :
494 0 : conn_data->conn_cache = conn_cache;
495 0 : subreq = sdap_cli_connect_send(conn_data, state->ev,
496 0 : state->id_conn->id_ctx->opts,
497 0 : state->id_conn->id_ctx->be,
498 0 : state->id_conn->service, false,
499 : CON_TLS_DFL, false);
500 :
501 0 : if (!subreq) {
502 0 : ret = ENOMEM;
503 0 : goto done;
504 : }
505 :
506 0 : tevent_req_set_callback(subreq, sdap_id_op_connect_done, conn_data);
507 0 : conn_data->connect_req = subreq;
508 :
509 0 : DLIST_ADD(conn_cache->connections, conn_data);
510 0 : conn_cache->cached_connection = conn_data;
511 :
512 0 : sdap_id_op_hook_conn_data(op, conn_data);
513 :
514 : done:
515 0 : if (ret != EOK && conn_data) {
516 0 : sdap_id_release_conn_data(conn_data);
517 : }
518 :
519 0 : if (ret != EOK) {
520 0 : talloc_zfree(subreq);
521 : }
522 :
523 0 : return ret;
524 : }
525 :
526 : static void sdap_id_op_connect_reinit_done(struct tevent_req *req);
527 :
528 : /* Subrequest callback for connection completion */
529 0 : static void sdap_id_op_connect_done(struct tevent_req *subreq)
530 : {
531 0 : struct sdap_id_conn_data *conn_data =
532 0 : tevent_req_callback_data(subreq, struct sdap_id_conn_data);
533 0 : struct sdap_id_conn_cache *conn_cache = conn_data->conn_cache;
534 0 : struct sdap_server_opts *srv_opts = NULL;
535 0 : struct sdap_server_opts *current_srv_opts = NULL;
536 0 : bool can_retry = false;
537 0 : bool is_offline = false;
538 0 : struct tevent_req *reinit_req = NULL;
539 0 : bool reinit = false;
540 : int ret;
541 :
542 0 : ret = sdap_cli_connect_recv(subreq, conn_data, &can_retry,
543 : &conn_data->sh, &srv_opts);
544 0 : conn_data->connect_req = NULL;
545 0 : talloc_zfree(subreq);
546 :
547 0 : conn_data->notify_lock++;
548 :
549 0 : if (ret == ENOTSUP) {
550 0 : DEBUG(SSSDBG_FATAL_FAILURE,
551 : "Authentication mechanism not Supported by server\n");
552 : }
553 :
554 0 : if (ret == EOK && (!conn_data->sh || !conn_data->sh->connected)) {
555 0 : DEBUG(SSSDBG_FATAL_FAILURE,
556 : "sdap_cli_connect_recv returned bogus connection\n");
557 0 : ret = EFAULT;
558 : }
559 :
560 0 : if (ret != EOK && !can_retry) {
561 0 : if (conn_cache->id_conn->ignore_mark_offline) {
562 0 : DEBUG(SSSDBG_TRACE_FUNC,
563 : "Failed to connect to server, but ignore mark offline "
564 : "is enabled.\n");
565 : } else {
566 : /* be is going offline as there is no more servers to try */
567 0 : DEBUG(SSSDBG_CRIT_FAILURE,
568 : "Failed to connect, going offline (%d [%s])\n",
569 : ret, strerror(ret));
570 0 : is_offline = true;
571 0 : be_mark_offline(conn_cache->id_conn->id_ctx->be);
572 : }
573 : }
574 :
575 0 : if (ret == EOK) {
576 0 : current_srv_opts = conn_cache->id_conn->id_ctx->srv_opts;
577 0 : if (current_srv_opts) {
578 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
579 : "Old USN: %lu, New USN: %lu\n", current_srv_opts->last_usn, srv_opts->last_usn);
580 :
581 0 : if (strcmp(srv_opts->server_id, current_srv_opts->server_id) == 0 &&
582 0 : srv_opts->supports_usn &&
583 0 : current_srv_opts->last_usn > srv_opts->last_usn) {
584 0 : DEBUG(SSSDBG_FUNC_DATA, "Server was probably re-initialized\n");
585 :
586 0 : current_srv_opts->max_user_value = 0;
587 0 : current_srv_opts->max_group_value = 0;
588 0 : current_srv_opts->max_service_value = 0;
589 0 : current_srv_opts->max_sudo_value = 0;
590 0 : current_srv_opts->last_usn = srv_opts->last_usn;
591 :
592 0 : reinit = true;
593 : }
594 : }
595 0 : ret = sdap_id_conn_data_set_expire_timer(conn_data);
596 0 : sdap_steal_server_opts(conn_cache->id_conn->id_ctx, &srv_opts);
597 : }
598 :
599 0 : if (can_retry) {
600 0 : switch (ret) {
601 : case EOK:
602 : case ENOTSUP:
603 : case EACCES:
604 : case EIO:
605 : case EFAULT:
606 : case ETIMEDOUT:
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 : }
|