r3507: - added deferred replies on sharing violation in pvfs open. The
[jelmer/samba4-debian.git] / source / ldap_server / ldap_server.c
index 09df833722368fdd5c17eb1c12abeea2a529faa4..9338baa165f8597fc76be44c62676587445fc3d8 100644 (file)
 */
 
 #include "includes.h"
+#include "events.h"
+#include "auth/auth.h"
+#include "dlinklist.h"
+#include "asn_1.h"
+#include "ldap_server/ldap_server.h"
 
 /*
   close the socket and shutdown a server_context
@@ -33,13 +38,19 @@ static void ldapsrv_terminate_connection(struct ldapsrv_connection *ldap_conn, c
   add a socket address to the list of events, one event per port
 */
 static void add_socket(struct server_service *service, 
-                      const struct model_ops *model_ops,
-                      struct socket_context *socket_ctx, 
-                      struct in_addr *ifip)
+                      const struct model_ops *model_ops, 
+                      struct ipv4_addr *ifip)
 {
+       struct server_socket *srv_sock;
        uint16_t port = 389;
+       char *ip_str = talloc_strdup(service, sys_inet_ntoa(*ifip));
 
-       service_setup_socket(service, model_ops, socket_ctx, ifip, &port);
+       srv_sock = service_setup_socket(service, model_ops, "ipv4", ip_str, &port);
+
+       port = 3268;
+       srv_sock = service_setup_socket(service, model_ops, "ipv4", ip_str, &port);
+
+       talloc_free(ip_str);
 }
 
 /****************************************************************************
@@ -48,7 +59,41 @@ static void add_socket(struct server_service *service,
 static void ldapsrv_init(struct server_service *service,
                         const struct model_ops *model_ops)
 {      
-       DEBUG(1,("ldapsrv_init\n"));
+       struct ldapsrv_service *ldap_service;
+       struct ldapsrv_partition *part;
+
+       DEBUG(10,("ldapsrv_init\n"));
+
+       ldap_service = talloc_p(service, struct ldapsrv_service);
+       if (!ldap_service) {
+               DEBUG(0,("talloc_p(service, struct ldapsrv_service) failed\n"));
+               return;
+       }
+       ZERO_STRUCTP(ldap_service);
+
+       part = talloc_p(ldap_service, struct ldapsrv_partition);
+       if (!ldap_service) {
+               DEBUG(0,("talloc_p(ldap_service, struct ldapsrv_partition) failed\n"));
+               return;
+       }
+       part->base_dn = ""; /* RootDSE */
+       part->ops = ldapsrv_get_rootdse_partition_ops();
+
+       ldap_service->rootDSE = part;
+       DLIST_ADD_END(ldap_service->partitions, part, struct ldapsrv_partition *);
+
+       part = talloc_p(ldap_service, struct ldapsrv_partition);
+       if (!ldap_service) {
+               DEBUG(0,("talloc_p(ldap_service, struct ldapsrv_partition) failed\n"));
+               return;
+       }
+       part->base_dn = "*"; /* default partition */
+       part->ops = ldapsrv_get_sldb_partition_ops();
+
+       ldap_service->default_partition = part;
+       DLIST_ADD_END(ldap_service->partitions, part, struct ldapsrv_partition *);
+
+       service->private_data = ldap_service;
 
        if (lp_interfaces() && lp_bind_interfaces_only()) {
                int num_interfaces = iface_count();
@@ -59,7 +104,7 @@ static void ldapsrv_init(struct server_service *service,
                   socket per interface and bind to only these.
                */
                for(i = 0; i < num_interfaces; i++) {
-                       struct in_addr *ifip = iface_n_ip(i);
+                       struct ipv4_addr *ifip = iface_n_ip(i);
 
                        if (ifip == NULL) {
                                DEBUG(0,("ldapsrv_init: interface %d has NULL "
@@ -67,21 +112,14 @@ static void ldapsrv_init(struct server_service *service,
                                continue;
                        }
 
-                       add_socket(service, model_ops, NULL, ifip);
+                       add_socket(service, model_ops, ifip);
                }
        } else {
-               struct in_addr *ifip;
-               TALLOC_CTX *mem_ctx = talloc_init("ldapsrv_init");
-
-               if (!mem_ctx) {
-                       smb_panic("No memory");
-               }       
+               struct ipv4_addr ifip;
 
                /* Just bind to lp_socket_address() (usually 0.0.0.0) */
-               ifip = interpret_addr2(mem_ctx, lp_socket_address());
-               add_socket(service, model_ops, NULL, ifip);
-
-               talloc_destroy(mem_ctx);
+               ifip = interpret_addr2(lp_socket_address());
+               add_socket(service, model_ops, &ifip);
        }
 }
 
@@ -90,7 +128,21 @@ static void ldapsrv_init(struct server_service *service,
    that a read(2) holds a complete request that is then thrown away
    completely. */
 
-static BOOL append_to_buf(struct rw_buffer *buf, uint8_t *data, size_t length)
+void ldapsrv_consumed_from_buf(struct rw_buffer *buf,
+                                  size_t length)
+{
+       memcpy(buf->data, buf->data+length, buf->length-length);
+       buf->length -= length;
+}
+
+static void peek_into_read_buf(struct rw_buffer *buf, uint8_t **out,
+                              size_t *out_length)
+{
+       *out = buf->data;
+       *out_length = buf->length;
+}
+
+BOOL ldapsrv_append_to_buf(struct rw_buffer *buf, uint8_t *data, size_t length)
 {
        buf->data = realloc(buf->data, buf->length+length);
 
@@ -103,407 +155,312 @@ static BOOL append_to_buf(struct rw_buffer *buf, uint8_t *data, size_t length)
        return True;
 }
 
-static BOOL read_into_buf(int fd, struct rw_buffer *buf)
+static BOOL read_into_buf(struct socket_context *sock, struct rw_buffer *buf)
 {
-       char tmp_buf[1024];
-       int len;
+       NTSTATUS status;
+       DATA_BLOB tmp_blob;
+       BOOL ret;
+       size_t nread;
 
-       len = read(fd, tmp_buf, sizeof(tmp_buf));
-       if (len == 0)
+       tmp_blob = data_blob_talloc(sock, NULL, 1024);
+       if (tmp_blob.data == NULL) {
                return False;
+       }
 
-       return append_to_buf(buf, tmp_buf, len);
-}
+       status = socket_recv(sock, tmp_blob.data, tmp_blob.length, &nread, 0);
+       if (NT_STATUS_IS_ERR(status)) {
+               DEBUG(10,("socket_recv: %s\n",nt_errstr(status)));
+               talloc_free(tmp_blob.data);
+               return False;
+       }
 
-static BOOL write_from_buf(int fd, struct rw_buffer *buf)
-{
-       int len;
+       ret = ldapsrv_append_to_buf(buf, tmp_blob.data, tmp_blob.length);
 
-       len = write(fd, buf->data, buf->length);
-       if (len != buf->length)
-               return False;
+       talloc_free(tmp_blob.data);
 
-       return True;
+       return ret;
 }
 
-static void peek_into_read_buf(struct rw_buffer *buf, uint8_t **out,
-                              size_t *out_length)
+static BOOL ldapsrv_read_buf(struct ldapsrv_connection *conn)
 {
-       *out = buf->data;
-       *out_length = buf->length;
-}
+       NTSTATUS status;
+       DATA_BLOB tmp_blob;
+       DATA_BLOB creds;
+       BOOL ret;
+       uint8_t *buf;
+       int buf_length, sasl_length;
+       struct socket_context *sock = conn->connection->socket;
+       TALLOC_CTX *mem_ctx;
+       size_t nread;
+
+       if (!conn->gensec || !conn->session_info ||
+          !(gensec_have_feature(conn->gensec, GENSEC_WANT_SIGN) &&
+            gensec_have_feature(conn->gensec, GENSEC_WANT_SEAL))) {
+               return read_into_buf(sock, &conn->in_buffer);
+       }
 
-static void consumed_from_read_buf(struct rw_buffer *buf,
-                                  size_t length)
-{
-       memcpy(buf->data, buf->data+length, buf->length-length);
-       buf->length -= length;
-}
+       mem_ctx = talloc(conn, 0);
+       if (!mem_ctx) {
+               DEBUG(0,("no memory\n"));
+               return False;
+       }
 
-static BOOL ldap_append_to_buf(struct ldap_message *msg, struct rw_buffer *buf)
-{
-       DATA_BLOB blob;
-       BOOL res;
+       tmp_blob = data_blob_talloc(mem_ctx, NULL, 1024);
+       if (tmp_blob.data == NULL) {
+               talloc_free(mem_ctx);
+               return False;
+       }
 
-       if (!ldap_encode(msg, &blob))
+       status = socket_recv(sock, tmp_blob.data, tmp_blob.length, &nread, 0);
+       if (NT_STATUS_IS_ERR(status)) {
+               DEBUG(10,("socket_recv: %s\n",nt_errstr(status)));
+               talloc_free(mem_ctx);
                return False;
+       }
+       tmp_blob.length = nread;
 
-       res = append_to_buf(buf, blob.data, blob.length);
+       ret = ldapsrv_append_to_buf(&conn->sasl_in_buffer, tmp_blob.data, tmp_blob.length);
+       if (!ret) {
+               talloc_free(mem_ctx);
+               return False;
+       }
 
-       data_blob_free(&blob);
-       return res;
-}
+       peek_into_read_buf(&conn->sasl_in_buffer, &buf, &buf_length);
 
-static void reply_unwilling(struct ldapsrv_connection *ldap_conn, int error)
-{
-       struct ldap_message *msg;
-       struct ldap_ExtendedResponse *r;
+       if (buf_length < 4) {
+               /* not enough yet */
+               talloc_free(mem_ctx);
+               return True;
+       }
 
-       msg = new_ldap_message();
+       sasl_length = RIVAL(buf, 0);
 
-       if (msg == NULL) {
-               ldapsrv_terminate_connection(ldap_conn, "new_ldap_message() failed");
-               return;
+       if (buf_length < (4 + sasl_length)) {
+               /* not enough yet */
+               talloc_free(mem_ctx);
+               return True;
+       }
+
+       creds.data = buf + 4;
+       creds.length = gensec_sig_size(conn->gensec);
+
+       if (creds.length > sasl_length) {
+               /* invalid packet? */
+               talloc_free(mem_ctx);
+               return False;
        }
 
-       msg->messageid = 0;
-       r = &msg->r.ExtendedResponse;   
+       tmp_blob.data = buf + (4 + creds.length);
+       tmp_blob.length = (4 + sasl_length) - (4 + creds.length);
+
+       if (gensec_have_feature(conn->gensec, GENSEC_WANT_SEAL)) {
+               status = gensec_unseal_packet(conn->gensec, mem_ctx,
+                                             tmp_blob.data, tmp_blob.length,
+                                             tmp_blob.data, tmp_blob.length,
+                                             &creds);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(0,("gensec_unseal_packet: %s\n",nt_errstr(status)));
+                       talloc_free(mem_ctx);
+                       return False;
+               }
+       } else {
+               status = gensec_check_packet(conn->gensec, mem_ctx,
+                                             tmp_blob.data, tmp_blob.length,
+                                             tmp_blob.data, tmp_blob.length,
+                                             &creds);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(0,("gensec_check_packet: %s\n",nt_errstr(status)));
+                       talloc_free(mem_ctx);
+                       return False;
+               }
+       }
 
-       /* When completely freaking out, OpenLDAP responds with an ExtResp */
-       msg->type = LDAP_TAG_ExtendedResponse;
-       r->response.resultcode = error;
-       r->response.dn = NULL;
-       r->response.errormessage = NULL;
-       r->response.referral = NULL;
-       r->name = NULL;
-       r->value.data = NULL;
-       r->value.length = 0;
+       ret = ldapsrv_append_to_buf(&conn->in_buffer, tmp_blob.data, tmp_blob.length);
+       if (!ret) {
+               talloc_free(mem_ctx);
+               return False;
+       }
 
-       ldap_append_to_buf(msg, &ldap_conn->out_buffer);
+       ldapsrv_consumed_from_buf(&conn->sasl_in_buffer, 4 + sasl_length);
 
-       talloc_destroy(msg->mem_ctx);
+       talloc_free(mem_ctx);
+       return ret;
 }
 
-static void ldap_reply_BindRequest(struct ldapsrv_connection *conn, 
-                                  struct ldap_message *request)
+static BOOL write_from_buf(struct socket_context *sock, struct rw_buffer *buf)
 {
-       struct ldap_BindRequest *req = &request->r.BindRequest;
-
-       struct ldap_message *msg;
-       struct ldap_BindResponse *resp;
+       NTSTATUS status;
+       DATA_BLOB tmp_blob;
+       size_t sendlen;
 
-       DEBUG(5, ("Binding as %s with pw %s\n",
-                 req->dn, req->creds.password));
+       tmp_blob.data = buf->data;
+       tmp_blob.length = buf->length;
 
-       msg = new_ldap_message();
-
-       if (msg == NULL) {
-               ldapsrv_terminate_connection(conn, "new_ldap_message() failed");
-               return;
+       status = socket_send(sock, &tmp_blob, &sendlen, 0);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(10,("socket_send() %s\n",nt_errstr(status)));
+               return False;
        }
 
-       resp = &msg->r.BindResponse;
-
-       msg->messageid = request->messageid;
-       msg->type = LDAP_TAG_BindResponse;
-       resp->response.resultcode = 0;
-       resp->response.dn = NULL;
-       resp->response.errormessage = NULL;
-       resp->response.referral = NULL;
-       resp->SASL.secblob = data_blob(NULL, 0);
+       ldapsrv_consumed_from_buf(buf, sendlen);
 
-       ldap_append_to_buf(msg, &conn->out_buffer);
-       talloc_destroy(msg->mem_ctx);
+       return True;
 }
 
-static void ldap_reply_SearchRequest(struct ldapsrv_connection *conn,
-                                    struct ldap_message *request)
+static BOOL ldapsrv_write_buf(struct ldapsrv_connection *conn)
 {
-       struct ldap_SearchRequest *req = &request->r.SearchRequest;
-
-       struct ldap_message *msg;
-       struct ldap_Result *resp;
+       NTSTATUS status;
+       DATA_BLOB tmp_blob;
+       DATA_BLOB creds;
+       DATA_BLOB sasl;
+       size_t sendlen;
+       BOOL ret;
+       struct socket_context *sock = conn->connection->socket;
+       TALLOC_CTX *mem_ctx;
+
+       if (!conn->gensec || !conn->session_info ||
+          !(gensec_have_feature(conn->gensec, GENSEC_WANT_SIGN) &&
+            gensec_have_feature(conn->gensec, GENSEC_WANT_SEAL))) {
+               return write_from_buf(sock, &conn->out_buffer);
+       }
 
-       DEBUG(10, ("Search filter: %s\n", req->filter));
+       mem_ctx = talloc(conn, 0);
+       if (!mem_ctx) {
+               DEBUG(0,("no memory\n"));
+               return False;
+       }
 
-       msg = new_ldap_message();
+       tmp_blob.data = conn->out_buffer.data;
+       tmp_blob.length = conn->out_buffer.length;
 
-       if (msg == NULL) {
-               ldapsrv_terminate_connection(conn, "new_ldap_message() failed");
-               return;
+       if (tmp_blob.length == 0) {
+               goto nodata;
        }
 
-       msg->messageid = request->messageid;
-       resp = &msg->r.SearchResultDone;
-
-       /* Is this a rootdse request? */
-       if ((strlen(req->basedn) == 0) &&
-           (req->scope == LDAP_SEARCH_SCOPE_BASE) &&
-           strequal(req->filter, "(objectclass=*)")) {
-
-#define ATTR_BLOB_CONST(val) data_blob(val, sizeof(val)-1)
-#define ATTR_CONST_SINGLE(attr, blob, nam, val) do { \
-       attr.name = nam; \
-       attr.num_values = ARRAY_SIZE(blob); \
-       attr.values = blob; \
-       blob[0] = ATTR_BLOB_CONST(val); \
-} while(0)
-#define ATTR_CONST_SINGLE_NOVAL(attr, blob, nam) do { \
-       attr.name = nam;\
-       attr.num_values = ARRAY_SIZE(blob); \
-       attr.values = blob;\
-} while(0)
-               TALLOC_CTX *mem_ctx;
-               struct ldap_attribute attrs[3];
-               DATA_BLOB currentTime[1];
-               DATA_BLOB supportedLDAPVersion[2];
-               DATA_BLOB dnsHostName[1];
-
-               mem_ctx = talloc_init("rootDSE");
-               if (!mem_ctx) {
-                       ldapsrv_terminate_connection(conn, "no memory");
-                       return;
+       if (gensec_have_feature(conn->gensec, GENSEC_WANT_SEAL)) {
+               status = gensec_seal_packet(conn->gensec, mem_ctx,
+                                           tmp_blob.data, tmp_blob.length,
+                                           tmp_blob.data, tmp_blob.length,
+                                           &creds);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(0,("gensec_seal_packet: %s\n",nt_errstr(status)));
+                       talloc_free(mem_ctx);
+                       return False;
                }
+       } else {
+               status = gensec_sign_packet(conn->gensec, mem_ctx,
+                                           tmp_blob.data, tmp_blob.length,
+                                           tmp_blob.data, tmp_blob.length,
+                                           &creds);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(0,("gensec_sign_packet: %s\n",nt_errstr(status)));
+                       talloc_free(mem_ctx);
+                       return False;
+               }               
+       }
 
-               /* 
-                * currentTime
-                * 20040918090350.0Z
-                */
-               ATTR_CONST_SINGLE_NOVAL(attrs[0], currentTime, "currentTime");
-               {
-                       char *str = ldap_timestring(mem_ctx, time(NULL));
-                       if (!str) {
-                               ldapsrv_terminate_connection(conn, "no memory");
-                               return;
-                       }
-                       currentTime[0] = data_blob(str, strlen(str));
-                       talloc_free(str);
-               }
+       sasl = data_blob_talloc(mem_ctx, NULL, 4 + creds.length + tmp_blob.length);
+       if (!sasl.data) {
+               DEBUG(0,("no memory\n"));
+               talloc_free(mem_ctx);
+               return False;
+       }
 
-               /* 
-                * subschemaSubentry 
-                * CN=Aggregate,CN=Schema,CN=Configuration,DC=DOM,DC=TLD
-                */
-
-               /* 
-                * dsServiceName
-                * CN=NTDS Settings,CN=NETBIOSNAME,CN=Servers,CN=Default-First-Site,CN=Sites,CN=Configuration,DC=DOM,DC=TLD
-                */
-
-               /* 
-                * namingContexts
-                * DC=DOM,DC=TLD
-                * CN=Configuration,DC=DOM,DC=TLD
-                * CN=Schema,CN=Configuration,DC=DOM,DC=TLD
-                * DC=DomainDnsZones,DC=DOM,DC=TLD
-                * DC=ForestDnsZones,DC=DOM,DC=TLD
-                */
-
-               /* 
-                * defaultNamingContext
-                * DC=DOM,DC=TLD
-                */
-
-               /* 
-                * schemaNamingContext
-                * CN=Schema,CN=Configuration,DC=DOM,DC=TLD
-                */
-
-               /* 
-                * configurationNamingContext
-                * CN=Configuration,DC=DOM,DC=TLD
-                */
-
-               /* 
-                * rootDomainNamingContext
-                * DC=DOM,DC=TLD
-                */
-
-               /* 
-                * supportedControl
-                * 1.2.840.113556.1.4.319
-                * 1.2.840.113556.1.4.801
-                * 1.2.840.113556.1.4.473
-                * 1.2.840.113556.1.4.528
-                * 1.2.840.113556.1.4.417
-                * 1.2.840.113556.1.4.619
-                * 1.2.840.113556.1.4.841
-                * 1.2.840.113556.1.4.529
-                * 1.2.840.113556.1.4.805
-                * 1.2.840.113556.1.4.521
-                * 1.2.840.113556.1.4.970
-                * 1.2.840.113556.1.4.1338
-                * 1.2.840.113556.1.4.474
-                * 1.2.840.113556.1.4.1339
-                * 1.2.840.113556.1.4.1340
-                * 1.2.840.113556.1.4.1413
-                * 2.16.840.1.113730.3.4.9
-                * 2.16.840.1.113730.3.4.10
-                * 1.2.840.113556.1.4.1504
-                * 1.2.840.113556.1.4.1852
-                * 1.2.840.113556.1.4.802
-                */
-
-               /* 
-                * supportedLDAPVersion 
-                * 3
-                * 2
-                */
-               ATTR_CONST_SINGLE_NOVAL(attrs[1], supportedLDAPVersion, "supportedLDAPVersion");
-               supportedLDAPVersion[0] = ATTR_BLOB_CONST("3");
-               supportedLDAPVersion[1] = ATTR_BLOB_CONST("2");
-
-               /* 
-                * supportedLDAPPolicies
-                * MaxPoolThreads
-                * MaxDatagramRecv
-                * MaxReceiveBuffer
-                * InitRecvTimeout
-                * MaxConnections
-                * MaxConnIdleTime
-                * MaxPageSize
-                * MaxQueryDuration
-                * MaxTempTableSize
-                * MaxResultSetSize
-                * MaxNotificationPerConn
-                * MaxValRange
-                */
-
-               /* 
-                * highestCommittedUSN 
-                * 4555
-                */
-
-               /* 
-                * supportedSASLMechanisms
-                * GSSAPI
-                * GSS-SPNEGO
-                * EXTERNAL
-                * DIGEST-MD5
-                */
-
-               /* 
-                * dnsHostName
-                * netbiosname.dom.tld
-                */
-               ATTR_CONST_SINGLE_NOVAL(attrs[2], dnsHostName, "dnsHostName");
-               dnsHostName[0] = data_blob(lp_netbios_name(),strlen(lp_netbios_name()));
-
-               /* 
-                * ldapServiceName
-                * dom.tld:netbiosname$@DOM.TLD
-                */
-
-               /* 
-                * serverName:
-                * CN=NETBIOSNAME,CN=Servers,CN=Default-First-Site,CN=Sites,CN=Configuration,DC=DOM,DC=TLD
-                */
-
-               /* 
-                * supportedCapabilities
-                * 1.2.840.113556.1.4.800
-                * 1.2.840.113556.1.4.1670
-                * 1.2.840.113556.1.4.1791
-                */
-
-               /* 
-                * isSynchronized:
-                * TRUE/FALSE
-                */
-
-               /* 
-                * isGlobalCatalogReady
-                * TRUE/FALSE
-                */
-
-               /* 
-                * domainFunctionality
-                * 0
-                */
-
-               /* 
-                * forestFunctionality
-                * 0
-                */
-
-               /* 
-                * domainControllerFunctionality
-                * 2
-                */
-
-               msg->type = LDAP_TAG_SearchResultEntry;
-               msg->r.SearchResultEntry.dn = "";
-               msg->r.SearchResultEntry.num_attributes = ARRAY_SIZE(attrs);
-               msg->r.SearchResultEntry.attributes = attrs;
-
-               ldap_append_to_buf(msg, &conn->out_buffer);
+       RSIVAL(sasl.data, 0, creds.length + tmp_blob.length);
+       memcpy(sasl.data + 4, creds.data, creds.length);
+       memcpy(sasl.data + 4 + creds.length, tmp_blob.data, tmp_blob.length);
+
+       ret = ldapsrv_append_to_buf(&conn->sasl_out_buffer, sasl.data, sasl.length);
+       if (!ret) {
                talloc_free(mem_ctx);
+               return False;
        }
+       ldapsrv_consumed_from_buf(&conn->out_buffer, tmp_blob.length);
+nodata:
+       tmp_blob.data = conn->sasl_out_buffer.data;
+       tmp_blob.length = conn->sasl_out_buffer.length;
+
+       status = socket_send(sock, &tmp_blob, &sendlen, 0);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(10,("socket_send() %s\n",nt_errstr(status)));
+               talloc_free(mem_ctx);
+               return False;
+       }
+
+       ldapsrv_consumed_from_buf(&conn->sasl_out_buffer, sendlen);
 
-       msg->type = LDAP_TAG_SearchResultDone;
-       resp->resultcode = 0;
-       resp->dn = NULL;
-       resp->errormessage = NULL;
-       resp->referral = NULL;
+       talloc_free(mem_ctx);
 
-       ldap_append_to_buf(msg, &conn->out_buffer);
-       talloc_destroy(msg->mem_ctx);
+       return True;
 }
 
-static void switch_ldap_message(struct ldapsrv_connection *conn,
-                        struct ldap_message *msg)
+static BOOL ldap_encode_to_buf(struct ldap_message *msg, struct rw_buffer *buf)
 {
-       switch(msg->type) {
-       case LDAP_TAG_BindRequest:
-               ldap_reply_BindRequest(conn, msg);
-               break;
-       case LDAP_TAG_SearchRequest:
-               ldap_reply_SearchRequest(conn, msg);
-               break;
-       default:
-               reply_unwilling(conn, 2);
-               break;
-       }
+       DATA_BLOB blob;
+       BOOL res;
+
+       if (!ldap_encode(msg, &blob))
+               return False;
+
+       res = ldapsrv_append_to_buf(buf, blob.data, blob.length);
+
+       data_blob_free(&blob);
+       return res;
 }
 
-static void ldap_queue_run(struct server_connection *conn)
+NTSTATUS ldapsrv_do_responses(struct ldapsrv_connection *conn)
 {
-       struct ldapsrv_connection *ldap_conn = conn->private_data;
-       
-       while (ldap_conn->in_queue) {
-               struct ldap_message_queue *req = ldap_conn->in_queue;
-               DLIST_REMOVE(ldap_conn->in_queue, req);
+       struct ldapsrv_call *call, *next_call = NULL;
+       struct ldapsrv_reply *reply, *next_reply = NULL;
 
-               switch_ldap_message(ldap_conn, req->msg);
-               talloc_destroy(req->msg->mem_ctx);
+       for (call=conn->calls; call; call=next_call) {
+               for (reply=call->replies; reply; reply=next_reply) {
+                       if (!ldap_encode_to_buf(&reply->msg, &conn->out_buffer)) {
+                               return NT_STATUS_FOOBAR;
+                       }
+                       next_reply = reply->next;
+                       DLIST_REMOVE(call->replies, reply);
+                       reply->state = LDAPSRV_REPLY_STATE_SEND;
+                       talloc_free(reply);
+               }
+               next_call = call->next;
+               DLIST_REMOVE(conn->calls, call);
+               call->state = LDAPSRV_CALL_STATE_COMPLETE;
+               talloc_free(call);
        }
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS ldapsrv_flush_responses(struct ldapsrv_connection *conn)
+{
+       return NT_STATUS_OK;
 }
 
 /*
   called when a LDAP socket becomes readable
 */
-static void ldapsrv_recv(struct server_connection *conn, time_t t,
+static void ldapsrv_recv(struct server_connection *conn, struct timeval t,
                         uint16_t flags)
 {
        struct ldapsrv_connection *ldap_conn = conn->private_data;
        uint8_t *buf;
        int buf_length, msg_length;
        DATA_BLOB blob;
-       ASN1_DATA data;
-       struct ldap_message *msg;
-       struct ldap_message_queue *queue_entry;
+       struct asn1_data data;
+       struct ldapsrv_call *call;
+       NTSTATUS status;
 
        DEBUG(10,("ldapsrv_recv\n"));
 
-       if (!read_into_buf(conn->event.fde->fd, &ldap_conn->in_buffer)) {
-               ldapsrv_terminate_connection(ldap_conn, "read_into_buf() failed");
+       if (!ldapsrv_read_buf(ldap_conn)) {
+               ldapsrv_terminate_connection(ldap_conn, "ldapsrv_read_buf() failed");
                return;
        }
 
        peek_into_read_buf(&ldap_conn->in_buffer, &buf, &buf_length);
 
        while (buf_length > 0) {
-
                /* LDAP Messages are always SEQUENCES */
 
                if (!asn1_object_length(buf, buf_length, ASN1_SEQUENCE(0),
@@ -529,33 +486,49 @@ static void ldapsrv_recv(struct server_connection *conn, time_t t,
                        return;
                }
 
-               msg = new_ldap_message();
-
-               if ((msg == NULL) || !ldap_decode(&data, msg)) {
-                       ldapsrv_terminate_connection(ldap_conn, "ldap_decode() failed");
-                       return;
+               call = talloc_p(ldap_conn, struct ldapsrv_call);
+               if (!call) {
+                       ldapsrv_terminate_connection(ldap_conn, "no memory");
+                       return;         
                }
 
-               queue_entry = talloc_p(msg->mem_ctx, struct ldap_message_queue);
+               ZERO_STRUCTP(call);
+               call->state = LDAPSRV_CALL_STATE_NEW;
+               call->conn = ldap_conn;
+               call->request.mem_ctx = call;
 
-               if (queue_entry == NULL) {
-                       ldapsrv_terminate_connection(ldap_conn, "alloc_p(msg->mem_ctx, struct ldap_message_queue) failed");
+               if (!ldap_decode(&data, &call->request)) {
+                       dump_data(0,buf, msg_length);
+                       asn1_free(&data);
+                       ldapsrv_terminate_connection(ldap_conn, "ldap_decode() failed");
                        return;
                }
 
-               queue_entry->msg = msg;
+               asn1_free(&data);
+
+               DLIST_ADD_END(ldap_conn->calls, call,
+                             struct ldapsrv_call *);
 
-               DLIST_ADD_END(ldap_conn->in_queue, queue_entry,
-                             struct ldap_message_queue *);
+               ldapsrv_consumed_from_buf(&ldap_conn->in_buffer, msg_length);
 
-               consumed_from_read_buf(&ldap_conn->in_buffer, msg_length);
+               status = ldapsrv_do_call(call);
+               if (!NT_STATUS_IS_OK(status)) {
+                       ldapsrv_terminate_connection(ldap_conn, "ldapsrv_do_call() failed");
+                       return;
+               }
 
                peek_into_read_buf(&ldap_conn->in_buffer, &buf, &buf_length);
        }
 
-       ldap_queue_run(conn);
+       status = ldapsrv_do_responses(ldap_conn);
+       if (!NT_STATUS_IS_OK(status)) {
+               ldapsrv_terminate_connection(ldap_conn, "ldapsrv_do_responses() failed");
+               return;
+       }
 
-       conn->event.fde->flags |= EVENT_FD_WRITE;
+       if ((ldap_conn->out_buffer.length > 0)||(ldap_conn->sasl_out_buffer.length > 0)) {
+               conn->event.fde->flags |= EVENT_FD_WRITE;
+       }
 
        return;
 }
@@ -563,19 +536,21 @@ static void ldapsrv_recv(struct server_connection *conn, time_t t,
 /*
   called when a LDAP socket becomes writable
 */
-static void ldapsrv_send(struct server_connection *conn, time_t t,
+static void ldapsrv_send(struct server_connection *conn, struct timeval t,
                         uint16_t flags)
 {
        struct ldapsrv_connection *ldap_conn = conn->private_data;
 
        DEBUG(10,("ldapsrv_send\n"));
 
-       if (!write_from_buf(conn->event.fde->fd, &ldap_conn->out_buffer)) {
-               ldapsrv_terminate_connection(ldap_conn, "write_from_buf() failed");
+       if (!ldapsrv_write_buf(ldap_conn)) {
+               ldapsrv_terminate_connection(ldap_conn, "ldapsrv_write_buf() failed");
                return;
        }
 
-       conn->event.fde->flags &= ~EVENT_FD_WRITE;
+       if (ldap_conn->out_buffer.length == 0 && ldap_conn->sasl_out_buffer.length == 0) {
+               conn->event.fde->flags &= ~EVENT_FD_WRITE;
+       }
 
        return;
 }
@@ -583,7 +558,7 @@ static void ldapsrv_send(struct server_connection *conn, time_t t,
 /*
   called when connection is idle
 */
-static void ldapsrv_idle(struct server_connection *conn, time_t t)
+static void ldapsrv_idle(struct server_connection *conn, struct timeval t)
 {
        DEBUG(10,("ldapsrv_idle: not implemented!\n"));
        return;
@@ -591,10 +566,6 @@ static void ldapsrv_idle(struct server_connection *conn, time_t t)
 
 static void ldapsrv_close(struct server_connection *conn, const char *reason)
 {
-       struct ldapsrv_connection *ldap_conn = conn->private_data;
-
-       talloc_free(ldap_conn);
-
        return;
 }
 
@@ -606,18 +577,19 @@ static void ldapsrv_accept(struct server_connection *conn)
 {
        struct ldapsrv_connection *ldap_conn;
 
-       DEBUG(5, ("ldapsrv_accept\n"));
+       DEBUG(10, ("ldapsrv_accept\n"));
 
-       ldap_conn = talloc_p(NULL, struct ldapsrv_connection);
+       ldap_conn = talloc_p(conn, struct ldapsrv_connection);
 
        if (ldap_conn == NULL)
                return;
 
        ZERO_STRUCTP(ldap_conn);
        ldap_conn->connection = conn;
+       ldap_conn->service = talloc_reference(ldap_conn, conn->service->private_data);
 
        conn->private_data = ldap_conn;
-       
+
        return;
 }
 
@@ -626,7 +598,7 @@ static void ldapsrv_accept(struct server_connection *conn)
 */
 static void ldapsrv_exit(struct server_service *service, const char *reason)
 {
-       DEBUG(1,("ldapsrv_exit\n"));
+       DEBUG(10,("ldapsrv_exit\n"));
        return;
 }