Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : Copyright (C) Gregor Beck 2013
5 :
6 : This program is free software; you can redistribute it and/or modify
7 : it under the terms of the GNU General Public License as published by
8 : the Free Software Foundation; either version 3 of the License, or
9 : (at your option) any later version.
10 :
11 : This program is distributed in the hope that it will be useful,
12 : but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : GNU General Public License for more details.
15 :
16 : You should have received a copy of the GNU General Public License
17 : along with this program. If not, see <http://www.gnu.org/licenses/>.
18 : */
19 :
20 : #include "includes.h"
21 : #include "system/network.h"
22 : #include "lib/util/tevent_ntstatus.h"
23 : #include "smb_common.h"
24 : #include "smbXcli_base.h"
25 :
26 : struct smb1cli_readx_state {
27 : uint32_t size;
28 : uint16_t vwv[12];
29 : uint32_t received;
30 : uint8_t *buf;
31 : bool out_valid;
32 : };
33 :
34 : static void smb1cli_readx_done(struct tevent_req *subreq);
35 :
36 : /**
37 : * Send an asynchrounus SMB_COM_READ_ANDX request.
38 : * <a href="http://msdn.microsoft.com/en-us/library/ee441839.aspx">MS-CIFS 2.2.4.42.1</a>,
39 : * <a href="http://msdn.microsoft.com/en-us/library/ff470250.aspx">MS-SMB 2.2.4.2.1</a>
40 : * @see smb1cli_readx_recv()
41 : * @todo fix API (min/max size, timeout)
42 : *
43 : * @param[in] mem_ctx The memory context for the result.
44 : * @param[in] ev The event context to work on.
45 : * @param[in] conn The smb connection.
46 : * @param[in] timeout_msec If positive a timeout for the request.
47 : * @param[in] pid The process identifier
48 : * @param[in] tcon The smb tree connect.
49 : * @param[in] session The smb session.
50 : * @param[in] fnum The file id of the file the data should be read from.
51 : * @param[in] offset The offset in bytes from the begin of file where to start reading.
52 : * @param[in] size The number of bytes to read.
53 : *
54 : * @return a tevent_req or NULL
55 : */
56 34 : struct tevent_req *smb1cli_readx_send(TALLOC_CTX *mem_ctx,
57 : struct tevent_context *ev,
58 : struct smbXcli_conn *conn,
59 : uint32_t timeout_msec,
60 : uint32_t pid,
61 : struct smbXcli_tcon *tcon,
62 : struct smbXcli_session *session,
63 : uint16_t fnum,
64 : uint64_t offset,
65 : uint32_t size)
66 : {
67 0 : NTSTATUS status;
68 0 : struct tevent_req *req, *subreq;
69 0 : struct smb1cli_readx_state *state;
70 34 : uint8_t wct = 10;
71 :
72 34 : req = tevent_req_create(mem_ctx, &state, struct smb1cli_readx_state);
73 34 : if (req == NULL) {
74 0 : return NULL;
75 : }
76 34 : state->size = size;
77 :
78 34 : SCVAL(state->vwv + 0, 0, 0xFF);
79 34 : SCVAL(state->vwv + 0, 1, 0);
80 34 : SSVAL(state->vwv + 1, 0, 0);
81 34 : SSVAL(state->vwv + 2, 0, fnum);
82 34 : SIVAL(state->vwv + 3, 0, offset);
83 34 : SSVAL(state->vwv + 5, 0, size);
84 34 : SSVAL(state->vwv + 6, 0, size);
85 34 : SSVAL(state->vwv + 7, 0, (size >> 16));
86 34 : SSVAL(state->vwv + 8, 0, 0);
87 34 : SSVAL(state->vwv + 9, 0, 0);
88 :
89 34 : if (smb1cli_conn_capabilities(conn) & CAP_LARGE_FILES) {
90 34 : SIVAL(state->vwv + 10, 0,
91 : (((uint64_t)offset)>>32) & 0xffffffff);
92 34 : wct = 12;
93 : } else {
94 0 : if ((((uint64_t)offset) & 0xffffffff00000000LL) != 0) {
95 0 : DEBUG(10, ("smb1cli_readx_send got large offset where "
96 : "the server does not support it\n"));
97 0 : tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
98 0 : return tevent_req_post(req, ev);
99 : }
100 : }
101 :
102 68 : subreq = smb1cli_req_create(state, ev, conn, SMBreadX,
103 : 0, 0, /* *_flags */
104 : 0, 0, /* *_flags2 */
105 : timeout_msec, pid, tcon, session,
106 34 : wct, state->vwv,
107 : 0, NULL);
108 34 : if (tevent_req_nomem(subreq, req)) {
109 0 : return tevent_req_post(req, ev);
110 : }
111 34 : tevent_req_set_callback(subreq, smb1cli_readx_done, req);
112 :
113 34 : status = smb1cli_req_chain_submit(&subreq, 1);
114 34 : if (tevent_req_nterror(req, status)) {
115 0 : return tevent_req_post(req, ev);
116 : }
117 :
118 34 : return req;
119 : }
120 :
121 34 : static void smb1cli_readx_done(struct tevent_req *subreq)
122 : {
123 34 : struct tevent_req *req = tevent_req_callback_data(
124 : subreq, struct tevent_req);
125 34 : struct smb1cli_readx_state *state = tevent_req_data(
126 : req, struct smb1cli_readx_state);
127 34 : struct iovec *recv_iov = NULL;
128 0 : uint8_t wct;
129 0 : uint16_t *vwv;
130 0 : uint32_t num_bytes;
131 0 : uint8_t *bytes;
132 0 : uint16_t data_offset;
133 0 : uint32_t bytes_offset;
134 0 : NTSTATUS status;
135 0 : static const struct smb1cli_req_expected_response expected[] = {
136 : {
137 : .status = NT_STATUS_OK,
138 : .wct = 0x0C
139 : },
140 : {
141 : .status = STATUS_BUFFER_OVERFLOW,
142 : .wct = 0x0C
143 : },
144 : };
145 :
146 34 : status = smb1cli_req_recv(subreq, state,
147 : &recv_iov,
148 : NULL, /* phdr */
149 : &wct,
150 : &vwv,
151 : NULL, /* pvwv_offset */
152 : &num_bytes,
153 : &bytes,
154 : &bytes_offset,
155 : NULL, /* inbuf */
156 : expected, ARRAY_SIZE(expected));
157 34 : TALLOC_FREE(subreq);
158 34 : if (NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
159 : /* no error */
160 : } else {
161 34 : if (tevent_req_nterror(req, status)) {
162 0 : return;
163 : }
164 : }
165 :
166 : /* size is the number of bytes the server returned.
167 : * Might be zero. */
168 34 : state->received = SVAL(vwv + 5, 0);
169 34 : state->received |= (((unsigned int)SVAL(vwv + 7, 0)) << 16);
170 :
171 34 : if (state->received > state->size) {
172 0 : DEBUG(5,("server returned more than we wanted!\n"));
173 0 : tevent_req_nterror(req, NT_STATUS_UNEXPECTED_IO_ERROR);
174 0 : return;
175 : }
176 :
177 : /*
178 : * bcc field must be valid for small reads, for large reads the 16-bit
179 : * bcc field can't be correct.
180 : */
181 34 : if ((state->received < 0xffff) && (state->received > num_bytes)) {
182 0 : DEBUG(5, ("server announced more bytes than sent\n"));
183 0 : tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
184 0 : return;
185 : }
186 :
187 34 : data_offset = SVAL(vwv+6, 0);
188 34 : if (data_offset < bytes_offset) {
189 0 : DEBUG(5, ("server returned invalid read&x data offset\n"));
190 0 : tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
191 0 : return;
192 : }
193 34 : if (smb_buffer_oob(num_bytes, data_offset - bytes_offset, state->received)) {
194 0 : DEBUG(5, ("server returned invalid read&x data offset\n"));
195 0 : tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
196 0 : return;
197 : }
198 :
199 34 : state->buf = bytes + (data_offset - bytes_offset);
200 :
201 34 : state->out_valid = true;
202 :
203 34 : if (tevent_req_nterror(req, status)) {
204 0 : return;
205 : }
206 :
207 34 : tevent_req_done(req);
208 : }
209 :
210 : /**
211 : * Receive the response to an asynchronous SMB_COM_READ_ANDX request.
212 : * <a href="http://msdn.microsoft.com/en-us/library/ee441872.aspx">MS-CIFS 2.2.4.42.2</a>,
213 : * <a href="http://msdn.microsoft.com/en-us/library/ff470017.aspx">MS-SMB 2.2.4.2.2</a>
214 : *
215 : * @warning rcvbuf is talloced from the request, so better make sure that you
216 : * copy it away before you talloc_free(req). rcvbuf is NOT a talloc_ctx of its
217 : * own, so do not talloc_move it!
218 : *
219 : * @param[in] req A tevent request created with smb1cli_readx_send()
220 : * @param[out] received The number of bytes received.
221 : * @param[out] rcvbuf Pointer to the bytes received.
222 : *
223 : * @return NT_STATUS_OK or STATUS_BUFFER_OVERFLOW on success.
224 : */
225 34 : NTSTATUS smb1cli_readx_recv(struct tevent_req *req,
226 : uint32_t *received,
227 : uint8_t **rcvbuf)
228 : {
229 34 : struct smb1cli_readx_state *state = tevent_req_data(
230 : req, struct smb1cli_readx_state);
231 34 : NTSTATUS status = NT_STATUS_OK;
232 :
233 34 : if (tevent_req_is_nterror(req, &status) && !state->out_valid) {
234 0 : *received = 0;
235 0 : *rcvbuf = NULL;
236 0 : return status;
237 : }
238 34 : *received = state->received;
239 34 : *rcvbuf = state->buf;
240 34 : return status;
241 : }
|