Fix for CVE-2009-2906.
[samba.git] / source / smbd / process.c
index 9624ca0e49573a1c884b445107b7af1a3565c377..c53bfda2219a6be756479b7afb09e1adff590c02 100644 (file)
 
 #include "includes.h"
 
-extern struct auth_context *negprot_global_auth_context;
 extern int smb_echo_count;
 
-const int total_buffer_size = (BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN);
-
-static char *InBuffer = NULL;
-static char *OutBuffer = NULL;
-static const char *current_inbuf = NULL;
-
-static char *NewInBuffer(char **old_inbuf);
-static char *NewOutBuffer(char **old_outbuf);
-
-/* 
+/*
  * Size of data we can send to client. Set
  *  by the client for all protocols above CORE.
  *  Set by us for CORE protocol.
@@ -44,83 +34,367 @@ int max_send = BUFFER_SIZE;
  */
 int max_recv = BUFFER_SIZE;
 
-extern int last_message;
-extern int smb_read_error;
 SIG_ATOMIC_T reload_after_sighup = 0;
 SIG_ATOMIC_T got_sig_term = 0;
-extern BOOL global_machine_password_needs_changing;
+extern bool global_machine_password_needs_changing;
 extern int max_send;
 
-/*
- * Initialize a struct smb_request from an inbuf
- */
+/* Accessor function for smb_read_error for smbd functions. */
+
+/****************************************************************************
+ Send an smb to a fd.
+****************************************************************************/
 
-void init_smb_request(struct smb_request *req, const uint8 *inbuf)
+bool srv_send_smb(int fd, char *buffer, bool do_encrypt)
 {
-       req->flags2 = SVAL(inbuf, smb_flg2);
-       req->smbpid = SVAL(inbuf, smb_pid);
-       req->mid    = SVAL(inbuf, smb_mid);
-       req->vuid   = SVAL(inbuf, smb_uid);
-       req->tid    = SVAL(inbuf, smb_tid);
-       req->wct    = CVAL(inbuf, smb_wct);
-       req->inbuf  = inbuf;
-       req->outbuf = NULL;
+       size_t len;
+       size_t nwritten=0;
+       ssize_t ret;
+       char *buf_out = buffer;
+
+       /* Sign the outgoing packet if required. */
+       srv_calculate_sign_mac(buf_out);
+
+       if (do_encrypt) {
+               NTSTATUS status = srv_encrypt_buffer(buffer, &buf_out);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(0, ("send_smb: SMB encryption failed "
+                               "on outgoing packet! Error %s\n",
+                               nt_errstr(status) ));
+                       return false;
+               }
+       }
+
+       len = smb_len(buf_out) + 4;
+
+       while (nwritten < len) {
+               ret = write_data(fd,buf_out+nwritten,len - nwritten);
+               if (ret <= 0) {
+                       DEBUG(0,("Error writing %d bytes to client. %d. (%s)\n",
+                               (int)len,(int)ret, strerror(errno) ));
+                       srv_free_enc_buffer(buf_out);
+                       return false;
+               }
+               nwritten += ret;
+       }
+
+       srv_free_enc_buffer(buf_out);
+       return true;
+}
+
+/*******************************************************************
+ Setup the word count and byte count for a smb message.
+********************************************************************/
+
+int srv_set_message(char *buf,
+                        int num_words,
+                        int num_bytes,
+                        bool zero)
+{
+       if (zero && (num_words || num_bytes)) {
+               memset(buf + smb_size,'\0',num_words*2 + num_bytes);
+       }
+       SCVAL(buf,smb_wct,num_words);
+       SSVAL(buf,smb_vwv + num_words*SIZEOFWORD,num_bytes);
+       smb_setlen(buf,(smb_size + num_words*2 + num_bytes - 4));
+       return (smb_size + num_words*2 + num_bytes);
+}
+
+static bool valid_smb_header(const uint8_t *inbuf)
+{
+       if (is_encrypted_packet(inbuf)) {
+               return true;
+       }
+       return (strncmp(smb_base(inbuf),"\377SMB",4) == 0);
+}
+
+/* Socket functions for smbd packet processing. */
+
+static bool valid_packet_size(size_t len)
+{
+       /*
+        * A WRITEX with CAP_LARGE_WRITEX can be 64k worth of data plus 65 bytes
+        * of header. Don't print the error if this fits.... JRA.
+        */
+
+       if (len > (BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE)) {
+               DEBUG(0,("Invalid packet length! (%lu bytes).\n",
+                                       (unsigned long)len));
+               return false;
+       }
+       return true;
 }
 
+static NTSTATUS read_packet_remainder(int fd, char *buffer,
+                                     unsigned int timeout, ssize_t len)
+{
+       if (len <= 0) {
+               return NT_STATUS_OK;
+       }
+
+       return read_socket_with_timeout(fd, buffer, len, len, timeout, NULL);
+}
+
+/****************************************************************************
+ Attempt a zerocopy writeX read. We know here that len > smb_size-4
+****************************************************************************/
+
 /*
- * From within a converted call you might have to call non-converted
- * subroutines that still take the old inbuf/outbuf/lenght/bufsize
- * parameters. This takes a struct smb_request and prepares the legacy
- * parameters.
+ * Unfortunately, earlier versions of smbclient/libsmbclient
+ * don't send this "standard" writeX header. I've fixed this
+ * for 3.2 but we'll use the old method with earlier versions.
+ * Windows and CIFSFS at least use this standard size. Not
+ * sure about MacOSX.
  */
 
-BOOL reply_prep_legacy(struct smb_request *req,
-                      char **pinbuf, char **poutbuf,
-                      int *psize, int *pbufsize)
+#define STANDARD_WRITE_AND_X_HEADER_SIZE (smb_size - 4 + /* basic header */ \
+                               (2*14) + /* word count (including bcc) */ \
+                               1 /* pad byte */)
+
+static NTSTATUS receive_smb_raw_talloc_partial_read(TALLOC_CTX *mem_ctx,
+                                                   const char lenbuf[4],
+                                                   int fd, char **buffer,
+                                                   unsigned int timeout,
+                                                   size_t *p_unread,
+                                                   size_t *len_ret)
 {
-       const int bufsize = (BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE
-                            + SAFETY_MARGIN);
-       char *inbuf, *outbuf;
+       /* Size of a WRITEX call (+4 byte len). */
+       char writeX_header[4 + STANDARD_WRITE_AND_X_HEADER_SIZE];
+       ssize_t len = smb_len_large(lenbuf); /* Could be a UNIX large writeX. */
+       ssize_t toread;
+       NTSTATUS status;
 
-       DEBUG(1, ("reply_prep_legacy called\n"));
+       memcpy(writeX_header, lenbuf, 4);
 
-       if (!(inbuf = TALLOC_ARRAY(req, char, bufsize))) {
-               DEBUG(0, ("Could not allocate legacy inbuf\n"));
-               return False;
+       status = read_socket_with_timeout(
+               fd, writeX_header + 4,
+               STANDARD_WRITE_AND_X_HEADER_SIZE,
+               STANDARD_WRITE_AND_X_HEADER_SIZE,
+               timeout, NULL);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
        }
-       memcpy(inbuf, req->inbuf, MIN(smb_len(req->inbuf)+4, bufsize));
-       req->inbuf = (uint8 *)inbuf;
 
-       if (!(outbuf = TALLOC_ARRAY(req, char, bufsize))) {
-               DEBUG(0, ("Could not allocate legacy outbuf\n"));
-               return False;
+       /*
+        * Ok - now try and see if this is a possible
+        * valid writeX call.
+        */
+
+       if (is_valid_writeX_buffer((uint8_t *)writeX_header)) {
+               /*
+                * If the data offset is beyond what
+                * we've read, drain the extra bytes.
+                */
+               uint16_t doff = SVAL(writeX_header,smb_vwv11);
+               ssize_t newlen;
+
+               if (doff > STANDARD_WRITE_AND_X_HEADER_SIZE) {
+                       size_t drain = doff - STANDARD_WRITE_AND_X_HEADER_SIZE;
+                       if (drain_socket(smbd_server_fd(), drain) != drain) {
+                               smb_panic("receive_smb_raw_talloc_partial_read:"
+                                       " failed to drain pending bytes");
+                       }
+               } else {
+                       doff = STANDARD_WRITE_AND_X_HEADER_SIZE;
+               }
+
+               /* Spoof down the length and null out the bcc. */
+               set_message_bcc(writeX_header, 0);
+               newlen = smb_len(writeX_header);
+
+               /* Copy the header we've written. */
+
+               *buffer = (char *)TALLOC_MEMDUP(mem_ctx,
+                               writeX_header,
+                               sizeof(writeX_header));
+
+               if (*buffer == NULL) {
+                       DEBUG(0, ("Could not allocate inbuf of length %d\n",
+                                 (int)sizeof(writeX_header)));
+                       return NT_STATUS_NO_MEMORY;
+               }
+
+               /* Work out the remaining bytes. */
+               *p_unread = len - STANDARD_WRITE_AND_X_HEADER_SIZE;
+               *len_ret = newlen + 4;
+               return NT_STATUS_OK;
        }
-       req->outbuf = (uint8 *)outbuf;
 
-       construct_reply_common(inbuf, outbuf);
+       if (!valid_packet_size(len)) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
 
-       *pinbuf   = inbuf;
-       *poutbuf  = outbuf;
-       *psize    = smb_len(inbuf)+4;
-       *pbufsize = bufsize;
+       /*
+        * Not a valid writeX call. Just do the standard
+        * talloc and return.
+        */
 
-       return True;
+       *buffer = TALLOC_ARRAY(mem_ctx, char, len+4);
+
+       if (*buffer == NULL) {
+               DEBUG(0, ("Could not allocate inbuf of length %d\n",
+                         (int)len+4));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       /* Copy in what we already read. */
+       memcpy(*buffer,
+               writeX_header,
+               4 + STANDARD_WRITE_AND_X_HEADER_SIZE);
+       toread = len - STANDARD_WRITE_AND_X_HEADER_SIZE;
+
+       if(toread > 0) {
+               status = read_packet_remainder(
+                       fd, (*buffer) + 4 + STANDARD_WRITE_AND_X_HEADER_SIZE,
+                       timeout, toread);
+
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(10, ("receive_smb_raw_talloc_partial_read: %s\n",
+                                  nt_errstr(status)));
+                       return status;
+               }
+       }
+
+       *len_ret = len + 4;
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS receive_smb_raw_talloc(TALLOC_CTX *mem_ctx, int fd,
+                                      char **buffer, unsigned int timeout,
+                                      size_t *p_unread, size_t *plen)
+{
+       char lenbuf[4];
+       size_t len;
+       int min_recv_size = lp_min_receive_file_size();
+       NTSTATUS status;
+
+       *p_unread = 0;
+
+       status = read_smb_length_return_keepalive(fd, lenbuf, timeout, &len);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(10, ("receive_smb_raw: %s\n", nt_errstr(status)));
+               return status;
+       }
+
+       if (CVAL(lenbuf,0) == 0 &&
+                       min_recv_size &&
+                       smb_len_large(lenbuf) > (min_recv_size + STANDARD_WRITE_AND_X_HEADER_SIZE) && /* Could be a UNIX large writeX. */
+                       !srv_is_signing_active()) {
+
+               return receive_smb_raw_talloc_partial_read(
+                       mem_ctx, lenbuf, fd, buffer, timeout, p_unread, plen);
+       }
+
+       if (!valid_packet_size(len)) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       /*
+        * The +4 here can't wrap, we've checked the length above already.
+        */
+
+       *buffer = TALLOC_ARRAY(mem_ctx, char, len+4);
+
+       if (*buffer == NULL) {
+               DEBUG(0, ("Could not allocate inbuf of length %d\n",
+                         (int)len+4));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       memcpy(*buffer, lenbuf, sizeof(lenbuf));
+
+       status = read_packet_remainder(fd, (*buffer)+4, timeout, len);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       *plen = len + 4;
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS receive_smb_talloc(TALLOC_CTX *mem_ctx,        int fd,
+                                  char **buffer, unsigned int timeout,
+                                  size_t *p_unread, bool *p_encrypted,
+                                  size_t *p_len)
+{
+       size_t len = 0;
+       NTSTATUS status;
+
+       *p_encrypted = false;
+
+       status = receive_smb_raw_talloc(mem_ctx, fd, buffer, timeout,
+                                       p_unread, &len);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       if (is_encrypted_packet((uint8_t *)*buffer)) {
+               status = srv_decrypt_buffer(*buffer);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(0, ("receive_smb_talloc: SMB decryption failed on "
+                               "incoming packet! Error %s\n",
+                               nt_errstr(status) ));
+                       return status;
+               }
+               *p_encrypted = true;
+       }
+
+       /* Check the incoming SMB signature. */
+       if (!srv_check_sign_mac(*buffer, true)) {
+               DEBUG(0, ("receive_smb: SMB Signature verification failed on "
+                         "incoming packet!\n"));
+               return NT_STATUS_INVALID_NETWORK_RESPONSE;
+       }
+
+       *p_len = len;
+       return NT_STATUS_OK;
 }
 
 /*
- * Post-process the output of the legacy routine so that the result fits into
- * the new reply_xxx API
+ * Initialize a struct smb_request from an inbuf
  */
 
-void reply_post_legacy(struct smb_request *req, int outsize)
+void init_smb_request(struct smb_request *req,
+                       const uint8 *inbuf,
+                       size_t unread_bytes,
+                       bool encrypted)
 {
-       if (outsize > 0) {
-               smb_setlen((char *)req->inbuf, (char *)req->outbuf,
-                          outsize);
+       size_t req_size = smb_len(inbuf) + 4;
+       /* Ensure we have at least smb_size bytes. */
+       if (req_size < smb_size) {
+               DEBUG(0,("init_smb_request: invalid request size %u\n",
+                       (unsigned int)req_size ));
+               exit_server_cleanly("Invalid SMB request");
        }
-       else {
-               TALLOC_FREE(req->outbuf);
+       req->flags2 = SVAL(inbuf, smb_flg2);
+       req->smbpid = SVAL(inbuf, smb_pid);
+       req->mid    = SVAL(inbuf, smb_mid);
+       req->vuid   = SVAL(inbuf, smb_uid);
+       req->tid    = SVAL(inbuf, smb_tid);
+       req->wct    = CVAL(inbuf, smb_wct);
+       req->unread_bytes = unread_bytes;
+       req->encrypted = encrypted;
+       req->conn = conn_find(req->tid);
+
+       /* Ensure we have at least wct words and 2 bytes of bcc. */
+       if (smb_size + req->wct*2 > req_size) {
+               DEBUG(0,("init_smb_request: invalid wct number %u (size %u)\n",
+                       (unsigned int)req->wct,
+                       (unsigned int)req_size));
+               exit_server_cleanly("Invalid SMB request");
+       }
+       /* Ensure bcc is correct. */
+       if (((uint8 *)smb_buf(inbuf)) + smb_buflen(inbuf) > inbuf + req_size) {
+               DEBUG(0,("init_smb_request: invalid bcc number %u "
+                       "(wct = %u, size %u)\n",
+                       (unsigned int)smb_buflen(inbuf),
+                       (unsigned int)req->wct,
+                       (unsigned int)req_size));
+               exit_server_cleanly("Invalid SMB request");
        }
+       req->inbuf  = inbuf;
+       req->outbuf = NULL;
 }
 
 /****************************************************************************
@@ -131,15 +405,16 @@ void reply_post_legacy(struct smb_request *req, int outsize)
 static struct pending_message_list *deferred_open_queue;
 
 /****************************************************************************
- Function to push a message onto the tail of a linked list of smb messages
ready for processing.
+ Function to push a message onto the tail of a linked list of smb messages ready
+ for processing.
 ****************************************************************************/
 
-static BOOL push_queued_message(const char *buf, int msg_len,
+static bool push_queued_message(struct smb_request *req,
                                struct timeval request_time,
                                struct timeval end_time,
                                char *private_data, size_t private_len)
 {
+       int msg_len = smb_len(req->inbuf) + 4;
        struct pending_message_list *msg;
 
        msg = TALLOC_ZERO_P(NULL, struct pending_message_list);
@@ -149,7 +424,7 @@ static BOOL push_queued_message(const char *buf, int msg_len,
                return False;
        }
 
-       msg->buf = data_blob_talloc(msg, buf, msg_len);
+       msg->buf = data_blob_talloc(msg, req->inbuf, msg_len);
        if(msg->buf.data == NULL) {
                DEBUG(0,("push_message: malloc fail (2)\n"));
                TALLOC_FREE(msg);
@@ -158,6 +433,8 @@ static BOOL push_queued_message(const char *buf, int msg_len,
 
        msg->request_time = request_time;
        msg->end_time = end_time;
+       msg->encrypted = req->encrypted;
+       msg->processed = false;
 
        if (private_data) {
                msg->private_data = data_blob_talloc(msg, private_data,
@@ -187,7 +464,7 @@ void remove_deferred_open_smb_message(uint16 mid)
 
        for (pml = deferred_open_queue; pml; pml = pml->next) {
                if (mid == SVAL(pml->buf.data,smb_mid)) {
-                       DEBUG(10,("remove_deferred_open_smb_message: "
+                       DEBUG(10,("remove_sharing_violation_open_smb_message: "
                                  "deleting mid %u len %u\n",
                                  (unsigned int)mid,
                                  (unsigned int)pml->buf.length ));
@@ -210,11 +487,21 @@ void schedule_deferred_open_smb_message(uint16 mid)
 
        for (pml = deferred_open_queue; pml; pml = pml->next) {
                uint16 msg_mid = SVAL(pml->buf.data,smb_mid);
-               DEBUG(10, ("schedule_deferred_open_smb_message: [%d] "
-                          "msg_mid = %u\n", i++, (unsigned int)msg_mid ));
+               DEBUG(10,("schedule_deferred_open_smb_message: [%d] msg_mid = %u\n", i++,
+                       (unsigned int)msg_mid ));
                if (mid == msg_mid) {
-                       DEBUG(10, ("schedule_deferred_open_smb_message: "
-                                  "scheduling mid %u\n", mid));
+
+                       if (pml->processed) {
+                               /* A processed message should not be
+                                * rescheduled. */
+                               DEBUG(0,("schedule_deferred_open_smb_message: LOGIC ERROR "
+                                       "message mid %u was already processed\n",
+                                       (unsigned int)msg_mid ));
+                               continue;
+                       }
+
+                       DEBUG(10,("schedule_deferred_open_smb_message: scheduling mid %u\n",
+                               mid ));
                        pml->end_time.tv_sec = 0;
                        pml->end_time.tv_usec = 0;
                        DLIST_PROMOTE(deferred_open_queue, pml);
@@ -222,20 +509,20 @@ void schedule_deferred_open_smb_message(uint16 mid)
                }
        }
 
-       DEBUG(10, ("schedule_deferred_open_smb_message: failed to find "
-                  "message mid %u\n", mid ));
+       DEBUG(10,("schedule_deferred_open_smb_message: failed to find message mid %u\n",
+               mid ));
 }
 
 /****************************************************************************
- Return true if this mid is on the deferred queue.
+ Return true if this mid is on the deferred queue and was not yet processed.
 ****************************************************************************/
 
-BOOL open_was_deferred(uint16 mid)
+bool open_was_deferred(uint16 mid)
 {
        struct pending_message_list *pml;
 
        for (pml = deferred_open_queue; pml; pml = pml->next) {
-               if (SVAL(pml->buf.data,smb_mid) == mid) {
+               if (SVAL(pml->buf.data,smb_mid) == mid && !pml->processed) {
                        return True;
                }
        }
@@ -263,23 +550,30 @@ struct pending_message_list *get_open_deferred_message(uint16 mid)
  messages ready for processing.
 ****************************************************************************/
 
-BOOL push_deferred_smb_message(uint16 mid,
+bool push_deferred_smb_message(struct smb_request *req,
                               struct timeval request_time,
                               struct timeval timeout,
                               char *private_data, size_t priv_len)
 {
        struct timeval end_time;
 
+       if (req->unread_bytes) {
+               DEBUG(0,("push_deferred_smb_message: logic error ! "
+                       "unread_bytes = %u\n",
+                       (unsigned int)req->unread_bytes ));
+               smb_panic("push_deferred_smb_message: "
+                       "logic error unread_bytes != 0" );
+       }
+
        end_time = timeval_sum(&request_time, &timeout);
 
        DEBUG(10,("push_deferred_open_smb_message: pushing message len %u mid %u "
                  "timeout time [%u.%06u]\n",
-                 (unsigned int) smb_len(current_inbuf)+4, (unsigned int)mid,
+                 (unsigned int) smb_len(req->inbuf)+4, (unsigned int)req->mid,
                  (unsigned int)end_time.tv_sec,
                  (unsigned int)end_time.tv_usec));
 
-       return push_queued_message(current_inbuf, smb_len(current_inbuf)+4,
-                                  request_time, end_time,
+       return push_queued_message(req, request_time, end_time,
                                   private_data, priv_len);
 }
 
@@ -287,7 +581,7 @@ struct idle_event {
        struct timed_event *te;
        struct timeval interval;
        char *name;
-       BOOL (*handler)(const struct timeval *now, void *private_data);
+       bool (*handler)(const struct timeval *now, void *private_data);
        void *private_data;
 };
 
@@ -320,7 +614,7 @@ struct idle_event *event_add_idle(struct event_context *event_ctx,
                                  TALLOC_CTX *mem_ctx,
                                  struct timeval interval,
                                  const char *name,
-                                 BOOL (*handler)(const struct timeval *now,
+                                 bool (*handler)(const struct timeval *now,
                                                  void *private_data),
                                  void *private_data)
 {
@@ -422,16 +716,18 @@ static int select_on_fd(int fd, int maxfd, fd_set *fds)
 The timeout is in milliseconds
 ****************************************************************************/
 
-static BOOL receive_message_or_smb(TALLOC_CTX *mem_ctx, char **buffer,
-                                  size_t *buffer_len, int timeout)
+static NTSTATUS receive_message_or_smb(TALLOC_CTX *mem_ctx, char **buffer,
+                                      size_t *buffer_len, int timeout,
+                                      size_t *p_unread, bool *p_encrypted)
 {
        fd_set r_fds, w_fds;
        int selrtn;
        struct timeval to;
        int maxfd = 0;
-       ssize_t len;
+       size_t len = 0;
+       NTSTATUS status;
 
-       smb_read_error = 0;
+       *p_unread = 0;
 
  again:
 
@@ -455,7 +751,7 @@ static BOOL receive_message_or_smb(TALLOC_CTX *mem_ctx, char **buffer,
         * and it's time to schedule.
         */
        if(deferred_open_queue != NULL) {
-               BOOL pop_message = False;
+               bool pop_message = False;
                struct pending_message_list *msg = deferred_open_queue;
 
                if (timeval_is_zero(&msg->end_time)) {
@@ -485,15 +781,19 @@ static BOOL receive_message_or_smb(TALLOC_CTX *mem_ctx, char **buffer,
                                                        msg->buf.length);
                        if (*buffer == NULL) {
                                DEBUG(0, ("talloc failed\n"));
-                               smb_read_error = READ_ERROR;
-                               return False;
+                               return NT_STATUS_NO_MEMORY;
                        }
                        *buffer_len = msg->buf.length;
+                       *p_encrypted = msg->encrypted;
 
                        /* We leave this message on the queue so the open code can
                           know this is a retry. */
                        DEBUG(5,("receive_message_or_smb: returning deferred open smb message.\n"));
-                       return True;
+
+                       /* Mark the message as processed so this is not
+                        * re-processed in error. */
+                       msg->processed = true;
+                       return NT_STATUS_OK;
                }
        }
 
@@ -579,14 +879,12 @@ static BOOL receive_message_or_smb(TALLOC_CTX *mem_ctx, char **buffer,
        /* Check if error */
        if (selrtn == -1) {
                /* something is wrong. Maybe the socket is dead? */
-               smb_read_error = READ_ERROR;
-               return False;
-       } 
-    
+               return map_nt_error_from_unix(errno);
+       }
+
        /* Did we timeout ? */
        if (selrtn == 0) {
-               smb_read_error = READ_TIMEOUT;
-               return False;
+               return NT_STATUS_IO_TIMEOUT;
        }
 
        /*
@@ -605,15 +903,25 @@ static BOOL receive_message_or_smb(TALLOC_CTX *mem_ctx, char **buffer,
                goto again;
        }
 
-       len = receive_smb_talloc(mem_ctx, smbd_server_fd(), buffer, 0);
+       /*
+        * We've just woken up from a protentially long select sleep.
+        * Ensure we process local messages as we need to synchronously
+        * process any messages from other smbd's to avoid file rename race
+        * conditions. This call is cheap if there are no messages waiting.
+        * JRA.
+        */
+       message_dispatch(smbd_messaging_context());
 
-       if (len == -1) {
-               return False;
+       status = receive_smb_talloc(mem_ctx, smbd_server_fd(), buffer, 0,
+                                   p_unread, p_encrypted, &len);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
        }
 
-       *buffer_len = (size_t)len;
+       *buffer_len = len;
 
-       return True;
+       return NT_STATUS_OK;
 }
 
 /*
@@ -685,267 +993,266 @@ force write permissions on print services.
 */
 static const struct smb_message_struct {
        const char *name;
-       int (*fn)(connection_struct *conn, char *, char *, int, int);
-       void (*fn_new)(connection_struct *conn, struct smb_request *req);
+       void (*fn_new)(struct smb_request *req);
        int flags;
 } smb_messages[256] = {
 
-/* 0x00 */ { "SMBmkdir",NULL,reply_mkdir,AS_USER | NEED_WRITE},
-/* 0x01 */ { "SMBrmdir",NULL,reply_rmdir,AS_USER | NEED_WRITE},
-/* 0x02 */ { "SMBopen",reply_open,NULL,AS_USER },
-/* 0x03 */ { "SMBcreate",reply_mknew,NULL,AS_USER},
-/* 0x04 */ { "SMBclose",NULL,reply_close,AS_USER | CAN_IPC },
-/* 0x05 */ { "SMBflush",NULL,reply_flush,AS_USER},
-/* 0x06 */ { "SMBunlink",NULL,reply_unlink,AS_USER | NEED_WRITE },
-/* 0x07 */ { "SMBmv",reply_mv,NULL,AS_USER | NEED_WRITE },
-/* 0x08 */ { "SMBgetatr",reply_getatr,NULL,AS_USER},
-/* 0x09 */ { "SMBsetatr",reply_setatr,NULL,AS_USER | NEED_WRITE},
-/* 0x0a */ { "SMBread",reply_read,NULL,AS_USER},
-/* 0x0b */ { "SMBwrite",reply_write,NULL,AS_USER | CAN_IPC },
-/* 0x0c */ { "SMBlock",reply_lock,NULL,AS_USER},
-/* 0x0d */ { "SMBunlock",reply_unlock,NULL,AS_USER},
-/* 0x0e */ { "SMBctemp",reply_ctemp,NULL,AS_USER },
-/* 0x0f */ { "SMBmknew",reply_mknew,NULL,AS_USER},
-/* 0x10 */ { "SMBcheckpath",NULL,reply_checkpath,AS_USER},
-/* 0x11 */ { "SMBexit",NULL,reply_exit,DO_CHDIR},
-/* 0x12 */ { "SMBlseek",reply_lseek,NULL,AS_USER},
-/* 0x13 */ { "SMBlockread",reply_lockread,NULL,AS_USER},
-/* 0x14 */ { "SMBwriteunlock",reply_writeunlock,NULL,AS_USER},
-/* 0x15 */ { NULL, NULL, NULL, 0 },
-/* 0x16 */ { NULL, NULL, NULL, 0 },
-/* 0x17 */ { NULL, NULL, NULL, 0 },
-/* 0x18 */ { NULL, NULL, NULL, 0 },
-/* 0x19 */ { NULL, NULL, NULL, 0 },
-/* 0x1a */ { "SMBreadbraw",reply_readbraw,NULL,AS_USER},
-/* 0x1b */ { "SMBreadBmpx",reply_readbmpx,NULL,AS_USER},
-/* 0x1c */ { "SMBreadBs",NULL, NULL,0 },
-/* 0x1d */ { "SMBwritebraw",reply_writebraw,NULL,AS_USER},
-/* 0x1e */ { "SMBwriteBmpx",reply_writebmpx,NULL,AS_USER},
-/* 0x1f */ { "SMBwriteBs",reply_writebs,NULL,AS_USER},
-/* 0x20 */ { "SMBwritec",NULL, NULL,0},
-/* 0x21 */ { NULL, NULL, NULL, 0 },
-/* 0x22 */ { "SMBsetattrE",reply_setattrE,NULL,AS_USER | NEED_WRITE },
-/* 0x23 */ { "SMBgetattrE",reply_getattrE,NULL,AS_USER },
-/* 0x24 */ { "SMBlockingX",reply_lockingX,NULL,AS_USER },
-/* 0x25 */ { "SMBtrans",NULL,reply_trans,AS_USER | CAN_IPC },
-/* 0x26 */ { "SMBtranss",NULL,reply_transs,AS_USER | CAN_IPC},
-/* 0x27 */ { "SMBioctl",reply_ioctl,NULL,0},
-/* 0x28 */ { "SMBioctls",NULL, NULL,AS_USER},
-/* 0x29 */ { "SMBcopy",reply_copy,NULL,AS_USER | NEED_WRITE },
-/* 0x2a */ { "SMBmove",NULL, NULL,AS_USER | NEED_WRITE },
-/* 0x2b */ { "SMBecho",NULL,reply_echo,0},
-/* 0x2c */ { "SMBwriteclose",reply_writeclose,NULL,AS_USER},
-/* 0x2d */ { "SMBopenX",NULL,reply_open_and_X,AS_USER | CAN_IPC },
-/* 0x2e */ { "SMBreadX",NULL,reply_read_and_X,AS_USER | CAN_IPC },
-/* 0x2f */ { "SMBwriteX",NULL,reply_write_and_X,AS_USER | CAN_IPC },
-/* 0x30 */ { NULL, NULL, NULL, 0 },
-/* 0x31 */ { NULL, NULL, NULL, 0 },
-/* 0x32 */ { "SMBtrans2", NULL,reply_trans2, AS_USER | CAN_IPC },
-/* 0x33 */ { "SMBtranss2", NULL,reply_transs2, AS_USER},
-/* 0x34 */ { "SMBfindclose", reply_findclose,NULL,AS_USER},
-/* 0x35 */ { "SMBfindnclose", reply_findnclose,NULL, AS_USER},
-/* 0x36 */ { NULL, NULL, NULL, 0 },
-/* 0x37 */ { NULL, NULL, NULL, 0 },
-/* 0x38 */ { NULL, NULL, NULL, 0 },
-/* 0x39 */ { NULL, NULL, NULL, 0 },
-/* 0x3a */ { NULL, NULL, NULL, 0 },
-/* 0x3b */ { NULL, NULL, NULL, 0 },
-/* 0x3c */ { NULL, NULL, NULL, 0 },
-/* 0x3d */ { NULL, NULL, NULL, 0 },
-/* 0x3e */ { NULL, NULL, NULL, 0 },
-/* 0x3f */ { NULL, NULL, NULL, 0 },
-/* 0x40 */ { NULL, NULL, NULL, 0 },
-/* 0x41 */ { NULL, NULL, NULL, 0 },
-/* 0x42 */ { NULL, NULL, NULL, 0 },
-/* 0x43 */ { NULL, NULL, NULL, 0 },
-/* 0x44 */ { NULL, NULL, NULL, 0 },
-/* 0x45 */ { NULL, NULL, NULL, 0 },
-/* 0x46 */ { NULL, NULL, NULL, 0 },
-/* 0x47 */ { NULL, NULL, NULL, 0 },
-/* 0x48 */ { NULL, NULL, NULL, 0 },
-/* 0x49 */ { NULL, NULL, NULL, 0 },
-/* 0x4a */ { NULL, NULL, NULL, 0 },
-/* 0x4b */ { NULL, NULL, NULL, 0 },
-/* 0x4c */ { NULL, NULL, NULL, 0 },
-/* 0x4d */ { NULL, NULL, NULL, 0 },
-/* 0x4e */ { NULL, NULL, NULL, 0 },
-/* 0x4f */ { NULL, NULL, NULL, 0 },
-/* 0x50 */ { NULL, NULL, NULL, 0 },
-/* 0x51 */ { NULL, NULL, NULL, 0 },
-/* 0x52 */ { NULL, NULL, NULL, 0 },
-/* 0x53 */ { NULL, NULL, NULL, 0 },
-/* 0x54 */ { NULL, NULL, NULL, 0 },
-/* 0x55 */ { NULL, NULL, NULL, 0 },
-/* 0x56 */ { NULL, NULL, NULL, 0 },
-/* 0x57 */ { NULL, NULL, NULL, 0 },
-/* 0x58 */ { NULL, NULL, NULL, 0 },
-/* 0x59 */ { NULL, NULL, NULL, 0 },
-/* 0x5a */ { NULL, NULL, NULL, 0 },
-/* 0x5b */ { NULL, NULL, NULL, 0 },
-/* 0x5c */ { NULL, NULL, NULL, 0 },
-/* 0x5d */ { NULL, NULL, NULL, 0 },
-/* 0x5e */ { NULL, NULL, NULL, 0 },
-/* 0x5f */ { NULL, NULL, NULL, 0 },
-/* 0x60 */ { NULL, NULL, NULL, 0 },
-/* 0x61 */ { NULL, NULL, NULL, 0 },
-/* 0x62 */ { NULL, NULL, NULL, 0 },
-/* 0x63 */ { NULL, NULL, NULL, 0 },
-/* 0x64 */ { NULL, NULL, NULL, 0 },
-/* 0x65 */ { NULL, NULL, NULL, 0 },
-/* 0x66 */ { NULL, NULL, NULL, 0 },
-/* 0x67 */ { NULL, NULL, NULL, 0 },
-/* 0x68 */ { NULL, NULL, NULL, 0 },
-/* 0x69 */ { NULL, NULL, NULL, 0 },
-/* 0x6a */ { NULL, NULL, NULL, 0 },
-/* 0x6b */ { NULL, NULL, NULL, 0 },
-/* 0x6c */ { NULL, NULL, NULL, 0 },
-/* 0x6d */ { NULL, NULL, NULL, 0 },
-/* 0x6e */ { NULL, NULL, NULL, 0 },
-/* 0x6f */ { NULL, NULL, NULL, 0 },
-/* 0x70 */ { "SMBtcon",reply_tcon,NULL,0},
-/* 0x71 */ { "SMBtdis",NULL,reply_tdis,DO_CHDIR},
-/* 0x72 */ { "SMBnegprot",NULL,reply_negprot,0},
-/* 0x73 */ { "SMBsesssetupX",NULL,reply_sesssetup_and_X,0},
-/* 0x74 */ { "SMBulogoffX", NULL,reply_ulogoffX, 0}, /* ulogoff doesn't give a valid TID */
-/* 0x75 */ { "SMBtconX",NULL,reply_tcon_and_X,0},
-/* 0x76 */ { NULL, NULL, NULL, 0 },
-/* 0x77 */ { NULL, NULL, NULL, 0 },
-/* 0x78 */ { NULL, NULL, NULL, 0 },
-/* 0x79 */ { NULL, NULL, NULL, 0 },
-/* 0x7a */ { NULL, NULL, NULL, 0 },
-/* 0x7b */ { NULL, NULL, NULL, 0 },
-/* 0x7c */ { NULL, NULL, NULL, 0 },
-/* 0x7d */ { NULL, NULL, NULL, 0 },
-/* 0x7e */ { NULL, NULL, NULL, 0 },
-/* 0x7f */ { NULL, NULL, NULL, 0 },
-/* 0x80 */ { "SMBdskattr",NULL,reply_dskattr,AS_USER},
-/* 0x81 */ { "SMBsearch",reply_search,NULL,AS_USER},
-/* 0x82 */ { "SMBffirst",reply_search,NULL,AS_USER},
-/* 0x83 */ { "SMBfunique",reply_search,NULL,AS_USER},
-/* 0x84 */ { "SMBfclose",reply_fclose,NULL,AS_USER},
-/* 0x85 */ { NULL, NULL, NULL, 0 },
-/* 0x86 */ { NULL, NULL, NULL, 0 },
-/* 0x87 */ { NULL, NULL, NULL, 0 },
-/* 0x88 */ { NULL, NULL, NULL, 0 },
-/* 0x89 */ { NULL, NULL, NULL, 0 },
-/* 0x8a */ { NULL, NULL, NULL, 0 },
-/* 0x8b */ { NULL, NULL, NULL, 0 },
-/* 0x8c */ { NULL, NULL, NULL, 0 },
-/* 0x8d */ { NULL, NULL, NULL, 0 },
-/* 0x8e */ { NULL, NULL, NULL, 0 },
-/* 0x8f */ { NULL, NULL, NULL, 0 },
-/* 0x90 */ { NULL, NULL, NULL, 0 },
-/* 0x91 */ { NULL, NULL, NULL, 0 },
-/* 0x92 */ { NULL, NULL, NULL, 0 },
-/* 0x93 */ { NULL, NULL, NULL, 0 },
-/* 0x94 */ { NULL, NULL, NULL, 0 },
-/* 0x95 */ { NULL, NULL, NULL, 0 },
-/* 0x96 */ { NULL, NULL, NULL, 0 },
-/* 0x97 */ { NULL, NULL, NULL, 0 },
-/* 0x98 */ { NULL, NULL, NULL, 0 },
-/* 0x99 */ { NULL, NULL, NULL, 0 },
-/* 0x9a */ { NULL, NULL, NULL, 0 },
-/* 0x9b */ { NULL, NULL, NULL, 0 },
-/* 0x9c */ { NULL, NULL, NULL, 0 },
-/* 0x9d */ { NULL, NULL, NULL, 0 },
-/* 0x9e */ { NULL, NULL, NULL, 0 },
-/* 0x9f */ { NULL, NULL, NULL, 0 },
-/* 0xa0 */ { "SMBnttrans", reply_nttrans,NULL, AS_USER | CAN_IPC },
-/* 0xa1 */ { "SMBnttranss", reply_nttranss,NULL, AS_USER | CAN_IPC },
-/* 0xa2 */ { "SMBntcreateX", NULL,reply_ntcreate_and_X, AS_USER | CAN_IPC },
-/* 0xa3 */ { NULL, NULL, NULL, 0 },
-/* 0xa4 */ { "SMBntcancel", NULL,reply_ntcancel, 0 },
-/* 0xa5 */ { "SMBntrename", reply_ntrename,NULL, AS_USER | NEED_WRITE },
-/* 0xa6 */ { NULL, NULL, NULL, 0 },
-/* 0xa7 */ { NULL, NULL, NULL, 0 },
-/* 0xa8 */ { NULL, NULL, NULL, 0 },
-/* 0xa9 */ { NULL, NULL, NULL, 0 },
-/* 0xaa */ { NULL, NULL, NULL, 0 },
-/* 0xab */ { NULL, NULL, NULL, 0 },
-/* 0xac */ { NULL, NULL, NULL, 0 },
-/* 0xad */ { NULL, NULL, NULL, 0 },
-/* 0xae */ { NULL, NULL, NULL, 0 },
-/* 0xaf */ { NULL, NULL, NULL, 0 },
-/* 0xb0 */ { NULL, NULL, NULL, 0 },
-/* 0xb1 */ { NULL, NULL, NULL, 0 },
-/* 0xb2 */ { NULL, NULL, NULL, 0 },
-/* 0xb3 */ { NULL, NULL, NULL, 0 },
-/* 0xb4 */ { NULL, NULL, NULL, 0 },
-/* 0xb5 */ { NULL, NULL, NULL, 0 },
-/* 0xb6 */ { NULL, NULL, NULL, 0 },
-/* 0xb7 */ { NULL, NULL, NULL, 0 },
-/* 0xb8 */ { NULL, NULL, NULL, 0 },
-/* 0xb9 */ { NULL, NULL, NULL, 0 },
-/* 0xba */ { NULL, NULL, NULL, 0 },
-/* 0xbb */ { NULL, NULL, NULL, 0 },
-/* 0xbc */ { NULL, NULL, NULL, 0 },
-/* 0xbd */ { NULL, NULL, NULL, 0 },
-/* 0xbe */ { NULL, NULL, NULL, 0 },
-/* 0xbf */ { NULL, NULL, NULL, 0 },
-/* 0xc0 */ { "SMBsplopen",reply_printopen,NULL,AS_USER},
-/* 0xc1 */ { "SMBsplwr",reply_printwrite,NULL,AS_USER},
-/* 0xc2 */ { "SMBsplclose",reply_printclose,NULL,AS_USER},
-/* 0xc3 */ { "SMBsplretq",reply_printqueue,NULL,AS_USER},
-/* 0xc4 */ { NULL, NULL, NULL, 0 },
-/* 0xc5 */ { NULL, NULL, NULL, 0 },
-/* 0xc6 */ { NULL, NULL, NULL, 0 },
-/* 0xc7 */ { NULL, NULL, NULL, 0 },
-/* 0xc8 */ { NULL, NULL, NULL, 0 },
-/* 0xc9 */ { NULL, NULL, NULL, 0 },
-/* 0xca */ { NULL, NULL, NULL, 0 },
-/* 0xcb */ { NULL, NULL, NULL, 0 },
-/* 0xcc */ { NULL, NULL, NULL, 0 },
-/* 0xcd */ { NULL, NULL, NULL, 0 },
-/* 0xce */ { NULL, NULL, NULL, 0 },
-/* 0xcf */ { NULL, NULL, NULL, 0 },
-/* 0xd0 */ { "SMBsends",reply_sends,NULL,AS_GUEST},
-/* 0xd1 */ { "SMBsendb",NULL, NULL,AS_GUEST},
-/* 0xd2 */ { "SMBfwdname",NULL, NULL,AS_GUEST},
-/* 0xd3 */ { "SMBcancelf",NULL, NULL,AS_GUEST},
-/* 0xd4 */ { "SMBgetmac",NULL, NULL,AS_GUEST},
-/* 0xd5 */ { "SMBsendstrt",reply_sendstrt,NULL,AS_GUEST},
-/* 0xd6 */ { "SMBsendend",reply_sendend,NULL,AS_GUEST},
-/* 0xd7 */ { "SMBsendtxt",reply_sendtxt,NULL,AS_GUEST},
-/* 0xd8 */ { NULL, NULL, NULL, 0 },
-/* 0xd9 */ { NULL, NULL, NULL, 0 },
-/* 0xda */ { NULL, NULL, NULL, 0 },
-/* 0xdb */ { NULL, NULL, NULL, 0 },
-/* 0xdc */ { NULL, NULL, NULL, 0 },
-/* 0xdd */ { NULL, NULL, NULL, 0 },
-/* 0xde */ { NULL, NULL, NULL, 0 },
-/* 0xdf */ { NULL, NULL, NULL, 0 },
-/* 0xe0 */ { NULL, NULL, NULL, 0 },
-/* 0xe1 */ { NULL, NULL, NULL, 0 },
-/* 0xe2 */ { NULL, NULL, NULL, 0 },
-/* 0xe3 */ { NULL, NULL, NULL, 0 },
-/* 0xe4 */ { NULL, NULL, NULL, 0 },
-/* 0xe5 */ { NULL, NULL, NULL, 0 },
-/* 0xe6 */ { NULL, NULL, NULL, 0 },
-/* 0xe7 */ { NULL, NULL, NULL, 0 },
-/* 0xe8 */ { NULL, NULL, NULL, 0 },
-/* 0xe9 */ { NULL, NULL, NULL, 0 },
-/* 0xea */ { NULL, NULL, NULL, 0 },
-/* 0xeb */ { NULL, NULL, NULL, 0 },
-/* 0xec */ { NULL, NULL, NULL, 0 },
-/* 0xed */ { NULL, NULL, NULL, 0 },
-/* 0xee */ { NULL, NULL, NULL, 0 },
-/* 0xef */ { NULL, NULL, NULL, 0 },
-/* 0xf0 */ { NULL, NULL, NULL, 0 },
-/* 0xf1 */ { NULL, NULL, NULL, 0 },
-/* 0xf2 */ { NULL, NULL, NULL, 0 },
-/* 0xf3 */ { NULL, NULL, NULL, 0 },
-/* 0xf4 */ { NULL, NULL, NULL, 0 },
-/* 0xf5 */ { NULL, NULL, NULL, 0 },
-/* 0xf6 */ { NULL, NULL, NULL, 0 },
-/* 0xf7 */ { NULL, NULL, NULL, 0 },
-/* 0xf8 */ { NULL, NULL, NULL, 0 },
-/* 0xf9 */ { NULL, NULL, NULL, 0 },
-/* 0xfa */ { NULL, NULL, NULL, 0 },
-/* 0xfb */ { NULL, NULL, NULL, 0 },
-/* 0xfc */ { NULL, NULL, NULL, 0 },
-/* 0xfd */ { NULL, NULL, NULL, 0 },
-/* 0xfe */ { NULL, NULL, NULL, 0 },
-/* 0xff */ { NULL, NULL, NULL, 0 }
+/* 0x00 */ { "SMBmkdir",reply_mkdir,AS_USER | NEED_WRITE},
+/* 0x01 */ { "SMBrmdir",reply_rmdir,AS_USER | NEED_WRITE},
+/* 0x02 */ { "SMBopen",reply_open,AS_USER },
+/* 0x03 */ { "SMBcreate",reply_mknew,AS_USER},
+/* 0x04 */ { "SMBclose",reply_close,AS_USER | CAN_IPC },
+/* 0x05 */ { "SMBflush",reply_flush,AS_USER},
+/* 0x06 */ { "SMBunlink",reply_unlink,AS_USER | NEED_WRITE },
+/* 0x07 */ { "SMBmv",reply_mv,AS_USER | NEED_WRITE },
+/* 0x08 */ { "SMBgetatr",reply_getatr,AS_USER},
+/* 0x09 */ { "SMBsetatr",reply_setatr,AS_USER | NEED_WRITE},
+/* 0x0a */ { "SMBread",reply_read,AS_USER},
+/* 0x0b */ { "SMBwrite",reply_write,AS_USER | CAN_IPC },
+/* 0x0c */ { "SMBlock",reply_lock,AS_USER},
+/* 0x0d */ { "SMBunlock",reply_unlock,AS_USER},
+/* 0x0e */ { "SMBctemp",reply_ctemp,AS_USER },
+/* 0x0f */ { "SMBmknew",reply_mknew,AS_USER},
+/* 0x10 */ { "SMBcheckpath",reply_checkpath,AS_USER},
+/* 0x11 */ { "SMBexit",reply_exit,DO_CHDIR},
+/* 0x12 */ { "SMBlseek",reply_lseek,AS_USER},
+/* 0x13 */ { "SMBlockread",reply_lockread,AS_USER},
+/* 0x14 */ { "SMBwriteunlock",reply_writeunlock,AS_USER},
+/* 0x15 */ { NULL, NULL, 0 },
+/* 0x16 */ { NULL, NULL, 0 },
+/* 0x17 */ { NULL, NULL, 0 },
+/* 0x18 */ { NULL, NULL, 0 },
+/* 0x19 */ { NULL, NULL, 0 },
+/* 0x1a */ { "SMBreadbraw",reply_readbraw,AS_USER},
+/* 0x1b */ { "SMBreadBmpx",reply_readbmpx,AS_USER},
+/* 0x1c */ { "SMBreadBs",reply_readbs,AS_USER },
+/* 0x1d */ { "SMBwritebraw",reply_writebraw,AS_USER},
+/* 0x1e */ { "SMBwriteBmpx",reply_writebmpx,AS_USER},
+/* 0x1f */ { "SMBwriteBs",reply_writebs,AS_USER},
+/* 0x20 */ { "SMBwritec", NULL,0},
+/* 0x21 */ { NULL, NULL, 0 },
+/* 0x22 */ { "SMBsetattrE",reply_setattrE,AS_USER | NEED_WRITE },
+/* 0x23 */ { "SMBgetattrE",reply_getattrE,AS_USER },
+/* 0x24 */ { "SMBlockingX",reply_lockingX,AS_USER },
+/* 0x25 */ { "SMBtrans",reply_trans,AS_USER | CAN_IPC },
+/* 0x26 */ { "SMBtranss",reply_transs,AS_USER | CAN_IPC},
+/* 0x27 */ { "SMBioctl",reply_ioctl,0},
+/* 0x28 */ { "SMBioctls", NULL,AS_USER},
+/* 0x29 */ { "SMBcopy",reply_copy,AS_USER | NEED_WRITE },
+/* 0x2a */ { "SMBmove", NULL,AS_USER | NEED_WRITE },
+/* 0x2b */ { "SMBecho",reply_echo,0},
+/* 0x2c */ { "SMBwriteclose",reply_writeclose,AS_USER},
+/* 0x2d */ { "SMBopenX",reply_open_and_X,AS_USER | CAN_IPC },
+/* 0x2e */ { "SMBreadX",reply_read_and_X,AS_USER | CAN_IPC },
+/* 0x2f */ { "SMBwriteX",reply_write_and_X,AS_USER | CAN_IPC },
+/* 0x30 */ { NULL, NULL, 0 },
+/* 0x31 */ { NULL, NULL, 0 },
+/* 0x32 */ { "SMBtrans2",reply_trans2, AS_USER | CAN_IPC },
+/* 0x33 */ { "SMBtranss2",reply_transs2, AS_USER | CAN_IPC},
+/* 0x34 */ { "SMBfindclose",reply_findclose,AS_USER},
+/* 0x35 */ { "SMBfindnclose",reply_findnclose,AS_USER},
+/* 0x36 */ { NULL, NULL, 0 },
+/* 0x37 */ { NULL, NULL, 0 },
+/* 0x38 */ { NULL, NULL, 0 },
+/* 0x39 */ { NULL, NULL, 0 },
+/* 0x3a */ { NULL, NULL, 0 },
+/* 0x3b */ { NULL, NULL, 0 },
+/* 0x3c */ { NULL, NULL, 0 },
+/* 0x3d */ { NULL, NULL, 0 },
+/* 0x3e */ { NULL, NULL, 0 },
+/* 0x3f */ { NULL, NULL, 0 },
+/* 0x40 */ { NULL, NULL, 0 },
+/* 0x41 */ { NULL, NULL, 0 },
+/* 0x42 */ { NULL, NULL, 0 },
+/* 0x43 */ { NULL, NULL, 0 },
+/* 0x44 */ { NULL, NULL, 0 },
+/* 0x45 */ { NULL, NULL, 0 },
+/* 0x46 */ { NULL, NULL, 0 },
+/* 0x47 */ { NULL, NULL, 0 },
+/* 0x48 */ { NULL, NULL, 0 },
+/* 0x49 */ { NULL, NULL, 0 },
+/* 0x4a */ { NULL, NULL, 0 },
+/* 0x4b */ { NULL, NULL, 0 },
+/* 0x4c */ { NULL, NULL, 0 },
+/* 0x4d */ { NULL, NULL, 0 },
+/* 0x4e */ { NULL, NULL, 0 },
+/* 0x4f */ { NULL, NULL, 0 },
+/* 0x50 */ { NULL, NULL, 0 },
+/* 0x51 */ { NULL, NULL, 0 },
+/* 0x52 */ { NULL, NULL, 0 },
+/* 0x53 */ { NULL, NULL, 0 },
+/* 0x54 */ { NULL, NULL, 0 },
+/* 0x55 */ { NULL, NULL, 0 },
+/* 0x56 */ { NULL, NULL, 0 },
+/* 0x57 */ { NULL, NULL, 0 },
+/* 0x58 */ { NULL, NULL, 0 },
+/* 0x59 */ { NULL, NULL, 0 },
+/* 0x5a */ { NULL, NULL, 0 },
+/* 0x5b */ { NULL, NULL, 0 },
+/* 0x5c */ { NULL, NULL, 0 },
+/* 0x5d */ { NULL, NULL, 0 },
+/* 0x5e */ { NULL, NULL, 0 },
+/* 0x5f */ { NULL, NULL, 0 },
+/* 0x60 */ { NULL, NULL, 0 },
+/* 0x61 */ { NULL, NULL, 0 },
+/* 0x62 */ { NULL, NULL, 0 },
+/* 0x63 */ { NULL, NULL, 0 },
+/* 0x64 */ { NULL, NULL, 0 },
+/* 0x65 */ { NULL, NULL, 0 },
+/* 0x66 */ { NULL, NULL, 0 },
+/* 0x67 */ { NULL, NULL, 0 },
+/* 0x68 */ { NULL, NULL, 0 },
+/* 0x69 */ { NULL, NULL, 0 },
+/* 0x6a */ { NULL, NULL, 0 },
+/* 0x6b */ { NULL, NULL, 0 },
+/* 0x6c */ { NULL, NULL, 0 },
+/* 0x6d */ { NULL, NULL, 0 },
+/* 0x6e */ { NULL, NULL, 0 },
+/* 0x6f */ { NULL, NULL, 0 },
+/* 0x70 */ { "SMBtcon",reply_tcon,0},
+/* 0x71 */ { "SMBtdis",reply_tdis,DO_CHDIR},
+/* 0x72 */ { "SMBnegprot",reply_negprot,0},
+/* 0x73 */ { "SMBsesssetupX",reply_sesssetup_and_X,0},
+/* 0x74 */ { "SMBulogoffX",reply_ulogoffX, 0}, /* ulogoff doesn't give a valid TID */
+/* 0x75 */ { "SMBtconX",reply_tcon_and_X,0},
+/* 0x76 */ { NULL, NULL, 0 },
+/* 0x77 */ { NULL, NULL, 0 },
+/* 0x78 */ { NULL, NULL, 0 },
+/* 0x79 */ { NULL, NULL, 0 },
+/* 0x7a */ { NULL, NULL, 0 },
+/* 0x7b */ { NULL, NULL, 0 },
+/* 0x7c */ { NULL, NULL, 0 },
+/* 0x7d */ { NULL, NULL, 0 },
+/* 0x7e */ { NULL, NULL, 0 },
+/* 0x7f */ { NULL, NULL, 0 },
+/* 0x80 */ { "SMBdskattr",reply_dskattr,AS_USER},
+/* 0x81 */ { "SMBsearch",reply_search,AS_USER},
+/* 0x82 */ { "SMBffirst",reply_search,AS_USER},
+/* 0x83 */ { "SMBfunique",reply_search,AS_USER},
+/* 0x84 */ { "SMBfclose",reply_fclose,AS_USER},
+/* 0x85 */ { NULL, NULL, 0 },
+/* 0x86 */ { NULL, NULL, 0 },
+/* 0x87 */ { NULL, NULL, 0 },
+/* 0x88 */ { NULL, NULL, 0 },
+/* 0x89 */ { NULL, NULL, 0 },
+/* 0x8a */ { NULL, NULL, 0 },
+/* 0x8b */ { NULL, NULL, 0 },
+/* 0x8c */ { NULL, NULL, 0 },
+/* 0x8d */ { NULL, NULL, 0 },
+/* 0x8e */ { NULL, NULL, 0 },
+/* 0x8f */ { NULL, NULL, 0 },
+/* 0x90 */ { NULL, NULL, 0 },
+/* 0x91 */ { NULL, NULL, 0 },
+/* 0x92 */ { NULL, NULL, 0 },
+/* 0x93 */ { NULL, NULL, 0 },
+/* 0x94 */ { NULL, NULL, 0 },
+/* 0x95 */ { NULL, NULL, 0 },
+/* 0x96 */ { NULL, NULL, 0 },
+/* 0x97 */ { NULL, NULL, 0 },
+/* 0x98 */ { NULL, NULL, 0 },
+/* 0x99 */ { NULL, NULL, 0 },
+/* 0x9a */ { NULL, NULL, 0 },
+/* 0x9b */ { NULL, NULL, 0 },
+/* 0x9c */ { NULL, NULL, 0 },
+/* 0x9d */ { NULL, NULL, 0 },
+/* 0x9e */ { NULL, NULL, 0 },
+/* 0x9f */ { NULL, NULL, 0 },
+/* 0xa0 */ { "SMBnttrans",reply_nttrans, AS_USER | CAN_IPC },
+/* 0xa1 */ { "SMBnttranss",reply_nttranss, AS_USER | CAN_IPC },
+/* 0xa2 */ { "SMBntcreateX",reply_ntcreate_and_X, AS_USER | CAN_IPC },
+/* 0xa3 */ { NULL, NULL, 0 },
+/* 0xa4 */ { "SMBntcancel",reply_ntcancel, 0 },
+/* 0xa5 */ { "SMBntrename",reply_ntrename, AS_USER | NEED_WRITE },
+/* 0xa6 */ { NULL, NULL, 0 },
+/* 0xa7 */ { NULL, NULL, 0 },
+/* 0xa8 */ { NULL, NULL, 0 },
+/* 0xa9 */ { NULL, NULL, 0 },
+/* 0xaa */ { NULL, NULL, 0 },
+/* 0xab */ { NULL, NULL, 0 },
+/* 0xac */ { NULL, NULL, 0 },
+/* 0xad */ { NULL, NULL, 0 },
+/* 0xae */ { NULL, NULL, 0 },
+/* 0xaf */ { NULL, NULL, 0 },
+/* 0xb0 */ { NULL, NULL, 0 },
+/* 0xb1 */ { NULL, NULL, 0 },
+/* 0xb2 */ { NULL, NULL, 0 },
+/* 0xb3 */ { NULL, NULL, 0 },
+/* 0xb4 */ { NULL, NULL, 0 },
+/* 0xb5 */ { NULL, NULL, 0 },
+/* 0xb6 */ { NULL, NULL, 0 },
+/* 0xb7 */ { NULL, NULL, 0 },
+/* 0xb8 */ { NULL, NULL, 0 },
+/* 0xb9 */ { NULL, NULL, 0 },
+/* 0xba */ { NULL, NULL, 0 },
+/* 0xbb */ { NULL, NULL, 0 },
+/* 0xbc */ { NULL, NULL, 0 },
+/* 0xbd */ { NULL, NULL, 0 },
+/* 0xbe */ { NULL, NULL, 0 },
+/* 0xbf */ { NULL, NULL, 0 },
+/* 0xc0 */ { "SMBsplopen",reply_printopen,AS_USER},
+/* 0xc1 */ { "SMBsplwr",reply_printwrite,AS_USER},
+/* 0xc2 */ { "SMBsplclose",reply_printclose,AS_USER},
+/* 0xc3 */ { "SMBsplretq",reply_printqueue,AS_USER},
+/* 0xc4 */ { NULL, NULL, 0 },
+/* 0xc5 */ { NULL, NULL, 0 },
+/* 0xc6 */ { NULL, NULL, 0 },
+/* 0xc7 */ { NULL, NULL, 0 },
+/* 0xc8 */ { NULL, NULL, 0 },
+/* 0xc9 */ { NULL, NULL, 0 },
+/* 0xca */ { NULL, NULL, 0 },
+/* 0xcb */ { NULL, NULL, 0 },
+/* 0xcc */ { NULL, NULL, 0 },
+/* 0xcd */ { NULL, NULL, 0 },
+/* 0xce */ { NULL, NULL, 0 },
+/* 0xcf */ { NULL, NULL, 0 },
+/* 0xd0 */ { "SMBsends",reply_sends,AS_GUEST},
+/* 0xd1 */ { "SMBsendb", NULL,AS_GUEST},
+/* 0xd2 */ { "SMBfwdname", NULL,AS_GUEST},
+/* 0xd3 */ { "SMBcancelf", NULL,AS_GUEST},
+/* 0xd4 */ { "SMBgetmac", NULL,AS_GUEST},
+/* 0xd5 */ { "SMBsendstrt",reply_sendstrt,AS_GUEST},
+/* 0xd6 */ { "SMBsendend",reply_sendend,AS_GUEST},
+/* 0xd7 */ { "SMBsendtxt",reply_sendtxt,AS_GUEST},
+/* 0xd8 */ { NULL, NULL, 0 },
+/* 0xd9 */ { NULL, NULL, 0 },
+/* 0xda */ { NULL, NULL, 0 },
+/* 0xdb */ { NULL, NULL, 0 },
+/* 0xdc */ { NULL, NULL, 0 },
+/* 0xdd */ { NULL, NULL, 0 },
+/* 0xde */ { NULL, NULL, 0 },
+/* 0xdf */ { NULL, NULL, 0 },
+/* 0xe0 */ { NULL, NULL, 0 },
+/* 0xe1 */ { NULL, NULL, 0 },
+/* 0xe2 */ { NULL, NULL, 0 },
+/* 0xe3 */ { NULL, NULL, 0 },
+/* 0xe4 */ { NULL, NULL, 0 },
+/* 0xe5 */ { NULL, NULL, 0 },
+/* 0xe6 */ { NULL, NULL, 0 },
+/* 0xe7 */ { NULL, NULL, 0 },
+/* 0xe8 */ { NULL, NULL, 0 },
+/* 0xe9 */ { NULL, NULL, 0 },
+/* 0xea */ { NULL, NULL, 0 },
+/* 0xeb */ { NULL, NULL, 0 },
+/* 0xec */ { NULL, NULL, 0 },
+/* 0xed */ { NULL, NULL, 0 },
+/* 0xee */ { NULL, NULL, 0 },
+/* 0xef */ { NULL, NULL, 0 },
+/* 0xf0 */ { NULL, NULL, 0 },
+/* 0xf1 */ { NULL, NULL, 0 },
+/* 0xf2 */ { NULL, NULL, 0 },
+/* 0xf3 */ { NULL, NULL, 0 },
+/* 0xf4 */ { NULL, NULL, 0 },
+/* 0xf5 */ { NULL, NULL, 0 },
+/* 0xf6 */ { NULL, NULL, 0 },
+/* 0xf7 */ { NULL, NULL, 0 },
+/* 0xf8 */ { NULL, NULL, 0 },
+/* 0xf9 */ { NULL, NULL, 0 },
+/* 0xfa */ { NULL, NULL, 0 },
+/* 0xfb */ { NULL, NULL, 0 },
+/* 0xfc */ { NULL, NULL, 0 },
+/* 0xfd */ { NULL, NULL, 0 },
+/* 0xfe */ { NULL, NULL, 0 },
+/* 0xff */ { NULL, NULL, 0 }
 
 };
 
@@ -953,8 +1260,21 @@ static const struct smb_message_struct {
  allocate and initialize a reply packet
 ********************************************************************/
 
-void reply_outbuf(struct smb_request *req, uint8 num_words, uint16 num_bytes)
+void reply_outbuf(struct smb_request *req, uint8 num_words, uint32 num_bytes)
 {
+       /*
+         * Protect against integer wrap
+         */
+       if ((num_bytes > 0xffffff)
+           || ((num_bytes + smb_size + num_words*2) > 0xffffff)) {
+               char *msg;
+               if (asprintf(&msg, "num_bytes too large: %u",
+                            (unsigned)num_bytes) == -1) {
+                       msg = CONST_DISCARD(char *, "num_bytes too large");
+               }
+               smb_panic(msg);
+       }
+
        if (!(req->outbuf = TALLOC_ARRAY(
                      req, uint8,
                      smb_size + num_words*2 + num_bytes))) {
@@ -962,8 +1282,7 @@ void reply_outbuf(struct smb_request *req, uint8 num_words, uint16 num_bytes)
        }
 
        construct_reply_common((char *)req->inbuf, (char *)req->outbuf);
-       set_message((char *)req->inbuf, (char *)req->outbuf,
-                   num_words, num_bytes, False);
+       srv_set_message((char *)req->outbuf, num_words, num_bytes, false);
        /*
         * Zero out the word area, the caller has to take care of the bcc area
         * himself
@@ -983,13 +1302,17 @@ void reply_outbuf(struct smb_request *req, uint8 num_words, uint16 num_bytes)
 static void smb_dump(const char *name, int type, const char *data, ssize_t len)
 {
        int fd, i;
-       pstring fname;
-       if (DEBUGLEVEL < 50) return;
+       char *fname = NULL;
+       if (DEBUGLEVEL < 50) {
+               return;
+       }
 
        if (len < 4) len = smb_len(data)+4;
        for (i=1;i<100;i++) {
-               slprintf(fname,sizeof(fname)-1, "/tmp/%s.%d.%s", name, i,
-                               type ? "req" : "resp");
+               if (asprintf(&fname, "/tmp/%s.%d.%s", name, i,
+                            type ? "req" : "resp") == -1) {
+                       return;
+               }
                fd = open(fname, O_WRONLY|O_CREAT|O_EXCL, 0644);
                if (fd != -1 || errno != EEXIST) break;
        }
@@ -1000,6 +1323,7 @@ static void smb_dump(const char *name, int type, const char *data, ssize_t len)
                close(fd);
                DEBUG(0,("created %s len %lu\n", fname, (unsigned long)len));
        }
+       SAFE_FREE(fname);
 }
 
 /****************************************************************************
@@ -1015,35 +1339,30 @@ static void smb_dump(const char *name, int type, const char *data, ssize_t len)
  find.
 ****************************************************************************/
 
-static BOOL switch_message_new(uint8 type, struct smb_request *req, int size,
-                              connection_struct **pconn)
+static connection_struct *switch_message(uint8 type, struct smb_request *req, int size)
 {
        int flags;
        uint16 session_tag;
-       connection_struct *conn;
+       connection_struct *conn = NULL;
 
        static uint16 last_session_tag = UID_FIELD_INVALID;
 
        errno = 0;
 
-       last_message = type;
-
        /* Make sure this is an SMB packet. smb_size contains NetBIOS header
         * so subtract 4 from it. */
-       if ((strncmp(smb_base(req->inbuf),"\377SMB",4) != 0)
+       if (!valid_smb_header(req->inbuf)
            || (size < (smb_size - 4))) {
                DEBUG(2,("Non-SMB packet of length %d. Terminating server\n",
                         smb_len(req->inbuf)));
                exit_server_cleanly("Non-SMB packet");
-               return True;
        }
 
-       if ((smb_messages[type].fn == NULL)
-           && (smb_messages[type].fn_new == NULL)) {
+       if (smb_messages[type].fn_new == NULL) {
                DEBUG(0,("Unknown message type %d!\n",type));
                smb_dump("Unknown", 1, (char *)req->inbuf, size);
                reply_unknown_new(req, type);
-               goto done;
+               return NULL;
        }
 
        flags = smb_messages[type].flags;
@@ -1051,7 +1370,7 @@ static BOOL switch_message_new(uint8 type, struct smb_request *req, int size,
        /* In share mode security we must ignore the vuid. */
        session_tag = (lp_security() == SEC_SHARE)
                ? UID_FIELD_INVALID : req->vuid;
-       conn = conn_find(req->tid);
+       conn = req->conn;
 
        DEBUG(3,("switch message %s (pid %d) conn 0x%lx\n", smb_fn_name(type),
                 (int)sys_getpid(), (unsigned long)conn));
@@ -1096,12 +1415,12 @@ static BOOL switch_message_new(uint8 type, struct smb_request *req, int size,
                        } else {
                                reply_doserror(req, ERRSRV, ERRinvnid);
                        }
-                       goto done;
+                       return NULL;
                }
 
                if (!change_to_user(conn,session_tag)) {
                        reply_nterror(req, NT_STATUS_DOS(ERRSRV, ERRbaduid));
-                       goto done;
+                       return conn;
                }
 
                /* All NEED_WRITE and CAN_IPC flags must also have AS_USER. */
@@ -1109,13 +1428,13 @@ static BOOL switch_message_new(uint8 type, struct smb_request *req, int size,
                /* Does it need write permission? */
                if ((flags & NEED_WRITE) && !CAN_WRITE(conn)) {
                        reply_nterror(req, NT_STATUS_MEDIA_WRITE_PROTECTED);
-                       goto done;
+                       return conn;
                }
 
                /* IPC services are limited */
                if (IS_IPC(conn) && !(flags & CAN_IPC)) {
                        reply_doserror(req, ERRSRV,ERRaccess);
-                       goto done;
+                       return conn;
                }
        } else {
                /* This call needs to be run as root */
@@ -1124,11 +1443,24 @@ static BOOL switch_message_new(uint8 type, struct smb_request *req, int size,
 
        /* load service specific parameters */
        if (conn) {
+               if (req->encrypted) {
+                       conn->encrypted_tid = true;
+                       /* encrypted required from now on. */
+                       conn->encrypt_level = Required;
+               } else if (ENCRYPTION_REQUIRED(conn)) {
+                       uint8 com = CVAL(req->inbuf,smb_com);
+                       if (com != SMBtrans2 && com != SMBtranss2) {
+                               exit_server_cleanly("encryption required "
+                                       "on connection");
+                               return conn;
+                       }
+               }
+
                if (!set_current_service(conn,SVAL(req->inbuf,smb_flg),
                                         (flags & (AS_USER|DO_CHDIR)
                                          ?True:False))) {
                        reply_doserror(req, ERRSRV, ERRaccess);
-                       goto done;
+                       return conn;
                }
                conn->num_smb_operations++;
        }
@@ -1139,112 +1471,63 @@ static BOOL switch_message_new(uint8 type, struct smb_request *req, int size,
                !check_access(smbd_server_fd(), lp_hostsallow(-1),
                              lp_hostsdeny(-1)))) {
                reply_doserror(req, ERRSRV, ERRaccess);
-               goto done;
-       }
-
-       current_inbuf = (char *)req->inbuf; /* In case we need to defer this
-                                            * message in open... */
-
-       if (smb_messages[type].fn_new != NULL) {
-               smb_messages[type].fn_new(conn, req);
-               goto done;
-       }
-
-       /*
-        * Indicate the upper layer that there's still work.
-        */
-       *pconn = conn;
-       return False;
-
- done:
-       return True;
-}
-
-
-/****************************************************************************
- Do a switch on the message type, and return the response size
-****************************************************************************/
-
-static int switch_message(uint8 type, struct smb_request *req, char **outbuf,
-                         int size, int bufsize)
-{
-       int outsize = 0;
-       connection_struct *conn = NULL;
-
-       if (switch_message_new(type, req, size, &conn)) {
-               if (req->outbuf != NULL) {
-                       *outbuf = (char *)req->outbuf;
-                       return smb_len(req->outbuf)+4;
-               }
-               return -1;
+               return conn;
        }
 
-       if (InBuffer == NULL) {
-               DEBUG(1, ("have to alloc InBuffer for %s\n",
-                         smb_fn_name(type)));
-               if (NewInBuffer(NULL) == NULL) {
-                       smb_panic("Could not allocate InBuffer");
-               }
-       }
-
-       if ((OutBuffer == NULL) && (NewOutBuffer(NULL) == NULL)) {
-               smb_panic("Could not allocate OutBuffer");
-       }
-
-       clobber_region(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE, OutBuffer,
-                      total_buffer_size);
-
-       memcpy(InBuffer, req->inbuf, MIN(size, total_buffer_size));
-
-       construct_reply_common(InBuffer, OutBuffer);
-
-       outsize = smb_messages[type].fn(conn, InBuffer, OutBuffer, size,
-                                       bufsize);
-
-       smb_dump(smb_fn_name(type), 0, OutBuffer, outsize);
-
-       *outbuf = OutBuffer;
-
-       return(outsize);
+       smb_messages[type].fn_new(req);
+       return req->conn;
 }
 
 /****************************************************************************
  Construct a reply to the incoming packet.
 ****************************************************************************/
 
-static void construct_reply(char *inbuf, int size)
+static void construct_reply(char *inbuf, int size, size_t unread_bytes, bool encrypted)
 {
+       struct pending_message_list *pml = NULL;
        uint8 type = CVAL(inbuf,smb_com);
-       int outsize = 0;
+       connection_struct *conn;
        struct smb_request *req;
-       char *outbuf;
 
        chain_size = 0;
        file_chain_reset();
        reset_chain_p();
 
-       if (!(req = talloc(tmp_talloc_ctx(), struct smb_request))) {
+       if (!(req = talloc(talloc_tos(), struct smb_request))) {
                smb_panic("could not allocate smb_request");
        }
-       init_smb_request(req, (uint8 *)inbuf);
+       init_smb_request(req, (uint8 *)inbuf, unread_bytes, encrypted);
 
-       outsize = switch_message(type, req, &outbuf, size, max_send);
+       conn = switch_message(type, req, size);
 
-       if (outsize > 4) {
-               smb_setlen(inbuf,outbuf,outsize - 4);
+       /* If this was a deferred message and it's still there and
+        * was processed, remove it. */
+       pml = get_open_deferred_message(req->mid);
+       if (pml && pml->processed) {
+               remove_deferred_open_smb_message(req->mid);
        }
 
-       if (outsize > 0) {
-               if (CVAL(outbuf,0) == 0)
-                       show_msg(outbuf);
-
-               if (outsize != smb_len(outbuf) + 4) {
-                       DEBUG(0,("ERROR: Invalid message response size! "
-                                "%d %d\n", outsize, smb_len(outbuf)));
-               } else if (!send_smb(smbd_server_fd(),outbuf)) {
-                       exit_server_cleanly("construct_reply: send_smb "
-                                           "failed.");
+       if (req->unread_bytes) {
+               /* writeX failed. drain socket. */
+               if (drain_socket(smbd_server_fd(), req->unread_bytes) !=
+                               req->unread_bytes) {
+                       smb_panic("failed to drain pending bytes");
                }
+               req->unread_bytes = 0;
+       }
+
+       if (req->outbuf == NULL) {
+               return;
+       }
+
+       if (CVAL(req->outbuf,0) == 0) {
+               show_msg((char *)req->outbuf);
+       }
+
+       if (!srv_send_smb(smbd_server_fd(),
+                       (char *)req->outbuf,
+                       IS_CONN_ENCRYPTED(conn)||req->encrypted)) {
+               exit_server_cleanly("construct_reply: srv_send_smb failed.");
        }
 
        TALLOC_FREE(req);
@@ -1256,7 +1539,7 @@ static void construct_reply(char *inbuf, int size)
  Process an smb from the client
 ****************************************************************************/
 
-static void process_smb(char *inbuf, size_t nread)
+static void process_smb(char *inbuf, size_t nread, size_t unread_bytes, bool encrypted)
 {
        static int trans_num;
        int msg_type = CVAL(inbuf,0);
@@ -1264,23 +1547,29 @@ static void process_smb(char *inbuf, size_t nread)
        DO_PROFILE_INC(smb_count);
 
        if (trans_num == 0) {
+               char addr[INET6_ADDRSTRLEN];
+
                /* on the first packet, check the global hosts allow/ hosts
                deny parameters before doing any parsing of the packet
                passed to us by the client.  This prevents attacks on our
                parsing code from hosts not in the hosts allow list */
+
                if (!check_access(smbd_server_fd(), lp_hostsallow(-1),
                                  lp_hostsdeny(-1))) {
                        /* send a negative session response "not listening on calling name" */
                        static unsigned char buf[5] = {0x83, 0, 0, 1, 0x81};
-                       DEBUG( 1, ( "Connection denied from %s\n", client_addr() ) );
-                       (void)send_smb(smbd_server_fd(),(char *)buf);
+                       DEBUG( 1, ( "Connection denied from %s\n",
+                               client_addr(get_client_fd(),addr,sizeof(addr)) ) );
+                       (void)srv_send_smb(smbd_server_fd(),(char *)buf,false);
                        exit_server_cleanly("connection denied");
                }
        }
 
        DEBUG( 6, ( "got message type 0x%x of len 0x%x\n", msg_type,
                    smb_len(inbuf) ) );
-       DEBUG( 3, ( "Transaction %d of length %d\n", trans_num, (int)nread ) );
+       DEBUG( 3, ( "Transaction %d of length %d (%u toread)\n", trans_num,
+                               (int)nread,
+                               (unsigned int)unread_bytes ));
 
        if (msg_type != 0) {
                /*
@@ -1292,8 +1581,8 @@ static void process_smb(char *inbuf, size_t nread)
 
        show_msg(inbuf);
 
-       construct_reply(inbuf,nread);
-      
+       construct_reply(inbuf,nread,unread_bytes,encrypted);
+
        trans_num++;
 }
 
@@ -1329,7 +1618,7 @@ void remove_from_common_flags2(uint32 v)
 
 void construct_reply_common(const char *inbuf, char *outbuf)
 {
-       set_message(inbuf,outbuf,0,0,False);
+       srv_set_message(outbuf,0,0,false);
        
        SCVAL(outbuf,smb_com,CVAL(inbuf,smb_com));
        SIVAL(outbuf,smb_rcls,0);
@@ -1349,29 +1638,38 @@ void construct_reply_common(const char *inbuf, char *outbuf)
  Construct a chained reply and add it to the already made reply
 ****************************************************************************/
 
-int chain_reply(char *inbuf,char **poutbuf,int size,int bufsize)
+void chain_reply(struct smb_request *req)
 {
        static char *orig_inbuf;
+
+       /*
+        * Dirty little const_discard: We mess with req->inbuf, which is
+        * declared as const. If maybe at some point this routine gets
+        * rewritten, this const_discard could go away.
+        */
+       char *inbuf = CONST_DISCARD(char *, req->inbuf);
+       int size = smb_len(req->inbuf)+4;
+
        int smb_com1, smb_com2 = CVAL(inbuf,smb_vwv0);
        unsigned smb_off2 = SVAL(inbuf,smb_vwv1);
        char *inbuf2;
-       char *outbuf2 = NULL;
        int outsize2;
        int new_size;
        char inbuf_saved[smb_wct];
-       char *outbuf = *poutbuf;
+       char *outbuf = (char *)req->outbuf;
        size_t outsize = smb_len(outbuf) + 4;
        size_t outsize_padded;
+       size_t padding;
        size_t ofs, to_move;
 
-       struct smb_request *req;
+       struct smb_request *req2;
        size_t caller_outputlen;
        char *caller_output;
 
        /* Maybe its not chained, or it's an error packet. */
        if (smb_com2 == 0xFF || SVAL(outbuf,smb_rcls) != 0) {
                SCVAL(outbuf,smb_vwv0,0xFF);
-               return outsize;
+               return;
        }
 
        if (chain_size == 0) {
@@ -1400,12 +1698,13 @@ int chain_reply(char *inbuf,char **poutbuf,int size,int bufsize)
         */
 
        outsize_padded = (outsize + 3) & ~3;
+       padding = outsize_padded - outsize;
 
        /*
         * remember how much the caller added to the chain, only counting
         * stuff after the parameter words
         */
-       chain_size += outsize_padded - smb_wct;
+       chain_size += (outsize_padded - smb_wct);
 
        /*
         * work out pointers into the original packets. The
@@ -1432,28 +1731,28 @@ int chain_reply(char *inbuf,char **poutbuf,int size,int bufsize)
                         "(orig size = %d, offset = %d)\n",
                         size, (int)(inbuf2 - inbuf) ));
                exit_server_cleanly("Bad chained packet");
-               return(-1);
+               return;
        }
 
        /* And set it in the header. */
-       smb_setlen(inbuf, inbuf2, new_size - 4);
+       smb_setlen(inbuf2, new_size - 4);
 
        DEBUG(3,("Chained message\n"));
        show_msg(inbuf2);
 
-       if (!(req = talloc(tmp_talloc_ctx(), struct smb_request))) {
+       if (!(req2 = talloc(talloc_tos(), struct smb_request))) {
                smb_panic("could not allocate smb_request");
        }
-       init_smb_request(req, (uint8 *)inbuf2);
+       init_smb_request(req2, (uint8 *)inbuf2,0, req->encrypted);
 
        /* process the request */
-       outsize2 = switch_message(smb_com2, req, &outbuf2, new_size,
-                                 bufsize-chain_size);
+       switch_message(smb_com2, req2, new_size);
 
        /*
         * We don't accept deferred operations in chained requests.
         */
-       SMB_ASSERT(outsize2 >= smb_wct);
+       SMB_ASSERT(req2->outbuf != NULL);
+       outsize2 = smb_len(req2->outbuf)+4;
 
        /*
         * Move away the new command output so that caller_output fits in,
@@ -1486,23 +1785,22 @@ int chain_reply(char *inbuf,char **poutbuf,int size,int bufsize)
         * which is always large enough.
         */
 
-       if (outbuf != OutBuffer) {
-               outbuf = TALLOC_REALLOC_ARRAY(NULL, outbuf, char,
-                                             to_move + ofs + smb_wct);
-               if (outbuf == NULL) {
-                       smb_panic("could not realloc outbuf");
-               }
+       outbuf = TALLOC_REALLOC_ARRAY(NULL, outbuf, char,
+                                     to_move + ofs + smb_wct);
+       if (outbuf == NULL) {
+               smb_panic("could not realloc outbuf");
        }
 
-       *poutbuf = outbuf;
+       req->outbuf = (uint8 *)outbuf;
 
-       memmove(outbuf + smb_wct + ofs, outbuf2 + smb_wct, to_move);
+       memmove(outbuf + smb_wct + ofs, req2->outbuf + smb_wct, to_move);
        memcpy(outbuf + smb_wct, caller_output, caller_outputlen);
 
        /*
-        * The secondary function has overwritten smb_com
+        * copy the new reply header over the old one but preserve the smb_com
+        * field
         */
-
+       memmove(outbuf, req2->outbuf, smb_wct);
        SCVAL(outbuf, smb_com, smb_com1);
 
        /*
@@ -1514,17 +1812,17 @@ int chain_reply(char *inbuf,char **poutbuf,int size,int bufsize)
        SCVAL(outbuf, smb_vwv0, smb_com2);
        SSVAL(outbuf, smb_vwv1, chain_size + smb_wct - 4);
 
-       if (outsize_padded > outsize) {
+       if (padding != 0) {
 
                /*
                 * Due to padding we have some uninitialized bytes after the
                 * caller's output
                 */
 
-               memset(outbuf + outsize, 0, outsize_padded - outsize);
+               memset(outbuf + outsize, 0, padding);
        }
 
-       smb_setlen(NULL, outbuf, outsize2 + chain_size - 4);
+       smb_setlen(outbuf, outsize2 + caller_outputlen + padding - 4);
 
        /*
         * restore the saved data, being careful not to overwrite any data
@@ -1533,17 +1831,15 @@ int chain_reply(char *inbuf,char **poutbuf,int size,int bufsize)
        memcpy(inbuf2,inbuf_saved,smb_wct);
 
        SAFE_FREE(caller_output);
-       TALLOC_FREE(req);
+       TALLOC_FREE(req2);
 
-       return outsize2 + chain_size;
-}
+       /*
+        * Reset the chain_size for our caller's offset calculations
+        */
 
-void chain_reply_new(struct smb_request *req)
-{
-       chain_reply(CONST_DISCARD(char *, req->inbuf),
-                   (char **)(void *)&req->outbuf,
-                   smb_len(req->inbuf)+4,
-                   smb_len(req->outbuf)+4);
+       chain_size -= (outsize_padded - smb_wct);
+
+       return;
 }
 
 /****************************************************************************
@@ -1620,27 +1916,11 @@ void check_reload(time_t t)
  Process any timeout housekeeping. Return False if the caller should exit.
 ****************************************************************************/
 
-static BOOL timeout_processing(int *select_timeout,
+static void timeout_processing(int *select_timeout,
                               time_t *last_timeout_processing_time)
 {
        time_t t;
 
-       if (smb_read_error == READ_EOF) {
-               DEBUG(3,("timeout_processing: End of file from client (client has disconnected).\n"));
-               return False;
-       }
-
-       if (smb_read_error == READ_ERROR) {
-               DEBUG(3,("timeout_processing: receive_smb error (%s) Exiting\n",
-                       strerror(errno)));
-               return False;
-       }
-
-       if (smb_read_error == READ_BAD_SIG) {
-               DEBUG(3,("timeout_processing: receive_smb error bad smb signature. Exiting\n"));
-               return False;
-       }
-
        *last_timeout_processing_time = t = time(NULL);
 
        /* become root again if waiting */
@@ -1656,6 +1936,7 @@ static BOOL timeout_processing(int *select_timeout,
 
                unsigned char trust_passwd_hash[16];
                time_t lct;
+               void *lock;
 
                /*
                 * We're in domain level security, and the code that
@@ -1667,17 +1948,19 @@ static BOOL timeout_processing(int *select_timeout,
                 * First, open the machine password file with an exclusive lock.
                 */
 
-               if (secrets_lock_trust_account_password(lp_workgroup(), True) == False) {
+               lock = secrets_get_trust_account_lock(NULL, lp_workgroup());
+
+               if (lock == NULL) {
                        DEBUG(0,("process: unable to lock the machine account password for \
 machine %s in domain %s.\n", global_myname(), lp_workgroup() ));
-                       return True;
+                       return;
                }
 
                if(!secrets_fetch_trust_account_password(lp_workgroup(), trust_passwd_hash, &lct, NULL)) {
                        DEBUG(0,("process: unable to read the machine account password for \
 machine %s in domain %s.\n", global_myname(), lp_workgroup()));
-                       secrets_lock_trust_account_password(lp_workgroup(), False);
-                       return True;
+                       TALLOC_FREE(lock);
+                       return;
                }
 
                /*
@@ -1686,15 +1969,15 @@ machine %s in domain %s.\n", global_myname(), lp_workgroup()));
 
                if(t < lct + lp_machine_password_timeout()) {
                        global_machine_password_needs_changing = False;
-                       secrets_lock_trust_account_password(lp_workgroup(), False);
-                       return True;
+                       TALLOC_FREE(lock);
+                       return;
                }
 
                /* always just contact the PDC here */
     
                change_trust_account_password( lp_workgroup(), NULL);
                global_machine_password_needs_changing = False;
-               secrets_lock_trust_account_password(lp_workgroup(), False);
+               TALLOC_FREE(lock);
        }
 
        /* update printer queue caches if necessary */
@@ -1719,61 +2002,7 @@ machine %s in domain %s.\n", global_myname(), lp_workgroup()));
 
        *select_timeout = setup_select_timeout();
 
-       return True;
-}
-
-/****************************************************************************
- Accessor functions for InBuffer, OutBuffer.
-****************************************************************************/
-
-char *get_InBuffer(void)
-{
-       return InBuffer;
-}
-
-char *get_OutBuffer(void)
-{
-       return OutBuffer;
-}
-
-/****************************************************************************
- Allocate a new InBuffer. Returns the new and old ones.
-****************************************************************************/
-
-static char *NewInBuffer(char **old_inbuf)
-{
-       char *new_inbuf = (char *)SMB_MALLOC(total_buffer_size);
-       if (!new_inbuf) {
-               return NULL;
-       }
-       if (old_inbuf) {
-               *old_inbuf = InBuffer;
-       }
-       InBuffer = new_inbuf;
-#if defined(DEVELOPER)
-       clobber_region(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE, InBuffer, total_buffer_size);
-#endif
-       return InBuffer;
-}
-
-/****************************************************************************
- Allocate a new OutBuffer. Returns the new and old ones.
-****************************************************************************/
-
-static char *NewOutBuffer(char **old_outbuf)
-{
-       char *new_outbuf = (char *)SMB_MALLOC(total_buffer_size);
-       if (!new_outbuf) {
-               return NULL;
-       }
-       if (old_outbuf) {
-               *old_outbuf = OutBuffer;
-       }
-       OutBuffer = new_outbuf;
-#if defined(DEVELOPER)
-       clobber_region(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE, OutBuffer, total_buffer_size);
-#endif
-       return OutBuffer;
+       return;
 }
 
 /****************************************************************************
@@ -1784,36 +2013,51 @@ void smbd_process(void)
 {
        time_t last_timeout_processing_time = time(NULL);
        unsigned int num_smbs = 0;
+       size_t unread_bytes = 0;
 
        max_recv = MIN(lp_maxxmit(),BUFFER_SIZE);
 
        while (True) {
                int select_timeout = setup_select_timeout();
                int num_echos;
-               char *inbuf;
-               size_t inbuf_len;
+               char *inbuf = NULL;
+               size_t inbuf_len = 0;
+               bool encrypted = false;
+               TALLOC_CTX *frame = talloc_stackframe_pool(8192);
 
-               errno = 0;      
-               
-               /* free up temporary memory */
-               lp_TALLOC_FREE();
-               main_loop_TALLOC_FREE();
+               errno = 0;
 
                /* Did someone ask for immediate checks on things like blocking locks ? */
                if (select_timeout == 0) {
-                       if(!timeout_processing(&select_timeout,
-                                              &last_timeout_processing_time))
-                               return;
+                       timeout_processing(&select_timeout,
+                                          &last_timeout_processing_time);
                        num_smbs = 0; /* Reset smb counter. */
                }
 
                run_events(smbd_event_context(), 0, NULL, NULL);
 
-               while (!receive_message_or_smb(NULL, &inbuf, &inbuf_len,
-                                              select_timeout)) {
-                       if(!timeout_processing(&select_timeout,
-                                              &last_timeout_processing_time))
-                               return;
+               while (True) {
+                       NTSTATUS status;
+
+                       status = receive_message_or_smb(
+                               talloc_tos(), &inbuf, &inbuf_len,
+                               select_timeout, &unread_bytes, &encrypted);
+
+                       if (NT_STATUS_IS_OK(status)) {
+                               break;
+                       }
+
+                       if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+                               timeout_processing(
+                                       &select_timeout,
+                                       &last_timeout_processing_time);
+                               continue;
+                       }
+
+                       DEBUG(3, ("receive_message_or_smb failed: %s, "
+                                 "exiting\n", nt_errstr(status)));
+                       return;
+
                        num_smbs = 0; /* Reset smb counter. */
                }
 
@@ -1826,16 +2070,16 @@ void smbd_process(void)
                 * faster than the select timeout, thus starving out the
                 * essential processing (change notify, blocking locks) that
                 * the timeout code does. JRA.
-                */ 
+                */
                num_echos = smb_echo_count;
 
-               process_smb(inbuf, inbuf_len);
+               process_smb(inbuf, inbuf_len, unread_bytes, encrypted);
 
                TALLOC_FREE(inbuf);
 
                if (smb_echo_count != num_echos) {
-                       if(!timeout_processing( &select_timeout, &last_timeout_processing_time))
-                               return;
+                       timeout_processing(&select_timeout,
+                                          &last_timeout_processing_time);
                        num_smbs = 0; /* Reset smb counter. */
                }
 
@@ -1851,10 +2095,9 @@ void smbd_process(void)
                if ((num_smbs % 200) == 0) {
                        time_t new_check_time = time(NULL);
                        if(new_check_time - last_timeout_processing_time >= (select_timeout/1000)) {
-                               if(!timeout_processing(
-                                          &select_timeout,
-                                          &last_timeout_processing_time))
-                                       return;
+                               timeout_processing(
+                                       &select_timeout,
+                                       &last_timeout_processing_time);
                                num_smbs = 0; /* Reset smb counter. */
                                last_timeout_processing_time = new_check_time; /* Reset time. */
                        }
@@ -1871,5 +2114,6 @@ void smbd_process(void)
                        change_to_root_user();
                        check_log_size();
                }
+               TALLOC_FREE(frame);
        }
 }