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