diff options
-rw-r--r-- | source4/libcli/raw/rawtrans.c | 195 |
1 files changed, 178 insertions, 17 deletions
diff --git a/source4/libcli/raw/rawtrans.c b/source4/libcli/raw/rawtrans.c index fb5f9fa8d0..7a90236c9c 100644 --- a/source4/libcli/raw/rawtrans.c +++ b/source4/libcli/raw/rawtrans.c @@ -537,6 +537,10 @@ _PUBLIC_ NTSTATUS smb_raw_trans(struct smbcli_tree *tree, } struct smb_raw_nttrans_recv_state { + uint32_t params_total; + uint32_t data_total; + uint32_t params_left; + uint32_t data_left; bool got_first; uint32_t recvd_data; uint32_t recvd_param; @@ -569,6 +573,9 @@ failed: return smbcli_request_destroy(req); } +static enum smbcli_request_state smb_raw_nttrans_ship_rest(struct smbcli_request *req, + struct smb_raw_nttrans_recv_state *state); + /* * This helper returns SMBCLI_REQUEST_RECV until all data has arrived */ @@ -599,6 +606,10 @@ static enum smbcli_request_state smb_raw_nttrans_recv_helper(struct smbcli_reque goto failed; } + if (state->params_left > 0 || state->data_left > 0) { + return smb_raw_nttrans_ship_rest(req, state); + } + /* this is the first packet of the response */ SMBCLI_CHECK_MIN_WCT(req, 18); @@ -716,7 +727,12 @@ struct smbcli_request *smb_raw_nttrans_send(struct smbcli_tree *tree, { struct smbcli_request *req; struct smb_raw_nttrans_recv_state *state; - uint8_t *outdata, *outparam; + uint32_t ofs; + size_t space_left; + DATA_BLOB params_chunk; + uint32_t params_ofs; + DATA_BLOB data_chunk; + uint32_t data_ofs; int align = 0; /* only align if there are parameters or data */ @@ -725,10 +741,7 @@ struct smbcli_request *smb_raw_nttrans_send(struct smbcli_tree *tree, } req = smbcli_request_setup(tree, SMBnttrans, - 19 + parms->in.setup_count, - align + - parms->in.params.length + - parms->in.data.length); + 19 + parms->in.setup_count, align); if (!req) { return NULL; } @@ -740,33 +753,81 @@ struct smbcli_request *smb_raw_nttrans_send(struct smbcli_tree *tree, } /* fill in SMB parameters */ - outparam = req->out.data + align; - outdata = outparam + parms->in.params.length; if (align != 0) { memset(req->out.data, 0, align); } + ofs = PTR_DIFF(req->out.data,req->out.hdr)+align; + + /* see how much bytes of the params block we can ship in the first request */ + space_left = raw_trans_space_left(req); + + params_chunk.length = MIN(parms->in.params.length, space_left); + params_chunk.data = parms->in.params.data; + params_ofs = ofs; + + state->params_left = parms->in.params.length - params_chunk.length; + + if (state->params_left > 0) { + /* we copy the whole params block, if needed we can optimize that latter */ + state->io.in.params = data_blob_talloc(state, NULL, parms->in.params.length); + if (!state->io.in.params.data) { + smbcli_request_destroy(req); + return NULL; + } + memcpy(state->io.in.params.data, + parms->in.params.data, + parms->in.params.length); + } + + /* see how much bytes of the data block we can ship in the first request */ + space_left -= params_chunk.length; + +#if TORTURE_TRANS_DATA + if (space_left > 1) { + space_left /= 2; + } +#endif + + data_chunk.length = MIN(parms->in.data.length, space_left); + data_chunk.data = parms->in.data.data; + data_ofs = params_ofs + params_chunk.length; + + state->data_left = parms->in.data.length - data_chunk.length; + + if (state->data_left > 0) { + /* we copy the whole params block, if needed we can optimize that latter */ + state->io.in.data = data_blob_talloc(state, NULL, parms->in.data.length); + if (!state->io.in.data.data) { + smbcli_request_destroy(req); + return NULL; + } + memcpy(state->io.in.data.data, + parms->in.data.data, + parms->in.data.length); + } + + state->params_total = parms->in.params.length; + state->data_total = parms->in.data.length; + SCVAL(req->out.vwv, 0, parms->in.max_setup); SSVAL(req->out.vwv, 1, 0); /* reserved */ SIVAL(req->out.vwv, 3, parms->in.params.length); SIVAL(req->out.vwv, 7, parms->in.data.length); SIVAL(req->out.vwv, 11, parms->in.max_param); SIVAL(req->out.vwv, 15, parms->in.max_data); - SIVAL(req->out.vwv, 19, parms->in.params.length); - SIVAL(req->out.vwv, 23, PTR_DIFF(outparam,req->out.hdr)); - SIVAL(req->out.vwv, 27, parms->in.data.length); - SIVAL(req->out.vwv, 31, PTR_DIFF(outdata,req->out.hdr)); + SIVAL(req->out.vwv, 19, params_chunk.length); + SIVAL(req->out.vwv, 23, params_ofs); + SIVAL(req->out.vwv, 27, data_chunk.length); + SIVAL(req->out.vwv, 31, data_ofs); SCVAL(req->out.vwv, 35, parms->in.setup_count); SSVAL(req->out.vwv, 36, parms->in.function); memcpy(req->out.vwv + VWV(19), parms->in.setup, sizeof(uint16_t) * parms->in.setup_count); - if (parms->in.params.length) { - memcpy(outparam, parms->in.params.data, parms->in.params.length); - } - if (parms->in.data.length) { - memcpy(outdata, parms->in.data.data, parms->in.data.length); - } + + smbcli_req_append_blob(req, ¶ms_chunk); + smbcli_req_append_blob(req, &data_chunk); /* add the helper which will check that all multi-part replies are in before an async client callack will be issued */ @@ -781,6 +842,106 @@ struct smbcli_request *smb_raw_nttrans_send(struct smbcli_tree *tree, return req; } +static enum smbcli_request_state smb_raw_nttrans_ship_next(struct smbcli_request *req, + struct smb_raw_nttrans_recv_state *state) +{ + struct smbcli_request *req2; + size_t space_left; + DATA_BLOB params_chunk; + uint32_t ofs; + uint32_t params_ofs = 0; + uint32_t params_disp = 0; + DATA_BLOB data_chunk; + uint32_t data_ofs = 0; + uint32_t data_disp = 0; + + req2 = smbcli_request_setup(req->tree, SMBnttranss, 18, 0); + if (!req2) { + goto nomem; + } + req2->mid = req->mid; + SSVAL(req2->out.hdr, HDR_MID, req2->mid); + + ofs = PTR_DIFF(req2->out.data,req2->out.hdr); + + /* see how much bytes of the params block we can ship in the first request */ + space_left = raw_trans_space_left(req2); + + params_disp = state->io.in.params.length - state->params_left; + params_chunk.length = MIN(state->params_left, space_left); + params_chunk.data = state->io.in.params.data + params_disp; + params_ofs = ofs; + + state->params_left -= params_chunk.length; + + /* see how much bytes of the data block we can ship in the first request */ + space_left -= params_chunk.length; + +#if TORTURE_TRANS_DATA + if (space_left > 1) { + space_left /= 2; + } +#endif + + data_disp = state->io.in.data.length - state->data_left; + data_chunk.length = MIN(state->data_left, space_left); + data_chunk.data = state->io.in.data.data + data_disp; + data_ofs = params_ofs+params_chunk.length; + + state->data_left -= data_chunk.length; + + SSVAL(req2->out.vwv,0, 0); /* reserved */ + SCVAL(req2->out.vwv,2, 0); /* reserved */ + SIVAL(req2->out.vwv,3, state->params_total); + SIVAL(req2->out.vwv,7, state->data_total); + SIVAL(req2->out.vwv,11, params_chunk.length); + SIVAL(req2->out.vwv,15, params_ofs); + SIVAL(req2->out.vwv,19, params_disp); + SIVAL(req2->out.vwv,23, data_chunk.length); + SIVAL(req2->out.vwv,27, data_ofs); + SIVAL(req2->out.vwv,31, data_disp); + SCVAL(req2->out.vwv,35, 0); /* reserved */ + + smbcli_req_append_blob(req2, ¶ms_chunk); + smbcli_req_append_blob(req2, &data_chunk); + + /* + * it's a one way request but we need + * the seq_num, so we destroy req2 by hand + */ + if (!smbcli_request_send(req2)) { + goto failed; + } + + req->seq_num = req2->seq_num; + smbcli_request_destroy(req2); + + return SMBCLI_REQUEST_RECV; + +nomem: + req->status = NT_STATUS_NO_MEMORY; +failed: + if (req2) { + req->status = smbcli_request_destroy(req2); + } + return SMBCLI_REQUEST_ERROR; +} + +static enum smbcli_request_state smb_raw_nttrans_ship_rest(struct smbcli_request *req, + struct smb_raw_nttrans_recv_state *state) +{ + enum smbcli_request_state ret = SMBCLI_REQUEST_ERROR; + + while (state->params_left > 0 || state->data_left > 0) { + ret = smb_raw_nttrans_ship_next(req, state); + if (ret != SMBCLI_REQUEST_RECV) { + break; + } + } + + return ret; +} + /**************************************************************************** receive a SMB nttrans response allocating the necessary memory |