smbXcli: use smb1cli_inbuf_parse_chain() and remember more details per chain response
authorStefan Metzmacher <metze@samba.org>
Wed, 16 Nov 2011 10:35:50 +0000 (11:35 +0100)
committerStefan Metzmacher <metze@samba.org>
Thu, 24 Nov 2011 18:02:30 +0000 (19:02 +0100)
metze

libcli/smb/smbXcli_base.c

index d53be9b6f2f970242be9fe894c2e9d2c495d45e1..2a996be04b212e9bfcf07f9f92d1c81bc2b5309b 100644 (file)
@@ -1347,9 +1347,12 @@ static NTSTATUS smb1cli_conn_dispatch_incoming(struct smbXcli_conn *conn,
        NTSTATUS status;
        size_t num_pending;
        size_t i;
+       uint8_t cmd;
        uint16_t mid;
        bool oplock_break;
        const uint8_t *inhdr = inbuf + NBT_HDR_SIZE;
+       struct iovec *iov = NULL;
+       int num_iov = 0;
 
        if ((IVAL(inhdr, 0) != SMB_MAGIC) /* 0xFF"SMB" */
            && (SVAL(inhdr, 0) != 0x45ff)) /* 0xFF"E" */ {
@@ -1429,25 +1432,31 @@ static NTSTATUS smb1cli_conn_dispatch_incoming(struct smbXcli_conn *conn,
                return NT_STATUS_ACCESS_DENIED;
        }
 
+       status = smb1cli_inbuf_parse_chain(inbuf, tmp_mem,
+                                          &iov, &num_iov);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(10,("smb1cli_inbuf_parse_chain - %s\n",
+                         nt_errstr(status)));
+               return status;
+       }
+
+       cmd = CVAL(inhdr, HDR_COM);
+       status = smb1cli_pull_raw_error(inhdr);
+
        if (state->smb1.chained_requests != NULL) {
                struct tevent_req **chain = talloc_move(tmp_mem,
                                            &state->smb1.chained_requests);
                size_t num_chained = talloc_array_length(chain);
+               size_t num_responses = (num_iov - 1)/2;
 
-               /*
-                * We steal the inbuf to the chain,
-                * so that it will stay until all
-                * requests of the chain are finished.
-                *
-                * Each requests in the chain will
-                * hold a talloc reference to the chain.
-                * This way we do not expose the talloc_reference()
-                * behavior to the callers.
-                */
-               talloc_steal(chain, inbuf);
+               if (num_responses > num_chained) {
+                       return NT_STATUS_INVALID_NETWORK_RESPONSE;
+               }
 
                for (i=0; i<num_chained; i++) {
-                       struct tevent_req **ref;
+                       size_t iov_idx = 1 + (i*2);
+                       struct iovec *cur = &iov[iov_idx];
+                       uint8_t *inbuf_ref;
 
                        req = chain[i];
                        state = tevent_req_data(req, struct smbXcli_req_state);
@@ -1461,26 +1470,62 @@ static NTSTATUS smb1cli_conn_dispatch_incoming(struct smbXcli_conn *conn,
                         */
                        tevent_req_defer_callback(req, state->ev);
 
-                       ref = talloc_reference(state, chain);
-                       if (tevent_req_nomem(ref, req)) {
+                       if (i >= num_responses) {
+                               tevent_req_nterror(req, NT_STATUS_REQUEST_ABORTED);
                                continue;
                        }
 
+                       state->smb1.recv_cmd = cmd;
+
+                       if (i == (num_responses - 1)) {
+                               /*
+                                * The last request in the chain gets the status
+                                */
+                               state->smb1.recv_status = status;
+                       } else {
+                               cmd = CVAL(cur[0].iov_base, 0);
+                               state->smb1.recv_status = NT_STATUS_OK;
+                       }
+
                        state->inbuf = inbuf;
                        state->smb1.chain_num = i;
                        state->smb1.chain_length = num_chained;
 
+                       /*
+                        * Note: here we use talloc_reference() in a way
+                        *       that does not expose it to the caller.
+                        */
+                       inbuf_ref = talloc_reference(state->smb1.recv_iov, inbuf);
+                       if (tevent_req_nomem(inbuf_ref, req)) {
+                               continue;
+                       }
+
+                       /* copy the related buffers */
+                       state->smb1.recv_iov[0] = iov[0];
+                       state->smb1.recv_iov[1] = cur[0];
+                       state->smb1.recv_iov[2] = cur[1];
+
                        tevent_req_done(req);
                }
                return NT_STATUS_RETRY;
        }
 
+       if (num_iov != 3) {
+               return NT_STATUS_INVALID_NETWORK_RESPONSE;
+       }
+
        smbXcli_req_unset_pending(req);
 
-       state->inbuf = talloc_move(state, &inbuf);
+       state->smb1.recv_cmd = cmd;
+       state->smb1.recv_status = status;
+       state->inbuf = talloc_move(state->smb1.recv_iov, &inbuf);
        state->smb1.chain_num = 0;
        state->smb1.chain_length = 1;
 
+       state->smb1.recv_iov[0] = iov[0];
+       state->smb1.recv_iov[1] = iov[1];
+       state->smb1.recv_iov[2] = iov[2];
+
        if (talloc_array_length(conn->pending) == 0) {
                tevent_req_done(req);
                return NT_STATUS_OK;