721ac9bfe4b96a3f9c1213d750124cff8f23b716
[kai/samba.git] / source3 / libsmb / async_smb.c
1 /*
2    Unix SMB/CIFS implementation.
3    Infrastructure for async SMB client requests
4    Copyright (C) Volker Lendecke 2008
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 "libsmb/libsmb.h"
22 #include "../lib/util/tevent_ntstatus.h"
23 #include "async_smb.h"
24 #include "../libcli/smb/smbXcli_base.h"
25
26 void cli_smb_req_unset_pending(struct tevent_req *req)
27 {
28         smbXcli_req_unset_pending(req);
29 }
30
31 bool cli_smb_req_set_pending(struct tevent_req *req)
32 {
33         return smbXcli_req_set_pending(req);
34 }
35
36 /*
37  * Fetch a smb request's mid. Only valid after the request has been sent by
38  * cli_smb_req_send().
39  */
40 uint16_t cli_smb_req_mid(struct tevent_req *req)
41 {
42         return smb1cli_req_mid(req);
43 }
44
45 void cli_smb_req_set_mid(struct tevent_req *req, uint16_t mid)
46 {
47         smb1cli_req_set_mid(req, mid);
48 }
49
50 uint32_t cli_smb_req_seqnum(struct tevent_req *req)
51 {
52         return smb1cli_req_seqnum(req);
53 }
54
55 void cli_smb_req_set_seqnum(struct tevent_req *req, uint32_t seqnum)
56 {
57         smb1cli_req_set_seqnum(req, seqnum);
58 }
59
60 struct cli_smb_req_state {
61         struct cli_state *cli;
62         uint8_t smb_command;
63         struct tevent_req *req;
64         struct cli_smb_req_state **ptr;
65 };
66
67 static int cli_smb_req_state_destructor(struct cli_smb_req_state *state)
68 {
69         talloc_set_destructor(state->ptr, NULL);
70         talloc_free(state->ptr);
71         return 0;
72 }
73
74 static int cli_smb_req_state_ptr_destructor(struct cli_smb_req_state **ptr)
75 {
76         struct cli_smb_req_state *state = *ptr;
77         void *parent = talloc_parent(state);
78
79         talloc_set_destructor(state, NULL);
80
81         talloc_reparent(state, parent, state->req);
82         talloc_free(state);
83         return 0;
84 }
85
86 struct tevent_req *cli_smb_req_create(TALLOC_CTX *mem_ctx,
87                                       struct tevent_context *ev,
88                                       struct cli_state *cli,
89                                       uint8_t smb_command,
90                                       uint8_t additional_flags,
91                                       uint8_t wct, uint16_t *vwv,
92                                       int iov_count,
93                                       struct iovec *bytes_iov)
94 {
95         struct cli_smb_req_state *state;
96         uint8_t clear_flags = 0;
97         uint16_t additional_flags2 = 0;
98         uint16_t clear_flags2 = 0;
99
100         state = talloc_zero(mem_ctx, struct cli_smb_req_state);
101         if (state == NULL) {
102                 return NULL;
103         }
104         state->cli = cli;
105         state->smb_command = smb_command;
106         state->ptr = talloc(state, struct cli_smb_req_state *);
107         if (state->ptr == NULL) {
108                 talloc_free(state);
109                 return NULL;
110         }
111         *state->ptr = state;
112
113         if (cli->case_sensitive) {
114                 clear_flags |= FLAG_CASELESS_PATHNAMES;
115         } else {
116                 /* Default setting, case insensitive. */
117                 additional_flags |= FLAG_CASELESS_PATHNAMES;
118         }
119
120         if ((cli_state_capabilities(cli) & CAP_DFS) && cli->dfsroot) {
121                 additional_flags2 |= FLAGS2_DFS_PATHNAMES;
122         }
123
124         state->req = smb1cli_req_create(state, ev, cli->conn, smb_command,
125                                         additional_flags, clear_flags,
126                                         additional_flags2, clear_flags2,
127                                         cli->timeout,
128                                         cli->smb1.pid,
129                                         cli->smb1.tid,
130                                         cli->smb1.uid,
131                                         wct, vwv, iov_count, bytes_iov);
132         if (state->req == NULL) {
133                 talloc_free(state);
134                 return NULL;
135         }
136
137         talloc_reparent(state, state->req, state->ptr);
138         talloc_set_destructor(state, cli_smb_req_state_destructor);
139         talloc_set_destructor(state->ptr, cli_smb_req_state_ptr_destructor);
140
141         return state->req;
142 }
143
144 NTSTATUS cli_smb_req_send(struct tevent_req *req)
145 {
146         return smb1cli_req_chain_submit(&req, 1);
147 }
148
149 struct tevent_req *cli_smb_send(TALLOC_CTX *mem_ctx,
150                                 struct tevent_context *ev,
151                                 struct cli_state *cli,
152                                 uint8_t smb_command,
153                                 uint8_t additional_flags,
154                                 uint8_t wct, uint16_t *vwv,
155                                 uint32_t num_bytes,
156                                 const uint8_t *bytes)
157 {
158         struct cli_smb_req_state *state;
159         uint8_t clear_flags = 0;
160         uint16_t additional_flags2 = 0;
161         uint16_t clear_flags2 = 0;
162
163         state = talloc_zero(mem_ctx, struct cli_smb_req_state);
164         if (state == NULL) {
165                 return NULL;
166         }
167         state->cli = cli;
168         state->smb_command = smb_command;
169         state->ptr = talloc(state, struct cli_smb_req_state *);
170         if (state->ptr == NULL) {
171                 talloc_free(state);
172                 return NULL;
173         }
174         *state->ptr = state;
175
176         if (cli->case_sensitive) {
177                 clear_flags |= FLAG_CASELESS_PATHNAMES;
178         } else {
179                 /* Default setting, case insensitive. */
180                 additional_flags |= FLAG_CASELESS_PATHNAMES;
181         }
182
183         if ((cli_state_capabilities(cli) & CAP_DFS) && cli->dfsroot) {
184                 additional_flags2 |= FLAGS2_DFS_PATHNAMES;
185         }
186
187         state->req = smb1cli_req_send(state, ev, cli->conn, smb_command,
188                                 additional_flags, clear_flags,
189                                 additional_flags2, clear_flags2,
190                                 cli->timeout,
191                                 cli->smb1.pid,
192                                 cli->smb1.tid,
193                                 cli->smb1.uid,
194                                 wct, vwv, num_bytes, bytes);
195         if (state->req == NULL) {
196                 talloc_free(state);
197                 return NULL;
198         }
199
200         talloc_reparent(state, state->req, state->ptr);
201         talloc_set_destructor(state, cli_smb_req_state_destructor);
202         talloc_set_destructor(state->ptr, cli_smb_req_state_ptr_destructor);
203
204         return state->req;
205 }
206
207 NTSTATUS cli_smb_recv(struct tevent_req *req,
208                       TALLOC_CTX *mem_ctx, uint8_t **pinbuf,
209                       uint8_t min_wct, uint8_t *pwct, uint16_t **pvwv,
210                       uint32_t *pnum_bytes, uint8_t **pbytes)
211 {
212         NTSTATUS status;
213         void *parent = talloc_parent(req);
214         struct cli_smb_req_state *state =
215                 talloc_get_type(parent,
216                 struct cli_smb_req_state);
217         struct iovec *recv_iov = NULL;
218         uint8_t wct = 0;
219         uint16_t *vwv = NULL;
220         uint32_t num_bytes;
221         uint8_t *bytes = NULL;
222         uint8_t *inbuf;
223         bool is_expected = false;
224         bool map_dos_errors = true;
225
226         if (pinbuf != NULL) {
227                 *pinbuf = NULL;
228         }
229         if (pwct != NULL) {
230                 *pwct = 0;
231         }
232         if (pvwv != NULL) {
233                 *pvwv = NULL;
234         }
235         if (pnum_bytes != NULL) {
236                 *pnum_bytes = 0;
237         }
238         if (pbytes != NULL) {
239                 *pbytes = NULL;
240         }
241
242         status = smb1cli_req_recv(req, req,
243                                   &recv_iov,
244                                   NULL, /* phdr */
245                                   &wct,
246                                   &vwv,
247                                   NULL, /* pvwv_offset */
248                                   &num_bytes,
249                                   &bytes,
250                                   NULL, /* pbytes_offset */
251                                   &inbuf,
252                                   NULL, 0); /* expected */
253
254         if (state) {
255                 if ((state->smb_command == SMBsesssetupX) &&
256                      NT_STATUS_EQUAL(status,
257                                 NT_STATUS_MORE_PROCESSING_REQUIRED)) {
258                         /*
259                          * NT_STATUS_MORE_PROCESSING_REQUIRED is a
260                          * valid return code for session setup
261                          */
262                         is_expected = true;
263                 }
264
265                 map_dos_errors = state->cli->map_dos_errors;
266                 state->cli->raw_status = status;
267                 talloc_free(state->ptr);
268                 state = NULL;
269         }
270
271         if (NT_STATUS_IS_DOS(status) && map_dos_errors) {
272                 uint8_t eclass = NT_STATUS_DOS_CLASS(status);
273                 uint16_t ecode = NT_STATUS_DOS_CODE(status);
274                 /*
275                  * TODO: is it really a good idea to do a mapping here?
276                  *
277                  * The old cli_pull_error() also does it, so I do not change
278                  * the behavior yet.
279                  */
280                 status = dos_to_ntstatus(eclass, ecode);
281         }
282
283         if (!NT_STATUS_IS_ERR(status)) {
284                 is_expected = true;
285         }
286
287         if (!is_expected) {
288                 TALLOC_FREE(recv_iov);
289                 return status;
290         }
291
292         if (wct < min_wct) {
293                 TALLOC_FREE(recv_iov);
294                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
295         }
296
297         if (pwct != NULL) {
298                 *pwct = wct;
299         }
300         if (pvwv != NULL) {
301                 *pvwv = vwv;
302         }
303         if (pnum_bytes != NULL) {
304                 *pnum_bytes = num_bytes;
305         }
306         if (pbytes != NULL) {
307                 *pbytes = bytes;
308         }
309
310         if (pinbuf != NULL && mem_ctx != NULL) {
311                 if (talloc_reference_count(inbuf) == 0) {
312                         *pinbuf = talloc_move(mem_ctx, &inbuf);
313                         TALLOC_FREE(recv_iov);
314                 } else {
315                         *pinbuf = inbuf;
316                 }
317         }
318
319         return status;
320 }
321
322 size_t cli_smb_wct_ofs(struct tevent_req **reqs, int num_reqs)
323 {
324         return smb1cli_req_wct_ofs(reqs, num_reqs);
325 }
326
327 NTSTATUS cli_smb_chain_send(struct tevent_req **reqs, int num_reqs)
328 {
329         return smb1cli_req_chain_submit(reqs, num_reqs);
330 }
331
332 bool cli_has_async_calls(struct cli_state *cli)
333 {
334         return smbXcli_conn_has_async_calls(cli->conn);
335 }