From 3d90e9346b1e934a529390f9fd37f108264565ff Mon Sep 17 00:00:00 2001 From: Gregor Beck Date: Thu, 12 Sep 2013 14:35:35 +0200 Subject: [PATCH] libcli/smb: add smb1cli_readx* Pair-Programmed-With: Stefan Metzmacher Signed-off-by: Gregor Beck Signed-off-by: Stefan Metzmacher Reviewed-by: Stefan Metzmacher Reviewed-by: Andreas Schneider --- libcli/smb/smb1cli_read.c | 224 ++++++++++++++++++++++++++++++++++++++ libcli/smb/smbXcli_base.h | 13 +++ libcli/smb/wscript | 1 + 3 files changed, 238 insertions(+) create mode 100644 libcli/smb/smb1cli_read.c diff --git a/libcli/smb/smb1cli_read.c b/libcli/smb/smb1cli_read.c new file mode 100644 index 00000000000..ab250abf143 --- /dev/null +++ b/libcli/smb/smb1cli_read.c @@ -0,0 +1,224 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Gregor Beck 2013 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/network.h" +#include "lib/util/tevent_ntstatus.h" +#include "smb_common.h" +#include "smbXcli_base.h" + +struct smb1cli_readx_state { + uint32_t size; + uint16_t vwv[12]; + NTSTATUS status; + uint32_t received; + uint8_t *buf; +}; + +static void smb1cli_readx_done(struct tevent_req *subreq); + +/** + * Send an asynchrounus SMB_COM_READ_ANDX request. + * MS-CIFS 2.2.4.42.1, + * MS-SMB 2.2.4.2.1 + * @see smb1cli_readx_recv() + * @todo fix API (min/max size, timeout) + * + * @param[in] mem_ctx The memory context for the result. + * @param[in] ev The event context to work on. + * @param[in] conn The smb connection. + * @param[in] timeout_msec If positiv a timeout for the request. + * @param[in] pid The process identifier + * @param[in] tcon The smb tree connect. + * @param[in] session The smb session. + * @param[in] fnum The file id of the file the data should be read from. + * @param[in] offset The offset in bytes from the begin of file where to start reading. + * @param[in] size The number of bytes to read. + * + * @return a tevent_req or NULL + */ +struct tevent_req *smb1cli_readx_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct smbXcli_conn *conn, + uint32_t timeout_msec, + uint32_t pid, + struct smbXcli_tcon *tcon, + struct smbXcli_session *session, + uint16_t fnum, + uint64_t offset, + uint32_t size) +{ + NTSTATUS status; + struct tevent_req *req, *subreq; + struct smb1cli_readx_state *state; + uint8_t wct = 10; + + req = tevent_req_create(mem_ctx, &state, struct smb1cli_readx_state); + if (req == NULL) { + return NULL; + } + state->size = size; + + SCVAL(state->vwv + 0, 0, 0xFF); + SCVAL(state->vwv + 0, 1, 0); + SSVAL(state->vwv + 1, 0, 0); + SSVAL(state->vwv + 2, 0, fnum); + SIVAL(state->vwv + 3, 0, offset); + SSVAL(state->vwv + 5, 0, size); + SSVAL(state->vwv + 6, 0, size); + SSVAL(state->vwv + 7, 0, (size >> 16)); + SSVAL(state->vwv + 8, 0, 0); + SSVAL(state->vwv + 9, 0, 0); + + if (smb1cli_conn_capabilities(conn) & CAP_LARGE_FILES) { + SIVAL(state->vwv + 10, 0, + (((uint64_t)offset)>>32) & 0xffffffff); + wct = 12; + } else { + if ((((uint64_t)offset) & 0xffffffff00000000LL) != 0) { + DEBUG(10, ("smb1cli_readx_send got large offset where " + "the server does not support it\n")); + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX); + return tevent_req_post(req, ev); + } + } + + subreq = smb1cli_req_create(state, ev, conn, SMBreadX, + 0, 0, /* *_flags */ + 0, 0, /* *_flags2 */ + timeout_msec, pid, tcon, session, + wct, state->vwv, + 0, NULL); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, smb1cli_readx_done, req); + + status = smb1cli_req_chain_submit(&subreq, 1); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + return req; +} + +static void smb1cli_readx_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct smb1cli_readx_state *state = tevent_req_data( + req, struct smb1cli_readx_state); + struct iovec *recv_iov = NULL; + uint8_t wct; + uint16_t *vwv; + uint32_t num_bytes; + uint8_t *bytes; + uint16_t data_offset; + uint32_t bytes_offset; + static const struct smb1cli_req_expected_response expected[] = { + { + .status = NT_STATUS_OK, + .wct = 0x0C + }, + }; + + state->status = smb1cli_req_recv(subreq, state, + &recv_iov, + NULL, /* phdr */ + &wct, + &vwv, + NULL, /* pvwv_offset */ + &num_bytes, + &bytes, + &bytes_offset, + NULL, /* inbuf */ + expected, ARRAY_SIZE(expected)); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, state->status)) { + return; + } + + /* size is the number of bytes the server returned. + * Might be zero. */ + state->received = SVAL(vwv + 5, 0); + state->received |= (((unsigned int)SVAL(vwv + 7, 0)) << 16); + + if (state->received > state->size) { + DEBUG(5,("server returned more than we wanted!\n")); + tevent_req_nterror(req, NT_STATUS_UNEXPECTED_IO_ERROR); + return; + } + + /* + * bcc field must be valid for small reads, for large reads the 16-bit + * bcc field can't be correct. + */ + if ((state->received < 0xffff) && (state->received > num_bytes)) { + DEBUG(5, ("server announced more bytes than sent\n")); + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + data_offset = SVAL(vwv+6, 0); + if (data_offset < bytes_offset) { + DEBUG(5, ("server returned invalid read&x data offset\n")); + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + if (smb_buffer_oob(num_bytes, data_offset - bytes_offset, state->received)) { + DEBUG(5, ("server returned invalid read&x data offset\n")); + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + state->buf = bytes + (data_offset - bytes_offset); + + tevent_req_done(req); +} + +/** + * Receive the response to an asynchronous SMB_COM_READ_ANDX request. + * MS-CIFS 2.2.4.42.2, + * MS-SMB 2.2.4.2.2 + * + * @warning rcvbuf is talloced from the request, so better make sure that you + * copy it away before you talloc_free(req). rcvbuf is NOT a talloc_ctx of its + * own, so do not talloc_move it! + * + * @param[in] req A tevent request created with smb1cli_readx_send() + * @param[out] received The number of bytes received. + * @param[out] rcvbuf Pointer to the bytes received. + * + * @return NT_STATUS_OK on succsess. + */ +NTSTATUS smb1cli_readx_recv(struct tevent_req *req, + uint32_t *received, + uint8_t **rcvbuf) +{ + struct smb1cli_readx_state *state = tevent_req_data( + req, struct smb1cli_readx_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *received = state->received; + *rcvbuf = state->buf; + return NT_STATUS_OK; +} diff --git a/libcli/smb/smbXcli_base.h b/libcli/smb/smbXcli_base.h index 395c00d3a28..888242b1126 100644 --- a/libcli/smb/smbXcli_base.h +++ b/libcli/smb/smbXcli_base.h @@ -281,6 +281,19 @@ NTSTATUS smb1cli_writex(struct smbXcli_conn *conn, uint32_t size, uint32_t *pwritten, uint16_t *pavailable); +struct tevent_req *smb1cli_readx_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct smbXcli_conn *conn, + uint32_t timeout_msec, + uint32_t pid, + struct smbXcli_tcon *tcon, + struct smbXcli_session *session, + uint16_t fnum, + uint64_t offset, + uint32_t size); +NTSTATUS smb1cli_readx_recv(struct tevent_req *req, + uint32_t *received, + uint8_t **rcvbuf); bool smb2cli_conn_req_possible(struct smbXcli_conn *conn, uint32_t *max_dyn_len); uint32_t smb2cli_conn_server_capabilities(struct smbXcli_conn *conn); diff --git a/libcli/smb/wscript b/libcli/smb/wscript index 6dfbe9efabb..52390100474 100755 --- a/libcli/smb/wscript +++ b/libcli/smb/wscript @@ -26,6 +26,7 @@ def build(bld): smb1cli_create.c smb1cli_close.c smb1cli_write.c + smb1cli_read.c smb2cli_session.c smb2cli_create.c smb2cli_close.c -- 2.34.1