libcli/smb: add smb1cli_readx*
[kai/samba-autobuild/.git] / libcli / smb / smb1cli_read.c
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         NTSTATUS status;
30         uint32_t received;
31         uint8_t *buf;
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 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         uint8_t wct = 10;
71
72         req = tevent_req_create(mem_ctx, &state, struct smb1cli_readx_state);
73         if (req == NULL) {
74                 return NULL;
75         }
76         state->size = size;
77
78         SCVAL(state->vwv + 0, 0, 0xFF);
79         SCVAL(state->vwv + 0, 1, 0);
80         SSVAL(state->vwv + 1, 0, 0);
81         SSVAL(state->vwv + 2, 0, fnum);
82         SIVAL(state->vwv + 3, 0, offset);
83         SSVAL(state->vwv + 5, 0, size);
84         SSVAL(state->vwv + 6, 0, size);
85         SSVAL(state->vwv + 7, 0, (size >> 16));
86         SSVAL(state->vwv + 8, 0, 0);
87         SSVAL(state->vwv + 9, 0, 0);
88
89         if (smb1cli_conn_capabilities(conn) & CAP_LARGE_FILES) {
90                 SIVAL(state->vwv + 10, 0,
91                       (((uint64_t)offset)>>32) & 0xffffffff);
92                 wct = 12;
93         } else {
94                 if ((((uint64_t)offset) & 0xffffffff00000000LL) != 0) {
95                         DEBUG(10, ("smb1cli_readx_send got large offset where "
96                                    "the server does not support it\n"));
97                         tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
98                         return tevent_req_post(req, ev);
99                 }
100         }
101
102         subreq = smb1cli_req_create(state, ev, conn, SMBreadX,
103                                     0, 0, /* *_flags */
104                                     0, 0, /* *_flags2 */
105                                     timeout_msec, pid, tcon, session,
106                                     wct, state->vwv,
107                                     0, NULL);
108         if (tevent_req_nomem(subreq, req)) {
109                 return tevent_req_post(req, ev);
110         }
111         tevent_req_set_callback(subreq, smb1cli_readx_done, req);
112
113         status = smb1cli_req_chain_submit(&subreq, 1);
114         if (tevent_req_nterror(req, status)) {
115                 return tevent_req_post(req, ev);
116         }
117
118         return req;
119 }
120
121 static void smb1cli_readx_done(struct tevent_req *subreq)
122 {
123         struct tevent_req *req = tevent_req_callback_data(
124                 subreq, struct tevent_req);
125         struct smb1cli_readx_state *state = tevent_req_data(
126                 req, struct smb1cli_readx_state);
127         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         static const struct smb1cli_req_expected_response expected[] = {
135         {
136                 .status = NT_STATUS_OK,
137                 .wct = 0x0C
138         },
139         };
140
141         state->status = smb1cli_req_recv(subreq, state,
142                                          &recv_iov,
143                                          NULL, /* phdr */
144                                          &wct,
145                                          &vwv,
146                                          NULL, /* pvwv_offset */
147                                          &num_bytes,
148                                          &bytes,
149                                          &bytes_offset,
150                                          NULL, /* inbuf */
151                                          expected, ARRAY_SIZE(expected));
152         TALLOC_FREE(subreq);
153         if (tevent_req_nterror(req, state->status)) {
154                 return;
155         }
156
157         /* size is the number of bytes the server returned.
158          * Might be zero. */
159         state->received = SVAL(vwv + 5, 0);
160         state->received |= (((unsigned int)SVAL(vwv + 7, 0)) << 16);
161
162         if (state->received > state->size) {
163                 DEBUG(5,("server returned more than we wanted!\n"));
164                 tevent_req_nterror(req, NT_STATUS_UNEXPECTED_IO_ERROR);
165                 return;
166         }
167
168         /*
169          * bcc field must be valid for small reads, for large reads the 16-bit
170          * bcc field can't be correct.
171          */
172         if ((state->received < 0xffff) && (state->received > num_bytes)) {
173                 DEBUG(5, ("server announced more bytes than sent\n"));
174                 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
175                 return;
176         }
177
178         data_offset = SVAL(vwv+6, 0);
179         if (data_offset < bytes_offset) {
180                 DEBUG(5, ("server returned invalid read&x data offset\n"));
181                 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
182                 return;
183         }
184         if (smb_buffer_oob(num_bytes, data_offset - bytes_offset, state->received)) {
185                 DEBUG(5, ("server returned invalid read&x data offset\n"));
186                 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
187                 return;
188         }
189
190         state->buf = bytes + (data_offset - bytes_offset);
191
192         tevent_req_done(req);
193 }
194
195 /**
196  * Receive the response to an asynchronous SMB_COM_READ_ANDX request.
197  * <a href="http://msdn.microsoft.com/en-us/library/ee441872.aspx">MS-CIFS 2.2.4.42.2</a>,
198  * <a href="http://msdn.microsoft.com/en-us/library/ff470017.aspx">MS-SMB 2.2.4.2.2</a>
199  *
200  * @warning rcvbuf is talloced from the request, so better make sure that you
201  * copy it away before  you talloc_free(req). rcvbuf is NOT a talloc_ctx of its
202  * own, so do not talloc_move it!
203  *
204  * @param[in] req A tevent request created with smb1cli_readx_send()
205  * @param[out] received The number of bytes received.
206  * @param[out] rcvbuf Pointer to the bytes received.
207  *
208  * @return NT_STATUS_OK on succsess.
209  */
210 NTSTATUS smb1cli_readx_recv(struct tevent_req *req,
211                             uint32_t *received,
212                             uint8_t **rcvbuf)
213 {
214         struct smb1cli_readx_state *state = tevent_req_data(
215                 req, struct smb1cli_readx_state);
216         NTSTATUS status;
217
218         if (tevent_req_is_nterror(req, &status)) {
219                 return status;
220         }
221         *received = state->received;
222         *rcvbuf = state->buf;
223         return NT_STATUS_OK;
224 }