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 positiv 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 0 : 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 : NTSTATUS status;
68 : struct tevent_req *req, *subreq;
69 : struct smb1cli_readx_state *state;
70 0 : uint8_t wct = 10;
71 :
72 0 : req = tevent_req_create(mem_ctx, &state, struct smb1cli_readx_state);
73 0 : if (req == NULL) {
74 0 : return NULL;
75 : }
76 0 : state->size = size;
77 :
78 0 : SCVAL(state->vwv + 0, 0, 0xFF);
79 0 : SCVAL(state->vwv + 0, 1, 0);
80 0 : SSVAL(state->vwv + 1, 0, 0);
81 0 : SSVAL(state->vwv + 2, 0, fnum);
82 0 : SIVAL(state->vwv + 3, 0, offset);
83 0 : SSVAL(state->vwv + 5, 0, size);
84 0 : SSVAL(state->vwv + 6, 0, size);
85 0 : SSVAL(state->vwv + 7, 0, (size >> 16));
86 0 : SSVAL(state->vwv + 8, 0, 0);
87 0 : SSVAL(state->vwv + 9, 0, 0);
88 :
89 0 : if (smb1cli_conn_capabilities(conn) & CAP_LARGE_FILES) {
90 0 : SIVAL(state->vwv + 10, 0,
91 : (((uint64_t)offset)>>32) & 0xffffffff);
92 0 : 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 0 : subreq = smb1cli_req_create(state, ev, conn, SMBreadX,
103 : 0, 0, /* *_flags */
104 : 0, 0, /* *_flags2 */
105 : timeout_msec, pid, tcon, session,
106 0 : wct, state->vwv,
107 : 0, NULL);
108 0 : if (tevent_req_nomem(subreq, req)) {
109 0 : return tevent_req_post(req, ev);
110 : }
111 0 : tevent_req_set_callback(subreq, smb1cli_readx_done, req);
112 :
113 0 : status = smb1cli_req_chain_submit(&subreq, 1);
114 0 : if (tevent_req_nterror(req, status)) {
115 0 : return tevent_req_post(req, ev);
116 : }
117 :
118 0 : return req;
119 : }
120 :
121 0 : static void smb1cli_readx_done(struct tevent_req *subreq)
122 : {
123 0 : struct tevent_req *req = tevent_req_callback_data(
124 : subreq, struct tevent_req);
125 0 : struct smb1cli_readx_state *state = tevent_req_data(
126 : req, struct smb1cli_readx_state);
127 0 : struct iovec *recv_iov = NULL;
128 : uint8_t wct;
129 : uint16_t *vwv;
130 : uint32_t num_bytes;
131 : uint8_t *bytes;
132 : uint16_t data_offset;
133 : uint32_t bytes_offset;
134 : NTSTATUS status;
135 : 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 0 : 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 0 : TALLOC_FREE(subreq);
158 0 : if (NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
159 : /* no error */
160 : } else {
161 0 : 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 0 : state->received = SVAL(vwv + 5, 0);
169 0 : state->received |= (((unsigned int)SVAL(vwv + 7, 0)) << 16);
170 :
171 0 : 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 0 : 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 0 : data_offset = SVAL(vwv+6, 0);
188 0 : 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 0 : 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 0 : state->buf = bytes + (data_offset - bytes_offset);
200 :
201 0 : state->out_valid = true;
202 :
203 0 : if (tevent_req_nterror(req, status)) {
204 0 : return;
205 : }
206 :
207 0 : 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 succsess.
224 : */
225 0 : NTSTATUS smb1cli_readx_recv(struct tevent_req *req,
226 : uint32_t *received,
227 : uint8_t **rcvbuf)
228 : {
229 0 : struct smb1cli_readx_state *state = tevent_req_data(
230 : req, struct smb1cli_readx_state);
231 0 : NTSTATUS status = NT_STATUS_OK;
232 :
233 0 : if (tevent_req_is_nterror(req, &status) && !state->out_valid) {
234 0 : *received = 0;
235 0 : *rcvbuf = NULL;
236 0 : return status;
237 : }
238 0 : *received = state->received;
239 0 : *rcvbuf = state->buf;
240 0 : return status;
241 : }
|