s3/libsmb: Use smbXcli_conn_dfs_supported instead of test on CAP_DFS
[obnox/samba/samba-obnox.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         if (cli->case_sensitive) {
80                 clear_flags |= FLAG_CASELESS_PATHNAMES;
81         } else {
82                 /* Default setting, case insensitive. */
83                 additional_flags |= FLAG_CASELESS_PATHNAMES;
84         }
85
86         if (smbXcli_conn_dfs_supported(cli->conn) && cli->dfsroot) {
87                 additional_flags2 |= FLAGS2_DFS_PATHNAMES;
88         }
89
90         state->req = smb1cli_req_create(state, ev, cli->conn, smb_command,
91                                         additional_flags, clear_flags,
92                                         additional_flags2, clear_flags2,
93                                         cli->timeout,
94                                         cli->smb1.pid,
95                                         cli->smb1.tcon,
96                                         cli->smb1.session,
97                                         wct, vwv, iov_count, bytes_iov);
98         if (state->req == NULL) {
99                 talloc_free(state);
100                 return NULL;
101         }
102
103         talloc_reparent(state, state->req, state->ptr);
104         talloc_set_destructor(state, cli_smb_req_state_destructor);
105         talloc_set_destructor(state->ptr, cli_smb_req_state_ptr_destructor);
106
107         return state->req;
108 }
109
110 struct tevent_req *cli_smb_send(TALLOC_CTX *mem_ctx,
111                                 struct tevent_context *ev,
112                                 struct cli_state *cli,
113                                 uint8_t smb_command,
114                                 uint8_t additional_flags,
115                                 uint8_t wct, uint16_t *vwv,
116                                 uint32_t num_bytes,
117                                 const uint8_t *bytes)
118 {
119         struct cli_smb_req_state *state;
120         uint8_t clear_flags = 0;
121         uint16_t additional_flags2 = 0;
122         uint16_t clear_flags2 = 0;
123
124         state = talloc_zero(mem_ctx, struct cli_smb_req_state);
125         if (state == NULL) {
126                 return NULL;
127         }
128         state->cli = cli;
129         state->smb_command = smb_command;
130         state->ptr = talloc(state, struct cli_smb_req_state *);
131         if (state->ptr == NULL) {
132                 talloc_free(state);
133                 return NULL;
134         }
135         *state->ptr = state;
136
137         if (cli->case_sensitive) {
138                 clear_flags |= FLAG_CASELESS_PATHNAMES;
139         } else {
140                 /* Default setting, case insensitive. */
141                 additional_flags |= FLAG_CASELESS_PATHNAMES;
142         }
143
144         if (smbXcli_conn_dfs_supported(cli->conn) && cli->dfsroot) {
145                 additional_flags2 |= FLAGS2_DFS_PATHNAMES;
146         }
147
148         state->req = smb1cli_req_send(state, ev, cli->conn, smb_command,
149                                 additional_flags, clear_flags,
150                                 additional_flags2, clear_flags2,
151                                 cli->timeout,
152                                 cli->smb1.pid,
153                                 cli->smb1.tcon,
154                                 cli->smb1.session,
155                                 wct, vwv, num_bytes, bytes);
156         if (state->req == NULL) {
157                 talloc_free(state);
158                 return NULL;
159         }
160
161         talloc_reparent(state, state->req, state->ptr);
162         talloc_set_destructor(state, cli_smb_req_state_destructor);
163         talloc_set_destructor(state->ptr, cli_smb_req_state_ptr_destructor);
164
165         return state->req;
166 }
167
168 NTSTATUS cli_smb_recv(struct tevent_req *req,
169                       TALLOC_CTX *mem_ctx, uint8_t **pinbuf,
170                       uint8_t min_wct, uint8_t *pwct, uint16_t **pvwv,
171                       uint32_t *pnum_bytes, uint8_t **pbytes)
172 {
173         NTSTATUS status;
174         void *parent = talloc_parent(req);
175         struct cli_smb_req_state *state =
176                 talloc_get_type(parent,
177                 struct cli_smb_req_state);
178         struct iovec *recv_iov = NULL;
179         uint8_t wct = 0;
180         uint16_t *vwv = NULL;
181         uint32_t num_bytes;
182         uint8_t *bytes = NULL;
183         uint8_t *inbuf;
184         bool is_expected = false;
185         bool map_dos_errors = true;
186
187         if (pinbuf != NULL) {
188                 *pinbuf = NULL;
189         }
190         if (pwct != NULL) {
191                 *pwct = 0;
192         }
193         if (pvwv != NULL) {
194                 *pvwv = NULL;
195         }
196         if (pnum_bytes != NULL) {
197                 *pnum_bytes = 0;
198         }
199         if (pbytes != NULL) {
200                 *pbytes = NULL;
201         }
202
203         status = smb1cli_req_recv(req, req,
204                                   &recv_iov,
205                                   NULL, /* phdr */
206                                   &wct,
207                                   &vwv,
208                                   NULL, /* pvwv_offset */
209                                   &num_bytes,
210                                   &bytes,
211                                   NULL, /* pbytes_offset */
212                                   &inbuf,
213                                   NULL, 0); /* expected */
214
215         if (state) {
216                 if ((state->smb_command == SMBsesssetupX) &&
217                      NT_STATUS_EQUAL(status,
218                                 NT_STATUS_MORE_PROCESSING_REQUIRED)) {
219                         /*
220                          * NT_STATUS_MORE_PROCESSING_REQUIRED is a
221                          * valid return code for session setup
222                          */
223                         is_expected = true;
224                 }
225
226                 map_dos_errors = state->cli->map_dos_errors;
227                 state->cli->raw_status = status;
228                 talloc_free(state->ptr);
229                 state = NULL;
230         }
231
232         if (NT_STATUS_IS_DOS(status) && map_dos_errors) {
233                 uint8_t eclass = NT_STATUS_DOS_CLASS(status);
234                 uint16_t ecode = NT_STATUS_DOS_CODE(status);
235                 /*
236                  * TODO: is it really a good idea to do a mapping here?
237                  *
238                  * The old cli_pull_error() also does it, so I do not change
239                  * the behavior yet.
240                  */
241                 status = dos_to_ntstatus(eclass, ecode);
242         }
243
244         if (!NT_STATUS_IS_ERR(status)) {
245                 is_expected = true;
246         }
247
248         if (!is_expected) {
249                 TALLOC_FREE(recv_iov);
250                 return status;
251         }
252
253         if (wct < min_wct) {
254                 TALLOC_FREE(recv_iov);
255                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
256         }
257
258         if (pwct != NULL) {
259                 *pwct = wct;
260         }
261         if (pvwv != NULL) {
262                 *pvwv = vwv;
263         }
264         if (pnum_bytes != NULL) {
265                 *pnum_bytes = num_bytes;
266         }
267         if (pbytes != NULL) {
268                 *pbytes = bytes;
269         }
270
271         if (pinbuf != NULL && mem_ctx != NULL) {
272                 if (talloc_reference_count(inbuf) == 0) {
273                         *pinbuf = talloc_move(mem_ctx, &inbuf);
274                         TALLOC_FREE(recv_iov);
275                 } else {
276                         *pinbuf = inbuf;
277                 }
278         } else if (mem_ctx != NULL) {
279                 if (talloc_reference_count(inbuf) == 0) {
280                         (void)talloc_move(mem_ctx, &inbuf);
281                         TALLOC_FREE(recv_iov);
282                 }
283         }
284
285         return status;
286 }