/*
SSSD
LDAP ID backend operation retry logic and connection cache
Authors:
Eugene Indenbom <eindenbom@gmail.com>
Copyright (C) 2008-2010 Red Hat
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "providers/ldap/ldap_common.h"
#include "providers/ldap/sdap_async.h"
#include "providers/ldap/sdap_id_op.h"
/* LDAP async connection cache */
struct sdap_id_conn_cache {
/* list of all open connections */
/* cached (current) connection */
};
/* LDAP async operation tracker:
* - keeps track of connection usage
* - keeps track of operation retries */
struct sdap_id_op {
/* ID backend context */
/* double linked list pointers */
/* current connection */
/* number of reconnects for this operation */
/* connection request
* It is required as we need to know which requests to notify
* when shared connection request to sdap_handle completes.
* This member is cleared when sdap_id_op_connect_state
* associated with request is destroyed */
};
/* LDAP connection cache connection attempt/established connection data */
struct sdap_id_conn_data {
/* LDAP connection cache */
/* double linked list pointers */
/* sdap handle */
/* connection request */
/* timer for connection expiration */
/* number of running connection notifies */
int notify_lock;
/* list of operations using connect */
/* A flag which is signalizing that this
* connection will be disconnected and should
* not be used any more */
bool disconnecting;
};
static void sdap_id_conn_cache_be_offline_cb(void *pvt);
static void sdap_id_conn_cache_fo_reconnect_cb(void *pvt);
struct tevent_timer *te,
struct timeval current_time,
void *pvt);
static int sdap_id_op_destroy(void *pvt);
static int sdap_id_op_connect_state_destroy(void *pvt);
/* Create a connection cache */
struct sdap_id_conn_ctx *id_conn,
struct sdap_id_conn_cache** conn_cache_out)
{
int ret;
if (!conn_cache) {
"talloc_zero(struct sdap_id_conn_cache) failed.\n");
goto fail;
}
NULL);
goto fail;
}
NULL);
goto fail;
}
return EOK;
fail:
return ret;
}
/* Callback on BE going offline */
{
/* Release any cached connection on going offline */
if (cached_connection != NULL) {
}
}
/* Callback for attempt to reconnect to primary server */
{
/* Release any cached connection on going offline */
if (cached_connection != NULL) {
cached_connection->disconnecting = true;
}
}
/* Release sdap_id_conn_data and destroy it if no longer needed */
{
/* connection is in use */
return;
}
return;
}
}
/* Destructor for struct sdap_id_conn_data */
{
/* we clean out list of ops to make sure that order of destruction does not matter */
}
return 0;
}
/* Check whether connection will expire after timeout seconds */
{
return true;
}
return true;
}
return false;
}
/* Check whether connection can be reused for next LDAP ID operation */
{
int timeout;
return false;
}
}
/* Set expiration timer for connection if needed */
{
int timeout;
return EOK;
}
if (timeout > 0) {
}
return EOK;
}
if (!conn_data->expire_timer) {
return ENOMEM;
}
return EOK;
}
/* Handler for connection expiration timer */
struct tevent_timer *te,
struct timeval current_time,
void *pvt)
{
struct sdap_id_conn_data);
"connection is about to expire, releasing it\n");
}
}
/* Create an operation object */
{
if (!op) {
return NULL;
}
return op;
}
{
if (!op) {
return;
}
return;
}
if (current) {
}
if (conn_data) {
}
if (current) {
}
}
/* Destructor for sdap_id_op */
{
}
return 0;
}
/* Check whether retry with reconnect can be performed for the operation */
{
/* we allow 2 retries for failover server configured:
* - one for connection broken during request execution
* - one for the following (probably failed) reconnect attempt */
int max_retries;
int count;
if (max_retries < 1) {
max_retries = 1;
}
}
/* state of connect request */
struct sdap_id_op_connect_state {
int dp_error;
int result;
};
/* Destructor for operation connection request */
{
struct sdap_id_op_connect_state);
/* clear destroyed connection request */
}
return 0;
}
/* Begin to connect to LDAP server */
int *ret_out)
{
if (!memctx) {
goto done;
}
if (op->connect_req) {
/* Connection already in progress, invalid operation */
"Bug: connection request is already running or completed and leaked.\n");
goto done;
}
if (!req) {
goto done;
}
/* If the operation is already connected,
* reuse existing connection regardless of its status */
goto done;
}
goto done;
}
done:
/* Connection is already established */
}
if (ret_out) {
}
return req;
}
/* Begin a connection retry to LDAP server */
{
/* Try to reuse context cached connection */
if (conn_data) {
if (conn_data->connect_req) {
goto done;
}
if (sdap_can_reuse_connection(conn_data)) {
goto done;
}
}
if (!conn_data) {
goto done;
}
CON_TLS_DFL, false);
if (!subreq) {
goto done;
}
done:
}
}
return ret;
}
/* Subrequest callback for connection completion */
{
bool can_retry = false;
bool is_offline = false;
bool reinit = false;
int ret;
conn_data->notify_lock++;
"Authentication mechanism not Supported by server\n");
}
"sdap_cli_connect_recv returned bogus connection\n");
}
"Failed to connect to server, but ignore mark offline "
"is enabled.\n");
} else {
/* be is going offline as there is no more servers to try */
"Failed to connect, going offline (%d [%s])\n",
is_offline = true;
}
}
if (current_srv_opts) {
srv_opts->supports_usn &&
reinit = true;
}
}
"sdap_id_conn_data_set_expire_timer() failed [%d]: %s",
/* Avoid causing the whole backend to be marked as offline because
* this operation failed. */
}
}
if (can_retry) {
switch (ret) {
case EOK:
case ENOTSUP:
case EACCES:
case EIO:
case EFAULT:
case ETIMEDOUT:
case ERR_AUTH_FAILED:
break;
default:
/* do not attempt to retry on errors like ENOMEM */
"Marking the backend \"%s\" offline [%d]: %s\n",
can_retry = false;
is_offline = true;
break;
}
}
int notify_count = 0;
/* Notify about connection */
for(;;) {
"connection was broken after %d notifies\n", notify_count);
}
if (op->connect_req) {
break;
}
}
if (!op) {
break;
}
/* another operation to notify */
notify_count++;
/* failed to connect or connection got broken during notify */
bool retry = false;
/* drop connection from cache now */
}
if (can_retry) {
/* determining whether retry is possible */
/* be is offline, no retry possible */
"skipping automatic retry on op #%d as be is offline\n", notify_count);
}
can_retry = false;
is_offline = true;
} else {
"attempting automatic retry on op #%d\n", notify_count);
retry = true;
} else if (sdap_id_op_can_reconnect(op)) {
"attempting failover retry on op #%d\n", notify_count);
retry = true;
}
}
}
can_retry = false;
}
continue;
}
}
"notify connected to op #%d\n", notify_count);
} else if (is_offline) {
} else {
}
}
/* all operations notified */
if (conn_data->notify_lock > 0) {
conn_data->notify_lock--;
}
"caching successful connection after %d notifies\n", notify_count);
/* Run any post-connection routines */
} else {
}
}
if (reinit) {
"Cleaning cache.\n");
if (reinit_req == NULL) {
"clean up.\n");
return;
}
NULL);
}
}
{
/* not fatal */
return;
}
}
/* Mark operation connection request as complete */
{
if (!req) {
return;
}
} else {
}
}
/* Get the result of an asynchronous connect operation on sdap_id_op
*
* In dp_error data provider error code is returned:
* DP_ERR_OK - connection established
* DP_ERR_OFFLINE - backend is offline, operation result is set EAGAIN
* DP_ERR_FATAL - operation failed
*/
{
struct sdap_id_op_connect_state);
}
/* Report completion of LDAP operation and release associated connection.
* Returns operation result (possible updated) passed in ret parameter.
*
* In dp_error data provider error code is returned:
* DP_ERR_OK (operation result = EOK) - operation completed
* DP_ERR_OK (operation result != EOK) - operation can be retried
* DP_ERR_OFFLINE - backend is offline, operation result is set EAGAIN
* DP_ERR_FATAL - operation failed */
{
bool communication_error;
switch (retval) {
case EIO:
case ETIMEDOUT:
/* this currently the only possible communication error after connection is established */
communication_error = true;
break;
default:
communication_error = false;
break;
}
if (communication_error && current_conn != 0
/* do not reuse failed connection */
"communication error on cached connection, moving to next server\n");
}
int dp_err;
/* if backend is already offline, just report offline, do not duplicate errors */
} else if (communication_error) {
/* communication error, can try to reconnect */
if (!sdap_id_op_can_reconnect(op)) {
"too many communication failures, giving up...\n");
} else {
}
} else {
}
/* reconnect retry */
} else {
/* end of request */
op->reconnect_retry_count = 0;
}
if (current_conn) {
}
*dp_err_out = dp_err;
return retval;
}
/* Get SDAP handle associated with operation by sdap_id_op_connect */
{
}