Rename "allow_smb2" -> "using_smb2" and make the usage clearer.
[samba.git] / source3 / smbd / process.c
index e1e4e77634584d279ea7e4d32cb746f575bbb504..1baf3538c6f12d65668e8ce836fce6959e9bb070 100644 (file)
 #include "../librpc/gen_ndr/srv_svcctl.h"
 #include "../librpc/gen_ndr/srv_winreg.h"
 #include "../librpc/gen_ndr/srv_wkssvc.h"
+#include "librpc/gen_ndr/messaging.h"
 
 extern bool global_machine_password_needs_changing;
 
 static void construct_reply_common(struct smb_request *req, const char *inbuf,
                                   char *outbuf);
+static struct pending_message_list *get_deferred_open_message_smb(uint64_t mid);
 
-bool smbd_lock_socket(struct smbd_server_connection *sconn)
+static bool smbd_lock_socket_internal(struct smbd_server_connection *sconn)
 {
+       bool ok;
+
+       if (smbd_server_conn->smb1.echo_handler.socket_lock_fd == -1) {
+               return true;
+       }
+
+       smbd_server_conn->smb1.echo_handler.ref_count++;
+
+       if (smbd_server_conn->smb1.echo_handler.ref_count > 1) {
+               return true;
+       }
+
+       DEBUG(10,("pid[%d] wait for socket lock\n", (int)sys_getpid()));
+
+       ok = fcntl_lock(smbd_server_conn->smb1.echo_handler.socket_lock_fd,
+                       SMB_F_SETLKW, 0, 0, F_WRLCK);
+       if (!ok) {
+               return false;
+       }
+
+       DEBUG(10,("pid[%d] got for socket lock\n", (int)sys_getpid()));
+
        return true;
 }
 
-bool smbd_unlock_socket(struct smbd_server_connection *sconn)
+void smbd_lock_socket(struct smbd_server_connection *sconn)
 {
+       if (!smbd_lock_socket_internal(sconn)) {
+               exit_server_cleanly("failed to lock socket");
+       }
+}
+
+static bool smbd_unlock_socket_internal(struct smbd_server_connection *sconn)
+{
+       bool ok;
+
+       if (smbd_server_conn->smb1.echo_handler.socket_lock_fd == -1) {
+               return true;
+       }
+
+       smbd_server_conn->smb1.echo_handler.ref_count--;
+
+       if (smbd_server_conn->smb1.echo_handler.ref_count > 0) {
+               return true;
+       }
+
+       ok = fcntl_lock(smbd_server_conn->smb1.echo_handler.socket_lock_fd,
+                       SMB_F_SETLKW, 0, 0, F_UNLCK);
+       if (!ok) {
+               return false;
+       }
+
+       DEBUG(10,("pid[%d] unlocked socket\n", (int)sys_getpid()));
+
        return true;
 }
 
+void smbd_unlock_socket(struct smbd_server_connection *sconn)
+{
+       if (!smbd_unlock_socket_internal(sconn)) {
+               exit_server_cleanly("failed to unlock socket");
+       }
+}
+
 /* Accessor function for smb_read_error for smbd functions. */
 
 /****************************************************************************
@@ -65,12 +123,8 @@ bool srv_send_smb(int fd, char *buffer,
        size_t nwritten=0;
        ssize_t ret;
        char *buf_out = buffer;
-       bool ok;
 
-       ok = smbd_lock_socket(smbd_server_conn);
-       if (!ok) {
-               exit_server_cleanly("failed to lock socket");
-       }
+       smbd_lock_socket(smbd_server_conn);
 
        if (do_signing) {
                /* Sign the outgoing packet if required. */
@@ -91,8 +145,8 @@ bool srv_send_smb(int fd, char *buffer,
 
        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) ));
+               DEBUG(0,("pid[%d] Error writing %d bytes to client. %d. (%s)\n",
+                       (int)sys_getpid(), (int)len,(int)ret, strerror(errno) ));
                srv_free_enc_buffer(buf_out);
                goto out;
        }
@@ -102,11 +156,7 @@ bool srv_send_smb(int fd, char *buffer,
 out:
        SMB_PERFCOUNT_END(pcd);
 
-       ok = smbd_unlock_socket(smbd_server_conn);
-       if (!ok) {
-               exit_server_cleanly("failed to unlock socket");
-       }
-
+       smbd_unlock_socket(smbd_server_conn);
        return true;
 }
 
@@ -312,7 +362,8 @@ static NTSTATUS receive_smb_raw_talloc(TALLOC_CTX *mem_ctx, int fd,
        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)) {
+           !srv_is_signing_active(smbd_server_conn) &&
+           smbd_server_conn->smb1.echo_handler.trusted_fde == NULL) {
 
                return receive_smb_raw_talloc_partial_read(
                        mem_ctx, lenbuf, fd, buffer, timeout, p_unread, plen);
@@ -349,7 +400,8 @@ 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,
-                                  uint32_t *seqnum)
+                                  uint32_t *seqnum,
+                                  bool trusted_channel)
 {
        size_t len = 0;
        NTSTATUS status;
@@ -374,7 +426,7 @@ static NTSTATUS receive_smb_talloc(TALLOC_CTX *mem_ctx,     int fd,
        }
 
        /* Check the incoming SMB signature. */
-       if (!srv_check_sign_mac(smbd_server_conn, *buffer, seqnum, false)) {
+       if (!srv_check_sign_mac(smbd_server_conn, *buffer, seqnum, trusted_channel)) {
                DEBUG(0, ("receive_smb: SMB Signature verification failed on "
                          "incoming packet!\n"));
                return NT_STATUS_INVALID_NETWORK_RESPONSE;
@@ -403,7 +455,7 @@ static bool init_smb_request(struct smb_request *req, const uint8 *inbuf,
        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->mid    = (uint64_t)SVAL(inbuf, smb_mid);
        req->seqnum = seqnum;
        req->vuid   = SVAL(inbuf, smb_uid);
        req->tid    = SVAL(inbuf, smb_tid);
@@ -417,6 +469,7 @@ static bool init_smb_request(struct smb_request *req, const uint8 *inbuf,
        req->chain_fsp = NULL;
        req->chain_outbuf = NULL;
        req->done = false;
+       req->smb2req = NULL;
        smb_init_perfcount_data(&req->pcd);
 
        /* Ensure we have at least wct words and 2 bytes of bcc. */
@@ -453,7 +506,7 @@ static void smbd_deferred_open_timer(struct event_context *ev,
        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);
+       uint64_t mid = (uint64_t)SVAL(msg->buf.data,smb_mid);
        uint8_t *inbuf;
 
        inbuf = (uint8_t *)talloc_memdup(mem_ctx, msg->buf.data,
@@ -465,8 +518,8 @@ static void smbd_deferred_open_timer(struct event_context *ev,
 
        /* 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 ));
+       DEBUG(5,("smbd_deferred_open_timer: trigger mid %llu.\n",
+               (unsigned long long)mid ));
 
        /* Mark the message as processed so this is not
         * re-processed in error. */
@@ -477,9 +530,9 @@ static void smbd_deferred_open_timer(struct event_context *ev,
                    msg->seqnum, msg->encrypted, &msg->pcd);
 
        /* If it's still there and was processed, remove it. */
-       msg = get_open_deferred_message(mid);
+       msg = get_deferred_open_message_smb(mid);
        if (msg && msg->processed) {
-               remove_deferred_open_smb_message(mid);
+               remove_deferred_open_message_smb(mid);
        }
 }
 
@@ -549,15 +602,20 @@ static bool push_queued_message(struct smb_request *req,
  Function to delete a sharing violation open message by mid.
 ****************************************************************************/
 
-void remove_deferred_open_smb_message(uint16 mid)
+void remove_deferred_open_message_smb(uint64_t mid)
 {
        struct pending_message_list *pml;
 
+       if (smbd_server_conn->using_smb2) {
+               remove_deferred_open_message_smb2(mid);
+               return;
+       }
+
        for (pml = deferred_open_queue; pml; pml = pml->next) {
-               if (mid == SVAL(pml->buf.data,smb_mid)) {
-                       DEBUG(10,("remove_deferred_open_smb_message: "
-                                 "deleting mid %u len %u\n",
-                                 (unsigned int)mid,
+               if (mid == (uint64_t)SVAL(pml->buf.data,smb_mid)) {
+                       DEBUG(10,("remove_deferred_open_message_smb: "
+                                 "deleting mid %llu len %u\n",
+                                 (unsigned long long)mid,
                                  (unsigned int)pml->buf.length ));
                        DLIST_REMOVE(deferred_open_queue, pml);
                        TALLOC_FREE(pml);
@@ -571,16 +629,23 @@ void remove_deferred_open_smb_message(uint16 mid)
  schedule it for immediate processing.
 ****************************************************************************/
 
-void schedule_deferred_open_smb_message(uint16 mid)
+void schedule_deferred_open_message_smb(uint64_t mid)
 {
        struct pending_message_list *pml;
        int i = 0;
 
+       if (smbd_server_conn->using_smb2) {
+               schedule_deferred_open_message_smb2(mid);
+               return;
+       }
+
        for (pml = deferred_open_queue; pml; pml = pml->next) {
-               uint16 msg_mid = SVAL(pml->buf.data,smb_mid);
+               uint64_t msg_mid = (uint64_t)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_message_smb: [%d] "
+                       "msg_mid = %llu\n",
+                       i++,
+                       (unsigned long long)msg_mid ));
 
                if (mid == msg_mid) {
                        struct timed_event *te;
@@ -588,14 +653,15 @@ void schedule_deferred_open_smb_message(uint16 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",
-                                       msg_mid ));
+                               DEBUG(0,("schedule_deferred_open_message_smb: LOGIC ERROR "
+                                       "message mid %llu was already processed\n",
+                                       (unsigned long long)msg_mid ));
                                continue;
                        }
 
-                       DEBUG(10,("schedule_deferred_open_smb_message: scheduling mid %u\n",
-                               mid ));
+                       DEBUG(10,("schedule_deferred_open_message_smb: "
+                               "scheduling mid %llu\n",
+                               (unsigned long long)mid ));
 
                        te = event_add_timed(smbd_event_context(),
                                             pml,
@@ -603,9 +669,10 @@ void schedule_deferred_open_smb_message(uint16 mid)
                                             smbd_deferred_open_timer,
                                             pml);
                        if (!te) {
-                               DEBUG(10,("schedule_deferred_open_smb_message: "
-                                         "event_add_timed() failed, skipping mid %u\n",
-                                         mid ));
+                               DEBUG(10,("schedule_deferred_open_message_smb: "
+                                       "event_add_timed() failed, "
+                                       "skipping mid %llu\n",
+                                       (unsigned long long)msg_mid ));
                        }
 
                        TALLOC_FREE(pml->te);
@@ -615,20 +682,25 @@ 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_message_smb: failed to "
+               "find message mid %llu\n",
+               (unsigned long long)mid ));
 }
 
 /****************************************************************************
  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(uint64_t mid)
 {
        struct pending_message_list *pml;
 
+       if (smbd_server_conn->using_smb2) {
+               return open_was_deferred_smb2(mid);
+       }
+
        for (pml = deferred_open_queue; pml; pml = pml->next) {
-               if (SVAL(pml->buf.data,smb_mid) == mid && !pml->processed) {
+               if (((uint64_t)SVAL(pml->buf.data,smb_mid)) == mid && !pml->processed) {
                        return True;
                }
        }
@@ -639,45 +711,85 @@ bool open_was_deferred(uint16 mid)
  Return the message queued by this mid.
 ****************************************************************************/
 
-struct pending_message_list *get_open_deferred_message(uint16 mid)
+static struct pending_message_list *get_deferred_open_message_smb(uint64_t mid)
 {
        struct pending_message_list *pml;
 
        for (pml = deferred_open_queue; pml; pml = pml->next) {
-               if (SVAL(pml->buf.data,smb_mid) == mid) {
+               if (((uint64_t)SVAL(pml->buf.data,smb_mid)) == mid) {
                        return pml;
                }
        }
        return NULL;
 }
 
+/****************************************************************************
+ Get the state data queued by this mid.
+****************************************************************************/
+
+bool get_deferred_open_message_state(struct smb_request *smbreq,
+                               struct timeval *p_request_time,
+                               void **pp_state)
+{
+       struct pending_message_list *pml;
+
+       if (smbd_server_conn->using_smb2) {
+               return get_deferred_open_message_state_smb2(smbreq->smb2req,
+                                       p_request_time,
+                                       pp_state);
+       }
+
+       pml = get_deferred_open_message_smb(smbreq->mid);
+       if (!pml) {
+               return false;
+       }
+       if (p_request_time) {
+               *p_request_time = pml->request_time;
+       }
+       if (pp_state) {
+               *pp_state = (void *)pml->private_data.data;
+       }
+       return true;
+}
+
 /****************************************************************************
  Function to push a deferred open smb message onto a linked list of local smb
  messages ready for processing.
 ****************************************************************************/
 
-bool push_deferred_smb_message(struct smb_request *req,
+bool push_deferred_open_message_smb(struct smb_request *req,
                               struct timeval request_time,
                               struct timeval timeout,
+                              struct file_id id,
                               char *private_data, size_t priv_len)
 {
        struct timeval end_time;
 
+       if (req->smb2req) {
+               return push_deferred_open_message_smb2(req->smb2req,
+                                               request_time,
+                                               timeout,
+                                               id,
+                                               private_data,
+                                               priv_len);
+       }
+
        if (req->unread_bytes) {
-               DEBUG(0,("push_deferred_smb_message: logic error ! "
+               DEBUG(0,("push_deferred_open_message_smb: logic error ! "
                        "unread_bytes = %u\n",
                        (unsigned int)req->unread_bytes ));
-               smb_panic("push_deferred_smb_message: "
+               smb_panic("push_deferred_open_message_smb: "
                        "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(req->inbuf)+4, (unsigned int)req->mid,
-                 (unsigned int)end_time.tv_sec,
-                 (unsigned int)end_time.tv_usec));
+       DEBUG(10,("push_deferred_open_message_smb: pushing message "
+               "len %u mid %llu timeout time [%u.%06u]\n",
+               (unsigned int) smb_len(req->inbuf)+4,
+               (unsigned long long)req->mid,
+               (unsigned int)end_time.tv_sec,
+               (unsigned int)end_time.tv_usec));
 
        return push_queued_message(req, request_time, end_time,
                                   private_data, priv_len);
@@ -883,7 +995,7 @@ static NTSTATUS smbd_server_connection_loop_once(struct smbd_server_connection *
  * prevent a DoS.
  */
 
-NTSTATUS allow_new_trans(struct trans_state *list, int mid)
+NTSTATUS allow_new_trans(struct trans_state *list, uint64_t mid)
 {
        int count = 0;
        for (; list != NULL; list = list->next) {
@@ -1339,8 +1451,7 @@ static connection_struct *switch_message(uint8 type, struct smb_request *req, in
                                set_current_user_info(
                                        vuser->server_info->sanitized_username,
                                        vuser->server_info->unix_name,
-                                       pdb_get_domain(vuser->server_info
-                                                      ->sam_account));
+                                       vuser->server_info->info3->base.domain.string);
                        }
                }
        }
@@ -1364,7 +1475,8 @@ static connection_struct *switch_message(uint8 type, struct smb_request *req, in
 
                if (!change_to_user(conn,session_tag)) {
                        DEBUG(0, ("Error: Could not change to user. Removing "
-                           "deferred open, mid=%d.\n", req->mid));
+                               "deferred open, mid=%llu.\n",
+                               (unsigned long long)req->mid));
                        reply_force_doserror(req, ERRSRV, ERRbaduid);
                        return conn;
                }
@@ -1517,12 +1629,18 @@ static void process_smb(struct smbd_server_connection *conn,
                goto done;
        }
 
-       if (smbd_server_conn->allow_smb2) {
+       if (smbd_server_conn->using_smb2) {
+               /* At this point we're not really using smb2,
+                * we make the decision here.. */
                if (smbd_is_smb2_header(inbuf, nread)) {
                        smbd_smb2_first_negprot(smbd_server_conn, inbuf, nread);
                        return;
+               } else if (nread >= smb_size && valid_smb_header(inbuf)
+                               && CVAL(inbuf, smb_com) != 0x72) {
+                       /* This is a non-negprot SMB1 packet.
+                          Disable SMB2 from now on. */
+                       smbd_server_conn->using_smb2 = false;
                }
-               smbd_server_conn->allow_smb2 = false;
        }
 
        show_msg((char *)inbuf);
@@ -2110,12 +2228,29 @@ void check_reload(time_t t)
        }
 }
 
+static bool fd_is_readable(int fd)
+{
+       fd_set fds;
+       struct timeval timeout = {0, };
+       int ret;
+
+       FD_ZERO(&fds);
+       FD_SET(fd, &fds);
+
+       ret = sys_select(fd+1, &fds, NULL, NULL, &timeout);
+       if (ret == -1) {
+               return false;
+       }
+       return FD_ISSET(fd, &fds);
+}
+
 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)
+static void smbd_server_connection_read_handler(
+       struct smbd_server_connection *conn, int fd)
 {
        uint8_t *inbuf = NULL;
        size_t inbuf_len = 0;
@@ -2125,23 +2260,35 @@ static void smbd_server_connection_read_handler(struct smbd_server_connection *c
        NTSTATUS status;
        uint32_t seqnum;
 
-       bool ok;
+       bool from_client = (smbd_server_fd() == fd)?true:false;
 
-       ok = smbd_lock_socket(conn);
-       if (!ok) {
-               exit_server_cleanly("failed to lock socket");
-       }
+       if (from_client) {
+               smbd_lock_socket(conn);
 
-       /* 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);
-       ok = smbd_unlock_socket(conn);
-       if (!ok) {
-               exit_server_cleanly("failed to unlock");
+               if (!fd_is_readable(smbd_server_fd())) {
+                       DEBUG(10,("the echo listener was faster\n"));
+                       smbd_unlock_socket(conn);
+                       return;
+               }
+
+               /* TODO: make this completely nonblocking */
+               status = receive_smb_talloc(mem_ctx, fd,
+                                           (char **)(void *)&inbuf,
+                                           0, /* timeout */
+                                           &unread_bytes,
+                                           &encrypted,
+                                           &inbuf_len, &seqnum,
+                                           false /* trusted channel */);
+               smbd_unlock_socket(conn);
+       } else {
+               /* TODO: make this completely nonblocking */
+               status = receive_smb_talloc(mem_ctx, fd,
+                                           (char **)(void *)&inbuf,
+                                           0, /* timeout */
+                                           &unread_bytes,
+                                           &encrypted,
+                                           &inbuf_len, &seqnum,
+                                           true /* trusted channel */);
        }
 
        if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
@@ -2170,10 +2317,25 @@ static void smbd_server_connection_handler(struct event_context *ev,
        if (flags & EVENT_FD_WRITE) {
                smbd_server_connection_write_handler(conn);
        } else if (flags & EVENT_FD_READ) {
-               smbd_server_connection_read_handler(conn);
+               smbd_server_connection_read_handler(conn, smbd_server_fd());
        }
 }
 
+static void smbd_server_echo_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, conn->smb1.echo_handler.trusted_fd);
+       }
+}
 
 /****************************************************************************
 received when we should release a specific IP
@@ -2234,20 +2396,17 @@ static int client_get_tcp_info(struct sockaddr_storage *server,
  */
 static bool keepalive_fn(const struct timeval *now, void *private_data)
 {
-       bool ok;
+       struct smbd_server_connection *sconn = smbd_server_conn;
        bool ret;
 
-       ok = smbd_lock_socket(smbd_server_conn);
-       if (!ok) {
-               exit_server_cleanly("failed to lock socket");
+       if (sconn->using_smb2) {
+               /* Don't do keepalives on an SMB2 connection. */
+               return false;
        }
 
+       smbd_lock_socket(smbd_server_conn);
        ret = send_keepalive(smbd_server_fd());
-
-       ok = smbd_unlock_socket(smbd_server_conn);
-       if (!ok) {
-               exit_server_cleanly("failed to unlock socket");
-       }
+       smbd_unlock_socket(smbd_server_conn);
 
        if (!ret) {
                DEBUG( 2, ( "Keepalive failed - exiting.\n" ) );
@@ -2262,9 +2421,21 @@ static bool keepalive_fn(const struct timeval *now, void *private_data)
 static bool deadtime_fn(const struct timeval *now, void *private_data)
 {
        struct smbd_server_connection *sconn = smbd_server_conn;
+
+       if (sconn->using_smb2) {
+               /* TODO: implement real idle check */
+               if (sconn->smb2.sessions.list) {
+                       return true;
+               }
+               DEBUG( 2, ( "Closing idle SMB2 connection\n" ) );
+               messaging_send(smbd_messaging_context(), procid_self(),
+                              MSG_SHUTDOWN, &data_blob_null);
+               return false;
+       }
+
        if ((conn_num_open(sconn) == 0)
            || (conn_idle_all(sconn, now->tv_sec))) {
-               DEBUG( 2, ( "Closing idle connection\n" ) );
+               DEBUG( 2, ( "Closing idle SMB1 connection\n" ) );
                messaging_send(smbd_messaging_context(), procid_self(),
                               MSG_SHUTDOWN, &data_blob_null);
                return False;
@@ -2298,6 +2469,377 @@ static bool housekeeping_fn(const struct timeval *now, void *private_data)
        return true;
 }
 
+static int create_unlink_tmp(const char *dir)
+{
+       char *fname;
+       int fd;
+
+       fname = talloc_asprintf(talloc_tos(), "%s/listenerlock_XXXXXX", dir);
+       if (fname == NULL) {
+               errno = ENOMEM;
+               return -1;
+       }
+       fd = mkstemp(fname);
+       if (fd == -1) {
+               TALLOC_FREE(fname);
+               return -1;
+       }
+       if (unlink(fname) == -1) {
+               int sys_errno = errno;
+               close(fd);
+               TALLOC_FREE(fname);
+               errno = sys_errno;
+               return -1;
+       }
+       TALLOC_FREE(fname);
+       return fd;
+}
+
+struct smbd_echo_state {
+       struct tevent_context *ev;
+       struct iovec *pending;
+       struct smbd_server_connection *sconn;
+       int parent_pipe;
+
+       struct tevent_fd *parent_fde;
+
+       struct tevent_fd *read_fde;
+       struct tevent_req *write_req;
+};
+
+static void smbd_echo_writer_done(struct tevent_req *req);
+
+static void smbd_echo_activate_writer(struct smbd_echo_state *state)
+{
+       int num_pending;
+
+       if (state->write_req != NULL) {
+               return;
+       }
+
+       num_pending = talloc_array_length(state->pending);
+       if (num_pending == 0) {
+               return;
+       }
+
+       state->write_req = writev_send(state, state->ev, NULL,
+                                      state->parent_pipe, false,
+                                      state->pending, num_pending);
+       if (state->write_req == NULL) {
+               DEBUG(1, ("writev_send failed\n"));
+               exit(1);
+       }
+
+       talloc_steal(state->write_req, state->pending);
+       state->pending = NULL;
+
+       tevent_req_set_callback(state->write_req, smbd_echo_writer_done,
+                               state);
+}
+
+static void smbd_echo_writer_done(struct tevent_req *req)
+{
+       struct smbd_echo_state *state = tevent_req_callback_data(
+               req, struct smbd_echo_state);
+       ssize_t written;
+       int err;
+
+       written = writev_recv(req, &err);
+       TALLOC_FREE(req);
+       state->write_req = NULL;
+       if (written == -1) {
+               DEBUG(1, ("writev to parent failed: %s\n", strerror(err)));
+               exit(1);
+       }
+       DEBUG(10,("echo_handler[%d]: forwarded pdu to main\n", (int)sys_getpid()));
+       smbd_echo_activate_writer(state);
+}
+
+static bool smbd_echo_reply(int fd,
+                           uint8_t *inbuf, size_t inbuf_len,
+                           uint32_t seqnum)
+{
+       struct smb_request req;
+       uint16_t num_replies;
+       size_t out_len;
+       char *outbuf;
+       bool ok;
+
+       if (inbuf_len < smb_size) {
+               DEBUG(10, ("Got short packet: %d bytes\n", (int)inbuf_len));
+               return false;
+       }
+       if (!valid_smb_header(inbuf)) {
+               DEBUG(10, ("Got invalid SMB header\n"));
+               return false;
+       }
+
+       if (!init_smb_request(&req, inbuf, 0, false, seqnum)) {
+               return false;
+       }
+       req.inbuf = inbuf;
+
+       DEBUG(10, ("smbecho handler got cmd %d (%s)\n", (int)req.cmd,
+                  smb_messages[req.cmd].name
+                  ? smb_messages[req.cmd].name : "unknown"));
+
+       if (req.cmd != SMBecho) {
+               return false;
+       }
+       if (req.wct < 1) {
+               return false;
+       }
+
+       num_replies = SVAL(req.vwv+0, 0);
+       if (num_replies != 1) {
+               /* Not a Windows "Hey, you're still there?" request */
+               return false;
+       }
+
+       if (!create_outbuf(talloc_tos(), &req, (char *)req.inbuf, &outbuf,
+                          1, req.buflen)) {
+               DEBUG(10, ("create_outbuf failed\n"));
+               return false;
+       }
+       req.outbuf = (uint8_t *)outbuf;
+
+       SSVAL(req.outbuf, smb_vwv0, num_replies);
+
+       if (req.buflen > 0) {
+               memcpy(smb_buf(req.outbuf), req.buf, req.buflen);
+       }
+
+       out_len = smb_len(req.outbuf) + 4;
+
+       ok = srv_send_smb(smbd_server_fd(),
+                         (char *)outbuf,
+                         true, seqnum+1,
+                         false, &req.pcd);
+       TALLOC_FREE(outbuf);
+       if (!ok) {
+               exit(1);
+       }
+
+       return true;
+}
+
+static void smbd_echo_exit(struct tevent_context *ev,
+                          struct tevent_fd *fde, uint16_t flags,
+                          void *private_data)
+{
+       DEBUG(2, ("smbd_echo_exit: lost connection to parent\n"));
+       exit(0);
+}
+
+static void smbd_echo_reader(struct tevent_context *ev,
+                            struct tevent_fd *fde, uint16_t flags,
+                            void *private_data)
+{
+       struct smbd_echo_state *state = talloc_get_type_abort(
+               private_data, struct smbd_echo_state);
+       struct smbd_server_connection *sconn = state->sconn;
+       size_t unread, num_pending;
+       NTSTATUS status;
+       struct iovec *tmp;
+       uint32_t seqnum = 0;
+       bool reply;
+       bool ok;
+       bool encrypted = false;
+
+       ok = smbd_lock_socket_internal(sconn);
+       if (!ok) {
+               DEBUG(0, ("%s: failed to lock socket\n",
+                       __location__));
+               exit(1);
+       }
+
+       if (!fd_is_readable(smbd_server_fd())) {
+               DEBUG(10,("echo_handler[%d] the parent smbd was faster\n",
+                         (int)sys_getpid()));
+               ok = smbd_unlock_socket_internal(sconn);
+               if (!ok) {
+                       DEBUG(1, ("%s: failed to unlock socket in\n",
+                               __location__));
+                       exit(1);
+               }
+               return;
+       }
+
+       num_pending = talloc_array_length(state->pending);
+       tmp = talloc_realloc(state, state->pending, struct iovec,
+                            num_pending+1);
+       if (tmp == NULL) {
+               DEBUG(1, ("talloc_realloc failed\n"));
+               exit(1);
+       }
+       state->pending = tmp;
+
+       DEBUG(10,("echo_handler[%d]: reading pdu\n", (int)sys_getpid()));
+
+       status = receive_smb_talloc(state->pending, smbd_server_fd(),
+                                   (char **)(void *)&state->pending[num_pending].iov_base,
+                                   0 /* timeout */,
+                                   &unread,
+                                   &encrypted,
+                                   &state->pending[num_pending].iov_len,
+                                   &seqnum,
+                                   false /* trusted_channel*/);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(1, ("echo_handler[%d]: receive_smb_raw_talloc failed: %s\n",
+                         (int)sys_getpid(), nt_errstr(status)));
+               exit(1);
+       }
+
+       ok = smbd_unlock_socket_internal(sconn);
+       if (!ok) {
+               DEBUG(1, ("%s: failed to unlock socket in\n",
+                       __location__));
+               exit(1);
+       }
+
+       /*
+        * place the seqnum in the packet so that the main process can reply
+        * with signing
+        */
+       SIVAL((uint8_t *)state->pending[num_pending].iov_base, smb_ss_field, seqnum);
+       SIVAL((uint8_t *)state->pending[num_pending].iov_base, smb_ss_field+4, NT_STATUS_V(NT_STATUS_OK));
+
+       reply = smbd_echo_reply(smbd_server_fd(),
+                               (uint8_t *)state->pending[num_pending].iov_base,
+                               state->pending[num_pending].iov_len,
+                               seqnum);
+       if (reply) {
+               DEBUG(10,("echo_handler[%d]: replied to client\n", (int)sys_getpid()));
+               /* no check, shrinking by some bytes does not fail */
+               state->pending = talloc_realloc(state, state->pending,
+                                               struct iovec,
+                                               num_pending);
+       } else {
+               DEBUG(10,("echo_handler[%d]: forward to main\n", (int)sys_getpid()));
+               smbd_echo_activate_writer(state);
+       }
+}
+
+static void smbd_echo_loop(struct smbd_server_connection *sconn,
+                          int parent_pipe)
+{
+       struct smbd_echo_state *state;
+
+       state = talloc_zero(sconn, struct smbd_echo_state);
+       if (state == NULL) {
+               DEBUG(1, ("talloc failed\n"));
+               return;
+       }
+       state->sconn = sconn;
+       state->parent_pipe = parent_pipe;
+       state->ev = s3_tevent_context_init(state);
+       if (state->ev == NULL) {
+               DEBUG(1, ("tevent_context_init failed\n"));
+               TALLOC_FREE(state);
+               return;
+       }
+       state->parent_fde = tevent_add_fd(state->ev, state, parent_pipe,
+                                       TEVENT_FD_READ, smbd_echo_exit,
+                                       state);
+       if (state->parent_fde == NULL) {
+               DEBUG(1, ("tevent_add_fd failed\n"));
+               TALLOC_FREE(state);
+               return;
+       }
+       state->read_fde = tevent_add_fd(state->ev, state, smbd_server_fd(),
+                                       TEVENT_FD_READ, smbd_echo_reader,
+                                       state);
+       if (state->read_fde == NULL) {
+               DEBUG(1, ("tevent_add_fd failed\n"));
+               TALLOC_FREE(state);
+               return;
+       }
+
+       while (true) {
+               if (tevent_loop_once(state->ev) == -1) {
+                       DEBUG(1, ("tevent_loop_once failed: %s\n",
+                                 strerror(errno)));
+                       break;
+               }
+       }
+       TALLOC_FREE(state);
+}
+
+/*
+ * Handle SMBecho requests in a forked child process
+ */
+static bool fork_echo_handler(struct smbd_server_connection *sconn)
+{
+       int listener_pipe[2];
+       int res;
+       pid_t child;
+
+       res = pipe(listener_pipe);
+       if (res == -1) {
+               DEBUG(1, ("pipe() failed: %s\n", strerror(errno)));
+               return false;
+       }
+       sconn->smb1.echo_handler.socket_lock_fd = create_unlink_tmp(lp_lockdir());
+       if (sconn->smb1.echo_handler.socket_lock_fd == -1) {
+               DEBUG(1, ("Could not create lock fd: %s\n", strerror(errno)));
+               goto fail;
+       }
+
+       child = sys_fork();
+       if (child == 0) {
+               NTSTATUS status;
+
+               close(listener_pipe[0]);
+
+               status = reinit_after_fork(smbd_messaging_context(),
+                                          smbd_event_context(), false);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(1, ("reinit_after_fork failed: %s\n",
+                                 nt_errstr(status)));
+                       exit(1);
+               }
+               smbd_echo_loop(sconn, listener_pipe[1]);
+               exit(0);
+       }
+       close(listener_pipe[1]);
+       listener_pipe[1] = -1;
+       sconn->smb1.echo_handler.trusted_fd = listener_pipe[0];
+
+       DEBUG(10,("fork_echo_handler: main[%d] echo_child[%d]\n", (int)sys_getpid(), child));
+
+       /*
+        * Without smb signing this is the same as the normal smbd
+        * listener. This needs to change once signing comes in.
+        */
+       sconn->smb1.echo_handler.trusted_fde = event_add_fd(smbd_event_context(),
+                                       sconn,
+                                       sconn->smb1.echo_handler.trusted_fd,
+                                       EVENT_FD_READ,
+                                       smbd_server_echo_handler,
+                                       sconn);
+       if (sconn->smb1.echo_handler.trusted_fde == NULL) {
+               DEBUG(1, ("event_add_fd failed\n"));
+               goto fail;
+       }
+
+       return true;
+
+fail:
+       if (listener_pipe[0] != -1) {
+               close(listener_pipe[0]);
+       }
+       if (listener_pipe[1] != -1) {
+               close(listener_pipe[1]);
+       }
+       sconn->smb1.echo_handler.trusted_fd = -1;
+       if (sconn->smb1.echo_handler.socket_lock_fd != -1) {
+               close(sconn->smb1.echo_handler.socket_lock_fd);
+       }
+       sconn->smb1.echo_handler.trusted_fd = -1;
+       sconn->smb1.echo_handler.socket_lock_fd = -1;
+       return false;
+}
+
 /****************************************************************************
  Process commands from the client
 ****************************************************************************/
@@ -2305,11 +2847,25 @@ static bool housekeeping_fn(const struct timeval *now, void *private_data)
 void smbd_process(void)
 {
        TALLOC_CTX *frame = talloc_stackframe();
-       char remaddr[INET6_ADDRSTRLEN];
+       struct sockaddr_storage ss;
+       struct sockaddr *sa = NULL;
+       socklen_t sa_len;
+       struct tsocket_address *local_address = NULL;
+       struct tsocket_address *remote_address = NULL;
+       const char *remaddr = NULL;
+       int ret;
 
        if (lp_maxprotocol() == PROTOCOL_SMB2 &&
-           lp_security() != SEC_SHARE) {
-               smbd_server_conn->allow_smb2 = true;
+           lp_security() != SEC_SHARE &&
+           !lp_async_smb_echo_handler()) {
+               /*
+                * We're not making the desion here,
+                * we're just allowing the client
+                * to decide between SMB1 and SMB2
+                * with the first negprot
+                * packet.
+                */
+               smbd_server_conn->using_smb2 = true;
        }
 
        /* Ensure child is set to blocking mode */
@@ -2318,12 +2874,57 @@ void smbd_process(void)
        set_socket_options(smbd_server_fd(),"SO_KEEPALIVE");
        set_socket_options(smbd_server_fd(), lp_socket_options());
 
+       sa = (struct sockaddr *)(void *)&ss;
+       sa_len = sizeof(ss);
+       ret = getpeername(smbd_server_fd(), sa, &sa_len);
+       if (ret != 0) {
+               int level = (errno == ENOTCONN)?2:0;
+               DEBUG(level,("getpeername() failed - %s\n", strerror(errno)));
+               exit_server("getpeername() failed.\n");
+       }
+       ret = tsocket_address_bsd_from_sockaddr(smbd_server_conn,
+                                               sa, sa_len,
+                                               &remote_address);
+       if (ret != 0) {
+               DEBUG(0,("%s: tsocket_address_bsd_from_sockaddr remote failed - %s\n",
+                       __location__, strerror(errno)));
+               exit_server("tsocket_address_bsd_from_sockaddr remote failed.\n");
+       }
+
+       sa = (struct sockaddr *)(void *)&ss;
+       sa_len = sizeof(ss);
+       ret = getsockname(smbd_server_fd(), sa, &sa_len);
+       if (ret != 0) {
+               int level = (errno == ENOTCONN)?2:0;
+               DEBUG(level,("getsockname() failed - %s\n", strerror(errno)));
+               exit_server("getsockname() failed.\n");
+       }
+       ret = tsocket_address_bsd_from_sockaddr(smbd_server_conn,
+                                               sa, sa_len,
+                                               &local_address);
+       if (ret != 0) {
+               DEBUG(0,("%s: tsocket_address_bsd_from_sockaddr remote failed - %s\n",
+                       __location__, strerror(errno)));
+               exit_server("tsocket_address_bsd_from_sockaddr remote failed.\n");
+       }
+
+       smbd_server_conn->local_address = local_address;
+       smbd_server_conn->remote_address = remote_address;
+
+       if (tsocket_address_is_inet(remote_address, "ip")) {
+               remaddr = tsocket_address_inet_addr_string(
+                               smbd_server_conn->remote_address,
+                               talloc_tos());
+               if (remaddr == NULL) {
+
+               }
+       } else {
+               remaddr = "0.0.0.0";
+       }
+
        /* 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);
+       set_remote_machine_name(remaddr, false);
        reload_services(true);
 
        /*
@@ -2335,20 +2936,23 @@ 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"
                 */
                unsigned char buf[5] = {0x83, 0, 0, 1, 0x81};
-               DEBUG( 1, ("Connection denied from %s\n",
-                          client_addr(get_client_fd(),addr,sizeof(addr)) ) );
+               DEBUG( 1, ("Connection denied from %s to %s\n",
+                          tsocket_address_string(remote_address, talloc_tos()),
+                          tsocket_address_string(local_address, talloc_tos())));
                (void)srv_send_smb(smbd_server_fd(),(char *)buf, false,
                                   0, false, NULL);
                exit_server_cleanly("connection denied");
        }
 
+       DEBUG(10, ("Connection allowed from %s to %s\n",
+                  tsocket_address_string(remote_address, talloc_tos()),
+                  tsocket_address_string(local_address, talloc_tos())));
+
        static_init_rpc;
 
        init_modules();
@@ -2375,13 +2979,14 @@ void smbd_process(void)
                exit_server("Failed to init smb_signing");
        }
 
+       if (lp_async_smb_echo_handler() && !fork_echo_handler(smbd_server_conn)) {
+               exit_server("Failed to fork echo handler");
+       }
+
        /* 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);
@@ -2471,9 +3076,6 @@ void smbd_process(void)
        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
 
        conn_init(smbd_server_conn);
        if (!init_dptrs(smbd_server_conn)) {