s3: Make "init_smb_request" static to process.c
[ira/wip.git] / source3 / smbd / process.c
index 4d415b2d2771631e54df5a7a79a6478e21da2ae8..44d53b23b675e64876a79327a9c6fc96e21b6c23 100644 (file)
 */
 
 #include "includes.h"
+#include "smbd/globals.h"
+#include "../librpc/gen_ndr/srv_dfs.h"
+#include "../librpc/gen_ndr/srv_dssetup.h"
+#include "../librpc/gen_ndr/srv_echo.h"
+#include "../librpc/gen_ndr/srv_eventlog.h"
+#include "../librpc/gen_ndr/srv_initshutdown.h"
+#include "../librpc/gen_ndr/srv_lsa.h"
+#include "../librpc/gen_ndr/srv_netlogon.h"
+#include "../librpc/gen_ndr/srv_ntsvcs.h"
+#include "../librpc/gen_ndr/srv_samr.h"
+#include "../librpc/gen_ndr/srv_spoolss.h"
+#include "../librpc/gen_ndr/srv_srvsvc.h"
+#include "../librpc/gen_ndr/srv_svcctl.h"
+#include "../librpc/gen_ndr/srv_winreg.h"
+#include "../librpc/gen_ndr/srv_wkssvc.h"
 
-/*
- * Size of data we can send to client. Set
- *  by the client for all protocols above CORE.
- *  Set by us for CORE protocol.
- */
-int max_send = BUFFER_SIZE;
-/*
- * Size of the data we can receive. Set by us.
- * Can be modified by the max xmit parameter.
- */
-int max_recv = BUFFER_SIZE;
-
-SIG_ATOMIC_T reload_after_sighup = 0;
-SIG_ATOMIC_T got_sig_term = 0;
 extern bool global_machine_password_needs_changing;
-extern int max_send;
+
+static void construct_reply_common(struct smb_request *req, const char *inbuf,
+                                  char *outbuf);
 
 /* Accessor function for smb_read_error for smbd functions. */
 
@@ -43,15 +46,20 @@ extern int max_send;
  Send an smb to a fd.
 ****************************************************************************/
 
-bool srv_send_smb(int fd, char *buffer, bool do_encrypt)
+bool srv_send_smb(int fd, char *buffer,
+                 bool do_signing, uint32_t seqnum,
+                 bool do_encrypt,
+                 struct smb_perfcount_data *pcd)
 {
-       size_t len;
+       size_t len = 0;
        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_signing) {
+               /* Sign the outgoing packet if required. */
+               srv_calculate_sign_mac(smbd_server_conn, buf_out, seqnum);
+       }
 
        if (do_encrypt) {
                NTSTATUS status = srv_encrypt_buffer(buffer, &buf_out);
@@ -59,24 +67,24 @@ bool srv_send_smb(int fd, char *buffer, bool do_encrypt)
                        DEBUG(0, ("send_smb: SMB encryption failed "
                                "on outgoing packet! Error %s\n",
                                nt_errstr(status) ));
-                       return false;
+                       goto out;
                }
        }
 
        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;
+       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);
+               goto out;
        }
 
+       SMB_PERFCOUNT_SET_MSGLEN_OUT(pcd, len);
        srv_free_enc_buffer(buf_out);
+out:
+       SMB_PERFCOUNT_END(pcd);
        return true;
 }
 
@@ -134,7 +142,7 @@ static NTSTATUS read_packet_remainder(int fd, char *buffer,
                return NT_STATUS_OK;
        }
 
-       return read_socket_with_timeout(fd, buffer, len, len, timeout, NULL);
+       return read_fd_with_timeout(fd, buffer, len, len, timeout, NULL);
 }
 
 /****************************************************************************
@@ -168,7 +176,7 @@ static NTSTATUS receive_smb_raw_talloc_partial_read(TALLOC_CTX *mem_ctx,
 
        memcpy(writeX_header, lenbuf, 4);
 
-       status = read_socket_with_timeout(
+       status = read_fd_with_timeout(
                fd, writeX_header + 4,
                STANDARD_WRITE_AND_X_HEADER_SIZE,
                STANDARD_WRITE_AND_X_HEADER_SIZE,
@@ -279,10 +287,10 @@ static NTSTATUS receive_smb_raw_talloc(TALLOC_CTX *mem_ctx, int fd,
                return status;
        }
 
-       if (CVAL(lenbuf,0) == 0 &&
-                       min_recv_size &&
-                       smb_len_large(lenbuf) > min_recv_size && /* Could be a UNIX large writeX. */
-                       !srv_is_signing_active()) {
+       if (CVAL(lenbuf,0) == 0 && min_recv_size &&
+           (smb_len_large(lenbuf) > /* Could be a UNIX large writeX. */
+               (min_recv_size + STANDARD_WRITE_AND_X_HEADER_SIZE)) &&
+           !srv_is_signing_active(smbd_server_conn)) {
 
                return receive_smb_raw_talloc_partial_read(
                        mem_ctx, lenbuf, fd, buffer, timeout, p_unread, plen);
@@ -318,7 +326,8 @@ static NTSTATUS receive_smb_raw_talloc(TALLOC_CTX *mem_ctx, int fd,
 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 *p_len,
+                                  uint32_t *seqnum)
 {
        size_t len = 0;
        NTSTATUS status;
@@ -343,7 +352,7 @@ static NTSTATUS receive_smb_talloc(TALLOC_CTX *mem_ctx,     int fd,
        }
 
        /* Check the incoming SMB signature. */
-       if (!srv_check_sign_mac(*buffer, true)) {
+       if (!srv_check_sign_mac(smbd_server_conn, *buffer, seqnum)) {
                DEBUG(0, ("receive_smb: SMB Signature verification failed on "
                          "incoming packet!\n"));
                return NT_STATUS_INVALID_NETWORK_RESPONSE;
@@ -357,11 +366,10 @@ static NTSTATUS receive_smb_talloc(TALLOC_CTX *mem_ctx,   int fd,
  * Initialize a struct smb_request from an inbuf
  */
 
-void init_smb_request(struct smb_request *req,
-                       const uint8 *inbuf,
-                       size_t unread_bytes,
-                       bool encrypted)
+static void init_smb_request(struct smb_request *req, const uint8 *inbuf,
+                            size_t unread_bytes, bool encrypted)
 {
+       struct smbd_server_connection *sconn = smbd_server_conn;
        size_t req_size = smb_len(inbuf) + 4;
        /* Ensure we have at least smb_size bytes. */
        if (req_size < smb_size) {
@@ -369,16 +377,24 @@ void init_smb_request(struct smb_request *req,
                        (unsigned int)req_size ));
                exit_server_cleanly("Invalid SMB request");
        }
+       req->cmd    = CVAL(inbuf, smb_com);
        req->flags2 = SVAL(inbuf, smb_flg2);
        req->smbpid = SVAL(inbuf, smb_pid);
        req->mid    = SVAL(inbuf, smb_mid);
+       req->seqnum = 0;
        req->vuid   = SVAL(inbuf, smb_uid);
        req->tid    = SVAL(inbuf, smb_tid);
        req->wct    = CVAL(inbuf, smb_wct);
+       req->vwv    = (uint16_t *)(inbuf+smb_vwv);
+       req->buflen = smb_buflen(inbuf);
+       req->buf    = (const uint8_t *)smb_buf(inbuf);
        req->unread_bytes = unread_bytes;
        req->encrypted = encrypted;
-       req->conn = conn_find(req->tid);
+       req->conn = conn_find(sconn,req->tid);
        req->chain_fsp = NULL;
+       req->chain_outbuf = NULL;
+       req->done = false;
+       smb_init_perfcount_data(&req->pcd);
 
        /* Ensure we have at least wct words and 2 bytes of bcc. */
        if (smb_size + req->wct*2 > req_size) {
@@ -388,24 +404,60 @@ void init_smb_request(struct smb_request *req,
                exit_server_cleanly("Invalid SMB request");
        }
        /* Ensure bcc is correct. */
-       if (((uint8 *)smb_buf(inbuf)) + smb_buflen(inbuf) > inbuf + req_size) {
+       if (((uint8 *)smb_buf(inbuf)) + req->buflen > 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->buflen,
                        (unsigned int)req->wct,
                        (unsigned int)req_size));
                exit_server_cleanly("Invalid SMB request");
        }
-       req->inbuf  = inbuf;
+
        req->outbuf = NULL;
 }
 
-/****************************************************************************
- structure to hold a linked list of queued messages.
- for processing.
-****************************************************************************/
+static void process_smb(struct smbd_server_connection *conn,
+                       uint8_t *inbuf, size_t nread, size_t unread_bytes,
+                       uint32_t seqnum, bool encrypted,
+                       struct smb_perfcount_data *deferred_pcd);
+
+static void smbd_deferred_open_timer(struct event_context *ev,
+                                    struct timed_event *te,
+                                    struct timeval _tval,
+                                    void *private_data)
+{
+       struct pending_message_list *msg = talloc_get_type(private_data,
+                                          struct pending_message_list);
+       TALLOC_CTX *mem_ctx = talloc_tos();
+       uint16_t mid = SVAL(msg->buf.data,smb_mid);
+       uint8_t *inbuf;
+
+       inbuf = (uint8_t *)talloc_memdup(mem_ctx, msg->buf.data,
+                                        msg->buf.length);
+       if (inbuf == NULL) {
+               exit_server("smbd_deferred_open_timer: talloc failed\n");
+               return;
+       }
+
+       /* We leave this message on the queue so the open code can
+          know this is a retry. */
+       DEBUG(5,("smbd_deferred_open_timer: trigger mid %u.\n",
+               (unsigned int)mid ));
 
-static struct pending_message_list *deferred_open_queue;
+       /* Mark the message as processed so this is not
+        * re-processed in error. */
+       msg->processed = true;
+
+       process_smb(smbd_server_conn, inbuf,
+                   msg->buf.length, 0,
+                   msg->seqnum, msg->encrypted, &msg->pcd);
+
+       /* If it's still there and was processed, remove it. */
+       msg = get_open_deferred_message(mid);
+       if (msg && msg->processed) {
+               remove_deferred_open_smb_message(mid);
+       }
+}
 
 /****************************************************************************
  Function to push a message onto the tail of a linked list of smb messages ready
@@ -435,8 +487,10 @@ static bool push_queued_message(struct smb_request *req,
        }
 
        msg->request_time = request_time;
-       msg->end_time = end_time;
+       msg->seqnum = req->seqnum;
        msg->encrypted = req->encrypted;
+       msg->processed = false;
+       SMB_PERFCOUNT_DEFER_OP(&req->pcd, &msg->pcd);
 
        if (private_data) {
                msg->private_data = data_blob_talloc(msg, private_data,
@@ -448,6 +502,17 @@ static bool push_queued_message(struct smb_request *req,
                }
        }
 
+       msg->te = event_add_timed(smbd_event_context(),
+                                 msg,
+                                 end_time,
+                                 smbd_deferred_open_timer,
+                                 msg);
+       if (!msg->te) {
+               DEBUG(0,("push_message: event_add_timed failed\n"));
+               TALLOC_FREE(msg);
+               return false;
+       }
+
        DLIST_ADD_END(deferred_open_queue, msg, struct pending_message_list *);
 
        DEBUG(10,("push_message: pushed message length %u on "
@@ -466,7 +531,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_sharing_violation_open_smb_message: "
+                       DEBUG(10,("remove_deferred_open_smb_message: "
                                  "deleting mid %u len %u\n",
                                  (unsigned int)mid,
                                  (unsigned int)pml->buf.length ));
@@ -489,13 +554,38 @@ 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 ));
+
                if (mid == msg_mid) {
+                       struct timed_event *te;
+
+                       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",
+                                       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;
+
+                       te = event_add_timed(smbd_event_context(),
+                                            pml,
+                                            timeval_zero(),
+                                            smbd_deferred_open_timer,
+                                            pml);
+                       if (!te) {
+                               DEBUG(10,("schedule_deferred_open_smb_message: "
+                                         "event_add_timed() failed, skipping mid %u\n",
+                                         mid ));
+                       }
+
+                       TALLOC_FREE(pml->te);
+                       pml->te = te;
                        DLIST_PROMOTE(deferred_open_queue, pml);
                        return;
                }
@@ -506,7 +596,7 @@ void schedule_deferred_open_smb_message(uint16 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)
@@ -514,7 +604,7 @@ 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;
                }
        }
@@ -577,26 +667,33 @@ struct idle_event {
        void *private_data;
 };
 
-static void idle_event_handler(struct event_context *ctx,
-                              struct timed_event *te,
-                              const struct timeval *now,
-                              void *private_data)
+static void smbd_idle_event_handler(struct event_context *ctx,
+                                   struct timed_event *te,
+                                   struct timeval now,
+                                   void *private_data)
 {
        struct idle_event *event =
                talloc_get_type_abort(private_data, struct idle_event);
 
        TALLOC_FREE(event->te);
 
-       if (!event->handler(now, event->private_data)) {
+       DEBUG(10,("smbd_idle_event_handler: %s %p called\n",
+                 event->name, event->te));
+
+       if (!event->handler(&now, event->private_data)) {
+               DEBUG(10,("smbd_idle_event_handler: %s %p stopped\n",
+                         event->name, event->te));
                /* Don't repeat, delete ourselves */
                TALLOC_FREE(event);
                return;
        }
 
+       DEBUG(10,("smbd_idle_event_handler: %s %p rescheduled\n",
+                 event->name, event->te));
+
        event->te = event_add_timed(ctx, event,
-                                   timeval_sum(now, &event->interval),
-                                   event->name,
-                                   idle_event_handler, event);
+                                   timeval_sum(&now, &event->interval),
+                                   smbd_idle_event_handler, event);
 
        /* We can't do much but fail here. */
        SMB_ASSERT(event->te != NULL);
@@ -631,155 +728,77 @@ struct idle_event *event_add_idle(struct event_context *event_ctx,
 
        result->te = event_add_timed(event_ctx, result,
                                     timeval_sum(&now, &interval),
-                                    result->name,
-                                    idle_event_handler, result);
+                                    smbd_idle_event_handler, result);
        if (result->te == NULL) {
                DEBUG(0, ("event_add_timed failed\n"));
                TALLOC_FREE(result);
                return NULL;
        }
 
+       DEBUG(10,("event_add_idle: %s %p\n", result->name, result->te));
        return result;
 }
 
-/****************************************************************************
- Do all async processing in here. This includes kernel oplock messages, change
- notify events etc.
-****************************************************************************/
-
-static void async_processing(fd_set *pfds)
+static void smbd_sig_term_handler(struct tevent_context *ev,
+                                 struct tevent_signal *se,
+                                 int signum,
+                                 int count,
+                                 void *siginfo,
+                                 void *private_data)
 {
-       DEBUG(10,("async_processing: Doing async processing.\n"));
-
-       process_aio_queue();
-
-       process_kernel_oplocks(smbd_messaging_context(), pfds);
-
-       /* Do the aio check again after receive_local_message as it does a
-          select and may have eaten our signal. */
-       /* Is this till true? -- vl */
-       process_aio_queue();
+       exit_server_cleanly("termination signal");
+}
 
-       if (got_sig_term) {
-               exit_server_cleanly("termination signal");
-       }
+void smbd_setup_sig_term_handler(void)
+{
+       struct tevent_signal *se;
 
-       /* check for sighup processing */
-       if (reload_after_sighup) {
-               change_to_root_user();
-               DEBUG(1,("Reloading services after SIGHUP\n"));
-               reload_services(False);
-               reload_after_sighup = 0;
+       se = tevent_add_signal(smbd_event_context(),
+                              smbd_event_context(),
+                              SIGTERM, 0,
+                              smbd_sig_term_handler,
+                              NULL);
+       if (!se) {
+               exit_server("failed to setup SIGTERM handler");
        }
 }
 
-/****************************************************************************
- Add a fd to the set we will be select(2)ing on.
-****************************************************************************/
-
-static int select_on_fd(int fd, int maxfd, fd_set *fds)
+static void smbd_sig_hup_handler(struct tevent_context *ev,
+                                 struct tevent_signal *se,
+                                 int signum,
+                                 int count,
+                                 void *siginfo,
+                                 void *private_data)
 {
-       if (fd != -1) {
-               FD_SET(fd, fds);
-               maxfd = MAX(maxfd, fd);
-       }
-
-       return maxfd;
+       change_to_root_user();
+       DEBUG(1,("Reloading services after SIGHUP\n"));
+       reload_services(False);
 }
 
-/****************************************************************************
-  Do a select on an two fd's - with timeout. 
-
-  If a local udp message has been pushed onto the
-  queue (this can only happen during oplock break
-  processing) call async_processing()
-
-  If a pending smb message has been pushed onto the
-  queue (this can only happen during oplock break
-  processing) return this next.
-
-  If the first smbfd is ready then read an smb from it.
-  if the second (loopback UDP) fd is ready then read a message
-  from it and setup the buffer header to identify the length
-  and from address.
-  Returns False on timeout or error.
-  Else returns True.
+void smbd_setup_sig_hup_handler(void)
+{
+       struct tevent_signal *se;
 
-The timeout is in milliseconds
-****************************************************************************/
+       se = tevent_add_signal(smbd_event_context(),
+                              smbd_event_context(),
+                              SIGHUP, 0,
+                              smbd_sig_hup_handler,
+                              NULL);
+       if (!se) {
+               exit_server("failed to setup SIGHUP handler");
+       }
+}
 
-static NTSTATUS receive_message_or_smb(TALLOC_CTX *mem_ctx, char **buffer,
-                                      size_t *buffer_len,
-                                      size_t *p_unread, bool *p_encrypted)
+static NTSTATUS smbd_server_connection_loop_once(struct smbd_server_connection *conn)
 {
        fd_set r_fds, w_fds;
        int selrtn;
        struct timeval to;
        int maxfd = 0;
-       size_t len = 0;
-       NTSTATUS status;
-
-       *p_unread = 0;
-
- again:
 
        to.tv_sec = SMBD_SELECT_TIMEOUT;
        to.tv_usec = 0;
 
-       /*
-        * Note that this call must be before processing any SMB
-        * messages as we need to synchronously process any messages
-        * we may have sent to ourselves from the previous SMB.
-        */
-       message_dispatch(smbd_messaging_context());
-
-       /*
-        * Check to see if we already have a message on the deferred open queue
-        * and it's time to schedule.
-        */
-       if(deferred_open_queue != NULL) {
-               bool pop_message = False;
-               struct pending_message_list *msg = deferred_open_queue;
-
-               if (timeval_is_zero(&msg->end_time)) {
-                       pop_message = True;
-               } else {
-                       struct timeval tv;
-                       int64_t tdif;
-
-                       GetTimeOfDay(&tv);
-                       tdif = usec_time_diff(&msg->end_time, &tv);
-                       if (tdif <= 0) {
-                               /* Timed out. Schedule...*/
-                               pop_message = True;
-                               DEBUG(10,("receive_message_or_smb: queued message timed out.\n"));
-                       } else {
-                               /* Make a more accurate select timeout. */
-                               to.tv_sec = tdif / 1000000;
-                               to.tv_usec = tdif % 1000000;
-                               DEBUG(10,("receive_message_or_smb: select with timeout of [%u.%06u]\n",
-                                       (unsigned int)to.tv_sec, (unsigned int)to.tv_usec ));
-                       }
-               }
-
-               if (pop_message) {
-
-                       *buffer = (char *)talloc_memdup(mem_ctx, msg->buf.data,
-                                                       msg->buf.length);
-                       if (*buffer == NULL) {
-                               DEBUG(0, ("talloc failed\n"));
-                               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 NT_STATUS_OK;
-               }
-       }
-
        /*
         * Setup the select fd sets.
         */
@@ -787,26 +806,6 @@ static NTSTATUS receive_message_or_smb(TALLOC_CTX *mem_ctx, char **buffer,
        FD_ZERO(&r_fds);
        FD_ZERO(&w_fds);
 
-       /*
-        * Ensure we process oplock break messages by preference.
-        * We have to do this before the select, after the select
-        * and if the select returns EINTR. This is due to the fact
-        * that the selects called from async_processing can eat an EINTR
-        * caused by a signal (we can't take the break message there).
-        * This is hideously complex - *MUST* be simplified for 3.0 ! JRA.
-        */
-
-       if (oplock_message_waiting(&r_fds)) {
-               DEBUG(10,("receive_message_or_smb: oplock_message is waiting.\n"));
-               async_processing(&r_fds);
-               /*
-                * After async processing we must go and do the select again, as
-                * the state of the flag in fds for the server file descriptor is
-                * indeterminate - we may have done I/O on it in the oplock processing. JRA.
-                */
-               goto again;
-       }
-
        /*
         * Are there any timed events waiting ? If so, ensure we don't
         * select for longer than it would take to wait for them.
@@ -820,20 +819,15 @@ static NTSTATUS receive_message_or_smb(TALLOC_CTX *mem_ctx, char **buffer,
                                         &r_fds, &w_fds, &to, &maxfd);
        }
 
-       if (timeval_is_zero(&to)) {
-               /* Process a timed event now... */
-               if (run_events(smbd_event_context(), 0, NULL, NULL)) {
-                       goto again;
-               }
+       /* Process a signal and timed events now... */
+       if (run_events(smbd_event_context(), 0, NULL, NULL)) {
+               return NT_STATUS_RETRY;
        }
-       
+
        {
                int sav;
                START_PROFILE(smbd_idle);
 
-               maxfd = select_on_fd(smbd_server_fd(), maxfd, &r_fds);
-               maxfd = select_on_fd(oplock_notify_fd(), maxfd, &r_fds);
-
                selrtn = sys_select(maxfd+1,&r_fds,&w_fds,NULL,&to);
                sav = errno;
 
@@ -842,21 +836,7 @@ static NTSTATUS receive_message_or_smb(TALLOC_CTX *mem_ctx, char **buffer,
        }
 
        if (run_events(smbd_event_context(), selrtn, &r_fds, &w_fds)) {
-               goto again;
-       }
-
-       /* if we get EINTR then maybe we have received an oplock
-          signal - treat this as select returning 1. This is ugly, but
-          is the best we can do until the oplock code knows more about
-          signals */
-       if (selrtn == -1 && errno == EINTR) {
-               async_processing(&r_fds);
-               /*
-                * After async processing we must go and do the select again, as
-                * the state of the flag in fds for the server file descriptor is
-                * indeterminate - we may have done I/O on it in the oplock processing. JRA.
-                */
-               goto again;
+               return NT_STATUS_RETRY;
        }
 
        /* Check if error */
@@ -867,44 +847,11 @@ static NTSTATUS receive_message_or_smb(TALLOC_CTX *mem_ctx, char **buffer,
 
        /* Did we timeout ? */
        if (selrtn == 0) {
-               goto again;
-       }
-
-       /*
-        * Ensure we process oplock break messages by preference.
-        * This is IMPORTANT ! Otherwise we can starve other processes
-        * sending us an oplock break message. JRA.
-        */
-
-       if (oplock_message_waiting(&r_fds)) {
-               async_processing(&r_fds);
-               /*
-                * After async processing we must go and do the select again, as
-                * the state of the flag in fds for the server file descriptor is
-                * indeterminate - we may have done I/O on it in the oplock processing. JRA.
-                */
-               goto again;
-       }
-
-       /*
-        * 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());
-
-       status = receive_smb_talloc(mem_ctx, smbd_server_fd(), buffer, 0,
-                                   p_unread, p_encrypted, &len);
-
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
+               return NT_STATUS_RETRY;
        }
 
-       *buffer_len = len;
-
-       return NT_STATUS_OK;
+       /* should not be reached */
+       return NT_STATUS_INTERNAL_ERROR;
 }
 
 /*
@@ -930,31 +877,6 @@ NTSTATUS allow_new_trans(struct trans_state *list, int mid)
        return NT_STATUS_OK;
 }
 
-/****************************************************************************
- We're terminating and have closed all our files/connections etc.
- If there are any pending local messages we need to respond to them
- before termination so that other smbds don't think we just died whilst
- holding oplocks.
-****************************************************************************/
-
-void respond_to_all_remaining_local_messages(void)
-{
-       /*
-        * Assert we have no exclusive open oplocks.
-        */
-
-       if(get_number_of_exclusive_open_oplocks()) {
-               DEBUG(0,("respond_to_all_remaining_local_messages: PANIC : we have %d exclusive oplocks.\n",
-                       get_number_of_exclusive_open_oplocks() ));
-               return;
-       }
-
-       process_kernel_oplocks(smbd_messaging_context(), NULL);
-
-       return;
-}
-
-
 /*
 These flags determine some of the permissions required to do an operation 
 
@@ -1031,7 +953,7 @@ static const struct smb_message_struct {
 /* 0x30 */ { NULL, NULL, 0 },
 /* 0x31 */ { NULL, NULL, 0 },
 /* 0x32 */ { "SMBtrans2",reply_trans2, AS_USER | CAN_IPC },
-/* 0x33 */ { "SMBtranss2",reply_transs2, AS_USER},
+/* 0x33 */ { "SMBtranss2",reply_transs2, AS_USER | CAN_IPC },
 /* 0x34 */ { "SMBfindclose",reply_findclose,AS_USER},
 /* 0x35 */ { "SMBfindnclose",reply_findnclose,AS_USER},
 /* 0x36 */ { NULL, NULL, 0 },
@@ -1243,8 +1165,9 @@ static const struct smb_message_struct {
  allocate and initialize a reply packet
 ********************************************************************/
 
-bool create_outbuf(TALLOC_CTX *mem_ctx, const char *inbuf, char **outbuf,
-                  uint8_t num_words, uint32_t num_bytes)
+static bool create_outbuf(TALLOC_CTX *mem_ctx, struct smb_request *req,
+                         const char *inbuf, char **outbuf, uint8_t num_words,
+                         uint32_t num_bytes)
 {
        /*
          * Protect against integer wrap
@@ -1265,7 +1188,7 @@ bool create_outbuf(TALLOC_CTX *mem_ctx, const char *inbuf, char **outbuf,
                return false;
        }
 
-       construct_reply_common(inbuf, *outbuf);
+       construct_reply_common(req, inbuf, *outbuf);
        srv_set_message(*outbuf, num_words, num_bytes, false);
        /*
         * Zero out the word area, the caller has to take care of the bcc area
@@ -1281,7 +1204,7 @@ bool create_outbuf(TALLOC_CTX *mem_ctx, const char *inbuf, char **outbuf,
 void reply_outbuf(struct smb_request *req, uint8 num_words, uint32 num_bytes)
 {
        char *outbuf;
-       if (!create_outbuf(req, (char *)req->inbuf, &outbuf, num_words,
+       if (!create_outbuf(req, req, (char *)req->inbuf, &outbuf, num_words,
                           num_bytes)) {
                smb_panic("could not allocate output buffer\n");
        }
@@ -1338,8 +1261,7 @@ static connection_struct *switch_message(uint8 type, struct smb_request *req, in
        int flags;
        uint16 session_tag;
        connection_struct *conn = NULL;
-
-       static uint16 last_session_tag = UID_FIELD_INVALID;
+       struct smbd_server_connection *sconn = smbd_server_conn;
 
        errno = 0;
 
@@ -1383,18 +1305,16 @@ static connection_struct *switch_message(uint8 type, struct smb_request *req, in
         * JRA.
         */
 
-       if (session_tag != last_session_tag) {
+       if (session_tag != sconn->smb1.sessions.last_session_tag) {
                user_struct *vuser = NULL;
 
-               last_session_tag = session_tag;
+               sconn->smb1.sessions.last_session_tag = session_tag;
                if(session_tag != UID_FIELD_INVALID) {
-                       vuser = get_valid_user_struct(session_tag);
+                       vuser = get_valid_user_struct(sconn, session_tag);
                        if (vuser) {
                                set_current_user_info(
                                        vuser->server_info->sanitized_username,
                                        vuser->server_info->unix_name,
-                                       pdb_get_fullname(vuser->server_info
-                                                        ->sam_account),
                                        pdb_get_domain(vuser->server_info
                                                       ->sam_account));
                        }
@@ -1413,13 +1333,15 @@ static connection_struct *switch_message(uint8 type, struct smb_request *req, in
                        if (type == SMBntcreateX) {
                                reply_nterror(req, NT_STATUS_INVALID_HANDLE);
                        } else {
-                               reply_doserror(req, ERRSRV, ERRinvnid);
+                               reply_nterror(req, NT_STATUS_NETWORK_NAME_DELETED);
                        }
                        return NULL;
                }
 
                if (!change_to_user(conn,session_tag)) {
-                       reply_nterror(req, NT_STATUS_DOS(ERRSRV, ERRbaduid));
+                       DEBUG(0, ("Error: Could not change to user. Removing "
+                           "deferred open, mid=%d.\n", req->mid));
+                       reply_force_doserror(req, ERRSRV, ERRbaduid);
                        return conn;
                }
 
@@ -1433,7 +1355,7 @@ static connection_struct *switch_message(uint8 type, struct smb_request *req, in
 
                /* IPC services are limited */
                if (IS_IPC(conn) && !(flags & CAN_IPC)) {
-                       reply_doserror(req, ERRSRV,ERRaccess);
+                       reply_nterror(req, NT_STATUS_ACCESS_DENIED);
                        return conn;
                }
        } else {
@@ -1448,8 +1370,7 @@ static connection_struct *switch_message(uint8 type, struct smb_request *req, in
                        /* 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) {
+                       if (req->cmd != SMBtrans2 && req->cmd != SMBtranss2) {
                                exit_server_cleanly("encryption required "
                                        "on connection");
                                return conn;
@@ -1459,7 +1380,7 @@ static connection_struct *switch_message(uint8 type, struct smb_request *req, in
                if (!set_current_service(conn,SVAL(req->inbuf,smb_flg),
                                         (flags & (AS_USER|DO_CHDIR)
                                          ?True:False))) {
-                       reply_doserror(req, ERRSRV, ERRaccess);
+                       reply_nterror(req, NT_STATUS_ACCESS_DENIED);
                        return conn;
                }
                conn->num_smb_operations++;
@@ -1470,7 +1391,7 @@ static connection_struct *switch_message(uint8 type, struct smb_request *req, in
            && (!change_to_guest() ||
                !check_access(smbd_server_fd(), lp_hostsallow(-1),
                              lp_hostsdeny(-1)))) {
-               reply_doserror(req, ERRSRV, ERRaccess);
+               reply_nterror(req, NT_STATUS_ACCESS_DENIED);
                return conn;
        }
 
@@ -1482,20 +1403,31 @@ static connection_struct *switch_message(uint8 type, struct smb_request *req, in
  Construct a reply to the incoming packet.
 ****************************************************************************/
 
-static void construct_reply(char *inbuf, int size, size_t unread_bytes, bool encrypted)
+static void construct_reply(char *inbuf, int size, size_t unread_bytes,
+                           uint32_t seqnum, bool encrypted,
+                           struct smb_perfcount_data *deferred_pcd)
 {
-       uint8 type = CVAL(inbuf,smb_com);
        connection_struct *conn;
        struct smb_request *req;
 
-       chain_size = 0;
-
        if (!(req = talloc(talloc_tos(), struct smb_request))) {
                smb_panic("could not allocate smb_request");
        }
+
        init_smb_request(req, (uint8 *)inbuf, unread_bytes, encrypted);
+       req->inbuf  = (uint8_t *)talloc_move(req, &inbuf);
+       req->seqnum = seqnum;
+
+       /* we popped this message off the queue - keep original perf data */
+       if (deferred_pcd)
+               req->pcd = *deferred_pcd;
+       else {
+               SMB_PERFCOUNT_START(&req->pcd);
+               SMB_PERFCOUNT_SET_OP(&req->pcd, req->cmd);
+               SMB_PERFCOUNT_SET_MSGLEN_IN(&req->pcd, size);
+       }
 
-       conn = switch_message(type, req, size);
+       conn = switch_message(req->cmd, req, size);
 
        if (req->unread_bytes) {
                /* writeX failed. drain socket. */
@@ -1506,6 +1438,11 @@ static void construct_reply(char *inbuf, int size, size_t unread_bytes, bool enc
                req->unread_bytes = 0;
        }
 
+       if (req->done) {
+               TALLOC_FREE(req);
+               return;
+       }
+
        if (req->outbuf == NULL) {
                return;
        }
@@ -1516,7 +1453,9 @@ static void construct_reply(char *inbuf, int size, size_t unread_bytes, bool enc
 
        if (!srv_send_smb(smbd_server_fd(),
                        (char *)req->outbuf,
-                       IS_CONN_ENCRYPTED(conn)||req->encrypted)) {
+                       true, req->seqnum+1,
+                       IS_CONN_ENCRYPTED(conn)||req->encrypted,
+                       &req->pcd)) {
                exit_server_cleanly("construct_reply: srv_send_smb failed.");
        }
 
@@ -1528,10 +1467,11 @@ static void construct_reply(char *inbuf, int size, size_t unread_bytes, bool enc
 /****************************************************************************
  Process an smb from the client
 ****************************************************************************/
-
-static void process_smb(char *inbuf, size_t nread, size_t unread_bytes, bool encrypted)
+static void process_smb(struct smbd_server_connection *conn,
+                       uint8_t *inbuf, size_t nread, size_t unread_bytes,
+                       uint32_t seqnum, bool encrypted,
+                       struct smb_perfcount_data *deferred_pcd)
 {
-       static int trans_num;
        int msg_type = CVAL(inbuf,0);
 
        DO_PROFILE_INC(smb_count);
@@ -1546,15 +1486,38 @@ static void process_smb(char *inbuf, size_t nread, size_t unread_bytes, bool enc
                /*
                 * NetBIOS session request, keepalive, etc.
                 */
-               reply_special(inbuf);
-               return;
+               reply_special((char *)inbuf);
+               goto done;
        }
 
-       show_msg(inbuf);
+       if (smbd_server_conn->allow_smb2) {
+               if (smbd_is_smb2_header(inbuf, nread)) {
+                       smbd_smb2_first_negprot(smbd_server_conn, inbuf, nread);
+                       return;
+               }
+               smbd_server_conn->allow_smb2 = false;
+       }
 
-       construct_reply(inbuf,nread,unread_bytes,encrypted);
+       show_msg((char *)inbuf);
 
+       construct_reply((char *)inbuf,nread,unread_bytes,seqnum,encrypted,deferred_pcd);
        trans_num++;
+
+done:
+       conn->smb1.num_requests++;
+
+       /* The timeout_processing function isn't run nearly
+          often enough to implement 'max log size' without
+          overrunning the size of the file by many megabytes.
+          This is especially true if we are running at debug
+          level 10.  Checking every 50 SMBs is a nice
+          tradeoff of performance vs log file size overrun. */
+
+       if ((conn->smb1.num_requests % 50) == 0 &&
+           need_to_check_log_size()) {
+               change_to_root_user();
+               check_log_size();
+       }
 }
 
 /****************************************************************************
@@ -1575,8 +1538,6 @@ const char *smb_fn_name(int type)
  Helper functions for contruct_reply.
 ****************************************************************************/
 
-static uint32 common_flags2 = FLAGS2_LONG_PATH_COMPONENTS|FLAGS2_32_BIT_ERROR_CODES;
-
 void add_to_common_flags2(uint32 v)
 {
        common_flags2 |= v;
@@ -1587,11 +1548,12 @@ void remove_from_common_flags2(uint32 v)
        common_flags2 &= ~v;
 }
 
-void construct_reply_common(const char *inbuf, char *outbuf)
+static void construct_reply_common(struct smb_request *req, const char *inbuf,
+                                  char *outbuf)
 {
        srv_set_message(outbuf,0,0,false);
        
-       SCVAL(outbuf,smb_com,CVAL(inbuf,smb_com));
+       SCVAL(outbuf, smb_com, req->cmd);
        SIVAL(outbuf,smb_rcls,0);
        SCVAL(outbuf,smb_flg, FLAG_REPLY | (CVAL(inbuf,smb_flg) & FLAG_CASELESS_PATHNAMES)); 
        SSVAL(outbuf,smb_flg2,
@@ -1605,213 +1567,462 @@ void construct_reply_common(const char *inbuf, char *outbuf)
        SSVAL(outbuf,smb_mid,SVAL(inbuf,smb_mid));
 }
 
-/****************************************************************************
- Construct a chained reply and add it to the already made reply
-****************************************************************************/
+void construct_reply_common_req(struct smb_request *req, char *outbuf)
+{
+       construct_reply_common(req, (char *)req->inbuf, outbuf);
+}
 
-void chain_reply(struct smb_request *req)
+/*
+ * How many bytes have we already accumulated up to the current wct field
+ * offset?
+ */
+
+size_t req_wct_ofs(struct smb_request *req)
 {
-       static char *orig_inbuf;
+       size_t buf_size;
+
+       if (req->chain_outbuf == NULL) {
+               return smb_wct - 4;
+       }
+       buf_size = talloc_get_size(req->chain_outbuf);
+       if ((buf_size % 4) != 0) {
+               buf_size += (4 - (buf_size % 4));
+       }
+       return buf_size - 4;
+}
+
+/*
+ * Hack around reply_nterror & friends not being aware of chained requests,
+ * generating illegal (i.e. wct==0) chain replies.
+ */
+
+static void fixup_chain_error_packet(struct smb_request *req)
+{
+       uint8_t *outbuf = req->outbuf;
+       req->outbuf = NULL;
+       reply_outbuf(req, 2, 0);
+       memcpy(req->outbuf, outbuf, smb_wct);
+       TALLOC_FREE(outbuf);
+       SCVAL(req->outbuf, smb_vwv0, 0xff);
+}
+
+/**
+ * @brief Find the smb_cmd offset of the last command pushed
+ * @param[in] buf      The buffer we're building up
+ * @retval             Where can we put our next andx cmd?
+ *
+ * While chaining requests, the "next" request we're looking at needs to put
+ * its SMB_Command before the data the previous request already built up added
+ * to the chain. Find the offset to the place where we have to put our cmd.
+ */
+
+static bool find_andx_cmd_ofs(uint8_t *buf, size_t *pofs)
+{
+       uint8_t cmd;
+       size_t ofs;
+
+       cmd = CVAL(buf, smb_com);
+
+       SMB_ASSERT(is_andx_req(cmd));
+
+       ofs = smb_vwv0;
+
+       while (CVAL(buf, ofs) != 0xff) {
+
+               if (!is_andx_req(CVAL(buf, ofs))) {
+                       return false;
+               }
+
+               /*
+                * ofs is from start of smb header, so add the 4 length
+                * bytes. The next cmd is right after the wct field.
+                */
+               ofs = SVAL(buf, ofs+2) + 4 + 1;
+
+               SMB_ASSERT(ofs+4 < talloc_get_size(buf));
+       }
+
+       *pofs = ofs;
+       return true;
+}
+
+/**
+ * @brief Do the smb chaining at a buffer level
+ * @param[in] poutbuf          Pointer to the talloc'ed buffer to be modified
+ * @param[in] smb_command      The command that we want to issue
+ * @param[in] wct              How many words?
+ * @param[in] vwv              The words, already in network order
+ * @param[in] bytes_alignment  How shall we align "bytes"?
+ * @param[in] num_bytes                How many bytes?
+ * @param[in] bytes            The data the request ships
+ *
+ * smb_splice_chain() adds the vwv and bytes to the request already present in
+ * *poutbuf.
+ */
+
+static bool smb_splice_chain(uint8_t **poutbuf, uint8_t smb_command,
+                            uint8_t wct, const uint16_t *vwv,
+                            size_t bytes_alignment,
+                            uint32_t num_bytes, const uint8_t *bytes)
+{
+       uint8_t *outbuf;
+       size_t old_size, new_size;
+       size_t ofs;
+       size_t chain_padding = 0;
+       size_t bytes_padding = 0;
+       bool first_request;
+
+       old_size = talloc_get_size(*poutbuf);
 
        /*
-        * 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.
+        * old_size == smb_wct means we're pushing the first request in for
+        * libsmb/
         */
-       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;
-       int outsize2;
-       int new_size;
-       char inbuf_saved[smb_wct];
-       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 *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;
-       }
 
-       if (chain_size == 0) {
-               /* this is the first part of the chain */
-               orig_inbuf = inbuf;
+       first_request = (old_size == smb_wct);
+
+       if (!first_request && ((old_size % 4) != 0)) {
+               /*
+                * Align the wct field of subsequent requests to a 4-byte
+                * boundary
+                */
+               chain_padding = 4 - (old_size % 4);
        }
 
        /*
-        * We need to save the output the caller added to the chain so that we
-        * can splice it into the final output buffer later.
+        * After the old request comes the new wct field (1 byte), the vwv's
+        * and the num_bytes field. After at we might need to align the bytes
+        * given to us to "bytes_alignment", increasing the num_bytes value.
         */
 
-       caller_outputlen = outsize - smb_wct;
+       new_size = old_size + chain_padding + 1 + wct * sizeof(uint16_t) + 2;
+
+       if ((bytes_alignment != 0) && ((new_size % bytes_alignment) != 0)) {
+               bytes_padding = bytes_alignment - (new_size % bytes_alignment);
+       }
+
+       new_size += bytes_padding + num_bytes;
+
+       if ((smb_command != SMBwriteX) && (new_size > 0xffff)) {
+               DEBUG(1, ("splice_chain: %u bytes won't fit\n",
+                         (unsigned)new_size));
+               return false;
+       }
+
+       outbuf = TALLOC_REALLOC_ARRAY(NULL, *poutbuf, uint8_t, new_size);
+       if (outbuf == NULL) {
+               DEBUG(0, ("talloc failed\n"));
+               return false;
+       }
+       *poutbuf = outbuf;
+
+       if (first_request) {
+               SCVAL(outbuf, smb_com, smb_command);
+       } else {
+               size_t andx_cmd_ofs;
+
+               if (!find_andx_cmd_ofs(outbuf, &andx_cmd_ofs)) {
+                       DEBUG(1, ("invalid command chain\n"));
+                       *poutbuf = TALLOC_REALLOC_ARRAY(
+                               NULL, *poutbuf, uint8_t, old_size);
+                       return false;
+               }
 
-       caller_output = (char *)memdup(outbuf + smb_wct, caller_outputlen);
+               if (chain_padding != 0) {
+                       memset(outbuf + old_size, 0, chain_padding);
+                       old_size += chain_padding;
+               }
 
-       if (caller_output == NULL) {
-               /* TODO: NT_STATUS_NO_MEMORY */
-               smb_panic("could not dup outbuf");
+               SCVAL(outbuf, andx_cmd_ofs, smb_command);
+               SSVAL(outbuf, andx_cmd_ofs + 2, old_size - 4);
        }
 
+       ofs = old_size;
+
        /*
-        * The original Win95 redirector dies on a reply to
-        * a lockingX and read chain unless the chain reply is
-        * 4 byte aligned. JRA.
+        * Push the chained request:
+        *
+        * wct field
         */
 
-       outsize_padded = (outsize + 3) & ~3;
-       padding = outsize_padded - outsize;
+       SCVAL(outbuf, ofs, wct);
+       ofs += 1;
 
        /*
-        * remember how much the caller added to the chain, only counting
-        * stuff after the parameter words
+        * vwv array
         */
-       chain_size += (outsize_padded - smb_wct);
+
+       memcpy(outbuf + ofs, vwv, sizeof(uint16_t) * wct);
+       ofs += sizeof(uint16_t) * wct;
 
        /*
-        * work out pointers into the original packets. The
-        * headers on these need to be filled in
+        * bcc (byte count)
         */
-       inbuf2 = orig_inbuf + smb_off2 + 4 - smb_wct;
 
-       /* remember the original command type */
-       smb_com1 = CVAL(orig_inbuf,smb_com);
+       SSVAL(outbuf, ofs, num_bytes + bytes_padding);
+       ofs += sizeof(uint16_t);
 
-       /* save the data which will be overwritten by the new headers */
-       memcpy(inbuf_saved,inbuf2,smb_wct);
+       /*
+        * padding
+        */
 
-       /* give the new packet the same header as the last part of the SMB */
-       memmove(inbuf2,inbuf,smb_wct);
+       if (bytes_padding != 0) {
+               memset(outbuf + ofs, 0, bytes_padding);
+               ofs += bytes_padding;
+       }
 
-       /* create the in buffer */
-       SCVAL(inbuf2,smb_com,smb_com2);
+       /*
+        * The bytes field
+        */
 
-       /* work out the new size for the in buffer. */
-       new_size = size - (inbuf2 - inbuf);
-       if (new_size < 0) {
-               DEBUG(0,("chain_reply: chain packet size incorrect "
-                        "(orig size = %d, offset = %d)\n",
-                        size, (int)(inbuf2 - inbuf) ));
-               exit_server_cleanly("Bad chained packet");
-               return;
-       }
+       memcpy(outbuf + ofs, bytes, num_bytes);
 
-       /* And set it in the header. */
-       smb_setlen(inbuf2, new_size - 4);
+       return true;
+}
 
-       DEBUG(3,("Chained message\n"));
-       show_msg(inbuf2);
+/****************************************************************************
+ Construct a chained reply and add it to the already made reply
+****************************************************************************/
 
-       if (!(req2 = talloc(talloc_tos(), struct smb_request))) {
-               smb_panic("could not allocate smb_request");
-       }
-       init_smb_request(req2, (uint8 *)inbuf2,0, req->encrypted);
-       req2->chain_fsp = req->chain_fsp;
+void chain_reply(struct smb_request *req)
+{
+       size_t smblen = smb_len(req->inbuf);
+       size_t already_used, length_needed;
+       uint8_t chain_cmd;
+       uint32_t chain_offset;  /* uint32_t to avoid overflow */
 
-       /* process the request */
-       switch_message(smb_com2, req2, new_size);
+       uint8_t wct;
+       uint16_t *vwv;
+       uint16_t buflen;
+       uint8_t *buf;
+
+       if (IVAL(req->outbuf, smb_rcls) != 0) {
+               fixup_chain_error_packet(req);
+       }
 
        /*
-        * We don't accept deferred operations in chained requests.
+        * Any of the AndX requests and replies have at least a wct of
+        * 2. vwv[0] is the next command, vwv[1] is the offset from the
+        * beginning of the SMB header to the next wct field.
+        *
+        * None of the AndX requests put anything valuable in vwv[0] and [1],
+        * so we can overwrite it here to form the chain.
         */
-       SMB_ASSERT(req2->outbuf != NULL);
-       outsize2 = smb_len(req2->outbuf)+4;
+
+       if ((req->wct < 2) || (CVAL(req->outbuf, smb_wct) < 2)) {
+               goto error;
+       }
 
        /*
-        * Move away the new command output so that caller_output fits in,
-        * copy in the caller_output saved above.
+        * Here we assume that this is the end of the chain. For that we need
+        * to set "next command" to 0xff and the offset to 0. If we later find
+        * more commands in the chain, this will be overwritten again.
         */
 
-       SMB_ASSERT(outsize_padded >= smb_wct);
+       SCVAL(req->outbuf, smb_vwv0, 0xff);
+       SCVAL(req->outbuf, smb_vwv0+1, 0);
+       SSVAL(req->outbuf, smb_vwv1, 0);
+
+       if (req->chain_outbuf == NULL) {
+               /*
+                * In req->chain_outbuf we collect all the replies. Start the
+                * chain by copying in the first reply.
+                *
+                * We do the realloc because later on we depend on
+                * talloc_get_size to determine the length of
+                * chain_outbuf. The reply_xxx routines might have
+                * over-allocated (reply_pipe_read_and_X used to be such an
+                * example).
+                */
+               req->chain_outbuf = TALLOC_REALLOC_ARRAY(
+                       req, req->outbuf, uint8_t, smb_len(req->outbuf) + 4);
+               if (req->chain_outbuf == NULL) {
+                       goto error;
+               }
+               req->outbuf = NULL;
+       } else {
+               /*
+                * Update smb headers where subsequent chained commands
+                * may have updated them.
+                */
+               SCVAL(req->chain_outbuf, smb_tid, CVAL(req->outbuf, smb_tid));
+               SCVAL(req->chain_outbuf, smb_uid, CVAL(req->outbuf, smb_uid));
+
+               if (!smb_splice_chain(&req->chain_outbuf,
+                                     CVAL(req->outbuf, smb_com),
+                                     CVAL(req->outbuf, smb_wct),
+                                     (uint16_t *)(req->outbuf + smb_vwv),
+                                     0, smb_buflen(req->outbuf),
+                                     (uint8_t *)smb_buf(req->outbuf))) {
+                       goto error;
+               }
+               TALLOC_FREE(req->outbuf);
+       }
 
        /*
-        * "ofs" is the space we need for caller_output. Equal to
-        * caller_outputlen plus the padding.
+        * We use the old request's vwv field to grab the next chained command
+        * and offset into the chained fields.
         */
 
-       ofs = outsize_padded - smb_wct;
+       chain_cmd = CVAL(req->vwv+0, 0);
+       chain_offset = SVAL(req->vwv+1, 0);
+
+       if (chain_cmd == 0xff) {
+               /*
+                * End of chain, no more requests from the client. So ship the
+                * replies.
+                */
+               smb_setlen((char *)(req->chain_outbuf),
+                          talloc_get_size(req->chain_outbuf) - 4);
+
+               if (!srv_send_smb(smbd_server_fd(), (char *)req->chain_outbuf,
+                                 true, req->seqnum+1,
+                                 IS_CONN_ENCRYPTED(req->conn)
+                                 ||req->encrypted,
+                                 &req->pcd)) {
+                       exit_server_cleanly("chain_reply: srv_send_smb "
+                                           "failed.");
+               }
+               TALLOC_FREE(req->chain_outbuf);
+               req->done = true;
+               return;
+       }
+
+       /* add a new perfcounter for this element of chain */
+       SMB_PERFCOUNT_ADD(&req->pcd);
+       SMB_PERFCOUNT_SET_OP(&req->pcd, chain_cmd);
+       SMB_PERFCOUNT_SET_MSGLEN_IN(&req->pcd, smblen);
 
        /*
-        * "to_move" is the amount of bytes the secondary routine gave us
+        * Check if the client tries to fool us. The request so far uses the
+        * space to the end of the byte buffer in the request just
+        * processed. The chain_offset can't point into that area. If that was
+        * the case, we could end up with an endless processing of the chain,
+        * we would always handle the same request.
         */
 
-       to_move = outsize2 - smb_wct;
-
-       if (to_move + ofs + smb_wct + chain_size > max_send) {
-               smb_panic("replies too large -- would have to cut");
+       already_used = PTR_DIFF(req->buf+req->buflen, smb_base(req->inbuf));
+       if (chain_offset < already_used) {
+               goto error;
        }
 
        /*
-        * In the "new" API "outbuf" is allocated via reply_outbuf, just for
-        * the first request in the chain. So we have to re-allocate it. In
-        * the "old" API the only outbuf ever used is the global OutBuffer
-        * which is always large enough.
+        * Next check: Make sure the chain offset does not point beyond the
+        * overall smb request length.
         */
 
-       outbuf = TALLOC_REALLOC_ARRAY(NULL, outbuf, char,
-                                     to_move + ofs + smb_wct);
-       if (outbuf == NULL) {
-               smb_panic("could not realloc outbuf");
+       length_needed = chain_offset+1; /* wct */
+       if (length_needed > smblen) {
+               goto error;
        }
 
-       req->outbuf = (uint8 *)outbuf;
+       /*
+        * Now comes the pointer magic. Goal here is to set up req->vwv and
+        * req->buf correctly again to be able to call the subsequent
+        * switch_message(). The chain offset (the former vwv[1]) points at
+        * the new wct field.
+        */
+
+       wct = CVAL(smb_base(req->inbuf), chain_offset);
+
+       /*
+        * Next consistency check: Make the new vwv array fits in the overall
+        * smb request.
+        */
 
-       memmove(outbuf + smb_wct + ofs, req2->outbuf + smb_wct, to_move);
-       memcpy(outbuf + smb_wct, caller_output, caller_outputlen);
+       length_needed += (wct+1)*sizeof(uint16_t); /* vwv+buflen */
+       if (length_needed > smblen) {
+               goto error;
+       }
+       vwv = (uint16_t *)(smb_base(req->inbuf) + chain_offset + 1);
 
        /*
-        * copy the new reply header over the old one but preserve the smb_com
-        * field
+        * Now grab the new byte buffer....
         */
-       memmove(outbuf, req2->outbuf, smb_wct);
-       SCVAL(outbuf, smb_com, smb_com1);
+
+       buflen = SVAL(vwv+wct, 0);
 
        /*
-        * We've just copied in the whole "wct" area from the secondary
-        * function. Fix up the chaining: com2 and the offset need to be
-        * readjusted.
+        * .. and check that it fits.
         */
 
-       SCVAL(outbuf, smb_vwv0, smb_com2);
-       SSVAL(outbuf, smb_vwv1, chain_size + smb_wct - 4);
+       length_needed += buflen;
+       if (length_needed > smblen) {
+               goto error;
+       }
+       buf = (uint8_t *)(vwv+wct+1);
 
-       if (padding != 0) {
+       req->cmd = chain_cmd;
+       req->wct = wct;
+       req->vwv = vwv;
+       req->buflen = buflen;
+       req->buf = buf;
 
+       switch_message(chain_cmd, req, smblen);
+
+       if (req->outbuf == NULL) {
                /*
-                * Due to padding we have some uninitialized bytes after the
-                * caller's output
+                * This happens if the chained command has suspended itself or
+                * if it has called srv_send_smb() itself.
                 */
-
-               memset(outbuf + outsize, 0, padding);
+               return;
        }
 
-       smb_setlen(outbuf, outsize2 + caller_outputlen + padding - 4);
+       /*
+        * We end up here if the chained command was not itself chained or
+        * suspended, but for example a close() command. We now need to splice
+        * the chained commands' outbuf into the already built up chain_outbuf
+        * and ship the result.
+        */
+       goto done;
 
+ error:
        /*
-        * restore the saved data, being careful not to overwrite any data
-        * from the reply header
+        * We end up here if there's any error in the chain syntax. Report a
+        * DOS error, just like Windows does.
         */
-       memcpy(inbuf2,inbuf_saved,smb_wct);
+       reply_force_doserror(req, ERRSRV, ERRerror);
+       fixup_chain_error_packet(req);
 
-       SAFE_FREE(caller_output);
-       TALLOC_FREE(req2);
+ done:
+       /*
+        * This scary statement intends to set the
+        * FLAGS2_32_BIT_ERROR_CODES flg2 field in req->chain_outbuf
+        * to the value req->outbuf carries
+        */
+       SSVAL(req->chain_outbuf, smb_flg2,
+             (SVAL(req->chain_outbuf, smb_flg2) & ~FLAGS2_32_BIT_ERROR_CODES)
+             | (SVAL(req->outbuf, smb_flg2) & FLAGS2_32_BIT_ERROR_CODES));
 
        /*
-        * Reset the chain_size for our caller's offset calculations
+        * Transfer the error codes from the subrequest to the main one
         */
+       SSVAL(req->chain_outbuf, smb_rcls, SVAL(req->outbuf, smb_rcls));
+       SSVAL(req->chain_outbuf, smb_err, SVAL(req->outbuf, smb_err));
 
-       chain_size -= (outsize_padded - smb_wct);
+       if (!smb_splice_chain(&req->chain_outbuf,
+                             CVAL(req->outbuf, smb_com),
+                             CVAL(req->outbuf, smb_wct),
+                             (uint16_t *)(req->outbuf + smb_vwv),
+                             0, smb_buflen(req->outbuf),
+                             (uint8_t *)smb_buf(req->outbuf))) {
+               exit_server_cleanly("chain_reply: smb_splice_chain failed\n");
+       }
+       TALLOC_FREE(req->outbuf);
 
-       return;
+       smb_setlen((char *)(req->chain_outbuf),
+                  talloc_get_size(req->chain_outbuf) - 4);
+
+       show_msg((char *)(req->chain_outbuf));
+
+       if (!srv_send_smb(smbd_server_fd(), (char *)req->chain_outbuf,
+                         true, req->seqnum+1,
+                         IS_CONN_ENCRYPTED(req->conn)||req->encrypted,
+                         &req->pcd)) {
+               exit_server_cleanly("construct_reply: srv_send_smb failed.");
+       }
+       TALLOC_FREE(req->chain_outbuf);
+       req->done = true;
 }
 
 /****************************************************************************
@@ -1820,9 +2031,6 @@ void chain_reply(struct smb_request *req)
 
 void check_reload(time_t t)
 {
-       static pid_t mypid = 0;
-       static time_t last_smb_conf_reload_time = 0;
-       static time_t last_printer_reload_time = 0;
        time_t printcap_cache_time = (time_t)lp_printcap_cache_time();
 
        if(last_smb_conf_reload_time == 0) {
@@ -1845,9 +2053,8 @@ void check_reload(time_t t)
                mypid = getpid();
        }
 
-       if (reload_after_sighup || (t >= last_smb_conf_reload_time+SMBD_RELOAD_CHECK)) {
+       if (t >= last_smb_conf_reload_time+SMBD_RELOAD_CHECK) {
                reload_services(True);
-               reload_after_sighup = False;
                last_smb_conf_reload_time = t;
        }
 
@@ -1867,16 +2074,195 @@ void check_reload(time_t t)
        }
 }
 
+static void smbd_server_connection_write_handler(struct smbd_server_connection *conn)
+{
+       /* TODO: make write nonblocking */
+}
+
+static void smbd_server_connection_read_handler(struct smbd_server_connection *conn)
+{
+       uint8_t *inbuf = NULL;
+       size_t inbuf_len = 0;
+       size_t unread_bytes = 0;
+       bool encrypted = false;
+       TALLOC_CTX *mem_ctx = talloc_tos();
+       NTSTATUS status;
+       uint32_t seqnum;
+
+       /* TODO: make this completely nonblocking */
+
+       status = receive_smb_talloc(mem_ctx, smbd_server_fd(),
+                                   (char **)(void *)&inbuf,
+                                   0, /* timeout */
+                                   &unread_bytes,
+                                   &encrypted,
+                                   &inbuf_len, &seqnum);
+       if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
+               goto process;
+       }
+       if (NT_STATUS_IS_ERR(status)) {
+               exit_server_cleanly("failed to receive smb request");
+       }
+       if (!NT_STATUS_IS_OK(status)) {
+               return;
+       }
+
+process:
+       process_smb(conn, inbuf, inbuf_len, unread_bytes,
+                   seqnum, encrypted, NULL);
+}
+
+static void smbd_server_connection_handler(struct event_context *ev,
+                                          struct fd_event *fde,
+                                          uint16_t flags,
+                                          void *private_data)
+{
+       struct smbd_server_connection *conn = talloc_get_type(private_data,
+                                             struct smbd_server_connection);
+
+       if (flags & EVENT_FD_WRITE) {
+               smbd_server_connection_write_handler(conn);
+       } else if (flags & EVENT_FD_READ) {
+               smbd_server_connection_read_handler(conn);
+       }
+}
+
+
+/****************************************************************************
+received when we should release a specific IP
+****************************************************************************/
+static void release_ip(const char *ip, void *priv)
+{
+       char addr[INET6_ADDRSTRLEN];
+       char *p = addr;
+
+       client_socket_addr(get_client_fd(),addr,sizeof(addr));
+
+       if (strncmp("::ffff:", addr, 7) == 0) {
+               p = addr + 7;
+       }
+
+       if ((strcmp(p, ip) == 0) || ((p != addr) && strcmp(addr, ip) == 0)) {
+               /* we can't afford to do a clean exit - that involves
+                  database writes, which would potentially mean we
+                  are still running after the failover has finished -
+                  we have to get rid of this process ID straight
+                  away */
+               DEBUG(0,("Got release IP message for our IP %s - exiting immediately\n",
+                       ip));
+               /* note we must exit with non-zero status so the unclean handler gets
+                  called in the parent, so that the brl database is tickled */
+               _exit(1);
+       }
+}
+
+static void msg_release_ip(struct messaging_context *msg_ctx, void *private_data,
+                          uint32_t msg_type, struct server_id server_id, DATA_BLOB *data)
+{
+       release_ip((char *)data->data, NULL);
+}
+
+#ifdef CLUSTER_SUPPORT
+static int client_get_tcp_info(struct sockaddr_storage *server,
+                              struct sockaddr_storage *client)
+{
+       socklen_t length;
+       if (server_fd == -1) {
+               return -1;
+       }
+       length = sizeof(*server);
+       if (getsockname(server_fd, (struct sockaddr *)server, &length) != 0) {
+               return -1;
+       }
+       length = sizeof(*client);
+       if (getpeername(server_fd, (struct sockaddr *)client, &length) != 0) {
+               return -1;
+       }
+       return 0;
+}
+#endif
+
+/*
+ * Send keepalive packets to our client
+ */
+static bool keepalive_fn(const struct timeval *now, void *private_data)
+{
+       if (!send_keepalive(smbd_server_fd())) {
+               DEBUG( 2, ( "Keepalive failed - exiting.\n" ) );
+               return False;
+       }
+       return True;
+}
+
+/*
+ * Do the recurring check if we're idle
+ */
+static bool deadtime_fn(const struct timeval *now, void *private_data)
+{
+       struct smbd_server_connection *sconn = smbd_server_conn;
+       if ((conn_num_open(sconn) == 0)
+           || (conn_idle_all(sconn, now->tv_sec))) {
+               DEBUG( 2, ( "Closing idle connection\n" ) );
+               messaging_send(smbd_messaging_context(), procid_self(),
+                              MSG_SHUTDOWN, &data_blob_null);
+               return False;
+       }
+
+       return True;
+}
+
+/*
+ * Do the recurring log file and smb.conf reload checks.
+ */
+
+static bool housekeeping_fn(const struct timeval *now, void *private_data)
+{
+       change_to_root_user();
+
+       /* update printer queue caches if necessary */
+       update_monitored_printq_cache();
+
+       /* check if we need to reload services */
+       check_reload(time(NULL));
+
+       /* Change machine password if neccessary. */
+       attempt_machine_password_change();
+
+        /*
+        * Force a log file check.
+        */
+       force_check_log_size();
+       check_log_size();
+       return true;
+}
+
 /****************************************************************************
  Process commands from the client
 ****************************************************************************/
 
 void smbd_process(void)
 {
-       unsigned int num_smbs = 0;
-       size_t unread_bytes = 0;
+       TALLOC_CTX *frame = talloc_stackframe();
+       char remaddr[INET6_ADDRSTRLEN];
 
-       char addr[INET6_ADDRSTRLEN];
+       if (lp_maxprotocol() == PROTOCOL_SMB2 &&
+           lp_security() != SEC_SHARE) {
+               smbd_server_conn->allow_smb2 = true;
+       }
+
+       /* Ensure child is set to blocking mode */
+       set_blocking(smbd_server_fd(),True);
+
+       set_socket_options(smbd_server_fd(),"SO_KEEPALIVE");
+       set_socket_options(smbd_server_fd(), lp_socket_options());
+
+       /* this is needed so that we get decent entries
+          in smbstatus for port 445 connects */
+       set_remote_machine_name(get_peer_addr(smbd_server_fd(),
+                                             remaddr,
+                                             sizeof(remaddr)),
+                                             false);
+       reload_services(true);
 
        /*
         * Before the first packet, check the global hosts allow/ hosts deny
@@ -1887,6 +2273,8 @@ void smbd_process(void)
 
        if (!check_access(smbd_server_fd(), lp_hostsallow(-1),
                          lp_hostsdeny(-1))) {
+               char addr[INET6_ADDRSTRLEN];
+
                /*
                 * send a negative session response "not listening on calling
                 * name"
@@ -1894,50 +2282,195 @@ void smbd_process(void)
                unsigned char buf[5] = {0x83, 0, 0, 1, 0x81};
                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);
+               (void)srv_send_smb(smbd_server_fd(),(char *)buf, false,
+                                  0, false, NULL);
                exit_server_cleanly("connection denied");
        }
 
-       max_recv = MIN(lp_maxxmit(),BUFFER_SIZE);
+       static_init_rpc;
 
-       while (True) {
-               NTSTATUS status;
-               char *inbuf = NULL;
-               size_t inbuf_len = 0;
-               bool encrypted = false;
-               TALLOC_CTX *frame = talloc_stackframe_pool(8192);
+       init_modules();
 
-               errno = 0;
+       smb_perfcount_init();
 
-               run_events(smbd_event_context(), 0, NULL, NULL);
+       if (!init_account_policy()) {
+               exit_server("Could not open account policy tdb.\n");
+       }
 
-               status = receive_message_or_smb(
-                       talloc_tos(), &inbuf, &inbuf_len,
-                       &unread_bytes, &encrypted);
+       if (*lp_rootdir()) {
+               if (chroot(lp_rootdir()) != 0) {
+                       DEBUG(0,("Failed to change root to %s\n", lp_rootdir()));
+                       exit_server("Failed to chroot()");
+               }
+               if (chdir("/") == -1) {
+                       DEBUG(0,("Failed to chdir to / on chroot to %s\n", lp_rootdir()));
+                       exit_server("Failed to chroot()");
+               }
+               DEBUG(0,("Changed root to %s\n", lp_rootdir()));
+       }
 
-               if (!NT_STATUS_IS_OK(status)) {
-                       DEBUG(3, ("receive_message_or_smb failed: %s, "
-                                 "exiting\n", nt_errstr(status)));
-                       return;
+       if (!srv_init_signing(smbd_server_conn)) {
+               exit_server("Failed to init smb_signing");
+       }
+
+       /* Setup oplocks */
+       if (!init_oplocks(smbd_messaging_context()))
+               exit_server("Failed to init oplocks");
+
+       /* Setup aio signal handler. */
+       initialize_async_io_handler();
+
+       /* register our message handlers */
+       messaging_register(smbd_messaging_context(), NULL,
+                          MSG_SMB_FORCE_TDIS, msg_force_tdis);
+       messaging_register(smbd_messaging_context(), NULL,
+                          MSG_SMB_RELEASE_IP, msg_release_ip);
+       messaging_register(smbd_messaging_context(), NULL,
+                          MSG_SMB_CLOSE_FILE, msg_close_file);
+
+       /*
+        * Use the default MSG_DEBUG handler to avoid rebroadcasting
+        * MSGs to all child processes
+        */
+       messaging_deregister(smbd_messaging_context(),
+                            MSG_DEBUG, NULL);
+       messaging_register(smbd_messaging_context(), NULL,
+                          MSG_DEBUG, debug_message);
+
+       if ((lp_keepalive() != 0)
+           && !(event_add_idle(smbd_event_context(), NULL,
+                               timeval_set(lp_keepalive(), 0),
+                               "keepalive", keepalive_fn,
+                               NULL))) {
+               DEBUG(0, ("Could not add keepalive event\n"));
+               exit(1);
+       }
+
+       if (!(event_add_idle(smbd_event_context(), NULL,
+                            timeval_set(IDLE_CLOSED_TIMEOUT, 0),
+                            "deadtime", deadtime_fn, NULL))) {
+               DEBUG(0, ("Could not add deadtime event\n"));
+               exit(1);
+       }
+
+       if (!(event_add_idle(smbd_event_context(), NULL,
+                            timeval_set(SMBD_SELECT_TIMEOUT, 0),
+                            "housekeeping", housekeeping_fn, NULL))) {
+               DEBUG(0, ("Could not add housekeeping event\n"));
+               exit(1);
+       }
+
+#ifdef CLUSTER_SUPPORT
+
+       if (lp_clustering()) {
+               /*
+                * We need to tell ctdb about our client's TCP
+                * connection, so that for failover ctdbd can send
+                * tickle acks, triggering a reconnection by the
+                * client.
+                */
+
+               struct sockaddr_storage srv, clnt;
+
+               if (client_get_tcp_info(&srv, &clnt) == 0) {
+
+                       NTSTATUS status;
+
+                       status = ctdbd_register_ips(
+                               messaging_ctdbd_connection(),
+                               &srv, &clnt, release_ip, NULL);
+
+                       if (!NT_STATUS_IS_OK(status)) {
+                               DEBUG(0, ("ctdbd_register_ips failed: %s\n",
+                                         nt_errstr(status)));
+                       }
+               } else
+               {
+                       DEBUG(0,("Unable to get tcp info for "
+                                "CTDB_CONTROL_TCP_CLIENT: %s\n",
+                                strerror(errno)));
                }
+       }
 
-               process_smb(inbuf, inbuf_len, unread_bytes, encrypted);
+#endif
 
-               TALLOC_FREE(inbuf);
+       smbd_server_conn->nbt.got_session = false;
 
-               num_smbs++;
+       smbd_server_conn->smb1.negprot.max_recv = MIN(lp_maxxmit(),BUFFER_SIZE);
 
-               /* The timeout_processing function isn't run nearly
-                  often enough to implement 'max log size' without
-                  overrunning the size of the file by many megabytes.
-                  This is especially true if we are running at debug
-                  level 10.  Checking every 50 SMBs is a nice
-                  tradeoff of performance vs log file size overrun. */
+       smbd_server_conn->smb1.sessions.done_sesssetup = false;
+       smbd_server_conn->smb1.sessions.max_send = BUFFER_SIZE;
+       smbd_server_conn->smb1.sessions.last_session_tag = UID_FIELD_INVALID;
+       /* users from session setup */
+       smbd_server_conn->smb1.sessions.session_userlist = NULL;
+       /* workgroup from session setup. */
+       smbd_server_conn->smb1.sessions.session_workgroup = NULL;
+       /* this holds info on user ids that are already validated for this VC */
+       smbd_server_conn->smb1.sessions.validated_users = NULL;
+       smbd_server_conn->smb1.sessions.next_vuid = VUID_OFFSET;
+       smbd_server_conn->smb1.sessions.num_validated_vuids = 0;
+#ifdef HAVE_NETGROUP
+       smbd_server_conn->smb1.sessions.my_yp_domain = NULL;
+#endif
 
-               if ((num_smbs % 50) == 0 && need_to_check_log_size()) {
-                       change_to_root_user();
-                       check_log_size();
+       conn_init(smbd_server_conn);
+       if (!init_dptrs(smbd_server_conn)) {
+               exit_server("init_dptrs() failed");
+       }
+
+       smbd_server_conn->smb1.fde = event_add_fd(smbd_event_context(),
+                                                 smbd_server_conn,
+                                                 smbd_server_fd(),
+                                                 EVENT_FD_READ,
+                                                 smbd_server_connection_handler,
+                                                 smbd_server_conn);
+       if (!smbd_server_conn->smb1.fde) {
+               exit_server("failed to create smbd_server_connection fde");
+       }
+
+       TALLOC_FREE(frame);
+
+       while (True) {
+               NTSTATUS status;
+
+               frame = talloc_stackframe_pool(8192);
+
+               errno = 0;
+
+               status = smbd_server_connection_loop_once(smbd_server_conn);
+               if (!NT_STATUS_EQUAL(status, NT_STATUS_RETRY) &&
+                   !NT_STATUS_IS_OK(status)) {
+                       DEBUG(3, ("smbd_server_connection_loop_once failed: %s,"
+                                 " exiting\n", nt_errstr(status)));
+                       break;
                }
+
                TALLOC_FREE(frame);
        }
+
+       exit_server_cleanly(NULL);
+}
+
+bool req_is_in_chain(struct smb_request *req)
+{
+       if (req->vwv != (uint16_t *)(req->inbuf+smb_vwv)) {
+               /*
+                * We're right now handling a subsequent request, so we must
+                * be in a chain
+                */
+               return true;
+       }
+
+       if (!is_andx_req(req->cmd)) {
+               return false;
+       }
+
+       if (req->wct < 2) {
+               /*
+                * Okay, an illegal request, but definitely not chained :-)
+                */
+               return false;
+       }
+
+       return (CVAL(req->vwv+0, 0) != 0xFF);
 }