dlist: remove unneeded type argument from DLIST_ADD_END()
[samba.git] / source4 / lib / stream / packet.c
index 2759c752149c11a63d98488c37b8c4f47718a85f..b05036940342e2dd4df28f27664fd07e09ecab49 100644 (file)
@@ -7,7 +7,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 "smb.h"
-#include "dlinklist.h"
+#include "../lib/util/dlinklist.h"
 #include "lib/events/events.h"
 #include "lib/socket/socket.h"
 #include "lib/stream/packet.h"
+#include "libcli/raw/smb.h"
 
 struct packet_context {
        packet_callback_fn_t callback;
@@ -36,17 +35,20 @@ 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;
-       BOOL serialise;
+       void *private_data;
+       struct tevent_fd *fde;
+       bool serialise;
        int processing;
-       BOOL recv_disable;
-       BOOL nofree;
+       bool recv_disable;
+       bool recv_need_enable;
+       bool nofree;
 
-       BOOL busy;
-       BOOL destructor_called;
+       bool busy;
+       bool destructor_called;
+
+       bool unreliable_select;
 
        struct send_element {
                struct send_element *next, *prev;
@@ -64,7 +66,7 @@ struct packet_context {
 static int packet_destructor(struct packet_context *pc)
 {
        if (pc->busy) {
-               pc->destructor_called = True;
+               pc->destructor_called = true;
                /* now we refuse the talloc_free() request. The free will
                   happen again in the packet_recv() code */
                return -1;
@@ -106,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;
 }
 
 /*
@@ -137,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;
 }
@@ -145,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;
 }
@@ -157,7 +159,7 @@ _PUBLIC_ void packet_set_fde(struct packet_context *pc, struct fd_event *fde)
 */
 _PUBLIC_ void packet_set_serialise(struct packet_context *pc)
 {
-       pc->serialise = True;
+       pc->serialise = true;
 }
 
 /*
@@ -174,9 +176,24 @@ _PUBLIC_ void packet_set_initial_read(struct packet_context *pc, uint32_t initia
 */
 _PUBLIC_ void packet_set_nofree(struct packet_context *pc)
 {
-       pc->nofree = True;
+       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
@@ -185,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;
 }
 
@@ -210,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);
@@ -231,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;
        }
 
@@ -270,15 +289,40 @@ _PUBLIC_ void packet_recv(struct packet_context *pc)
                return;
        }
 
+again:
+
+       if (npending + pc->num_read < npending) {
+               packet_error(pc, NT_STATUS_INVALID_PARAMETER);
+               return;
+       }
+
+       if (npending + pc->num_read < pc->num_read) {
+               packet_error(pc, NT_STATUS_INVALID_PARAMETER);
+               return;
+       }
+
        /* possibly expand the partial packet buffer */
        if (npending + pc->num_read > pc->partial.length) {
-               status = data_blob_realloc(pc, &pc->partial, npending+pc->num_read);
-               if (!NT_STATUS_IS_OK(status)) {
-                       packet_error(pc, status);
+               if (!data_blob_realloc(pc, &pc->partial, npending+pc->num_read)) {
+                       packet_error(pc, NT_STATUS_NO_MEMORY);
                        return;
                }
        }
 
+       if (pc->partial.length < pc->num_read + npending) {
+               packet_error(pc, NT_STATUS_INVALID_PARAMETER);
+               return;
+       }
+
+       if ((uint8_t *)pc->partial.data + pc->num_read < (uint8_t *)pc->partial.data) {
+               packet_error(pc, NT_STATUS_INVALID_PARAMETER);
+               return;
+       }
+       if ((uint8_t *)pc->partial.data + pc->num_read + npending < (uint8_t *)pc->partial.data) {
+               packet_error(pc, NT_STATUS_INVALID_PARAMETER);
+               return;
+       }
+
        status = socket_recv(pc->sock, pc->partial.data + pc->num_read, 
                             npending, &nread);
 
@@ -286,30 +330,45 @@ _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;
 
-next_partial:
-       if (pc->partial.length != pc->num_read) {
-               status = data_blob_realloc(pc, &pc->partial, pc->num_read);
+       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)) {
+                       packet_error(pc, NT_STATUS_NO_MEMORY);
+                       return;
+               }
        }
 
        /* 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;
@@ -337,9 +396,9 @@ next_partial:
                        packet_error(pc, NT_STATUS_NO_MEMORY);
                        return;
                }
-               status = data_blob_realloc(pc, &blob, pc->packet_size);
-               if (!NT_STATUS_IS_OK(status)) {
-                       packet_error(pc, status);
+               /* Trunate the blob sent to the caller to only the packet length */
+               if (!data_blob_realloc(pc, &blob, pc->packet_size)) {
+                       packet_error(pc, NT_STATUS_NO_MEMORY);
                        return;
                }
        } else {
@@ -352,11 +411,11 @@ next_partial:
                pc->processing = 1;
        }
 
-       pc->busy = True;
+       pc->busy = true;
 
-       status = pc->callback(pc->private, blob);
+       status = pc->callback(pc->private_data, blob);
 
-       pc->busy = False;
+       pc->busy = false;
 
        if (pc->destructor_called) {
                talloc_free(pc);
@@ -365,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;
        }
@@ -388,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;
@@ -398,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);
 }
 
 
@@ -407,8 +466,7 @@ next_partial:
 */
 _PUBLIC_ void packet_recv_disable(struct packet_context *pc)
 {
-       EVENT_FD_NOT_READABLE(pc->fde);
-       pc->recv_disable = True;
+       pc->recv_disable = true;
 }
 
 /*
@@ -416,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);
-       pc->recv_disable = False;
+       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);
        }
 }
 
@@ -448,7 +509,13 @@ _PUBLIC_ void packet_queue_run(struct packet_context *pc)
                if (el->nsent == el->blob.length) {
                        DLIST_REMOVE(pc->send_queue, el);
                        if (el->send_callback) {
+                               pc->busy = true;
                                el->send_callback(el->send_callback_private);
+                               pc->busy = false;
+                               if (pc->destructor_called) {
+                                       talloc_free(pc);
+                                       return;
+                               }
                        }
                        talloc_free(el);
                }
@@ -456,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 */
@@ -490,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;
 }
@@ -511,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;
        }
@@ -528,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;
@@ -539,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;
+}