LCOV - code coverage report
Current view: top level - providers/ldap - sdap_id_op.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 372 0.0 %
Date: 2016-06-29 Functions: 0 22 0.0 %

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

Generated by: LCOV version 1.10