smbd: Move smbd_add_connection to smb2_process.c
authorDavid Mulder <dmulder@suse.com>
Fri, 18 Mar 2022 21:37:17 +0000 (15:37 -0600)
committerJeremy Allison <jra@samba.org>
Thu, 7 Apr 2022 17:37:30 +0000 (17:37 +0000)
Signed-off-by: David Mulder <dmulder@suse.com>
Reviewed-by: Jeremy Allison <jra@samba.org>
source3/smbd/process.c
source3/smbd/proto.h
source3/smbd/smb2_process.c

index 6c4f15d45ce2b2e726502886f81c441da2a0b2f1..797a25810d8fe0c4fe9a2ff76f7b80c12892f010 100644 (file)
@@ -66,7 +66,7 @@ struct pending_message_list {
 
 static bool smb_splice_chain(uint8_t **poutbuf, const uint8_t *andx_buf);
 
-static void smbd_echo_init(struct smbXsrv_connection *xconn)
+void smbd_echo_init(struct smbXsrv_connection *xconn)
 {
        xconn->smb1.echo_handler.trusted_fd = -1;
        xconn->smb1.echo_handler.socket_lock_fd = -1;
@@ -1414,10 +1414,10 @@ static connection_struct *switch_message(uint8_t type, struct smb_request *req)
  Construct a reply to the incoming packet.
 ****************************************************************************/
 
-static void construct_reply(struct smbXsrv_connection *xconn,
-                           char *inbuf, int size, size_t unread_bytes,
-                           uint32_t seqnum, bool encrypted,
-                           struct smb_perfcount_data *deferred_pcd)
+void construct_reply(struct smbXsrv_connection *xconn,
+                    char *inbuf, int size, size_t unread_bytes,
+                    uint32_t seqnum, bool encrypted,
+                    struct smb_perfcount_data *deferred_pcd)
 {
        struct smbd_server_connection *sconn = xconn->client->sconn;
        struct smb_request *req;
@@ -2150,126 +2150,8 @@ static void smbd_server_connection_write_handler(
        /* TODO: make write nonblocking */
 }
 
-static void smbd_smb2_server_connection_read_handler(
-                       struct smbXsrv_connection *xconn, int fd)
-{
-       char lenbuf[NBT_HDR_SIZE];
-       size_t len = 0;
-       uint8_t *buffer = NULL;
-       size_t bufferlen = 0;
-       NTSTATUS status;
-       uint8_t msg_type = 0;
-
-       /* Read the first 4 bytes - contains length of remainder. */
-       status = read_smb_length_return_keepalive(fd, lenbuf, 0, &len);
-       if (!NT_STATUS_IS_OK(status)) {
-               exit_server_cleanly("failed to receive request length");
-               return;
-       }
-
-       /* Integer wrap check. */
-       if (len + NBT_HDR_SIZE < len) {
-               exit_server_cleanly("Invalid length on initial request");
-               return;
-       }
-
-       /*
-        * The +4 here can't wrap, we've checked the length above already.
-        */
-       bufferlen = len+NBT_HDR_SIZE;
-
-       buffer = talloc_array(talloc_tos(), uint8_t, bufferlen);
-       if (buffer == NULL) {
-               DBG_ERR("Could not allocate request inbuf of length %zu\n",
-                       bufferlen);
-                exit_server_cleanly("talloc fail");
-               return;
-       }
-
-       /* Copy the NBT_HDR_SIZE length. */
-       memcpy(buffer, lenbuf, sizeof(lenbuf));
-
-       status = read_packet_remainder(fd, (char *)buffer+NBT_HDR_SIZE, 0, len);
-       if (!NT_STATUS_IS_OK(status)) {
-               exit_server_cleanly("Failed to read remainder of initial request");
-               return;
-       }
-
-       /* Check the message type. */
-       msg_type = PULL_LE_U8(buffer,0);
-       if (msg_type == NBSSrequest) {
-               /*
-                * clients can send this request before
-                * bootstrapping into SMB2. Cope with this
-                * message only, don't allow any other strange
-                * NBSS types.
-                */
-               reply_special(xconn, (char *)buffer, bufferlen);
-               xconn->client->sconn->num_requests++;
-               return;
-       }
-
-       /* Only a 'normal' message type allowed now. */
-       if (msg_type != NBSSmessage) {
-               DBG_ERR("Invalid message type %d\n", msg_type);
-               exit_server_cleanly("Invalid message type for initial request");
-               return;
-       }
-
-       /* Could this be an SMB1 negprot bootstrap into SMB2 ? */
-       if (bufferlen < smb_size) {
-               exit_server_cleanly("Invalid initial SMB1 or SMB2 packet");
-               return;
-       }
-#if defined(WITH_SMB1SERVER)
-       if (valid_smb_header(buffer)) {
-               /* Can *only* allow an SMB1 negprot here. */
-               uint8_t cmd = PULL_LE_U8(buffer, smb_com);
-               if (cmd != SMBnegprot) {
-                       DBG_ERR("Incorrect SMB1 command 0x%hhx, "
-                               "should be SMBnegprot (0x72)\n",
-                               cmd);
-                       exit_server_cleanly("Invalid initial SMB1 packet");
-               }
-               /* Minimal process_smb(). */
-               show_msg((char *)buffer);
-               construct_reply(xconn,
-                               (char *)buffer,
-                               bufferlen,
-                               0,
-                               0,
-                               false,
-                               NULL);
-               xconn->client->sconn->trans_num++;
-               xconn->client->sconn->num_requests++;
-               return;
-
-       } else
-#endif
-       if (!smbd_is_smb2_header(buffer, bufferlen)) {
-               exit_server_cleanly("Invalid initial SMB2 packet");
-               return;
-       }
-
-       /* Here we know we're a valid SMB2 packet. */
-
-       /*
-        * Point at the start of the SMB2 PDU.
-        * len is the length of the SMB2 PDU.
-        */
-
-       status = smbd_smb2_process_negprot(xconn,
-                                          0,
-                                          (const uint8_t *)buffer+NBT_HDR_SIZE,
-                                          len);
-       if (!NT_STATUS_IS_OK(status)) {
-               exit_server_cleanly("SMB2 negprot fail");
-       }
-       return;
-}
-
-static void smbd_smb1_server_connection_read_handler(
-       struct smbXsrv_connection *xconn, int fd)
+void smbd_smb1_server_connection_read_handler(struct smbXsrv_connection *xconn,
+                                             int fd)
 {
        uint8_t *inbuf = NULL;
        size_t inbuf_len = 0;
@@ -2333,44 +2215,6 @@ process:
                    seqnum, encrypted, NULL);
 }
 
-static void smbd_server_connection_handler(struct tevent_context *ev,
-                                          struct tevent_fd *fde,
-                                          uint16_t flags,
-                                          void *private_data)
-{
-       struct smbXsrv_connection *xconn =
-               talloc_get_type_abort(private_data,
-               struct smbXsrv_connection);
-
-       if (!NT_STATUS_IS_OK(xconn->transport.status)) {
-               /*
-                * we're not supposed to do any io
-                */
-               TEVENT_FD_NOT_READABLE(xconn->transport.fde);
-               TEVENT_FD_NOT_WRITEABLE(xconn->transport.fde);
-               return;
-       }
-
-       if (flags & TEVENT_FD_WRITE) {
-               smbd_server_connection_write_handler(xconn);
-               return;
-       }
-       if (flags & TEVENT_FD_READ) {
-#if defined(WITH_SMB1SERVER)
-               if (lp_server_min_protocol() > PROTOCOL_NT1) {
-#endif
-                       smbd_smb2_server_connection_read_handler(xconn,
-                                               xconn->transport.sock);
-#if defined(WITH_SMB1SERVER)
-               } else {
-                       smbd_smb1_server_connection_read_handler(xconn,
-                                               xconn->transport.sock);
-               }
-#endif
-               return;
-       }
-}
-
 static void smbd_server_echo_handler(struct tevent_context *ev,
                                     struct tevent_fd *fde,
                                     uint16_t flags,
@@ -2400,180 +2244,6 @@ static void smbd_server_echo_handler(struct tevent_context *ev,
        }
 }
 
-struct smbd_release_ip_state {
-       struct smbXsrv_connection *xconn;
-       struct tevent_immediate *im;
-       char addr[INET6_ADDRSTRLEN];
-};
-
-static void smbd_release_ip_immediate(struct tevent_context *ctx,
-                                     struct tevent_immediate *im,
-                                     void *private_data)
-{
-       struct smbd_release_ip_state *state =
-               talloc_get_type_abort(private_data,
-               struct smbd_release_ip_state);
-       struct smbXsrv_connection *xconn = state->xconn;
-
-       if (!NT_STATUS_EQUAL(xconn->transport.status, NT_STATUS_ADDRESS_CLOSED)) {
-               /*
-                * smbd_server_connection_terminate() already triggered ?
-                */
-               return;
-       }
-
-       smbd_server_connection_terminate(xconn, "CTDB_SRVID_RELEASE_IP");
-}
-
-/****************************************************************************
-received when we should release a specific IP
-****************************************************************************/
-static int release_ip(struct tevent_context *ev,
-                     uint32_t src_vnn, uint32_t dst_vnn,
-                     uint64_t dst_srvid,
-                     const uint8_t *msg, size_t msglen,
-                     void *private_data)
-{
-       struct smbd_release_ip_state *state =
-               talloc_get_type_abort(private_data,
-               struct smbd_release_ip_state);
-       struct smbXsrv_connection *xconn = state->xconn;
-       const char *ip;
-       const char *addr = state->addr;
-       const char *p = addr;
-
-       if (msglen == 0) {
-               return 0;
-       }
-       if (msg[msglen-1] != '\0') {
-               return 0;
-       }
-
-       ip = (const char *)msg;
-
-       if (!NT_STATUS_IS_OK(xconn->transport.status)) {
-               /* avoid recursion */
-               return 0;
-       }
-
-       if (strncmp("::ffff:", addr, 7) == 0) {
-               p = addr + 7;
-       }
-
-       DEBUG(10, ("Got release IP message for %s, "
-                  "our address is %s\n", ip, p));
-
-       if ((strcmp(p, ip) == 0) || ((p != addr) && strcmp(addr, ip) == 0)) {
-               DEBUG(0,("Got release IP message for our IP %s - exiting immediately\n",
-                       ip));
-               /*
-                * With SMB2 we should do a clean disconnect,
-                * the previous_session_id in the session setup
-                * will cleanup the old session, tcons and opens.
-                *
-                * A clean disconnect is needed in order to support
-                * durable handles.
-                *
-                * Note: typically this is never triggered
-                *       as we got a TCP RST (triggered by ctdb event scripts)
-                *       before we get CTDB_SRVID_RELEASE_IP.
-                *
-                * We used to call _exit(1) here, but as this was mostly never
-                * triggered and has implication on our process model,
-                * we can just use smbd_server_connection_terminate()
-                * (also for SMB1).
-                *
-                * We don't call smbd_server_connection_terminate() directly
-                * as we might be called from within ctdbd_migrate(),
-                * we need to defer our action to the next event loop
-                */
-               tevent_schedule_immediate(state->im,
-                                         xconn->client->raw_ev_ctx,
-                                         smbd_release_ip_immediate,
-                                         state);
-
-               /*
-                * Make sure we don't get any io on the connection.
-                */
-               xconn->transport.status = NT_STATUS_ADDRESS_CLOSED;
-               return EADDRNOTAVAIL;
-       }
-
-       return 0;
-}
-
-static int match_cluster_movable_ip(uint32_t total_ip_count,
-                                   const struct sockaddr_storage *ip,
-                                   bool is_movable_ip,
-                                   void *private_data)
-{
-       const struct sockaddr_storage *srv = private_data;
-       struct samba_sockaddr pub_ip = {
-               .u = {
-                       .ss = *ip,
-               },
-       };
-       struct samba_sockaddr srv_ip = {
-               .u = {
-                       .ss = *srv,
-               },
-       };
-
-       if (is_movable_ip && sockaddr_equal(&pub_ip.u.sa, &srv_ip.u.sa)) {
-               return EADDRNOTAVAIL;
-       }
-
-       return 0;
-}
-
-static NTSTATUS smbd_register_ips(struct smbXsrv_connection *xconn,
-                                 struct sockaddr_storage *srv,
-                                 struct sockaddr_storage *clnt)
-{
-       struct smbd_release_ip_state *state;
-       struct ctdbd_connection *cconn;
-       int ret;
-
-       cconn = messaging_ctdb_connection();
-       if (cconn == NULL) {
-               return NT_STATUS_NO_MEMORY;
-       }
-
-       state = talloc_zero(xconn, struct smbd_release_ip_state);
-       if (state == NULL) {
-               return NT_STATUS_NO_MEMORY;
-       }
-       state->xconn = xconn;
-       state->im = tevent_create_immediate(state);
-       if (state->im == NULL) {
-               return NT_STATUS_NO_MEMORY;
-       }
-       if (print_sockaddr(state->addr, sizeof(state->addr), srv) == NULL) {
-               return NT_STATUS_NO_MEMORY;
-       }
-
-       if (xconn->client->server_multi_channel_enabled) {
-               ret = ctdbd_public_ip_foreach(cconn,
-                                             match_cluster_movable_ip,
-                                             srv);
-               if (ret == EADDRNOTAVAIL) {
-                       xconn->has_cluster_movable_ip = true;
-                       DBG_DEBUG("cluster movable IP on %s\n",
-                                 smbXsrv_connection_dbg(xconn));
-               } else if (ret != 0) {
-                       DBG_ERR("failed to iterate cluster IPs: %s\n",
-                               strerror(ret));
-                       return NT_STATUS_INTERNAL_ERROR;
-               }
-       }
-
-       ret = ctdbd_register_ips(cconn, srv, clnt, release_ip, state);
-       if (ret != 0) {
-               return map_nt_error_from_unix(ret);
-       }
-       return NT_STATUS_OK;
-}
-
 static void msg_kill_client_ip(struct messaging_context *msg_ctx,
                                  void *private_data, uint32_t msg_type,
                                  struct server_id server_id, DATA_BLOB *data)
@@ -3411,223 +3081,6 @@ static void smbd_tevent_trace_callback(enum tevent_trace_point point,
        errno = 0;
 }
 
-static int smbXsrv_connection_destructor(struct smbXsrv_connection *xconn)
-{
-       DBG_DEBUG("xconn[%s]\n", smbXsrv_connection_dbg(xconn));
-       return 0;
-}
-
-NTSTATUS smbd_add_connection(struct smbXsrv_client *client, int sock_fd,
-                            NTTIME now, struct smbXsrv_connection **_xconn)
-{
-       TALLOC_CTX *frame = talloc_stackframe();
-       struct smbXsrv_connection *xconn;
-       struct sockaddr_storage ss_srv;
-       void *sp_srv = (void *)&ss_srv;
-       struct sockaddr *sa_srv = (struct sockaddr *)sp_srv;
-       struct sockaddr_storage ss_clnt;
-       void *sp_clnt = (void *)&ss_clnt;
-       struct sockaddr *sa_clnt = (struct sockaddr *)sp_clnt;
-       socklen_t sa_socklen;
-       struct tsocket_address *local_address = NULL;
-       struct tsocket_address *remote_address = NULL;
-       const char *remaddr = NULL;
-       char *p;
-       const char *rhost = NULL;
-       int ret;
-       int tmp;
-
-       *_xconn = NULL;
-
-       DO_PROFILE_INC(connect);
-
-       xconn = talloc_zero(client, struct smbXsrv_connection);
-       if (xconn == NULL) {
-               DEBUG(0,("talloc_zero(struct smbXsrv_connection)\n"));
-               TALLOC_FREE(frame);
-               return NT_STATUS_NO_MEMORY;
-       }
-       talloc_set_destructor(xconn, smbXsrv_connection_destructor);
-       talloc_steal(frame, xconn);
-       xconn->client = client;
-       xconn->connect_time = now;
-       if (client->next_channel_id != 0) {
-               xconn->channel_id = client->next_channel_id++;
-       }
-
-       xconn->transport.sock = sock_fd;
-#if defined(WITH_SMB1SERVER)
-       smbd_echo_init(xconn);
-#endif
-       xconn->protocol = PROTOCOL_NONE;
-
-       /* Ensure child is set to blocking mode */
-       set_blocking(sock_fd,True);
-
-       set_socket_options(sock_fd, "SO_KEEPALIVE");
-       set_socket_options(sock_fd, lp_socket_options());
-
-       sa_socklen = sizeof(ss_clnt);
-       ret = getpeername(sock_fd, sa_clnt, &sa_socklen);
-       if (ret != 0) {
-               int saved_errno = errno;
-               int level = (errno == ENOTCONN)?2:0;
-               DEBUG(level,("getpeername() failed - %s\n",
-                     strerror(saved_errno)));
-               TALLOC_FREE(frame);
-               return map_nt_error_from_unix_common(saved_errno);
-       }
-       ret = tsocket_address_bsd_from_sockaddr(xconn,
-                                               sa_clnt, sa_socklen,
-                                               &remote_address);
-       if (ret != 0) {
-               int saved_errno = errno;
-               DEBUG(0,("%s: tsocket_address_bsd_from_sockaddr remote failed - %s\n",
-                       __location__, strerror(saved_errno)));
-               TALLOC_FREE(frame);
-               return map_nt_error_from_unix_common(saved_errno);
-       }
-
-       sa_socklen = sizeof(ss_srv);
-       ret = getsockname(sock_fd, sa_srv, &sa_socklen);
-       if (ret != 0) {
-               int saved_errno = errno;
-               int level = (errno == ENOTCONN)?2:0;
-               DEBUG(level,("getsockname() failed - %s\n",
-                     strerror(saved_errno)));
-               TALLOC_FREE(frame);
-               return map_nt_error_from_unix_common(saved_errno);
-       }
-       ret = tsocket_address_bsd_from_sockaddr(xconn,
-                                               sa_srv, sa_socklen,
-                                               &local_address);
-       if (ret != 0) {
-               int saved_errno = errno;
-               DEBUG(0,("%s: tsocket_address_bsd_from_sockaddr remote failed - %s\n",
-                       __location__, strerror(saved_errno)));
-               TALLOC_FREE(frame);
-               return map_nt_error_from_unix_common(saved_errno);
-       }
-
-       if (tsocket_address_is_inet(remote_address, "ip")) {
-               remaddr = tsocket_address_inet_addr_string(remote_address,
-                                                          talloc_tos());
-               if (remaddr == NULL) {
-                       DEBUG(0,("%s: tsocket_address_inet_addr_string remote failed - %s\n",
-                                __location__, strerror(errno)));
-                       TALLOC_FREE(frame);
-                       return NT_STATUS_NO_MEMORY;
-               }
-       } else {
-               remaddr = "0.0.0.0";
-       }
-
-       /*
-        * Before the first packet, check the global hosts allow/ hosts deny
-        * parameters before doing any parsing of packets passed to us by the
-        * client. This prevents attacks on our parsing code from hosts not in
-        * the hosts allow list.
-        */
-
-       ret = get_remote_hostname(remote_address,
-                                 &p, talloc_tos());
-       if (ret < 0) {
-               int saved_errno = errno;
-               DEBUG(0,("%s: get_remote_hostname failed - %s\n",
-                       __location__, strerror(saved_errno)));
-               TALLOC_FREE(frame);
-               return map_nt_error_from_unix_common(saved_errno);
-       }
-       rhost = p;
-       if (strequal(rhost, "UNKNOWN")) {
-               rhost = remaddr;
-       }
-
-       xconn->local_address = local_address;
-       xconn->remote_address = remote_address;
-       xconn->remote_hostname = talloc_strdup(xconn, rhost);
-       if (xconn->remote_hostname == NULL) {
-               return NT_STATUS_NO_MEMORY;
-       }
-
-       if (!srv_init_signing(xconn)) {
-               DEBUG(0, ("Failed to init smb_signing\n"));
-               TALLOC_FREE(frame);
-               return NT_STATUS_INTERNAL_ERROR;
-       }
-
-       if (!allow_access(lp_hosts_deny(-1), lp_hosts_allow(-1),
-                         xconn->remote_hostname,
-                         remaddr)) {
-               DEBUG( 1, ("Connection denied from %s to %s\n",
-                          tsocket_address_string(remote_address, talloc_tos()),
-                          tsocket_address_string(local_address, talloc_tos())));
-
-               /*
-                * We return a valid xconn
-                * so that the caller can return an error message
-                * to the client
-                */
-               DLIST_ADD_END(client->connections, xconn);
-               talloc_steal(client, xconn);
-
-               *_xconn = xconn;
-               TALLOC_FREE(frame);
-               return NT_STATUS_NETWORK_ACCESS_DENIED;
-       }
-
-       DEBUG(10, ("Connection allowed from %s to %s\n",
-                  tsocket_address_string(remote_address, talloc_tos()),
-                  tsocket_address_string(local_address, talloc_tos())));
-
-       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.
-                */
-               NTSTATUS status;
-
-               status = smbd_register_ips(xconn, &ss_srv, &ss_clnt);
-               if (!NT_STATUS_IS_OK(status)) {
-                       DEBUG(0, ("ctdbd_register_ips failed: %s\n",
-                                 nt_errstr(status)));
-               }
-       }
-
-       tmp = lp_max_xmit();
-       tmp = MAX(tmp, SMB_BUFFER_SIZE_MIN);
-       tmp = MIN(tmp, SMB_BUFFER_SIZE_MAX);
-
-#if defined(WITH_SMB1SERVER)
-       xconn->smb1.negprot.max_recv = tmp;
-
-       xconn->smb1.sessions.done_sesssetup = false;
-       xconn->smb1.sessions.max_send = SMB_BUFFER_SIZE_MAX;
-#endif
-
-       xconn->transport.fde = tevent_add_fd(client->raw_ev_ctx,
-                                            xconn,
-                                            sock_fd,
-                                            TEVENT_FD_READ,
-                                            smbd_server_connection_handler,
-                                            xconn);
-       if (!xconn->transport.fde) {
-               TALLOC_FREE(frame);
-               return NT_STATUS_NO_MEMORY;
-       }
-       tevent_fd_set_auto_close(xconn->transport.fde);
-
-       /* for now we only have one connection */
-       DLIST_ADD_END(client->connections, xconn);
-       talloc_steal(client, xconn);
-
-       *_xconn = xconn;
-       TALLOC_FREE(frame);
-       return NT_STATUS_OK;
-}
-
 /****************************************************************************
  Process commands from the client
 ****************************************************************************/
index 910d33f2a92c40b7cfd0974f16cd5d09048ee242..86aa2ae0a13a1147ad76f74efa0f7ec7fa25f340 100644 (file)
@@ -892,6 +892,13 @@ void process_smb1(struct smbXsrv_connection *xconn,
                  uint32_t seqnum, bool encrypted,
                  struct smb_perfcount_data *deferred_pcd);
 bool valid_smb_header(const uint8_t *inbuf);
+void smbd_echo_init(struct smbXsrv_connection *xconn);
+void construct_reply(struct smbXsrv_connection *xconn,
+                    char *inbuf, int size, size_t unread_bytes,
+                    uint32_t seqnum, bool encrypted,
+                    struct smb_perfcount_data *deferred_pcd);
+void smbd_smb1_server_connection_read_handler(struct smbXsrv_connection *xconn,
+                                             int fd);
 
 /* The following definitions come from smbd/smb2_process.c  */
 
index 29e4a622c6a995441da46dd6ca940993f485fe1f..c97e188e23201b05951fd5e643f65e667c67aeb5 100644 (file)
@@ -731,3 +731,556 @@ const char *smbXsrv_connection_dbg(const struct smbXsrv_connection *xconn)
 
        return ret;
 }
+
+static void smbd_server_connection_write_handler(
+       struct smbXsrv_connection *xconn)
+{
+       /* TODO: make write nonblocking */
+}
+
+static void smbd_smb2_server_connection_read_handler(
+                       struct smbXsrv_connection *xconn, int fd)
+{
+       char lenbuf[NBT_HDR_SIZE];
+       size_t len = 0;
+       uint8_t *buffer = NULL;
+       size_t bufferlen = 0;
+       NTSTATUS status;
+       uint8_t msg_type = 0;
+
+       /* Read the first 4 bytes - contains length of remainder. */
+       status = read_smb_length_return_keepalive(fd, lenbuf, 0, &len);
+       if (!NT_STATUS_IS_OK(status)) {
+               exit_server_cleanly("failed to receive request length");
+               return;
+       }
+
+       /* Integer wrap check. */
+       if (len + NBT_HDR_SIZE < len) {
+               exit_server_cleanly("Invalid length on initial request");
+               return;
+       }
+
+       /*
+        * The +4 here can't wrap, we've checked the length above already.
+        */
+       bufferlen = len+NBT_HDR_SIZE;
+
+       buffer = talloc_array(talloc_tos(), uint8_t, bufferlen);
+       if (buffer == NULL) {
+               DBG_ERR("Could not allocate request inbuf of length %zu\n",
+                       bufferlen);
+                exit_server_cleanly("talloc fail");
+               return;
+       }
+
+       /* Copy the NBT_HDR_SIZE length. */
+       memcpy(buffer, lenbuf, sizeof(lenbuf));
+
+       status = read_packet_remainder(fd, (char *)buffer+NBT_HDR_SIZE, 0, len);
+       if (!NT_STATUS_IS_OK(status)) {
+               exit_server_cleanly("Failed to read remainder of initial request");
+               return;
+       }
+
+       /* Check the message type. */
+       msg_type = PULL_LE_U8(buffer,0);
+       if (msg_type == NBSSrequest) {
+               /*
+                * clients can send this request before
+                * bootstrapping into SMB2. Cope with this
+                * message only, don't allow any other strange
+                * NBSS types.
+                */
+               reply_special(xconn, (char *)buffer, bufferlen);
+               xconn->client->sconn->num_requests++;
+               return;
+       }
+
+       /* Only a 'normal' message type allowed now. */
+       if (msg_type != NBSSmessage) {
+               DBG_ERR("Invalid message type %d\n", msg_type);
+               exit_server_cleanly("Invalid message type for initial request");
+               return;
+       }
+
+       /* Could this be an SMB1 negprot bootstrap into SMB2 ? */
+       if (bufferlen < smb_size) {
+               exit_server_cleanly("Invalid initial SMB1 or SMB2 packet");
+               return;
+       }
+#if defined(WITH_SMB1SERVER)
+       if (valid_smb_header(buffer)) {
+               /* Can *only* allow an SMB1 negprot here. */
+               uint8_t cmd = PULL_LE_U8(buffer, smb_com);
+               if (cmd != SMBnegprot) {
+                       DBG_ERR("Incorrect SMB1 command 0x%hhx, "
+                               "should be SMBnegprot (0x72)\n",
+                               cmd);
+                       exit_server_cleanly("Invalid initial SMB1 packet");
+               }
+               /* Minimal process_smb(). */
+               show_msg((char *)buffer);
+               construct_reply(xconn,
+                               (char *)buffer,
+                               bufferlen,
+                               0,
+                               0,
+                               false,
+                               NULL);
+               xconn->client->sconn->trans_num++;
+               xconn->client->sconn->num_requests++;
+               return;
+
+       } else
+#endif
+       if (!smbd_is_smb2_header(buffer, bufferlen)) {
+               exit_server_cleanly("Invalid initial SMB2 packet");
+               return;
+       }
+
+       /* Here we know we're a valid SMB2 packet. */
+
+       /*
+        * Point at the start of the SMB2 PDU.
+        * len is the length of the SMB2 PDU.
+        */
+
+       status = smbd_smb2_process_negprot(xconn,
+                                          0,
+                                          (const uint8_t *)buffer+NBT_HDR_SIZE,
+                                          len);
+       if (!NT_STATUS_IS_OK(status)) {
+               exit_server_cleanly("SMB2 negprot fail");
+       }
+       return;
+}
+
+static void smbd_server_connection_handler(struct tevent_context *ev,
+                                          struct tevent_fd *fde,
+                                          uint16_t flags,
+                                          void *private_data)
+{
+       struct smbXsrv_connection *xconn =
+               talloc_get_type_abort(private_data,
+               struct smbXsrv_connection);
+
+       if (!NT_STATUS_IS_OK(xconn->transport.status)) {
+               /*
+                * we're not supposed to do any io
+                */
+               TEVENT_FD_NOT_READABLE(xconn->transport.fde);
+               TEVENT_FD_NOT_WRITEABLE(xconn->transport.fde);
+               return;
+       }
+
+       if (flags & TEVENT_FD_WRITE) {
+               smbd_server_connection_write_handler(xconn);
+               return;
+       }
+       if (flags & TEVENT_FD_READ) {
+#if defined(WITH_SMB1SERVER)
+               if (lp_server_min_protocol() > PROTOCOL_NT1) {
+#endif
+                       smbd_smb2_server_connection_read_handler(xconn,
+                                               xconn->transport.sock);
+#if defined(WITH_SMB1SERVER)
+               } else {
+                       smbd_smb1_server_connection_read_handler(xconn,
+                                               xconn->transport.sock);
+               }
+#endif
+               return;
+       }
+}
+
+struct smbd_release_ip_state {
+       struct smbXsrv_connection *xconn;
+       struct tevent_immediate *im;
+       char addr[INET6_ADDRSTRLEN];
+};
+
+static void smbd_release_ip_immediate(struct tevent_context *ctx,
+                                     struct tevent_immediate *im,
+                                     void *private_data)
+{
+       struct smbd_release_ip_state *state =
+               talloc_get_type_abort(private_data,
+               struct smbd_release_ip_state);
+       struct smbXsrv_connection *xconn = state->xconn;
+
+       if (!NT_STATUS_EQUAL(xconn->transport.status, NT_STATUS_ADDRESS_CLOSED)) {
+               /*
+                * smbd_server_connection_terminate() already triggered ?
+                */
+               return;
+       }
+
+       smbd_server_connection_terminate(xconn, "CTDB_SRVID_RELEASE_IP");
+}
+
+/****************************************************************************
+received when we should release a specific IP
+****************************************************************************/
+static int release_ip(struct tevent_context *ev,
+                     uint32_t src_vnn, uint32_t dst_vnn,
+                     uint64_t dst_srvid,
+                     const uint8_t *msg, size_t msglen,
+                     void *private_data)
+{
+       struct smbd_release_ip_state *state =
+               talloc_get_type_abort(private_data,
+               struct smbd_release_ip_state);
+       struct smbXsrv_connection *xconn = state->xconn;
+       const char *ip;
+       const char *addr = state->addr;
+       const char *p = addr;
+
+       if (msglen == 0) {
+               return 0;
+       }
+       if (msg[msglen-1] != '\0') {
+               return 0;
+       }
+
+       ip = (const char *)msg;
+
+       if (!NT_STATUS_IS_OK(xconn->transport.status)) {
+               /* avoid recursion */
+               return 0;
+       }
+
+       if (strncmp("::ffff:", addr, 7) == 0) {
+               p = addr + 7;
+       }
+
+       DEBUG(10, ("Got release IP message for %s, "
+                  "our address is %s\n", ip, p));
+
+       if ((strcmp(p, ip) == 0) || ((p != addr) && strcmp(addr, ip) == 0)) {
+               DEBUG(0,("Got release IP message for our IP %s - exiting immediately\n",
+                       ip));
+               /*
+                * With SMB2 we should do a clean disconnect,
+                * the previous_session_id in the session setup
+                * will cleanup the old session, tcons and opens.
+                *
+                * A clean disconnect is needed in order to support
+                * durable handles.
+                *
+                * Note: typically this is never triggered
+                *       as we got a TCP RST (triggered by ctdb event scripts)
+                *       before we get CTDB_SRVID_RELEASE_IP.
+                *
+                * We used to call _exit(1) here, but as this was mostly never
+                * triggered and has implication on our process model,
+                * we can just use smbd_server_connection_terminate()
+                * (also for SMB1).
+                *
+                * We don't call smbd_server_connection_terminate() directly
+                * as we might be called from within ctdbd_migrate(),
+                * we need to defer our action to the next event loop
+                */
+               tevent_schedule_immediate(state->im,
+                                         xconn->client->raw_ev_ctx,
+                                         smbd_release_ip_immediate,
+                                         state);
+
+               /*
+                * Make sure we don't get any io on the connection.
+                */
+               xconn->transport.status = NT_STATUS_ADDRESS_CLOSED;
+               return EADDRNOTAVAIL;
+       }
+
+       return 0;
+}
+
+static int match_cluster_movable_ip(uint32_t total_ip_count,
+                                   const struct sockaddr_storage *ip,
+                                   bool is_movable_ip,
+                                   void *private_data)
+{
+       const struct sockaddr_storage *srv = private_data;
+       struct samba_sockaddr pub_ip = {
+               .u = {
+                       .ss = *ip,
+               },
+       };
+       struct samba_sockaddr srv_ip = {
+               .u = {
+                       .ss = *srv,
+               },
+       };
+
+       if (is_movable_ip && sockaddr_equal(&pub_ip.u.sa, &srv_ip.u.sa)) {
+               return EADDRNOTAVAIL;
+       }
+
+       return 0;
+}
+
+static NTSTATUS smbd_register_ips(struct smbXsrv_connection *xconn,
+                                 struct sockaddr_storage *srv,
+                                 struct sockaddr_storage *clnt)
+{
+       struct smbd_release_ip_state *state;
+       struct ctdbd_connection *cconn;
+       int ret;
+
+       cconn = messaging_ctdb_connection();
+       if (cconn == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       state = talloc_zero(xconn, struct smbd_release_ip_state);
+       if (state == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       state->xconn = xconn;
+       state->im = tevent_create_immediate(state);
+       if (state->im == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       if (print_sockaddr(state->addr, sizeof(state->addr), srv) == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (xconn->client->server_multi_channel_enabled) {
+               ret = ctdbd_public_ip_foreach(cconn,
+                                             match_cluster_movable_ip,
+                                             srv);
+               if (ret == EADDRNOTAVAIL) {
+                       xconn->has_cluster_movable_ip = true;
+                       DBG_DEBUG("cluster movable IP on %s\n",
+                                 smbXsrv_connection_dbg(xconn));
+               } else if (ret != 0) {
+                       DBG_ERR("failed to iterate cluster IPs: %s\n",
+                               strerror(ret));
+                       return NT_STATUS_INTERNAL_ERROR;
+               }
+       }
+
+       ret = ctdbd_register_ips(cconn, srv, clnt, release_ip, state);
+       if (ret != 0) {
+               return map_nt_error_from_unix(ret);
+       }
+       return NT_STATUS_OK;
+}
+
+static int smbXsrv_connection_destructor(struct smbXsrv_connection *xconn)
+{
+       DBG_DEBUG("xconn[%s]\n", smbXsrv_connection_dbg(xconn));
+       return 0;
+}
+
+NTSTATUS smbd_add_connection(struct smbXsrv_client *client, int sock_fd,
+                            NTTIME now, struct smbXsrv_connection **_xconn)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct smbXsrv_connection *xconn;
+       struct sockaddr_storage ss_srv;
+       void *sp_srv = (void *)&ss_srv;
+       struct sockaddr *sa_srv = (struct sockaddr *)sp_srv;
+       struct sockaddr_storage ss_clnt;
+       void *sp_clnt = (void *)&ss_clnt;
+       struct sockaddr *sa_clnt = (struct sockaddr *)sp_clnt;
+       socklen_t sa_socklen;
+       struct tsocket_address *local_address = NULL;
+       struct tsocket_address *remote_address = NULL;
+       const char *remaddr = NULL;
+       char *p;
+       const char *rhost = NULL;
+       int ret;
+       int tmp;
+
+       *_xconn = NULL;
+
+       DO_PROFILE_INC(connect);
+
+       xconn = talloc_zero(client, struct smbXsrv_connection);
+       if (xconn == NULL) {
+               DEBUG(0,("talloc_zero(struct smbXsrv_connection)\n"));
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+       talloc_set_destructor(xconn, smbXsrv_connection_destructor);
+       talloc_steal(frame, xconn);
+       xconn->client = client;
+       xconn->connect_time = now;
+       if (client->next_channel_id != 0) {
+               xconn->channel_id = client->next_channel_id++;
+       }
+
+       xconn->transport.sock = sock_fd;
+#if defined(WITH_SMB1SERVER)
+       smbd_echo_init(xconn);
+#endif
+       xconn->protocol = PROTOCOL_NONE;
+
+       /* Ensure child is set to blocking mode */
+       set_blocking(sock_fd,True);
+
+       set_socket_options(sock_fd, "SO_KEEPALIVE");
+       set_socket_options(sock_fd, lp_socket_options());
+
+       sa_socklen = sizeof(ss_clnt);
+       ret = getpeername(sock_fd, sa_clnt, &sa_socklen);
+       if (ret != 0) {
+               int saved_errno = errno;
+               int level = (errno == ENOTCONN)?2:0;
+               DEBUG(level,("getpeername() failed - %s\n",
+                     strerror(saved_errno)));
+               TALLOC_FREE(frame);
+               return map_nt_error_from_unix_common(saved_errno);
+       }
+       ret = tsocket_address_bsd_from_sockaddr(xconn,
+                                               sa_clnt, sa_socklen,
+                                               &remote_address);
+       if (ret != 0) {
+               int saved_errno = errno;
+               DEBUG(0,("%s: tsocket_address_bsd_from_sockaddr remote failed - %s\n",
+                       __location__, strerror(saved_errno)));
+               TALLOC_FREE(frame);
+               return map_nt_error_from_unix_common(saved_errno);
+       }
+
+       sa_socklen = sizeof(ss_srv);
+       ret = getsockname(sock_fd, sa_srv, &sa_socklen);
+       if (ret != 0) {
+               int saved_errno = errno;
+               int level = (errno == ENOTCONN)?2:0;
+               DEBUG(level,("getsockname() failed - %s\n",
+                     strerror(saved_errno)));
+               TALLOC_FREE(frame);
+               return map_nt_error_from_unix_common(saved_errno);
+       }
+       ret = tsocket_address_bsd_from_sockaddr(xconn,
+                                               sa_srv, sa_socklen,
+                                               &local_address);
+       if (ret != 0) {
+               int saved_errno = errno;
+               DEBUG(0,("%s: tsocket_address_bsd_from_sockaddr remote failed - %s\n",
+                       __location__, strerror(saved_errno)));
+               TALLOC_FREE(frame);
+               return map_nt_error_from_unix_common(saved_errno);
+       }
+
+       if (tsocket_address_is_inet(remote_address, "ip")) {
+               remaddr = tsocket_address_inet_addr_string(remote_address,
+                                                          talloc_tos());
+               if (remaddr == NULL) {
+                       DEBUG(0,("%s: tsocket_address_inet_addr_string remote failed - %s\n",
+                                __location__, strerror(errno)));
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_NO_MEMORY;
+               }
+       } else {
+               remaddr = "0.0.0.0";
+       }
+
+       /*
+        * Before the first packet, check the global hosts allow/ hosts deny
+        * parameters before doing any parsing of packets passed to us by the
+        * client. This prevents attacks on our parsing code from hosts not in
+        * the hosts allow list.
+        */
+
+       ret = get_remote_hostname(remote_address,
+                                 &p, talloc_tos());
+       if (ret < 0) {
+               int saved_errno = errno;
+               DEBUG(0,("%s: get_remote_hostname failed - %s\n",
+                       __location__, strerror(saved_errno)));
+               TALLOC_FREE(frame);
+               return map_nt_error_from_unix_common(saved_errno);
+       }
+       rhost = p;
+       if (strequal(rhost, "UNKNOWN")) {
+               rhost = remaddr;
+       }
+
+       xconn->local_address = local_address;
+       xconn->remote_address = remote_address;
+       xconn->remote_hostname = talloc_strdup(xconn, rhost);
+       if (xconn->remote_hostname == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (!srv_init_signing(xconn)) {
+               DEBUG(0, ("Failed to init smb_signing\n"));
+               TALLOC_FREE(frame);
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
+       if (!allow_access(lp_hosts_deny(-1), lp_hosts_allow(-1),
+                         xconn->remote_hostname,
+                         remaddr)) {
+               DEBUG( 1, ("Connection denied from %s to %s\n",
+                          tsocket_address_string(remote_address, talloc_tos()),
+                          tsocket_address_string(local_address, talloc_tos())));
+
+               /*
+                * We return a valid xconn
+                * so that the caller can return an error message
+                * to the client
+                */
+               DLIST_ADD_END(client->connections, xconn);
+               talloc_steal(client, xconn);
+
+               *_xconn = xconn;
+               TALLOC_FREE(frame);
+               return NT_STATUS_NETWORK_ACCESS_DENIED;
+       }
+
+       DEBUG(10, ("Connection allowed from %s to %s\n",
+                  tsocket_address_string(remote_address, talloc_tos()),
+                  tsocket_address_string(local_address, talloc_tos())));
+
+       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.
+                */
+               NTSTATUS status;
+
+               status = smbd_register_ips(xconn, &ss_srv, &ss_clnt);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(0, ("ctdbd_register_ips failed: %s\n",
+                                 nt_errstr(status)));
+               }
+       }
+
+       tmp = lp_max_xmit();
+       tmp = MAX(tmp, SMB_BUFFER_SIZE_MIN);
+       tmp = MIN(tmp, SMB_BUFFER_SIZE_MAX);
+
+#if defined(WITH_SMB1SERVER)
+       xconn->smb1.negprot.max_recv = tmp;
+
+       xconn->smb1.sessions.done_sesssetup = false;
+       xconn->smb1.sessions.max_send = SMB_BUFFER_SIZE_MAX;
+#endif
+
+       xconn->transport.fde = tevent_add_fd(client->raw_ev_ctx,
+                                            xconn,
+                                            sock_fd,
+                                            TEVENT_FD_READ,
+                                            smbd_server_connection_handler,
+                                            xconn);
+       if (!xconn->transport.fde) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+       tevent_fd_set_auto_close(xconn->transport.fde);
+
+       /* for now we only have one connection */
+       DLIST_ADD_END(client->connections, xconn);
+       talloc_steal(client, xconn);
+
+       *_xconn = xconn;
+       TALLOC_FREE(frame);
+       return NT_STATUS_OK;
+}