From e96c468ed06c3378e2aee6992dabe926d79e1a2d Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Tue, 28 Jun 2011 12:58:26 +0200 Subject: Use ldap_init_fd() instead of ldap_initialize() if available --- src/util/sss_ldap.c | 336 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/util/sss_ldap.h | 10 ++ 2 files changed, 346 insertions(+) (limited to 'src/util') diff --git a/src/util/sss_ldap.c b/src/util/sss_ldap.c index 49d989b6..9f76870f 100644 --- a/src/util/sss_ldap.c +++ b/src/util/sss_ldap.c @@ -19,6 +19,11 @@ */ #include #include +#include +#include +#include +#include +#include #include "config.h" @@ -94,3 +99,334 @@ sss_ldap_escape_ip_address(TALLOC_CTX *mem_ctx, int family, const char *addr) return family == AF_INET6 ? talloc_asprintf(mem_ctx, "[%s]", addr) : talloc_strdup(mem_ctx, addr); } + +#ifdef HAVE_LDAP_INIT_FD +struct sdap_async_sys_connect_state { + long old_flags; + struct tevent_fd *fde; + int fd; + socklen_t addr_len; + struct sockaddr_storage addr; +}; + +static void sdap_async_sys_connect_done(struct tevent_context *ev, + struct tevent_fd *fde, uint16_t flags, + void *priv); + +static struct tevent_req *sdap_async_sys_connect_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + int fd, + const struct sockaddr *addr, + socklen_t addr_len) +{ + struct tevent_req *req; + struct sdap_async_sys_connect_state *state; + long flags; + int ret; + + flags = fcntl(fd, F_GETFL, 0); + if (flags == -1) { + DEBUG(1, ("fcntl F_GETFL failed.\n")); + return NULL; + } + + req = tevent_req_create(mem_ctx, &state, + struct sdap_async_sys_connect_state); + if (req == NULL) { + DEBUG(1, ("tevent_req_create failed.\n")); + return NULL; + } + + state->old_flags = flags; + state->fd = fd; + state->addr_len = addr_len; + memcpy(&state->addr, addr, addr_len); + + ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK); + if (ret != EOK) { + DEBUG(1, ("fcntl F_SETFL failed.\n")); + goto done; + } + + ret = connect(fd, addr, addr_len); + if (ret == EOK) { + tevent_req_done(req); + goto done; + } + + ret = errno; + switch(ret) { + case EINPROGRESS: + case EINTR: + state->fde = tevent_add_fd(ev, state, fd, + TEVENT_FD_READ | TEVENT_FD_WRITE, + sdap_async_sys_connect_done, req); + if (state->fde == NULL) { + DEBUG(1, ("tevent_add_fd failed.\n")); + ret = ENOMEM; + goto done; + } + + return req; + + break; + default: + DEBUG(1, ("connect failed [%d][%s].\n", ret, strerror(ret))); + } + +done: + if (ret != EOK) { + tevent_req_error(req, ret); + } + + ret = fcntl(fd, F_SETFL, flags); + if (ret != EOK) { + DEBUG(1, ("fcntl F_SETFL failed.\n")); + } + tevent_req_post(req, ev); + return req; +} + +static void sdap_async_sys_connect_done(struct tevent_context *ev, + struct tevent_fd *fde, uint16_t flags, + void *priv) +{ + struct tevent_req *req = talloc_get_type(priv, struct tevent_req); + struct sdap_async_sys_connect_state *state = tevent_req_data(req, + struct sdap_async_sys_connect_state); + int ret = EOK; + + /* I found the following comment in samba's lib/async_req/async_sock.c: + * Stevens, Network Programming says that if there's a + * successful connect, the socket is only writable. Upon an + * error, it's both readable and writable. + */ + if ((flags & (TEVENT_FD_READ|TEVENT_FD_WRITE)) == + (TEVENT_FD_READ|TEVENT_FD_WRITE)) { + + ret = connect(state->fd, (struct sockaddr *) &state->addr, + state->addr_len); + if (ret == EOK) { + goto done; + } + + ret = errno; + if (ret == EINPROGRESS || ret == EINTR) { + return; + } + + DEBUG(1, ("connect failed [%d][%s].\n", ret, strerror(ret))); + } + +done: + talloc_zfree(fde); + if (ret == EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } + + ret = fcntl(state->fd, F_SETFL, state->old_flags); + if (ret != EOK) { + DEBUG(1, ("fcntl F_SETFL failed.\n")); + } + return; +} + +static int sdap_async_sys_connect_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} + +static errno_t set_fd_flags_and_opts(int fd) +{ + int ret; + long flags; + int dummy = 1; + + flags = fcntl(fd, F_GETFD, 0); + if (flags == -1) { + ret = errno; + DEBUG(1, ("fcntl F_GETFD failed [%d][%s].\n", ret, strerror(ret))); + return ret; + } + + flags = fcntl(fd, F_SETFD, flags| FD_CLOEXEC); + if (flags == -1) { + ret = errno; + DEBUG(1, ("fcntl F_SETFD failed [%d][%s].\n", ret, strerror(ret))); + return ret; + } + + flags = fcntl(fd, F_GETFL, 0); + if (flags == -1) { + ret = errno; + DEBUG(1, ("fcntl F_GETFL failed [%d][%s].\n", ret, strerror(ret))); + return ret; + } + + flags = fcntl(fd, F_SETFL, flags| O_NONBLOCK); + if (flags == -1) { + ret = errno; + DEBUG(1, ("fcntl F_SETFL failed [%d][%s].\n", ret, strerror(ret))); + return ret; + } + + /* SO_KEEPALIVE and TCP_NODELAY are set by OpenLDAP client libraries but + * failures are ignored.*/ + ret = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &dummy, sizeof(dummy)); + if (ret != 0) { + ret = errno; + DEBUG(5, ("setsockopt SO_KEEPALIVE failed.[%d][%s].\n", ret, + strerror(ret))); + } + + ret = setsockopt(fd, SOL_TCP, TCP_NODELAY, &dummy, sizeof(dummy)); + if (ret != 0) { + ret = errno; + DEBUG(5, ("setsockopt TCP_NODELAY failed.[%d][%s].\n", ret, + strerror(ret))); + } + + return EOK; +} + +#define LDAP_PROTO_TCP 1 /* ldap:// */ +#define LDAP_PROTO_UDP 2 /* reserved */ +#define LDAP_PROTO_IPC 3 /* ldapi:// */ +#define LDAP_PROTO_EXT 4 /* user-defined socket/sockbuf */ + +extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url, LDAP **ld); + +static void sss_ldap_init_sys_connect_done(struct tevent_req *subreq); +#endif + +struct sss_ldap_init_state { + LDAP *ldap; + int sd; + const char *uri; +}; + + +struct tevent_req *sss_ldap_init_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const char *uri, + struct sockaddr_storage *addr, + int addr_len) +{ + int ret = EOK; + struct tevent_req *req; + struct sss_ldap_init_state *state; + + req = tevent_req_create(mem_ctx, &state, struct sss_ldap_init_state); + if (req == NULL) { + DEBUG(1, ("tevent_req_create failed.\n")); + return NULL; + } + + state->ldap = NULL; + state->uri = uri; + +#ifdef HAVE_LDAP_INIT_FD + struct tevent_req *subreq; + + state->sd = socket(addr->ss_family, SOCK_STREAM, 0); + if (state->sd == -1) { + ret = errno; + DEBUG(1, ("socket failed [%d][%s].\n", ret, strerror(ret))); + goto fail; + } + + ret = set_fd_flags_and_opts(state->sd); + if (ret != EOK) { + DEBUG(1, ("set_fd_flags_and_opts failed.\n")); + goto fail; + } + + DEBUG(9, ("Using file descriptor [%d] for LDAP connection.\n", state->sd)); + + subreq = sdap_async_sys_connect_send(state, ev, state->sd, + (struct sockaddr *) addr, addr_len); + if (subreq == NULL) { + ret = ENOMEM; + DEBUG(1, ("sdap_async_sys_connect_send failed.\n")); + goto fail; + } + + tevent_req_set_callback(subreq, sss_ldap_init_sys_connect_done, req); + return req; + +fail: + close(state->sd); + tevent_req_error(req, ret); +#else + DEBUG(3, ("ldap_init_fd not available, " + "will use ldap_initialize with uri [%s].\n", uri)); + state->sd = -1; + ret = ldap_initialize(&state->ldap, uri); + if (ret == LDAP_SUCCESS) { + tevent_req_done(req); + } else { + DEBUG(1, ("ldap_initialize failed [%s].\n", ldap_err2string(ret))); + if (ret == LDAP_SERVER_DOWN) { + tevent_req_error(req, ETIMEDOUT); + } else { + tevent_req_error(req, EIO); + } + } +#endif + + tevent_req_post(req, ev); + return req; +} + +#ifdef HAVE_LDAP_INIT_FD +static void sss_ldap_init_sys_connect_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sss_ldap_init_state *state = tevent_req_data(req, + struct sss_ldap_init_state); + int ret; + int lret; + + ret = sdap_async_sys_connect_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(1, ("sdap_async_sys_connect request failed.\n")); + close(state->sd); + tevent_req_error(req, ret); + return; + } + /* Initialize LDAP handler */ + + lret = ldap_init_fd(state->sd, LDAP_PROTO_TCP, state->uri, &state->ldap); + if (lret != LDAP_SUCCESS) { + DEBUG(1, ("ldap_init_fd failed: %s\n", ldap_err2string(lret))); + close(state->sd); + if (lret == LDAP_SERVER_DOWN) { + tevent_req_error(req, ETIMEDOUT); + } else { + tevent_req_error(req, EIO); + } + return; + } + + tevent_req_done(req); + return; +} +#endif + +int sss_ldap_init_recv(struct tevent_req *req, LDAP **ldap, int *sd) +{ + struct sss_ldap_init_state *state = tevent_req_data(req, + struct sss_ldap_init_state); + TEVENT_REQ_RETURN_ON_ERROR(req); + + *ldap = state->ldap; + *sd = state->sd; + + return EOK; +} diff --git a/src/util/sss_ldap.h b/src/util/sss_ldap.h index 16c22641..14fff29e 100644 --- a/src/util/sss_ldap.h +++ b/src/util/sss_ldap.h @@ -21,8 +21,11 @@ #ifndef __SSS_LDAP_H__ #define __SSS_LDAP_H__ +#include +#include #include #include +#include #ifdef LDAP_OPT_DIAGNOSTIC_MESSAGE #define SDAP_DIAGNOSTIC_MESSAGE LDAP_OPT_DIAGNOSTIC_MESSAGE @@ -49,4 +52,11 @@ int sss_ldap_control_create(const char *oid, int iscritical, inline const char * sss_ldap_escape_ip_address(TALLOC_CTX *mem_ctx, int family, const char *addr); +struct tevent_req *sss_ldap_init_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const char *uri, + struct sockaddr_storage *addr, + int addr_len); + +int sss_ldap_init_recv(struct tevent_req *req, LDAP **ldap, int *sd); #endif /* __SSS_LDAP_H__ */ -- cgit