dlist: remove unneeded type argument from DLIST_ADD_END()
[samba.git] / source4 / lib / stream / packet.c
index 72a7e6485c708ed1bafd559638762fa7e8d8bc79..b05036940342e2dd4df28f27664fd07e09ecab49 100644 (file)
@@ -35,18 +35,21 @@ struct packet_context {
        uint32_t num_read;
        uint32_t initial_read;
        struct socket_context *sock;
-       struct event_context *ev;
+       struct tevent_context *ev;
        size_t packet_size;
-       void *private;
-       struct fd_event *fde;
+       void *private_data;
+       struct tevent_fd *fde;
        bool serialise;
        int processing;
        bool recv_disable;
+       bool recv_need_enable;
        bool nofree;
 
        bool busy;
        bool destructor_called;
 
+       bool unreliable_select;
+
        struct send_element {
                struct send_element *next, *prev;
                DATA_BLOB blob;
@@ -105,9 +108,9 @@ _PUBLIC_ void packet_set_error_handler(struct packet_context *pc, packet_error_h
 /*
   set the private pointer passed to the callback functions
 */
-_PUBLIC_ void packet_set_private(struct packet_context *pc, void *private)
+_PUBLIC_ void packet_set_private(struct packet_context *pc, void *private_data)
 {
-       pc->private = private;
+       pc->private_data = private_data;
 }
 
 /*
@@ -136,7 +139,7 @@ _PUBLIC_ void packet_set_socket(struct packet_context *pc, struct socket_context
   time on a socket. This can matter for code that relies on not
   getting more than one packet per event
 */
-_PUBLIC_ void packet_set_event_context(struct packet_context *pc, struct event_context *ev)
+_PUBLIC_ void packet_set_event_context(struct packet_context *pc, struct tevent_context *ev)
 {
        pc->ev = ev;
 }
@@ -144,7 +147,7 @@ _PUBLIC_ void packet_set_event_context(struct packet_context *pc, struct event_c
 /*
   tell the packet layer the fde for the socket
 */
-_PUBLIC_ void packet_set_fde(struct packet_context *pc, struct fd_event *fde)
+_PUBLIC_ void packet_set_fde(struct packet_context *pc, struct tevent_fd *fde)
 {
        pc->fde = fde;
 }
@@ -176,6 +179,21 @@ _PUBLIC_ void packet_set_nofree(struct packet_context *pc)
        pc->nofree = true;
 }
 
+/*
+  tell the packet system that select/poll/epoll on the underlying
+  socket may not be a reliable way to determine if data is available
+  for receive. This happens with underlying socket systems such as the
+  one implemented on top of GNUTLS, where there may be data in
+  encryption/compression buffers that could be received by
+  socket_recv(), while there is no data waiting at the real socket
+  level as seen by select/poll/epoll. The GNUTLS library is supposed
+  to cope with this by always leaving some data sitting in the socket
+  buffer, but it does not seem to be reliable.
+ */
+_PUBLIC_ void packet_set_unreliable_select(struct packet_context *pc)
+{
+       pc->unreliable_select = true;
+}
 
 /*
   tell the caller we have an error
@@ -184,15 +202,15 @@ static void packet_error(struct packet_context *pc, NTSTATUS status)
 {
        pc->sock = NULL;
        if (pc->error_handler) {
-               pc->error_handler(pc->private, status);
+               pc->error_handler(pc->private_data, status);
                return;
        }
        /* default error handler is to free the callers private pointer */
        if (!NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) {
                DEBUG(0,("packet_error on %s - %s\n", 
-                        talloc_get_name(pc->private), nt_errstr(status)));
+                        talloc_get_name(pc->private_data), nt_errstr(status)));
        }
-       talloc_free(pc->private);
+       talloc_free(pc->private_data);
        return;
 }
 
@@ -209,10 +227,10 @@ static void packet_eof(struct packet_context *pc)
 /*
   used to put packets on event boundaries
 */
-static void packet_next_event(struct event_context *ev, struct timed_event *te, 
-                             struct timeval t, void *private)
+static void packet_next_event(struct tevent_context *ev, struct tevent_timer *te, 
+                             struct timeval t, void *private_data)
 {
-       struct packet_context *pc = talloc_get_type(private, struct packet_context);
+       struct packet_context *pc = talloc_get_type(private_data, struct packet_context);
        if (pc->num_read != 0 && pc->packet_size != 0 &&
            pc->packet_size <= pc->num_read) {
                packet_recv(pc);
@@ -230,15 +248,17 @@ _PUBLIC_ void packet_recv(struct packet_context *pc)
        NTSTATUS status;
        size_t nread = 0;
        DATA_BLOB blob;
+       bool recv_retry = false;
 
        if (pc->processing) {
-               EVENT_FD_NOT_READABLE(pc->fde);
+               TEVENT_FD_NOT_READABLE(pc->fde);
                pc->processing++;
                return;
        }
 
        if (pc->recv_disable) {
-               EVENT_FD_NOT_READABLE(pc->fde);
+               pc->recv_need_enable = true;
+               TEVENT_FD_NOT_READABLE(pc->fde);
                return;
        }
 
@@ -269,6 +289,8 @@ _PUBLIC_ void packet_recv(struct packet_context *pc)
                return;
        }
 
+again:
+
        if (npending + pc->num_read < npending) {
                packet_error(pc, NT_STATUS_INVALID_PARAMETER);
                return;
@@ -308,17 +330,33 @@ _PUBLIC_ void packet_recv(struct packet_context *pc)
                packet_error(pc, status);
                return;
        }
+       if (recv_retry && NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
+               nread = 0;
+               status = NT_STATUS_OK;
+       }
        if (!NT_STATUS_IS_OK(status)) {
                return;
        }
 
-       if (nread == 0) {
+       if (nread == 0 && !recv_retry) {
                packet_eof(pc);
                return;
        }
 
        pc->num_read += nread;
 
+       if (pc->unreliable_select && nread != 0) {
+               recv_retry = true;
+               status = socket_pending(pc->sock, &npending);
+               if (!NT_STATUS_IS_OK(status)) {
+                       packet_error(pc, status);
+                       return;
+               }
+               if (npending != 0) {
+                       goto again;
+               }
+       }
+
 next_partial:
        if (pc->partial.length != pc->num_read) {
                if (!data_blob_realloc(pc, &pc->partial, pc->num_read)) {
@@ -330,7 +368,7 @@ next_partial:
        /* see if its a full request */
        blob = pc->partial;
        blob.length = pc->num_read;
-       status = pc->full_request(pc->private, blob, &pc->packet_size);
+       status = pc->full_request(pc->private_data, blob, &pc->packet_size);
        if (NT_STATUS_IS_ERR(status)) {
                packet_error(pc, status);
                return;
@@ -375,7 +413,7 @@ next_partial:
 
        pc->busy = true;
 
-       status = pc->callback(pc->private, blob);
+       status = pc->callback(pc->private_data, blob);
 
        pc->busy = false;
 
@@ -386,7 +424,7 @@ next_partial:
 
        if (pc->processing) {
                if (pc->processing > 1) {
-                       EVENT_FD_READABLE(pc->fde);
+                       TEVENT_FD_READABLE(pc->fde);
                }
                pc->processing = 0;
        }
@@ -409,7 +447,7 @@ next_partial:
        blob = pc->partial;
        blob.length = pc->num_read;
 
-       status = pc->full_request(pc->private, blob, &pc->packet_size);
+       status = pc->full_request(pc->private_data, blob, &pc->packet_size);
        if (NT_STATUS_IS_ERR(status)) {
                packet_error(pc, status);
                return;
@@ -419,7 +457,7 @@ next_partial:
                return;
        }
 
-       event_add_timed(pc->ev, pc, timeval_zero(), packet_next_event, pc);
+       tevent_add_timer(pc->ev, pc, timeval_zero(), packet_next_event, pc);
 }
 
 
@@ -428,7 +466,6 @@ next_partial:
 */
 _PUBLIC_ void packet_recv_disable(struct packet_context *pc)
 {
-       EVENT_FD_NOT_READABLE(pc->fde);
        pc->recv_disable = true;
 }
 
@@ -437,10 +474,13 @@ _PUBLIC_ void packet_recv_disable(struct packet_context *pc)
 */
 _PUBLIC_ void packet_recv_enable(struct packet_context *pc)
 {
-       EVENT_FD_READABLE(pc->fde);
+       if (pc->recv_need_enable) {
+               pc->recv_need_enable = false;
+               TEVENT_FD_READABLE(pc->fde);
+       }
        pc->recv_disable = false;
        if (pc->num_read != 0 && pc->packet_size >= pc->num_read) {
-               event_add_timed(pc->ev, pc, timeval_zero(), packet_next_event, pc);
+               tevent_add_timer(pc->ev, pc, timeval_zero(), packet_next_event, pc);
        }
 }
 
@@ -483,29 +523,29 @@ _PUBLIC_ void packet_queue_run(struct packet_context *pc)
 
        /* we're out of requests to send, so don't wait for write
           events any more */
-       EVENT_FD_NOT_WRITEABLE(pc->fde);
+       TEVENT_FD_NOT_WRITEABLE(pc->fde);
 }
 
 /*
   put a packet in the send queue.  When the packet is actually sent,
   call send_callback.  
 
-  Useful for operations that must occour after sending a message, such
+  Useful for operations that must occur after sending a message, such
   as the switch to SASL encryption after as sucessful LDAP bind relpy.
 */
 _PUBLIC_ NTSTATUS packet_send_callback(struct packet_context *pc, DATA_BLOB blob,
                                       packet_send_callback_fn_t send_callback, 
-                                      void *private)
+                                      void *private_data)
 {
        struct send_element *el;
        el = talloc(pc, struct send_element);
        NT_STATUS_HAVE_NO_MEMORY(el);
 
-       DLIST_ADD_END(pc->send_queue, el, struct send_element *);
+       DLIST_ADD_END(pc->send_queue, el);
        el->blob = blob;
        el->nsent = 0;
        el->send_callback = send_callback;
-       el->send_callback_private = private;
+       el->send_callback_private = private_data;
 
        /* if we aren't going to free the packet then we must reference it
           to ensure it doesn't disappear before going out */
@@ -517,11 +557,11 @@ _PUBLIC_ NTSTATUS packet_send_callback(struct packet_context *pc, DATA_BLOB blob
                talloc_steal(el, blob.data);
        }
 
-       if (private && !talloc_reference(el, private)) {
+       if (private_data && !talloc_reference(el, private_data)) {
                return NT_STATUS_NO_MEMORY;
        }
 
-       EVENT_FD_WRITEABLE(pc->fde);
+       TEVENT_FD_WRITEABLE(pc->fde);
 
        return NT_STATUS_OK;
 }
@@ -538,12 +578,18 @@ _PUBLIC_ NTSTATUS packet_send(struct packet_context *pc, DATA_BLOB blob)
 /*
   a full request checker for NBT formatted packets (first 3 bytes are length)
 */
-_PUBLIC_ NTSTATUS packet_full_request_nbt(void *private, DATA_BLOB blob, size_t *size)
+_PUBLIC_ NTSTATUS packet_full_request_nbt(void *private_data, DATA_BLOB blob, size_t *size)
 {
        if (blob.length < 4) {
                return STATUS_MORE_ENTRIES;
        }
-       *size = 4 + smb_len(blob.data);
+       /*
+        * Note: that we use smb_len_tcp() instead
+        *       of smb_len_nbt() as this function is not
+        *       used for nbt and the source4 copy
+        *       of smb_len() was smb_len_tcp()
+        */
+       *size = 4 + smb_len_tcp(blob.data);
        if (*size > blob.length) {
                return STATUS_MORE_ENTRIES;
        }
@@ -555,7 +601,7 @@ _PUBLIC_ NTSTATUS packet_full_request_nbt(void *private, DATA_BLOB blob, size_t
   work out if a packet is complete for protocols that use a 32 bit network byte
   order length
 */
-_PUBLIC_ NTSTATUS packet_full_request_u32(void *private, DATA_BLOB blob, size_t *size)
+_PUBLIC_ NTSTATUS packet_full_request_u32(void *private_data, DATA_BLOB blob, size_t *size)
 {
        if (blob.length < 4) {
                return STATUS_MORE_ENTRIES;
@@ -566,3 +612,15 @@ _PUBLIC_ NTSTATUS packet_full_request_u32(void *private, DATA_BLOB blob, size_t
        }
        return NT_STATUS_OK;
 }
+
+_PUBLIC_ NTSTATUS packet_full_request_u16(void *private_data, DATA_BLOB blob, size_t *size)
+{
+       if (blob.length < 2) {
+               return STATUS_MORE_ENTRIES;
+       }
+       *size = 2 + RSVAL(blob.data, 0);
+       if (*size > blob.length) {
+               return STATUS_MORE_ENTRIES;
+       }
+       return NT_STATUS_OK;
+}