LCOV - code coverage report
Current view: top level - providers/ldap - sdap_id_op.c (source / functions) Hit Total Coverage
Test: .coverage.total Lines: 10 372 2.7 %
Date: 2015-10-19 Functions: 1 22 4.5 %

          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             : }

Generated by: LCOV version 1.10