Second part of fix for bug #7331 - Compound async SMB 2 requests don't work right.
authorJeremy Allison <jra@samba.org>
Sun, 18 Apr 2010 05:42:23 +0000 (22:42 -0700)
committerJeremy Allison <jra@samba.org>
Sun, 18 Apr 2010 05:42:23 +0000 (22:42 -0700)
Cause us to match W2K8R2 by sending an interim compound reply
followed by a async reply.

Jeremy.

source3/smbd/smb2_server.c

index 64f9eaba1465617a21e27993b9db2eb8a18610ef..b697cb924b2cb668d5447601fc10e25af71c4055 100644 (file)
@@ -488,7 +488,7 @@ static bool dup_smb2_vec(struct iovec *dstvec,
        return true;
 }
 
-static struct smbd_smb2_request *dup_smb2_req(struct smbd_smb2_request *req)
+static struct smbd_smb2_request *dup_smb2_req(const struct smbd_smb2_request *req)
 {
        struct smbd_smb2_request *newreq = NULL;
        struct iovec *outvec = NULL;
@@ -536,13 +536,101 @@ static struct smbd_smb2_request *dup_smb2_req(struct smbd_smb2_request *req)
 
 static void smbd_smb2_request_writev_done(struct tevent_req *subreq);
 
+static NTSTATUS smb2_send_async_interim_response(const struct smbd_smb2_request *req)
+{
+       int i = 0;
+       uint8_t *outhdr = NULL;
+       struct smbd_smb2_request *nreq = NULL;
+
+       /* Create a new smb2 request we'll use
+          for the interim return. */
+       nreq = dup_smb2_req(req);
+       if (!nreq) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       /* Lose the last 3 out vectors. They're the
+          ones we'll be using for the async reply. */
+       nreq->out.vector_count -= 3;
+
+       smb2_setup_nbt_length(nreq->out.vector,
+               nreq->out.vector_count);
+
+       /* Step back to the previous reply. */
+       i = nreq->current_idx - 3;
+       outhdr = nreq->out.vector[i].iov_base;
+       /* And end the chain. */
+       SIVAL(outhdr, SMB2_HDR_NEXT_COMMAND, 0);
+
+       /* Re-sign if needed. */
+       if (nreq->do_signing) {
+               NTSTATUS status;
+               status = smb2_signing_sign_pdu(nreq->session->session_key,
+                                       &nreq->out.vector[i], 3);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+       }
+       if (DEBUGLEVEL >= 10) {
+               dbgtext("smb2_send_async_interim_response: nreq->current_idx = %u\n",
+                       (unsigned int)nreq->current_idx );
+               dbgtext("smb2_send_async_interim_response: returning %u vectors\n",
+                       (unsigned int)nreq->out.vector_count );
+               print_req_vectors(nreq);
+       }
+       nreq->subreq = tstream_writev_queue_send(nreq,
+                                       nreq->sconn->smb2.event_ctx,
+                                       nreq->sconn->smb2.stream,
+                                       nreq->sconn->smb2.send_queue,
+                                       nreq->out.vector,
+                                       nreq->out.vector_count);
+
+       if (nreq->subreq == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       tevent_req_set_callback(nreq->subreq,
+                       smbd_smb2_request_writev_done,
+                       nreq);
+
+       return NT_STATUS_OK;
+}
+
+struct smbd_smb2_request_pending_state {
+        struct smbd_server_connection *sconn;
+        uint8_t buf[4 + SMB2_HDR_BODY + 0x08 + 1];
+        struct iovec vector[3];
+};
+
+static void smbd_smb2_request_pending_writev_done(struct tevent_req *subreq)
+{
+       struct smbd_smb2_request_pending_state *state =
+               tevent_req_callback_data(subreq,
+                       struct smbd_smb2_request_pending_state);
+       struct smbd_server_connection *sconn = state->sconn;
+       int ret;
+       int sys_errno;
+
+       ret = tstream_writev_queue_recv(subreq, &sys_errno);
+       TALLOC_FREE(subreq);
+       if (ret == -1) {
+               NTSTATUS status = map_nt_error_from_unix(sys_errno);
+               smbd_server_connection_terminate(sconn, nt_errstr(status));
+               return;
+       }
+
+       TALLOC_FREE(state);
+}
+
 NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req,
                                         struct tevent_req *subreq)
 {
+       NTSTATUS status;
+       struct smbd_smb2_request_pending_state *state = NULL;
        int i = req->current_idx;
-       struct smbd_smb2_request *nreq = NULL;
-       uint8_t *outhdr = NULL;
-       uint8_t *outbody = NULL;
+       uint8_t *reqhdr = NULL;
+       uint8_t *hdr = NULL;
+       uint8_t *body = NULL;
        uint32_t flags = 0;
        uint64_t message_id = 0;
        uint64_t async_id = 0;
@@ -577,75 +665,87 @@ NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req,
                print_req_vectors(req);
        }
 
-       /* Create a new smb2 request we'll use to return. */
-       nreq = dup_smb2_req(req);
-       if (!nreq) {
-               return NT_STATUS_NO_MEMORY;
+       if (req->out.vector_count > 4) {
+               /* This is a compound reply. We
+                * must do an interim response
+                * followed by the async response
+                * to match W2K8R2.
+                */
+               status = smb2_send_async_interim_response(req);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
        }
 
-       outhdr = (uint8_t *)nreq->out.vector[i].iov_base;
-
-       flags = IVAL(outhdr, SMB2_HDR_FLAGS);
-       message_id = BVAL(outhdr, SMB2_HDR_MESSAGE_ID);
-
+       reqhdr = (uint8_t *)req->out.vector[i].iov_base;
+       flags = IVAL(reqhdr, SMB2_HDR_FLAGS);
+       message_id = BVAL(reqhdr, SMB2_HDR_MESSAGE_ID);
        async_id = message_id; /* keep it simple for now... */
-       SIVAL(outhdr, SMB2_HDR_FLAGS,   flags | SMB2_HDR_FLAG_ASYNC);
-       SBVAL(outhdr, SMB2_HDR_PID,     async_id);
-       SIVAL(outhdr, SMB2_HDR_STATUS,  NT_STATUS_V(STATUS_PENDING));
-
-       nreq->out.vector[i+1].iov_base = talloc_zero_array(nreq->out.vector,
-                                                       uint8_t,
-                                                       9);
-       if (!nreq->out.vector[i+1].iov_base) {
+
+       state = talloc_zero(req->sconn, struct smbd_smb2_request_pending_state);
+       if (state == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
-       nreq->out.vector[i+1].iov_len = 9;
-       outbody = (uint8_t *)nreq->out.vector[i+1].iov_base;
+       state->sconn = req->sconn;
 
-       /* setup error body header */
-       SSVAL(outbody, 0x00, 0x08 + 1);
-       SSVAL(outbody, 0x02, 0);
-       SIVAL(outbody, 0x04, 0);
-       /* Match W2K8R2... */
-       SCVAL(outbody, 8, 0x21);
+       state->vector[0].iov_base = (void *)state->buf;
+       state->vector[0].iov_len = 4;
 
-       nreq->out.vector[i+2].iov_base = NULL;
-       nreq->out.vector[i+2].iov_len = 0;
+       state->vector[1].iov_base = state->buf + 4;
+       state->vector[1].iov_len = SMB2_HDR_BODY;
 
-       smb2_setup_nbt_length(nreq->out.vector,
-               nreq->out.vector_count);
+       state->vector[2].iov_base = state->buf + 4 + SMB2_HDR_BODY;
+       state->vector[2].iov_len = 9;
 
-       if (nreq->do_signing) {
-               NTSTATUS status;
-               status = smb2_signing_sign_pdu(nreq->session->session_key,
-                                       &nreq->out.vector[i], 3);
+       smb2_setup_nbt_length(state->vector, 3);
+
+       hdr = state->vector[1].iov_base;
+       body = state->vector[2].iov_base;
+
+       SIVAL(hdr, SMB2_HDR_PROTOCOL_ID, SMB2_MAGIC);
+       SSVAL(hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY);
+       SSVAL(hdr, SMB2_HDR_EPOCH, 0);
+       SIVAL(hdr, SMB2_HDR_STATUS, NT_STATUS_V(STATUS_PENDING));
+       SSVAL(hdr, SMB2_HDR_OPCODE, SVAL(reqhdr, SMB2_HDR_OPCODE));
+       SSVAL(hdr, SMB2_HDR_CREDIT, 5);
+       SIVAL(hdr, SMB2_HDR_FLAGS, flags | SMB2_HDR_FLAG_ASYNC);
+       SIVAL(hdr, SMB2_HDR_NEXT_COMMAND, 0);
+       SBVAL(hdr, SMB2_HDR_MESSAGE_ID, message_id);
+       SBVAL(hdr, SMB2_HDR_PID, async_id);
+       SBVAL(hdr, SMB2_HDR_SESSION_ID,
+               BVAL(reqhdr, SMB2_HDR_SESSION_ID));
+       memset(hdr+SMB2_HDR_SIGNATURE, 0, 16);
+
+       SSVAL(body, 0x00, 0x08 + 1);
+
+       SCVAL(body, 0x02, 0);
+       SCVAL(body, 0x03, 0);
+       SIVAL(body, 0x04, 0);
+       /* Match W2K8R2... */
+       SCVAL(body, 0x08, 0x21);
+
+       if (req->do_signing) {
+               status = smb2_signing_sign_pdu(req->session->session_key,
+                                       state->vector, 3);
                if (!NT_STATUS_IS_OK(status)) {
                        return status;
                }
        }
 
-       if (DEBUGLEVEL >= 10) {
-               dbgtext("smbd_smb2_request_pending_queue: nreq->current_idx = %u\n",
-                       (unsigned int)nreq->current_idx );
-               dbgtext("smbd_smb2_request_pending_queue: returning %u vectors\n",
-                       (unsigned int)nreq->out.vector_count );
-               print_req_vectors(nreq);
-       }
-
-       nreq->subreq = tstream_writev_queue_send(nreq,
-                                       nreq->sconn->smb2.event_ctx,
-                                       nreq->sconn->smb2.stream,
-                                       nreq->sconn->smb2.send_queue,
-                                       nreq->out.vector,
-                                       nreq->out.vector_count);
+       subreq = tstream_writev_queue_send(state,
+                                       req->sconn->smb2.event_ctx,
+                                       req->sconn->smb2.stream,
+                                       req->sconn->smb2.send_queue,
+                                       state->vector,
+                                       3);
 
-       if (nreq->subreq == NULL) {
+       if (subreq == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
 
-       tevent_req_set_callback(nreq->subreq,
-                       smbd_smb2_request_writev_done,
-                       nreq);
+       tevent_req_set_callback(subreq,
+                       smbd_smb2_request_pending_writev_done,
+                       state);
 
        /* Note we're going async with this request. */
        req->async = true;
@@ -716,9 +816,9 @@ NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req,
                req->out.vector_count);
 
        /* Ensure our final reply matches the interim one. */
-       outhdr = (uint8_t *)req->out.vector[1].iov_base;
-       SIVAL(outhdr, SMB2_HDR_FLAGS,   flags | SMB2_HDR_FLAG_ASYNC);
-       SBVAL(outhdr, SMB2_HDR_PID,     async_id);
+       reqhdr = (uint8_t *)req->out.vector[1].iov_base;
+       SIVAL(reqhdr, SMB2_HDR_FLAGS, flags | SMB2_HDR_FLAG_ASYNC);
+       SBVAL(reqhdr, SMB2_HDR_PID, async_id);
 
        {
                const uint8_t *inhdr =