8d5c3d4b264f25fb7e1919e0662291e3bd49bdb3
[metze/samba/wip.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 struct cli_smb_req_state {
27         struct cli_state *cli;
28         uint8_t smb_command;
29         struct tevent_req *req;
30         struct cli_smb_req_state **ptr;
31 };
32
33 static int cli_smb_req_state_destructor(struct cli_smb_req_state *state)
34 {
35         talloc_set_destructor(state->ptr, NULL);
36         talloc_free(state->ptr);
37         return 0;
38 }
39
40 static int cli_smb_req_state_ptr_destructor(struct cli_smb_req_state **ptr)
41 {
42         struct cli_smb_req_state *state = *ptr;
43         void *parent = talloc_parent(state);
44
45         talloc_set_destructor(state, NULL);
46
47         talloc_reparent(state, parent, state->req);
48         talloc_free(state);
49         return 0;
50 }
51
52 struct tevent_req *cli_smb_req_create(TALLOC_CTX *mem_ctx,
53                                       struct tevent_context *ev,
54                                       struct cli_state *cli,
55                                       uint8_t smb_command,
56                                       uint8_t additional_flags,
57                                       uint8_t wct, uint16_t *vwv,
58                                       int iov_count,
59                                       struct iovec *bytes_iov)
60 {
61         struct cli_smb_req_state *state;
62         uint8_t clear_flags = 0;
63         uint16_t additional_flags2 = 0;
64         uint16_t clear_flags2 = 0;
65
66         state = talloc_zero(mem_ctx, struct cli_smb_req_state);
67         if (state == NULL) {
68                 return NULL;
69         }
70         state->cli = cli;
71         state->smb_command = smb_command;
72         state->ptr = talloc(state, struct cli_smb_req_state *);
73         if (state->ptr == NULL) {
74                 talloc_free(state);
75                 return NULL;
76         }
77         *state->ptr = state;
78
79         state->req = smb1cli_req_create(state, ev, cli->conn, smb_command,
80                                         additional_flags, clear_flags,
81                                         additional_flags2, clear_flags2,
82                                         cli->timeout,
83                                         cli->smb1.pid,
84                                         cli->smb1.tcon,
85                                         cli->smb1.session,
86                                         wct, vwv, iov_count, bytes_iov);
87         if (state->req == NULL) {
88                 talloc_free(state);
89                 return NULL;
90         }
91
92         talloc_reparent(state, state->req, state->ptr);
93         talloc_set_destructor(state, cli_smb_req_state_destructor);
94         talloc_set_destructor(state->ptr, cli_smb_req_state_ptr_destructor);
95
96         return state->req;
97 }
98
99 struct tevent_req *cli_smb_send(TALLOC_CTX *mem_ctx,
100                                 struct tevent_context *ev,
101                                 struct cli_state *cli,
102                                 uint8_t smb_command,
103                                 uint8_t additional_flags,
104                                 uint8_t wct, uint16_t *vwv,
105                                 uint32_t num_bytes,
106                                 const uint8_t *bytes)
107 {
108         struct cli_smb_req_state *state;
109         uint8_t clear_flags = 0;
110         uint16_t additional_flags2 = 0;
111         uint16_t clear_flags2 = 0;
112
113         state = talloc_zero(mem_ctx, struct cli_smb_req_state);
114         if (state == NULL) {
115                 return NULL;
116         }
117         state->cli = cli;
118         state->smb_command = smb_command;
119         state->ptr = talloc(state, struct cli_smb_req_state *);
120         if (state->ptr == NULL) {
121                 talloc_free(state);
122                 return NULL;
123         }
124         *state->ptr = state;
125
126         state->req = smb1cli_req_send(state, ev, cli->conn, smb_command,
127                                 additional_flags, clear_flags,
128                                 additional_flags2, clear_flags2,
129                                 cli->timeout,
130                                 cli->smb1.pid,
131                                 cli->smb1.tcon,
132                                 cli->smb1.session,
133                                 wct, vwv, num_bytes, bytes);
134         if (state->req == NULL) {
135                 talloc_free(state);
136                 return NULL;
137         }
138
139         talloc_reparent(state, state->req, state->ptr);
140         talloc_set_destructor(state, cli_smb_req_state_destructor);
141         talloc_set_destructor(state->ptr, cli_smb_req_state_ptr_destructor);
142
143         return state->req;
144 }
145
146 NTSTATUS cli_smb_recv(struct tevent_req *req,
147                       TALLOC_CTX *mem_ctx, uint8_t **pinbuf,
148                       uint8_t min_wct, uint8_t *pwct, uint16_t **pvwv,
149                       uint32_t *pnum_bytes, uint8_t **pbytes)
150 {
151         NTSTATUS status;
152         void *parent = talloc_parent(req);
153         struct cli_smb_req_state *state =
154                 talloc_get_type(parent,
155                 struct cli_smb_req_state);
156         struct iovec *recv_iov = NULL;
157         uint8_t wct = 0;
158         uint16_t *vwv = NULL;
159         uint32_t num_bytes;
160         uint8_t *bytes = NULL;
161         uint8_t *inbuf;
162         bool is_expected = false;
163         bool map_dos_errors = true;
164
165         if (pinbuf != NULL) {
166                 *pinbuf = NULL;
167         }
168         if (pwct != NULL) {
169                 *pwct = 0;
170         }
171         if (pvwv != NULL) {
172                 *pvwv = NULL;
173         }
174         if (pnum_bytes != NULL) {
175                 *pnum_bytes = 0;
176         }
177         if (pbytes != NULL) {
178                 *pbytes = NULL;
179         }
180
181         status = smb1cli_req_recv(req, req,
182                                   &recv_iov,
183                                   NULL, /* phdr */
184                                   &wct,
185                                   &vwv,
186                                   NULL, /* pvwv_offset */
187                                   &num_bytes,
188                                   &bytes,
189                                   NULL, /* pbytes_offset */
190                                   &inbuf,
191                                   NULL, 0); /* expected */
192
193         if (state) {
194                 if ((state->smb_command == SMBsesssetupX) &&
195                      NT_STATUS_EQUAL(status,
196                                 NT_STATUS_MORE_PROCESSING_REQUIRED)) {
197                         /*
198                          * NT_STATUS_MORE_PROCESSING_REQUIRED is a
199                          * valid return code for session setup
200                          */
201                         is_expected = true;
202                 }
203
204                 map_dos_errors = state->cli->map_dos_errors;
205                 state->cli->raw_status = status;
206                 talloc_free(state->ptr);
207                 state = NULL;
208         }
209
210         if (NT_STATUS_IS_DOS(status) && map_dos_errors) {
211                 uint8_t eclass = NT_STATUS_DOS_CLASS(status);
212                 uint16_t ecode = NT_STATUS_DOS_CODE(status);
213                 /*
214                  * TODO: is it really a good idea to do a mapping here?
215                  *
216                  * The old cli_pull_error() also does it, so I do not change
217                  * the behavior yet.
218                  */
219                 status = dos_to_ntstatus(eclass, ecode);
220         }
221
222         if (!NT_STATUS_IS_ERR(status)) {
223                 is_expected = true;
224         }
225
226         if (!is_expected) {
227                 TALLOC_FREE(recv_iov);
228                 return status;
229         }
230
231         if (wct < min_wct) {
232                 TALLOC_FREE(recv_iov);
233                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
234         }
235
236         if (pwct != NULL) {
237                 *pwct = wct;
238         }
239         if (pvwv != NULL) {
240                 *pvwv = vwv;
241         }
242         if (pnum_bytes != NULL) {
243                 *pnum_bytes = num_bytes;
244         }
245         if (pbytes != NULL) {
246                 *pbytes = bytes;
247         }
248
249         if (pinbuf != NULL && mem_ctx != NULL) {
250                 if (talloc_reference_count(inbuf) == 0) {
251                         *pinbuf = talloc_move(mem_ctx, &inbuf);
252                         TALLOC_FREE(recv_iov);
253                 } else {
254                         *pinbuf = inbuf;
255                 }
256         } else if (mem_ctx != NULL) {
257                 if (talloc_reference_count(inbuf) == 0) {
258                         (void)talloc_move(mem_ctx, &inbuf);
259                         TALLOC_FREE(recv_iov);
260                 }
261         }
262
263         return status;
264 }