s4: fix various warnings (not "const" related ones)
[sfrench/samba-autobuild/.git] / source4 / libcli / ldap / ldap_client.c
index e3904c7a6b4d907308f35fe41b304451cad22db9..3c78a7c7e29dc0f5a7d58a25466044ee8db1ad81 100644 (file)
@@ -9,7 +9,7 @@
     
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   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,
    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, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
 */
 
 #include "includes.h"
-#include "system/network.h"
-#include "system/filesys.h"
-#include "auth/auth.h"
-#include "asn_1.h"
-#include "dlinklist.h"
+#include <tevent.h>
+#include "lib/socket/socket.h"
+#include "../lib/util/asn1.h"
+#include "../lib/util/dlinklist.h"
 #include "libcli/ldap/ldap.h"
-
-
-
-/****************************************************************************
- Check the timeout. 
-****************************************************************************/
-static BOOL timeout_until(struct timeval *timeout,
-                         const struct timeval *endtime)
-{
-       struct timeval now;
-
-       GetTimeOfDay(&now);
-
-       if ((now.tv_sec > endtime->tv_sec) ||
-           ((now.tv_sec == endtime->tv_sec) &&
-            (now.tv_usec > endtime->tv_usec)))
-               return False;
-
-       timeout->tv_sec = endtime->tv_sec - now.tv_sec;
-       timeout->tv_usec = endtime->tv_usec - now.tv_usec;
-       return True;
-}
-
-
-/****************************************************************************
- Read data from the client, reading exactly N bytes, with timeout. 
-****************************************************************************/
-static ssize_t read_data_until(int fd,char *buffer,size_t N,
-                              const struct timeval *endtime)
+#include "libcli/ldap/ldap_proto.h"
+#include "libcli/ldap/ldap_client.h"
+#include "libcli/composite/composite.h"
+#include "lib/stream/packet.h"
+#include "lib/tls/tls.h"
+#include "auth/gensec/gensec.h"
+#include "system/time.h"
+#include "param/param.h"
+#include "libcli/resolve/resolve.h"
+
+/**
+  create a new ldap_connection stucture. The event context is optional
+*/
+_PUBLIC_ struct ldap_connection *ldap4_new_connection(TALLOC_CTX *mem_ctx, 
+                                            struct loadparm_context *lp_ctx,
+                                            struct tevent_context *ev)
 {
-       ssize_t ret;
-       size_t total=0;  
-       while (total < N) {
+       struct ldap_connection *conn;
 
-               if (endtime != NULL) {
-                       fd_set r_fds;
-                       struct timeval timeout;
-                       int res;
+       if (ev == NULL) {
+               return NULL;
+       }
 
-                       FD_ZERO(&r_fds);
-                       FD_SET(fd, &r_fds);
+       conn = talloc_zero(mem_ctx, struct ldap_connection);
+       if (conn == NULL) {
+               return NULL;
+       }
 
-                       if (!timeout_until(&timeout, endtime))
-                               return -1;
+       conn->next_messageid  = 1;
+       conn->event.event_ctx = ev;
 
-                       res = sys_select(fd+1, &r_fds, NULL, NULL, &timeout);
-                       if (res <= 0)
-                               return -1;
-               }
+       conn->lp_ctx = lp_ctx;
 
-               ret = sys_read(fd,buffer + total,N - total);
+       /* set a reasonable request timeout */
+       conn->timeout = 60;
 
-               if (ret == 0) {
-                       DEBUG(10,("read_data: read of %d returned 0. Error = %s\n", (int)(N - total), strerror(errno) ));
-                       return 0;
-               }
+       /* explicitly avoid reconnections by default */
+       conn->reconnect.max_retries = 0;
+       
+       return conn;
+}
 
-               if (ret == -1) {
-                       DEBUG(0,("read_data: read failure for %d. Error = %s\n", (int)(N - total), strerror(errno) ));
-                       return -1;
+/*
+  the connection is dead
+*/
+static void ldap_connection_dead(struct ldap_connection *conn)
+{
+       struct ldap_request *req;
+
+       talloc_free(conn->sock);  /* this will also free event.fde */
+       talloc_free(conn->packet);
+       conn->sock = NULL;
+       conn->event.fde = NULL;
+       conn->packet = NULL;
+
+       /* return an error for any pending request ... */
+       while (conn->pending) {
+               req = conn->pending;
+               DLIST_REMOVE(req->conn->pending, req);
+               req->state = LDAP_REQUEST_DONE;
+               req->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+               if (req->async.fn) {
+                       req->async.fn(req);
                }
-               total += ret;
        }
-       return (ssize_t)total;
 }
 
+static void ldap_reconnect(struct ldap_connection *conn);
 
-/****************************************************************************
- Write data to a fd with timeout.
-****************************************************************************/
-static ssize_t write_data_until(int fd,char *buffer,size_t N,
-                               const struct timeval *endtime)
+/*
+  handle packet errors
+*/
+static void ldap_error_handler(void *private_data, NTSTATUS status)
 {
-       size_t total=0;
-       ssize_t ret;
+       struct ldap_connection *conn = talloc_get_type(private_data, 
+                                                      struct ldap_connection);
+       ldap_connection_dead(conn);
 
-       while (total < N) {
+       /* but try to reconnect so that the ldb client can go on */
+       ldap_reconnect(conn);
+}
 
-               if (endtime != NULL) {
-                       fd_set w_fds;
-                       struct timeval timeout;
-                       int res;
 
-                       FD_ZERO(&w_fds);
-                       FD_SET(fd, &w_fds);
+/*
+  match up with a pending message, adding to the replies list
+*/
+static void ldap_match_message(struct ldap_connection *conn, struct ldap_message *msg)
+{
+       struct ldap_request *req;
+       int i;
 
-                       if (!timeout_until(&timeout, endtime))
-                               return -1;
+       for (req=conn->pending; req; req=req->next) {
+               if (req->messageid == msg->messageid) break;
+       }
+       /* match a zero message id to the last request sent.
+          It seems that servers send 0 if unable to parse */
+       if (req == NULL && msg->messageid == 0) {
+               req = conn->pending;
+       }
+       if (req == NULL) {
+               DEBUG(0,("ldap: no matching message id for %u\n",
+                        msg->messageid));
+               talloc_free(msg);
+               return;
+       }
 
-                       res = sys_select(fd+1, NULL, &w_fds, NULL, &timeout);
-                       if (res <= 0)
-                               return -1;
+       /* Check for undecoded critical extensions */
+       for (i=0; msg->controls && msg->controls[i]; i++) {
+               if (!msg->controls_decoded[i] && 
+                   msg->controls[i]->critical) {
+                       req->status = NT_STATUS_LDAP(LDAP_UNAVAILABLE_CRITICAL_EXTENSION);
+                       req->state = LDAP_REQUEST_DONE;
+                       DLIST_REMOVE(conn->pending, req);
+                       if (req->async.fn) {
+                               req->async.fn(req);
+                       }
+                       return;
                }
+       }
 
-               ret = sys_write(fd,buffer + total,N - total);
-
-               if (ret == -1) {
-                       DEBUG(0,("write_data: write failure. Error = %s\n", strerror(errno) ));
-                       return -1;
+       /* add to the list of replies received */
+       talloc_steal(req, msg);
+       req->replies = talloc_realloc(req, req->replies, 
+                                     struct ldap_message *, req->num_replies+1);
+       if (req->replies == NULL) {
+               req->status = NT_STATUS_NO_MEMORY;
+               req->state = LDAP_REQUEST_DONE;
+               DLIST_REMOVE(conn->pending, req);
+               if (req->async.fn) {
+                       req->async.fn(req);
                }
-               if (ret == 0)
-                       return total;
-
-               total += ret;
+               return;
        }
-       return (ssize_t)total;
-}
-
 
+       req->replies[req->num_replies] = talloc_steal(req->replies, msg);
+       req->num_replies++;
 
-static BOOL read_one_uint8(int sock, uint8_t *result, struct asn1_data *data,
-                          const struct timeval *endtime)
-{
-       if (read_data_until(sock, result, 1, endtime) != 1)
-               return False;
+       if (msg->type != LDAP_TAG_SearchResultEntry &&
+           msg->type != LDAP_TAG_SearchResultReference) {
+               /* currently only search results expect multiple
+                  replies */
+               req->state = LDAP_REQUEST_DONE;
+               DLIST_REMOVE(conn->pending, req);
+       }
 
-       return asn1_write(data, result, 1);
+       if (req->async.fn) {
+               req->async.fn(req);
+       }
 }
 
-/* Read a complete ASN sequence (ie LDAP result) from a socket */
-static BOOL asn1_read_sequence_until(int sock, struct asn1_data *data,
-                                    const struct timeval *endtime)
-{
-       uint8_t b;
-       size_t len;
-       char *buf;
-
-       ZERO_STRUCTP(data);
 
-       if (!read_one_uint8(sock, &b, data, endtime))
-               return False;
+/*
+  decode/process LDAP data
+*/
+static NTSTATUS ldap_recv_handler(void *private_data, DATA_BLOB blob)
+{
+       NTSTATUS status;
+       struct ldap_connection *conn = talloc_get_type(private_data, 
+                                                      struct ldap_connection);
+       struct ldap_message *msg = talloc(conn, struct ldap_message);
+       struct asn1_data *asn1 = asn1_init(conn);
 
-       if (b != 0x30) {
-               data->has_error = True;
-               return False;
+       if (asn1 == NULL || msg == NULL) {
+               return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
        }
 
-       if (!read_one_uint8(sock, &b, data, endtime))
-               return False;
-
-       if (b & 0x80) {
-               int n = b & 0x7f;
-               if (!read_one_uint8(sock, &b, data, endtime))
-                       return False;
-               len = b;
-               while (n > 1) {
-                       if (!read_one_uint8(sock, &b, data, endtime))
-                               return False;
-                       len = (len<<8) | b;
-                       n--;
-               }
-       } else {
-               len = b;
+       if (!asn1_load(asn1, blob)) {
+               talloc_free(msg);
+               talloc_free(asn1);
+               return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+       }
+       
+       status = ldap_decode(asn1, samba_ldap_control_handlers(), msg);
+       if (!NT_STATUS_IS_OK(status)) {
+               asn1_free(asn1);
+               return status;
        }
 
-       buf = talloc_size(NULL, len);
-       if (buf == NULL)
-               return False;
-
-       if (read_data_until(sock, buf, len, endtime) != len)
-               return False;
-
-       if (!asn1_write(data, buf, len))
-               return False;
-
-       talloc_free(buf);
+       ldap_match_message(conn, msg);
 
-       data->ofs = 0;
-       
-       return True;
+       data_blob_free(&blob);
+       asn1_free(asn1);
+       return NT_STATUS_OK;
 }
 
+/* Handle read events, from the GENSEC socket callback, or real events */
+void ldap_read_io_handler(void *private_data, uint16_t flags) 
+{
+       struct ldap_connection *conn = talloc_get_type(private_data, 
+                                                      struct ldap_connection);
+       packet_recv(conn->packet);
+}
 
-
-/****************************************************************************
-  create an outgoing socket. timeout is in milliseconds.
-  **************************************************************************/
-static int open_socket_out(int type, struct ipv4_addr *addr, int port, int timeout)
+/*
+  handle ldap socket events
+*/
+static void ldap_io_handler(struct tevent_context *ev, struct tevent_fd *fde, 
+                           uint16_t flags, void *private_data)
 {
-       struct sockaddr_in sock_out;
-       int res,ret;
-       int connect_loop = 250; /* 250 milliseconds */
-       int loops = (timeout) / connect_loop;
-
-       /* create a socket to write to */
-       res = socket(PF_INET, type, 0);
-       if (res == -1) 
-       { DEBUG(0,("socket error\n")); return -1; }
-       
-       if (type != SOCK_STREAM) return(res);
-       
-       memset((char *)&sock_out,'\0',sizeof(sock_out));
-       sock_out.sin_addr.s_addr = addr->addr;
-       
-       sock_out.sin_port = htons( port );
-       sock_out.sin_family = PF_INET;
-       
-       /* set it non-blocking */
-       set_blocking(res,False);
-       
-       DEBUG(3,("Connecting to %s at port %d\n", sys_inet_ntoa(*addr),port));
-       
-       /* and connect it to the destination */
-connect_again:
-       ret = connect(res,(struct sockaddr *)&sock_out,sizeof(sock_out));
-       
-       /* Some systems return EAGAIN when they mean EINPROGRESS */
-       if (ret < 0 && (errno == EINPROGRESS || errno == EALREADY ||
-                       errno == EAGAIN) && loops--) {
-               msleep(connect_loop);
-               goto connect_again;
-       }
-       
-       if (ret < 0 && (errno == EINPROGRESS || errno == EALREADY ||
-                       errno == EAGAIN)) {
-               DEBUG(1,("timeout connecting to %s:%d\n", sys_inet_ntoa(*addr),port));
-               close(res);
-               return -1;
+       struct ldap_connection *conn = talloc_get_type(private_data, 
+                                                      struct ldap_connection);
+       if (flags & TEVENT_FD_WRITE) {
+               packet_queue_run(conn->packet);
+               if (!tls_enabled(conn->sock)) return;
        }
-       
-#ifdef EISCONN
-       if (ret < 0 && errno == EISCONN) {
-               errno = 0;
-               ret = 0;
+       if (flags & TEVENT_FD_READ) {
+               ldap_read_io_handler(private_data, flags);
        }
-#endif
-       
-       if (ret < 0) {
-               DEBUG(2,("error connecting to %s:%d (%s)\n",
-                        sys_inet_ntoa(*addr),port,strerror(errno)));
-               close(res);
-               return -1;
-       }
-       
-       /* set it blocking again */
-       set_blocking(res,True);
-       
-       return res;
 }
 
-#if 0
-static struct ldap_message *new_ldap_search_message(struct ldap_connection *conn,
-                                            const char *base,
-                                            enum ldap_scope scope,
-                                            char *filter,
-                                            int num_attributes,
-                                            const char **attributes)
+/*
+  parse a ldap URL
+*/
+static NTSTATUS ldap_parse_basic_url(TALLOC_CTX *mem_ctx, const char *url,
+                                    char **host, uint16_t *port, bool *ldaps)
 {
-       struct ldap_message *res;
+       int tmp_port = 0;
+       char protocol[11];
+       char tmp_host[1025];
+       int ret;
 
-       res = new_ldap_message(conn);
-       if (!res) {
-               return NULL;
+       /* Paranoia check */
+       SMB_ASSERT(sizeof(protocol)>10 && sizeof(tmp_host)>254);
+               
+       ret = sscanf(url, "%10[^:]://%254[^:/]:%d", protocol, tmp_host, &tmp_port);
+       if (ret < 2) {
+               return NT_STATUS_INVALID_PARAMETER;
        }
 
-       res->type = LDAP_TAG_SearchRequest;
-       res->r.SearchRequest.basedn = base;
-       res->r.SearchRequest.scope = scope;
-       res->r.SearchRequest.deref = LDAP_DEREFERENCE_NEVER;
-       res->r.SearchRequest.timelimit = 0;
-       res->r.SearchRequest.sizelimit = 0;
-       res->r.SearchRequest.attributesonly = False;
-       res->r.SearchRequest.filter = filter;
-       res->r.SearchRequest.num_attributes = num_attributes;
-       res->r.SearchRequest.attributes = attributes;
-
-       return res;
-}
-#endif
-
-static struct ldap_message *new_ldap_simple_bind_msg(struct ldap_connection *conn, const char *dn, const char *pw)
-{
-       struct ldap_message *res;
-
-       res = new_ldap_message(conn);
-       if (!res) {
-               return NULL;
+       if (strequal(protocol, "ldap")) {
+               *port = 389;
+               *ldaps = false;
+       } else if (strequal(protocol, "ldaps")) {
+               *port = 636;
+               *ldaps = true;
+       } else {
+               DEBUG(0, ("unrecognised ldap protocol (%s)!\n", protocol));
+               return NT_STATUS_PROTOCOL_UNREACHABLE;
        }
 
-       res->type = LDAP_TAG_BindRequest;
-       res->r.BindRequest.version = 3;
-       res->r.BindRequest.dn = talloc_strdup(res->mem_ctx, dn);
-       res->r.BindRequest.mechanism = LDAP_AUTH_MECH_SIMPLE;
-       res->r.BindRequest.creds.password = talloc_strdup(res->mem_ctx, pw);
+       if (tmp_port != 0)
+               *port = tmp_port;
 
-       return res;
-}
+       *host = talloc_strdup(mem_ctx, tmp_host);
+       NT_STATUS_HAVE_NO_MEMORY(*host);
 
-static struct ldap_message *new_ldap_sasl_bind_msg(struct ldap_connection *conn, const char *sasl_mechanism, DATA_BLOB *secblob)
-{
-       struct ldap_message *res;
+       return NT_STATUS_OK;
+}
 
-       res = new_ldap_message(conn);
-       if (!res) {
-               return NULL;
-       }
+/*
+  connect to a ldap server
+*/
 
-       res->type = LDAP_TAG_BindRequest;
-       res->r.BindRequest.version = 3;
-       res->r.BindRequest.dn = "";
-       res->r.BindRequest.mechanism = LDAP_AUTH_MECH_SASL;
-       res->r.BindRequest.creds.SASL.mechanism = talloc_strdup(res->mem_ctx, sasl_mechanism);
-       res->r.BindRequest.creds.SASL.secblob = *secblob;
+struct ldap_connect_state {
+       struct composite_context *ctx;
+       struct ldap_connection *conn;
+};
 
-       return res;
-}
+static void ldap_connect_recv_unix_conn(struct composite_context *ctx);
+static void ldap_connect_recv_tcp_conn(struct composite_context *ctx);
 
-static struct ldap_connection *new_ldap_connection(TALLOC_CTX *mem_ctx)
+_PUBLIC_ struct composite_context *ldap_connect_send(struct ldap_connection *conn,
+                                           const char *url)
 {
-       struct ldap_connection *result;
+       struct composite_context *result, *ctx;
+       struct ldap_connect_state *state;
+       char protocol[11];
+       int ret;
 
-       result = talloc(mem_ctx, struct ldap_connection);
+       result = talloc_zero(conn, struct composite_context);
+       if (result == NULL) goto failed;
+       result->state = COMPOSITE_STATE_IN_PROGRESS;
+       result->async.fn = NULL;
+       result->event_ctx = conn->event.event_ctx;
 
-       if (!result) {
-               return NULL;
-       }
+       state = talloc(result, struct ldap_connect_state);
+       if (state == NULL) goto failed;
+       state->ctx = result;
+       result->private_data = state;
 
-       result->mem_ctx = result;
-       result->next_msgid = 1;
-       result->outstanding = NULL;
-       result->searchid = 0;
-       result->search_entries = NULL;
-       result->auth_dn = NULL;
-       result->simple_pw = NULL;
-       result->gensec = NULL;
+       state->conn = conn;
 
-       return result;
-}
-
-struct ldap_connection *ldap_connect(TALLOC_CTX *mem_ctx, const char *url)
-{
-       struct hostent *hp;
-       struct ipv4_addr ip;
-       struct ldap_connection *conn;
-       BOOL ret;
-
-       conn = new_ldap_connection(mem_ctx);
-       if (!conn) {
-               return NULL;
+       if (conn->reconnect.url == NULL) {
+               conn->reconnect.url = talloc_strdup(conn, url);
+               if (conn->reconnect.url == NULL) goto failed;
        }
 
-       ret = ldap_parse_basic_url(conn->mem_ctx, url, &conn->host,
-                                 &conn->port, &conn->ldaps);
-       if (!ret) {
-               talloc_free(conn);
-               return NULL;
-       }
+       /* Paranoia check */
+       SMB_ASSERT(sizeof(protocol)>10);
 
-       hp = sys_gethostbyname(conn->host);
-       if (!hp || !hp->h_addr) {
-               talloc_free(conn);
+       ret = sscanf(url, "%10[^:]://", protocol);
+       if (ret < 1) {
                return NULL;
        }
 
-       memcpy((char *)&ip, (char *)hp->h_addr, 4);
-
-       conn->sock = open_socket_out(SOCK_STREAM, &ip, conn->port, LDAP_CONNECTION_TIMEOUT);
-       if (conn->sock < 0) {
-               talloc_free(conn);
-               return NULL;
-       }
+       if (strequal(protocol, "ldapi")) {
+               struct socket_address *unix_addr;
+               char path[1025];
+       
+               NTSTATUS status = socket_create("unix", SOCKET_TYPE_STREAM, &conn->sock, 0);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return NULL;
+               }
+               talloc_steal(conn, conn->sock);
+               SMB_ASSERT(sizeof(protocol)>10);
+               SMB_ASSERT(sizeof(path)>1024);
+       
+               /* LDAPI connections are to localhost, so give the
+                * local host name as the target for gensec's
+                * DIGEST-MD5 mechanism */
+               conn->host = talloc_asprintf(conn, "%s.%s", lp_netbios_name(conn->lp_ctx),  lp_realm(conn->lp_ctx));
+               if (composite_nomem(conn->host, state->ctx)) {
+                       return result;
+               }
 
-       return conn;
-}
+               /* The %c specifier doesn't null terminate :-( */
+               ZERO_STRUCT(path);
+               ret = sscanf(url, "%10[^:]://%1025c", protocol, path);
+               if (ret < 2) {
+                       composite_error(state->ctx, NT_STATUS_INVALID_PARAMETER);
+                       return result;
+               }
 
-struct ldap_message *new_ldap_message(TALLOC_CTX *mem_ctx)
-{
-       struct ldap_message *result;
+               rfc1738_unescape(path);
+       
+               unix_addr = socket_address_from_strings(conn, conn->sock->backend_name, 
+                                                       path, 0);
+               if (!unix_addr) {
+                       return NULL;
+               }
 
-       result = talloc(mem_ctx, struct ldap_message);
+               ctx = socket_connect_send(conn->sock, NULL, unix_addr, 
+                                         0, conn->event.event_ctx);
+               ctx->async.fn = ldap_connect_recv_unix_conn;
+               ctx->async.private_data = state;
+               return result;
+       } else {
+               NTSTATUS status = ldap_parse_basic_url(conn, url, &conn->host,
+                                                         &conn->port, &conn->ldaps);
+               if (!NT_STATUS_IS_OK(state->ctx->status)) {
+                       composite_error(state->ctx, status);
+                       return result;
+               }
+               
+               ctx = socket_connect_multi_send(state, conn->host, 1, &conn->port,
+                                               lp_resolve_context(conn->lp_ctx), conn->event.event_ctx);
+               if (ctx == NULL) goto failed;
 
-       if (!result) {
-               return NULL;
+               ctx->async.fn = ldap_connect_recv_tcp_conn;
+               ctx->async.private_data = state;
+               return result;
        }
-
-       result->mem_ctx = result;
-
-       return result;
+ failed:
+       talloc_free(result);
+       return NULL;
 }
 
-BOOL ldap_send_msg(struct ldap_connection *conn, struct ldap_message *msg,
-                  const struct timeval *endtime)
+static void ldap_connect_got_sock(struct composite_context *ctx, 
+                                 struct ldap_connection *conn) 
 {
-       DATA_BLOB request;
-       BOOL result;
-       struct ldap_queue_entry *entry;
-
-       msg->messageid = conn->next_msgid++;
+       /* setup a handler for events on this socket */
+       conn->event.fde = tevent_add_fd(conn->event.event_ctx, conn->sock,
+                                       socket_get_fd(conn->sock),
+                                       TEVENT_FD_READ, ldap_io_handler, conn);
+       if (conn->event.fde == NULL) {
+               composite_error(ctx, NT_STATUS_INTERNAL_ERROR);
+               return;
+       }
 
-       if (!ldap_encode(msg, &request))
-               return False;
+       tevent_fd_set_close_fn(conn->event.fde, socket_tevent_fd_close_fn);
+       socket_set_flags(conn->sock, SOCKET_FLAG_NOCLOSE);
 
-       result = (write_data_until(conn->sock, request.data, request.length,
-                                  endtime) == request.length);
+       talloc_steal(conn, conn->sock);
+       if (conn->ldaps) {
+               struct socket_context *tls_socket;
+               char *cafile = lp_tls_cafile(conn->sock, conn->lp_ctx);
 
-       data_blob_free(&request);
+               if (!cafile || !*cafile) {
+                       talloc_free(conn->sock);
+                       return;
+               }
 
-       if (!result)
-               return result;
+               tls_socket = tls_init_client(conn->sock, conn->event.fde, cafile);
+               talloc_free(cafile);
 
-       /* abandon and unbind don't expect results */
+               if (tls_socket == NULL) {
+                       talloc_free(conn->sock);
+                       return;
+               }
 
-       if ((msg->type == LDAP_TAG_AbandonRequest) ||
-           (msg->type == LDAP_TAG_UnbindRequest))
-               return True;
+               conn->sock = talloc_steal(conn, tls_socket);
+       }
 
-       entry = malloc_p(struct ldap_queue_entry);
+       conn->packet = packet_init(conn);
+       if (conn->packet == NULL) {
+               talloc_free(conn->sock);
+               return;
+       }
 
-       if (entry == NULL)
-               return False;
+       packet_set_private(conn->packet, conn);
+       packet_set_socket(conn->packet, conn->sock);
+       packet_set_callback(conn->packet, ldap_recv_handler);
+       packet_set_full_request(conn->packet, ldap_full_packet);
+       packet_set_error_handler(conn->packet, ldap_error_handler);
+       packet_set_event_context(conn->packet, conn->event.event_ctx);
+       packet_set_fde(conn->packet, conn->event.fde);
+/*     packet_set_serialise(conn->packet); */
 
-       entry->msgid = msg->messageid;
-       entry->msg = NULL;
-       DLIST_ADD(conn->outstanding, entry);
+       if (conn->ldaps) {
+               packet_set_unreliable_select(conn->packet);
+       }
 
-       return True;
+       composite_done(ctx);
 }
 
-BOOL ldap_receive_msg(struct ldap_connection *conn, struct ldap_message *msg,
-                     const struct timeval *endtime)
+static void ldap_connect_recv_tcp_conn(struct composite_context *ctx)
 {
-        struct asn1_data data;
-        BOOL result;
-
-        if (!asn1_read_sequence_until(conn->sock, &data, endtime))
-                return False;
-
-        result = ldap_decode(&data, msg);
+       struct ldap_connect_state *state =
+               talloc_get_type(ctx->async.private_data,
+                               struct ldap_connect_state);
+       struct ldap_connection *conn = state->conn;
+       uint16_t port;
+       NTSTATUS status = socket_connect_multi_recv(ctx, state, &conn->sock,
+                                                      &port);
+       if (!NT_STATUS_IS_OK(status)) {
+               composite_error(state->ctx, status);
+               return;
+       }
 
-        asn1_free(&data);
-        return result;
+       ldap_connect_got_sock(state->ctx, conn);
 }
 
-static struct ldap_message *recv_from_queue(struct ldap_connection *conn,
-                                           int msgid)
+static void ldap_connect_recv_unix_conn(struct composite_context *ctx)
 {
-       struct ldap_queue_entry *e;
+       struct ldap_connect_state *state =
+               talloc_get_type(ctx->async.private_data,
+                               struct ldap_connect_state);
+       struct ldap_connection *conn = state->conn;
 
-       for (e = conn->outstanding; e != NULL; e = e->next) {
+       NTSTATUS status = socket_connect_recv(ctx);
 
-               if (e->msgid == msgid) {
-                       struct ldap_message *result = e->msg;
-                       DLIST_REMOVE(conn->outstanding, e);
-                       SAFE_FREE(e);
-                       return result;
-               }
+       if (!NT_STATUS_IS_OK(state->ctx->status)) {
+               composite_error(state->ctx, status);
+               return;
        }
 
-       return NULL;
+       ldap_connect_got_sock(state->ctx, conn);
 }
 
-static void add_search_entry(struct ldap_connection *conn,
-                            struct ldap_message *msg)
+_PUBLIC_ NTSTATUS ldap_connect_recv(struct composite_context *ctx)
 {
-       struct ldap_queue_entry *e = malloc_p(struct ldap_queue_entry);
-
-       if (e == NULL)
-               return;
-
-       e->msg = msg;
-       DLIST_ADD_END(conn->search_entries, e, struct ldap_queue_entry *);
-       return;
+       NTSTATUS status = composite_wait(ctx);
+       talloc_free(ctx);
+       return status;
 }
 
-static void fill_outstanding_request(struct ldap_connection *conn,
-                                    struct ldap_message *msg)
+_PUBLIC_ NTSTATUS ldap_connect(struct ldap_connection *conn, const char *url)
 {
-       struct ldap_queue_entry *e;
+       struct composite_context *ctx = ldap_connect_send(conn, url);
+       return ldap_connect_recv(ctx);
+}
 
-       for (e = conn->outstanding; e != NULL; e = e->next) {
-               if (e->msgid == msg->messageid) {
-                       e->msg = msg;
-                       return;
-               }
-       }
+/* set reconnect parameters */
 
-       /* This reply has not been expected, destroy the incoming msg */
-       talloc_free(msg);
-       return;
+_PUBLIC_ void ldap_set_reconn_params(struct ldap_connection *conn, int max_retries)
+{
+       if (conn) {
+               conn->reconnect.max_retries = max_retries;
+               conn->reconnect.retries = 0;
+               conn->reconnect.previous = time(NULL);
+       }
 }
 
-struct ldap_message *ldap_receive(struct ldap_connection *conn, int msgid,
-                                 const struct timeval *endtime)
+/* Actually this function is NOT ASYNC safe, FIXME? */
+static void ldap_reconnect(struct ldap_connection *conn)
 {
-       struct ldap_message *result = recv_from_queue(conn, msgid);
-
-       if (result != NULL)
-               return result;
-
-       while (True) {
-               struct asn1_data data;
-               BOOL res;
-
-               result = new_ldap_message(conn);
+       NTSTATUS status;
+       time_t now = time(NULL);
 
-               if (!asn1_read_sequence_until(conn->sock, &data, endtime))
-                       return NULL;
+       /* do we have set up reconnect ? */
+       if (conn->reconnect.max_retries == 0) return;
 
-               res = ldap_decode(&data, result);
-               asn1_free(&data);
+       /* is the retry time expired ? */
+       if (now > conn->reconnect.previous + 30) {
+               conn->reconnect.retries = 0;
+               conn->reconnect.previous = now;
+       }
 
-               if (!res)
-                       return NULL;
+       /* are we reconnectind too often and too fast? */
+       if (conn->reconnect.retries > conn->reconnect.max_retries) return;
 
-               if (result->messageid == msgid)
-                       return result;
+       /* keep track of the number of reconnections */
+       conn->reconnect.retries++;
 
-               if (result->type == LDAP_TAG_SearchResultEntry) {
-                       add_search_entry(conn, result);
-               } else {
-                       fill_outstanding_request(conn, result);
-               }
+       /* reconnect */
+       status = ldap_connect(conn, conn->reconnect.url);
+       if ( ! NT_STATUS_IS_OK(status)) {
+               return;
        }
 
-       return NULL;
+       /* rebind */
+       status = ldap_rebind(conn);
+       if ( ! NT_STATUS_IS_OK(status)) {
+               ldap_connection_dead(conn);
+       }
 }
 
-struct ldap_message *ldap_transaction(struct ldap_connection *conn,
-                                     struct ldap_message *request)
+/* destroy an open ldap request */
+static int ldap_request_destructor(struct ldap_request *req)
 {
-       if (!ldap_send_msg(conn, request, NULL))
-               return False;
-
-       return ldap_receive(conn, request->messageid, NULL);
+       if (req->state == LDAP_REQUEST_PENDING) {
+               DLIST_REMOVE(req->conn->pending, req);
+       }
+       return 0;
 }
 
-int ldap_bind_simple(struct ldap_connection *conn, const char *userdn, const char *password)
+/*
+  called on timeout of a ldap request
+*/
+static void ldap_request_timeout(struct tevent_context *ev, struct tevent_timer *te, 
+                                     struct timeval t, void *private_data)
 {
-       struct ldap_message *response;
-       struct ldap_message *msg;
-       const char *dn, *pw;
-       int result = LDAP_OTHER;
-
-       if (conn == NULL)
-               return result;
-
-       if (userdn) {
-               dn = userdn;
-       } else {
-               if (conn->auth_dn) {
-                       dn = conn->auth_dn;
-               } else {
-                       dn = "";
-               }
+       struct ldap_request *req = talloc_get_type(private_data, struct ldap_request);
+       req->status = NT_STATUS_IO_TIMEOUT;
+       if (req->state == LDAP_REQUEST_PENDING) {
+               DLIST_REMOVE(req->conn->pending, req);
        }
-
-       if (password) {
-               pw = password;
-       } else {
-               if (conn->simple_pw) {
-                       pw = conn->simple_pw;
-               } else {
-                       pw = "";
-               }
+       req->state = LDAP_REQUEST_DONE;
+       if (req->async.fn) {
+               req->async.fn(req);
        }
+}
 
-       msg =  new_ldap_simple_bind_msg(conn, dn, pw);
-       if (!msg)
-               return result;
 
-       response = ldap_transaction(conn, msg);
-       if (!response) {
-               talloc_free(msg);
-               return result;
+/*
+  called on completion of a one-way ldap request
+*/
+static void ldap_request_complete(struct tevent_context *ev, struct tevent_timer *te, 
+                                 struct timeval t, void *private_data)
+{
+       struct ldap_request *req = talloc_get_type(private_data, struct ldap_request);
+       if (req->async.fn) {
+               req->async.fn(req);
        }
-               
-       result = response->r.BindResponse.response.resultcode;
-
-       talloc_free(msg);
-       talloc_free(response);
-
-       return result;
 }
 
-int ldap_bind_sasl(struct ldap_connection *conn, const char *username, const char *domain, const char *password)
+/*
+  send a ldap message - async interface
+*/
+_PUBLIC_ struct ldap_request *ldap_request_send(struct ldap_connection *conn,
+                                      struct ldap_message *msg)
 {
-       NTSTATUS status;
-       TALLOC_CTX *mem_ctx = NULL;
-       struct ldap_message *response;
-       struct ldap_message *msg;
-       DATA_BLOB input = data_blob(NULL, 0);
-       DATA_BLOB output = data_blob(NULL, 0);
-       int result = LDAP_OTHER;
-
-       if (conn == NULL)
-               return result;
-
-       status = gensec_client_start(conn, &conn->gensec);
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(0, ("Failed to start GENSEC engine (%s)\n", nt_errstr(status)));
-               return result;
-       }
+       struct ldap_request *req;
+       NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
 
-       gensec_want_feature(conn->gensec, GENSEC_FEATURE_SIGN | GENSEC_FEATURE_SEAL);
+       req = talloc_zero(conn, struct ldap_request);
+       if (req == NULL) return NULL;
 
-       status = gensec_set_domain(conn->gensec, domain);
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(1, ("Failed to start set GENSEC client domain to %s: %s\n", 
-                         domain, nt_errstr(status)));
-               goto done;
+       if (conn->sock == NULL) {
+               status = NT_STATUS_INVALID_CONNECTION;
+               goto failed;
        }
 
-       status = gensec_set_username(conn->gensec, username);
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(1, ("Failed to start set GENSEC client username to %s: %s\n", 
-                         username, nt_errstr(status)));
-               goto done;
+       req->state       = LDAP_REQUEST_SEND;
+       req->conn        = conn;
+       req->messageid   = conn->next_messageid++;
+       if (conn->next_messageid == 0) {
+               conn->next_messageid = 1;
        }
-
-       status = gensec_set_password(conn->gensec, password);
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(1, ("Failed to start set GENSEC client password: %s\n", 
-                         nt_errstr(status)));
-               goto done;
+       req->type        = msg->type;
+       if (req->messageid == -1) {
+               goto failed;
        }
 
-       status = gensec_set_target_hostname(conn->gensec, conn->host);
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(1, ("Failed to start set GENSEC target hostname: %s\n", 
-                         nt_errstr(status)));
-               goto done;
-       }
+       talloc_set_destructor(req, ldap_request_destructor);
 
-       status = gensec_set_target_service(conn->gensec, "ldap");
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(1, ("Failed to start set GENSEC target service: %s\n", 
-                         nt_errstr(status)));
-               goto done;
+       msg->messageid = req->messageid;
+
+       if (!ldap_encode(msg, samba_ldap_control_handlers(), &req->data, req)) {
+               status = NT_STATUS_INTERNAL_ERROR;
+               goto failed;            
        }
 
-       status = gensec_start_mech_by_sasl_name(conn->gensec, "GSS-SPNEGO");
+       status = packet_send(conn->packet, req->data);
        if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(1, ("Failed to start set GENSEC client SPNEGO mechanism: %s\n",
-                         nt_errstr(status)));
-               goto done;
+               goto failed;
        }
 
-       mem_ctx = talloc_init("ldap_bind_sasl");
-       if (!mem_ctx)
-               goto done;
+       /* some requests don't expect a reply, so don't add those to the
+          pending queue */
+       if (req->type == LDAP_TAG_AbandonRequest ||
+           req->type == LDAP_TAG_UnbindRequest) {
+               req->status = NT_STATUS_OK;
+               req->state = LDAP_REQUEST_DONE;
+               /* we can't call the async callback now, as it isn't setup, so
+                  call it as next event */
+               tevent_add_timer(conn->event.event_ctx, req, timeval_zero(),
+                                ldap_request_complete, req);
+               return req;
+       }
 
-       status = gensec_update(conn->gensec, mem_ctx,
-                              input,
-                              &output);
+       req->state = LDAP_REQUEST_PENDING;
+       DLIST_ADD(conn->pending, req);
 
-       while(1) {
-               if (NT_STATUS_IS_OK(status) && output.length == 0) {
-                       break;
-               }
-               if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(status)) {
-                       break;
-               }
+       /* put a timeout on the request */
+       req->time_event = tevent_add_timer(conn->event.event_ctx, req,
+                                          timeval_current_ofs(conn->timeout, 0),
+                                          ldap_request_timeout, req);
 
-               msg =  new_ldap_sasl_bind_msg(conn, "GSS-SPNEGO", &output);
-               if (!msg)
-                       goto done;
+       return req;
 
-               response = ldap_transaction(conn, msg);
-               talloc_free(msg);
+failed:
+       req->status = status;
+       req->state = LDAP_REQUEST_ERROR;
+       tevent_add_timer(conn->event.event_ctx, req, timeval_zero(),
+                        ldap_request_complete, req);
 
-               if (!response) {
-                       goto done;
-               }
+       return req;
+}
 
-               result = response->r.BindResponse.response.resultcode;
 
-               if (result != LDAP_SUCCESS && result != LDAP_SASL_BIND_IN_PROGRESS) {
+/*
+  wait for a request to complete
+  note that this does not destroy the request
+*/
+_PUBLIC_ NTSTATUS ldap_request_wait(struct ldap_request *req)
+{
+       while (req->state < LDAP_REQUEST_DONE) {
+               if (tevent_loop_once(req->conn->event.event_ctx) != 0) {
+                       req->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
                        break;
                }
-
-               if (!NT_STATUS_IS_OK(status)) {
-                       status = gensec_update(conn->gensec, mem_ctx,
-                                              response->r.BindResponse.SASL.secblob,
-                                              &output);
-               } else {
-                       output.length = 0;
-               }
-
-               talloc_free(response);
        }
-
-done:
-       if (mem_ctx)
-               talloc_free(mem_ctx);
-
-       return result;
+       return req->status;
 }
 
-struct ldap_connection *ldap_setup_connection(TALLOC_CTX *mem_ctx, const char *url, 
-                                               const char *userdn, const char *password)
+
+/*
+  a mapping of ldap response code to strings
+*/
+static const struct {
+       enum ldap_result_code code;
+       const char *str;
+} ldap_code_map[] = {
+#define _LDAP_MAP_CODE(c) { c, #c }
+       _LDAP_MAP_CODE(LDAP_SUCCESS),
+       _LDAP_MAP_CODE(LDAP_OPERATIONS_ERROR),
+       _LDAP_MAP_CODE(LDAP_PROTOCOL_ERROR),
+       _LDAP_MAP_CODE(LDAP_TIME_LIMIT_EXCEEDED),
+       _LDAP_MAP_CODE(LDAP_SIZE_LIMIT_EXCEEDED),
+       _LDAP_MAP_CODE(LDAP_COMPARE_FALSE),
+       _LDAP_MAP_CODE(LDAP_COMPARE_TRUE),
+       _LDAP_MAP_CODE(LDAP_AUTH_METHOD_NOT_SUPPORTED),
+       _LDAP_MAP_CODE(LDAP_STRONG_AUTH_REQUIRED),
+       _LDAP_MAP_CODE(LDAP_REFERRAL),
+       _LDAP_MAP_CODE(LDAP_ADMIN_LIMIT_EXCEEDED),
+       _LDAP_MAP_CODE(LDAP_UNAVAILABLE_CRITICAL_EXTENSION),
+       _LDAP_MAP_CODE(LDAP_CONFIDENTIALITY_REQUIRED),
+       _LDAP_MAP_CODE(LDAP_SASL_BIND_IN_PROGRESS),
+       _LDAP_MAP_CODE(LDAP_NO_SUCH_ATTRIBUTE),
+       _LDAP_MAP_CODE(LDAP_UNDEFINED_ATTRIBUTE_TYPE),
+       _LDAP_MAP_CODE(LDAP_INAPPROPRIATE_MATCHING),
+       _LDAP_MAP_CODE(LDAP_CONSTRAINT_VIOLATION),
+       _LDAP_MAP_CODE(LDAP_ATTRIBUTE_OR_VALUE_EXISTS),
+       _LDAP_MAP_CODE(LDAP_INVALID_ATTRIBUTE_SYNTAX),
+       _LDAP_MAP_CODE(LDAP_NO_SUCH_OBJECT),
+       _LDAP_MAP_CODE(LDAP_ALIAS_PROBLEM),
+       _LDAP_MAP_CODE(LDAP_INVALID_DN_SYNTAX),
+       _LDAP_MAP_CODE(LDAP_ALIAS_DEREFERENCING_PROBLEM),
+       _LDAP_MAP_CODE(LDAP_INAPPROPRIATE_AUTHENTICATION),
+       _LDAP_MAP_CODE(LDAP_INVALID_CREDENTIALS),
+       _LDAP_MAP_CODE(LDAP_INSUFFICIENT_ACCESS_RIGHTS),
+       _LDAP_MAP_CODE(LDAP_BUSY),
+       _LDAP_MAP_CODE(LDAP_UNAVAILABLE),
+       _LDAP_MAP_CODE(LDAP_UNWILLING_TO_PERFORM),
+       _LDAP_MAP_CODE(LDAP_LOOP_DETECT),
+       _LDAP_MAP_CODE(LDAP_NAMING_VIOLATION),
+       _LDAP_MAP_CODE(LDAP_OBJECT_CLASS_VIOLATION),
+       _LDAP_MAP_CODE(LDAP_NOT_ALLOWED_ON_NON_LEAF),
+       _LDAP_MAP_CODE(LDAP_NOT_ALLOWED_ON_RDN),
+       _LDAP_MAP_CODE(LDAP_ENTRY_ALREADY_EXISTS),
+       _LDAP_MAP_CODE(LDAP_OBJECT_CLASS_MODS_PROHIBITED),
+       _LDAP_MAP_CODE(LDAP_AFFECTS_MULTIPLE_DSAS),
+       _LDAP_MAP_CODE(LDAP_OTHER)
+};
+
+/*
+  used to setup the status code from a ldap response
+*/
+_PUBLIC_ NTSTATUS ldap_check_response(struct ldap_connection *conn, struct ldap_Result *r)
 {
-       struct ldap_connection *conn;
-       int result;
+       int i;
+       const char *codename = "unknown";
 
-       conn =ldap_connect(mem_ctx, url);
-       if (!conn) {
-               return NULL;
+       if (r->resultcode == LDAP_SUCCESS) {
+               return NT_STATUS_OK;
        }
 
-       result = ldap_bind_simple(conn, userdn, password);
-       if (result != LDAP_SUCCESS) {
-               talloc_free(conn);
-               return NULL;
+       if (conn->last_error) {
+               talloc_free(conn->last_error);
        }
 
-       return conn;
-}
-
-struct ldap_connection *ldap_setup_connection_with_sasl(TALLOC_CTX *mem_ctx, const char *url,
-                                                       const char *username, const char *domain, const char *password)
-{
-       struct ldap_connection *conn;
-       int result;
-
-       conn =ldap_connect(mem_ctx, url);
-       if (!conn) {
-               return NULL;
-       }
-
-       result = ldap_bind_sasl(conn, username, domain, password);
-       if (result != LDAP_SUCCESS) {
-               talloc_free(conn);
-               return NULL;
+       for (i=0;i<ARRAY_SIZE(ldap_code_map);i++) {
+               if (r->resultcode == ldap_code_map[i].code) {
+                       codename = ldap_code_map[i].str;
+                       break;
+               }
        }
 
-       return conn;
+       conn->last_error = talloc_asprintf(conn, "LDAP error %u %s - %s <%s> <%s>", 
+                                          r->resultcode,
+                                          codename,
+                                          r->dn?r->dn:"(NULL)", 
+                                          r->errormessage?r->errormessage:"", 
+                                          r->referral?r->referral:"");
+       
+       return NT_STATUS_LDAP(r->resultcode);
 }
 
-BOOL ldap_abandon_message(struct ldap_connection *conn, int msgid,
-                                const struct timeval *endtime)
+/*
+  return error string representing the last error
+*/
+_PUBLIC_ const char *ldap_errstr(struct ldap_connection *conn, 
+                       TALLOC_CTX *mem_ctx, 
+                       NTSTATUS status)
 {
-       struct ldap_message *msg = new_ldap_message(conn);
-       BOOL result;
-
-       if (msg == NULL)
-               return False;
-
-       msg->type = LDAP_TAG_AbandonRequest;
-       msg->r.AbandonRequest.messageid = msgid;
-
-       result = ldap_send_msg(conn, msg, endtime);
-       talloc_free(msg);
-       return result;
+       if (NT_STATUS_IS_LDAP(status) && conn->last_error != NULL) {
+               return talloc_strdup(mem_ctx, conn->last_error);
+       }
+       return talloc_asprintf(mem_ctx, "LDAP client internal error: %s", nt_errstr(status));
 }
 
-BOOL ldap_setsearchent(struct ldap_connection *conn, struct ldap_message *msg,
-                      const struct timeval *endtime)
-{
-       if ((conn->searchid != 0) &&
-           (!ldap_abandon_message(conn, conn->searchid, endtime)))
-               return False;
-
-       conn->searchid = conn->next_msgid;
-       return ldap_send_msg(conn, msg, endtime);
-}
 
-struct ldap_message *ldap_getsearchent(struct ldap_connection *conn,
-                                      const struct timeval *endtime)
+/*
+  return the Nth result message, waiting if necessary
+*/
+_PUBLIC_ NTSTATUS ldap_result_n(struct ldap_request *req, int n, struct ldap_message **msg)
 {
-       struct ldap_message *result;
+       *msg = NULL;
 
-       if (conn->search_entries != NULL) {
-               struct ldap_queue_entry *e = conn->search_entries;
+       NT_STATUS_HAVE_NO_MEMORY(req);
 
-               result = e->msg;
-               DLIST_REMOVE(conn->search_entries, e);
-               SAFE_FREE(e);
-               return result;
+       while (req->state < LDAP_REQUEST_DONE && n >= req->num_replies) {
+               if (tevent_loop_once(req->conn->event.event_ctx) != 0) {
+                       return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+               }
        }
 
-       result = ldap_receive(conn, conn->searchid, endtime);
-       if (!result) {
-               return NULL;
+       if (n < req->num_replies) {
+               *msg = req->replies[n];
+               return NT_STATUS_OK;
        }
 
-       if (result->type == LDAP_TAG_SearchResultEntry)
-               return result;
-
-       if (result->type == LDAP_TAG_SearchResultDone) {
-               /* TODO: Handle Paged Results */
-               talloc_free(result);
-               return NULL;
+       if (!NT_STATUS_IS_OK(req->status)) {
+               return req->status;
        }
 
-       /* TODO: Handle Search References here */
-       return NULL;
+       return NT_STATUS_NO_MORE_ENTRIES;
 }
 
-void ldap_endsearchent(struct ldap_connection *conn,
-                      const struct timeval *endtime)
-{
-       struct ldap_queue_entry *e;
-
-       e = conn->search_entries;
-
-       while (e != NULL) {
-               struct ldap_queue_entry *next = e->next;
-               DLIST_REMOVE(conn->search_entries, e);
-               SAFE_FREE(e);
-               e = next;
-       }
-}
 
-struct ldap_message *ldap_searchone(struct ldap_connection *conn,
-                                   struct ldap_message *msg,
-                                   const struct timeval *endtime)
+/*
+  return a single result message, checking if it is of the expected LDAP type
+*/
+_PUBLIC_ NTSTATUS ldap_result_one(struct ldap_request *req, struct ldap_message **msg, int type)
 {
-       struct ldap_message *res1, *res2 = NULL;
-       if (!ldap_setsearchent(conn, msg, endtime))
-               return NULL;
-
-       res1 = ldap_getsearchent(conn, endtime);
-
-       if (res1 != NULL)
-               res2 = ldap_getsearchent(conn, endtime);
-
-       ldap_endsearchent(conn, endtime);
-
-       if (res1 == NULL)
-               return NULL;
-
-       if (res2 != NULL) {
-               /* More than one entry */
-               talloc_free(res1);
-               talloc_free(res2);
-               return NULL;
+       NTSTATUS status;
+       status = ldap_result_n(req, 0, msg);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
        }
-
-       return res1;
-}
-
-BOOL ldap_find_single_value(struct ldap_message *msg, const char *attr,
-                           DATA_BLOB *value)
-{
-       int i;
-       struct ldap_SearchResEntry *r = &msg->r.SearchResultEntry;
-
-       if (msg->type != LDAP_TAG_SearchResultEntry)
-               return False;
-
-       for (i=0; i<r->num_attributes; i++) {
-               if (strequal(attr, r->attributes[i].name)) {
-                       if (r->attributes[i].num_values != 1)
-                               return False;
-
-                       *value = r->attributes[i].values[0];
-                       return True;
-               }
+       if ((*msg)->type != type) {
+               *msg = NULL;
+               return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
        }
-       return False;
-}
-
-BOOL ldap_find_single_string(struct ldap_message *msg, const char *attr,
-                            TALLOC_CTX *mem_ctx, char **value)
-{
-       DATA_BLOB blob;
-
-       if (!ldap_find_single_value(msg, attr, &blob))
-               return False;
-
-       *value = talloc_size(mem_ctx, blob.length+1);
-
-       if (*value == NULL)
-               return False;
-
-       memcpy(*value, blob.data, blob.length);
-       (*value)[blob.length] = '\0';
-       return True;
-}
-
-BOOL ldap_find_single_int(struct ldap_message *msg, const char *attr,
-                         int *value)
-{
-       DATA_BLOB blob;
-       char *val;
-       int errno_save;
-       BOOL res;
-
-       if (!ldap_find_single_value(msg, attr, &blob))
-               return False;
-
-       val = malloc(blob.length+1);
-       if (val == NULL)
-               return False;
-
-       memcpy(val, blob.data, blob.length);
-       val[blob.length] = '\0';
-
-       errno_save = errno;
-       errno = 0;
-
-       *value = strtol(val, NULL, 10);
-
-       res = (errno == 0);
-
-       free(val);
-       errno = errno_save;
-
-       return res;
-}
-
-int ldap_error(struct ldap_connection *conn)
-{
-       return 0;
+       return status;
 }
 
-NTSTATUS ldap2nterror(int ldaperror)
+/*
+  a simple ldap transaction, for single result requests that only need a status code
+  this relies on single valued requests having the response type == request type + 1
+*/
+_PUBLIC_ NTSTATUS ldap_transaction(struct ldap_connection *conn, struct ldap_message *msg)
 {
-       return NT_STATUS_OK;
+       struct ldap_request *req = ldap_request_send(conn, msg);
+       struct ldap_message *res;
+       NTSTATUS status;
+       status = ldap_result_n(req, 0, &res);
+       if (!NT_STATUS_IS_OK(status)) {
+               talloc_free(req);
+               return status;
+       }
+       if (res->type != msg->type + 1) {
+               talloc_free(req);
+               return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+       }
+       status = ldap_check_response(conn, &res->r.GeneralResult);
+       talloc_free(req);
+       return status;
 }