r11620: switch the ldap client code over to using the generic packet code
authorAndrew Tridgell <tridge@samba.org>
Thu, 10 Nov 2005 00:28:02 +0000 (00:28 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 18:45:59 +0000 (13:45 -0500)
source/libcli/ldap/ldap.c
source/libcli/ldap/ldap_client.c
source/libcli/ldap/ldap_client.h
source/libcli/util/asn1.c

index 7d1758a8fab09c243964f9d9f634f863eedef500..3cfbe3a1e1329514171c196b2dbcdef818cda090 100644 (file)
@@ -1289,3 +1289,11 @@ BOOL ldap_decode(struct asn1_data *data, struct ldap_message *msg)
 }
 
 
+/*
+  return NT_STATUS_OK if a blob has enough bytes in it to be a full
+  ldap packet. Set packet_size if true.
+*/
+NTSTATUS ldap_full_packet(void *private, DATA_BLOB blob, size_t *packet_size)
+{
+       return asn1_full_tag(blob, ASN1_SEQUENCE(0), packet_size);
+}
index 3b801ca225ff0fb5e6cf3add093462afa0f22d87..503016e896dd4ad481df7e2877b8074ab95d8e01 100644 (file)
@@ -32,6 +32,7 @@
 #include "libcli/ldap/ldap.h"
 #include "libcli/ldap/ldap_client.h"
 #include "libcli/composite/composite.h"
+#include "lib/stream/packet.h"
 
 
 /*
@@ -82,20 +83,20 @@ static void ldap_connection_dead(struct ldap_connection *conn)
                }
        }       
 
-       while (conn->send_queue) {
-               req = conn->send_queue;
-               DLIST_REMOVE(req->conn->send_queue, req);
-               req->state = LDAP_REQUEST_DONE;
-               req->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
-               if (req->async.fn) {
-                       req->async.fn(req);
-               }
-       }
-
        talloc_free(conn->tls);
        conn->tls = NULL;
 }
 
+/*
+  handle packet errors
+*/
+static void ldap_error_handler(void *private, NTSTATUS status)
+{
+       struct ldap_connection *conn = talloc_get_type(private, 
+                                                      struct ldap_connection);
+       ldap_connection_dead(conn);
+}
+
 
 /*
   match up with a pending message, adding to the replies list
@@ -149,184 +150,99 @@ static void ldap_match_message(struct ldap_connection *conn, struct ldap_message
        }
 }
 
+
 /*
-  try and decode/process plain data
+  check if a blob is a complete ldap packet
+  handle wrapper or unwrapped connections
 */
-static void ldap_try_decode_plain(struct ldap_connection *conn)
+NTSTATUS ldap_complete_packet(void *private, DATA_BLOB blob, size_t *size)
 {
-       struct asn1_data asn1;
-
-       if (!asn1_load(&asn1, conn->partial)) {
-               ldap_connection_dead(conn);
-               return;
-       }
-
-       /* try and decode - this will fail if we don't have a full packet yet */
-       while (asn1.ofs < asn1.length) {
-               struct ldap_message *msg = talloc(conn, struct ldap_message);
-               off_t saved_ofs = asn1.ofs;
-                       
-               if (msg == NULL) {
-                       ldap_connection_dead(conn);
-                       return;
-               }
-
-               if (ldap_decode(&asn1, msg)) {
-                       ldap_match_message(conn, msg);
-               } else {
-                       asn1.ofs = saved_ofs;
-                       talloc_free(msg);
-                       break;
-               }
-       }
-
-       /* keep any remaining data in conn->partial */
-       data_blob_free(&conn->partial);
-       if (asn1.ofs != asn1.length) {
-               conn->partial = data_blob_talloc(conn, 
-                                                asn1.data + asn1.ofs, 
-                                                asn1.length - asn1.ofs);
+       struct ldap_connection *conn = talloc_get_type(private, 
+                                                      struct ldap_connection);
+       if (conn->enable_wrap) {
+               return packet_full_request_u32(private, blob, size);
        }
-       asn1_free(&asn1);
+       return ldap_full_packet(private, blob, size);
 }
 
 /*
-  try and decode/process wrapped data
+  decode/process plain data
 */
-static void ldap_try_decode_wrapped(struct ldap_connection *conn)
+static NTSTATUS ldap_decode_plain(struct ldap_connection *conn, DATA_BLOB blob)
 {
-       uint32_t len;
-
-       /* keep decoding while we have a full wrapped packet */
-       while (conn->partial.length >= 4 &&
-              (len=RIVAL(conn->partial.data, 0)) <= conn->partial.length-4) {
-               DATA_BLOB wrapped, unwrapped;
-               struct asn1_data asn1;
-               struct ldap_message *msg = talloc(conn, struct ldap_message);
-               NTSTATUS status;
-
-               if (msg == NULL) {
-                       ldap_connection_dead(conn);
-                       return;
-               }
+       struct asn1_data asn1;
+       struct ldap_message *msg = talloc(conn, struct ldap_message);
 
-               wrapped.data   = conn->partial.data+4;
-               wrapped.length = len;
+       if (msg == NULL) {
+               return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+       }
 
-               status = gensec_unwrap(conn->gensec, msg, &wrapped, &unwrapped);
-               if (!NT_STATUS_IS_OK(status)) {
-                       ldap_connection_dead(conn);
-                       return;
-               }
+       if (!asn1_load(&asn1, blob)) {
+               return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+       }
+       
+       if (!ldap_decode(&asn1, msg)) {
+               return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+       }
 
-               if (!asn1_load(&asn1, unwrapped)) {
-                       ldap_connection_dead(conn);
-                       return;
-               }
+       ldap_match_message(conn, msg);
 
-               while (ldap_decode(&asn1, msg)) {
-                       ldap_match_message(conn, msg);
-                       msg = talloc(conn, struct ldap_message);
-               }
-               
-               talloc_free(msg);
-               asn1_free(&asn1);
-
-               if (conn->partial.length == len + 4) {
-                       data_blob_free(&conn->partial);
-               } else {
-                       memmove(conn->partial.data, conn->partial.data+len+4,
-                               conn->partial.length - (len+4));
-                       conn->partial.length -= len + 4;
-               }
-       }
+       data_blob_free(&blob);
+       asn1_free(&asn1);
+       return NT_STATUS_OK;
 }
 
-
 /*
-  handle ldap recv events
+  decode/process wrapped data
 */
-static void ldap_recv_handler(struct ldap_connection *conn)
+static NTSTATUS ldap_decode_wrapped(struct ldap_connection *conn, DATA_BLOB blob)
 {
+       DATA_BLOB wrapped, unwrapped;
+       struct asn1_data asn1;
+       struct ldap_message *msg = talloc(conn, struct ldap_message);
        NTSTATUS status;
-       size_t npending=0, nread;
 
-       /* work out how much data is pending */
-       status = tls_socket_pending(conn->tls, &npending);
-       if (!NT_STATUS_IS_OK(status) || npending == 0) {
-               ldap_connection_dead(conn);
-               return;
+       if (msg == NULL) {
+               return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
        }
 
-       conn->partial.data = talloc_realloc_size(conn, conn->partial.data, 
-                                                conn->partial.length + npending);
-       if (conn->partial.data == NULL) {
-               ldap_connection_dead(conn);
-               return;
-       }
+       wrapped = data_blob_const(blob.data+4, blob.length-4);
 
-       /* receive the pending data */
-       status = tls_socket_recv(conn->tls, conn->partial.data + conn->partial.length,
-                                npending, &nread);
-       if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
-               return;
-       }
+       status = gensec_unwrap(conn->gensec, msg, &wrapped, &unwrapped);
        if (!NT_STATUS_IS_OK(status)) {
-               ldap_connection_dead(conn);
-               return;
+               return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
        }
-       conn->partial.length += nread;
 
-       /* see if we can decode what we have */
-       if (conn->enable_wrap) {
-               ldap_try_decode_wrapped(conn);
-       } else {
-               ldap_try_decode_plain(conn);
+       data_blob_free(&blob);
+
+       if (!asn1_load(&asn1, unwrapped)) {
+               return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
        }
+
+       while (ldap_decode(&asn1, msg)) {
+               ldap_match_message(conn, msg);
+               msg = talloc(conn, struct ldap_message);
+       }
+               
+       talloc_free(msg);
+       asn1_free(&asn1);
+
+       return NT_STATUS_OK;
 }
 
 
 /*
-  handle ldap send events
+  handle ldap recv events
 */
-static void ldap_send_handler(struct ldap_connection *conn)
+static NTSTATUS ldap_recv_handler(void *private, DATA_BLOB blob)
 {
-       while (conn->send_queue) {
-               struct ldap_request *req = conn->send_queue;
-               size_t nsent;
-               NTSTATUS status;
-
-               status = tls_socket_send(conn->tls, &req->data, &nsent);
-               if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
-                       break;
-               }
-               if (!NT_STATUS_IS_OK(status)) {
-                       ldap_connection_dead(conn);
-                       return;
-               }
-
-               req->data.data += nsent;
-               req->data.length -= nsent;
-               if (req->data.length == 0) {
-                       req->state = LDAP_REQUEST_PENDING;
-                       DLIST_REMOVE(conn->send_queue, req);
-
-                       /* some types of requests don't expect a reply */
-                       if (req->type == LDAP_TAG_AbandonRequest ||
-                           req->type == LDAP_TAG_UnbindRequest) {
-                               req->status = NT_STATUS_OK;
-                               req->state = LDAP_REQUEST_DONE;
-                               if (req->async.fn) {
-                                       req->async.fn(req);
-                               }
-                       } else {
-                               DLIST_ADD(conn->pending, req);
-                       }
-               }
-       }
-       if (conn->send_queue == NULL) {
-               EVENT_FD_NOT_WRITEABLE(conn->event.fde);
+       struct ldap_connection *conn = talloc_get_type(private, 
+                                                      struct ldap_connection);
+       if (conn->enable_wrap) {
+               return ldap_decode_wrapped(conn, blob);
        }
+
+       return ldap_decode_plain(conn, blob);
 }
 
 
@@ -336,13 +252,14 @@ static void ldap_send_handler(struct ldap_connection *conn)
 static void ldap_io_handler(struct event_context *ev, struct fd_event *fde, 
                            uint16_t flags, void *private)
 {
-       struct ldap_connection *conn = talloc_get_type(private, struct ldap_connection);
+       struct ldap_connection *conn = talloc_get_type(private, 
+                                                      struct ldap_connection);
        if (flags & EVENT_FD_WRITE) {
-               ldap_send_handler(conn);
+               packet_queue_run(conn->packet);
                if (conn->tls == NULL) return;
        }
        if (flags & EVENT_FD_READ) {
-               ldap_recv_handler(conn);
+               packet_recv(conn->packet);
        }
 }
 
@@ -470,6 +387,19 @@ static void ldap_connect_recv_conn(struct composite_context *ctx)
        talloc_steal(conn, conn->tls);
        talloc_steal(conn->tls, conn->sock);
 
+       conn->packet = packet_init(conn);
+       if (conn->packet == NULL) {
+               talloc_free(conn->sock);
+               return;
+       }
+       packet_set_private(conn->packet, conn);
+       packet_set_tls(conn->packet, conn->tls);
+       packet_set_callback(conn->packet, ldap_recv_handler);
+       packet_set_full_request(conn->packet, ldap_complete_packet);
+       packet_set_error_handler(conn->packet, ldap_error_handler);
+       packet_set_event_context(conn->packet, conn->event.event_ctx);
+       packet_set_serialise(conn->packet, conn->event.fde);
+
        composite_done(state->ctx);
 
        return;
@@ -492,9 +422,6 @@ NTSTATUS ldap_connect(struct ldap_connection *conn, const char *url)
 static int ldap_request_destructor(void *ptr)
 {
        struct ldap_request *req = talloc_get_type(ptr, struct ldap_request);
-       if (req->state == LDAP_REQUEST_SEND) {
-               DLIST_REMOVE(req->conn->send_queue, req);
-       }
        if (req->state == LDAP_REQUEST_PENDING) {
                DLIST_REMOVE(req->conn->pending, req);
        }
@@ -509,9 +436,6 @@ static void ldap_request_timeout(struct event_context *ev, struct timed_event *t
 {
        struct ldap_request *req = talloc_get_type(private, struct ldap_request);
        req->status = NT_STATUS_IO_TIMEOUT;
-       if (req->state == LDAP_REQUEST_SEND) {
-               DLIST_REMOVE(req->conn->send_queue, req);
-       }
        if (req->state == LDAP_REQUEST_PENDING) {
                DLIST_REMOVE(req->conn->pending, req);
        }
@@ -521,6 +445,19 @@ static void ldap_request_timeout(struct event_context *ev, struct timed_event *t
        }
 }
 
+
+/*
+  called on completion of a one-way ldap request
+*/
+static void ldap_request_complete(struct event_context *ev, struct timed_event *te, 
+                                 struct timeval t, void *private)
+{
+       struct ldap_request *req = talloc_get_type(private, struct ldap_request);
+       if (req->async.fn) {
+               req->async.fn(req);
+       }
+}
+
 /*
   send a ldap message - async interface
 */
@@ -528,6 +465,7 @@ struct ldap_request *ldap_request_send(struct ldap_connection *conn,
                                       struct ldap_message *msg)
 {
        struct ldap_request *req;
+       NTSTATUS status;
 
        if (conn->tls == NULL) {
                return NULL;
@@ -558,7 +496,6 @@ struct ldap_request *ldap_request_send(struct ldap_connection *conn,
        /* possibly encrypt/sign the request */
        if (conn->enable_wrap) {
                DATA_BLOB wrapped;
-               NTSTATUS status;
 
                status = gensec_wrap(conn->gensec, req, &req->data, &wrapped);
                if (!NT_STATUS_IS_OK(status)) {
@@ -574,11 +511,26 @@ struct ldap_request *ldap_request_send(struct ldap_connection *conn,
                data_blob_free(&wrapped);
        }
 
+       status = packet_send(conn->packet, req->data);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto failed;
+       }
 
-       if (conn->send_queue == NULL) {
-               EVENT_FD_WRITEABLE(conn->event.fde);
+       /* 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 */
+               event_add_timed(conn->event.event_ctx, req, timeval_zero(),
+                               ldap_request_complete, req);
+               return req;
        }
-       DLIST_ADD_END(conn->send_queue, req, struct ldap_request *);
+
+       req->state = LDAP_REQUEST_PENDING;
+       DLIST_ADD(conn->pending, req);
 
        /* put a timeout on the request */
        event_add_timed(conn->event.event_ctx, req, 
index 38e043da1f6e85ca3594b0204d508dc1a7f28a4c..ee458dc5b076816230bda2e0258aded3c7567e65 100644 (file)
@@ -61,9 +61,6 @@ struct ldap_connection {
        /* next message id to assign */
        unsigned next_messageid;
 
-       /* outgoing send queue */
-       struct ldap_request *send_queue;
-
        /* Outstanding LDAP requests that have not yet been replied to */
        struct ldap_request *pending;
 
@@ -73,9 +70,6 @@ struct ldap_connection {
        /* set if we are wrapping requests */
        BOOL enable_wrap;
 
-       /* partially received packet */
-       DATA_BLOB partial;
-
        /* the default timeout for messages */
        int timeout;
 
@@ -86,4 +80,6 @@ struct ldap_connection {
                struct event_context *event_ctx;
                struct fd_event *fde;
        } event;
+
+       struct packet_context *packet;
 };
index 6ca2221b1a9417b714ad9da5daf627b04df8adb0..0dceb1bba60ab1491f08919f82eb70e3f1c2ce59 100644 (file)
@@ -607,3 +607,30 @@ BOOL asn1_write_enumerated(struct asn1_data *data, uint8_t v)
        asn1_pop_tag(data);
        return !data->has_error;
 }
+
+/*
+  check if a ASN.1 blob is a full tag
+*/
+NTSTATUS asn1_full_tag(DATA_BLOB blob, uint8_t tag, size_t *packet_size)
+{
+       struct asn1_data asn1;
+       int size;
+
+       ZERO_STRUCT(asn1);
+       asn1.data = blob.data;
+       asn1.length = blob.length;
+       asn1_start_tag(&asn1, tag);
+       if (asn1.has_error) {
+               talloc_free(asn1.nesting);
+               return STATUS_MORE_ENTRIES;
+       }
+       size = asn1_tag_remaining(&asn1) + asn1.ofs;
+       talloc_free(asn1.nesting);
+
+       if (size > blob.length) {
+               return STATUS_MORE_ENTRIES;
+       }               
+
+       *packet_size = size;
+       return NT_STATUS_OK;
+}