/src/samba/libcli/smb/smb1cli_read.c
Line | Count | Source |
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 | | 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 | 0 | { |
67 | 0 | NTSTATUS status; |
68 | 0 | struct tevent_req *req, *subreq; |
69 | 0 | 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 | 0 | } |
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 | 0 | (((uint64_t)offset)>>32) & 0xffffffff); |
92 | 0 | wct = 12; |
93 | 0 | } else { |
94 | 0 | if ((((uint64_t)offset) & 0xffffffff00000000LL) != 0) { |
95 | 0 | DEBUG(10, ("smb1cli_readx_send got large offset where " |
96 | 0 | "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 | 0 | } |
100 | 0 | } |
101 | | |
102 | 0 | subreq = smb1cli_req_create(state, ev, conn, SMBreadX, |
103 | 0 | 0, 0, /* *_flags */ |
104 | 0 | 0, 0, /* *_flags2 */ |
105 | 0 | timeout_msec, pid, tcon, session, |
106 | 0 | wct, state->vwv, |
107 | 0 | 0, NULL); |
108 | 0 | if (tevent_req_nomem(subreq, req)) { |
109 | 0 | return tevent_req_post(req, ev); |
110 | 0 | } |
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 | 0 | } |
117 | | |
118 | 0 | return req; |
119 | 0 | } |
120 | | |
121 | | static void smb1cli_readx_done(struct tevent_req *subreq) |
122 | 0 | { |
123 | 0 | struct tevent_req *req = tevent_req_callback_data( |
124 | 0 | subreq, struct tevent_req); |
125 | 0 | struct smb1cli_readx_state *state = tevent_req_data( |
126 | 0 | req, struct smb1cli_readx_state); |
127 | 0 | 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 | 0 | { |
137 | 0 | .status = NT_STATUS_OK, |
138 | 0 | .wct = 0x0C |
139 | 0 | }, |
140 | 0 | { |
141 | 0 | .status = STATUS_BUFFER_OVERFLOW, |
142 | 0 | .wct = 0x0C |
143 | 0 | }, |
144 | 0 | }; |
145 | |
|
146 | 0 | status = smb1cli_req_recv(subreq, state, |
147 | 0 | &recv_iov, |
148 | 0 | NULL, /* phdr */ |
149 | 0 | &wct, |
150 | 0 | &vwv, |
151 | 0 | NULL, /* pvwv_offset */ |
152 | 0 | &num_bytes, |
153 | 0 | &bytes, |
154 | 0 | &bytes_offset, |
155 | 0 | NULL, /* inbuf */ |
156 | 0 | expected, ARRAY_SIZE(expected)); |
157 | 0 | TALLOC_FREE(subreq); |
158 | 0 | if (NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) { |
159 | | /* no error */ |
160 | 0 | } else { |
161 | 0 | if (tevent_req_nterror(req, status)) { |
162 | 0 | return; |
163 | 0 | } |
164 | 0 | } |
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 | 0 | } |
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 | 0 | } |
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 | 0 | } |
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 | 0 | } |
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 | 0 | } |
206 | | |
207 | 0 | tevent_req_done(req); |
208 | 0 | } |
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 | | NTSTATUS smb1cli_readx_recv(struct tevent_req *req, |
226 | | uint32_t *received, |
227 | | uint8_t **rcvbuf) |
228 | 0 | { |
229 | 0 | struct smb1cli_readx_state *state = tevent_req_data( |
230 | 0 | 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 | 0 | } |
238 | 0 | *received = state->received; |
239 | 0 | *rcvbuf = state->buf; |
240 | 0 | return status; |
241 | 0 | } |