tevent: Add utility function epoll_handle_hup_or_err()
[Samba.git] / source3 / libsmb / async_smb.c
blobed68a5d927cc7ad2bfd6e0fe8bb180f3d674fe1a
1 /*
2 Unix SMB/CIFS implementation.
3 Infrastructure for async SMB client requests
4 Copyright (C) Volker Lendecke 2008
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.
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.
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/>.
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"
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;
33 static int cli_smb_req_state_destructor(struct cli_smb_req_state *state)
35 talloc_set_destructor(state->ptr, NULL);
36 talloc_free(state->ptr);
37 return 0;
40 static int cli_smb_req_state_ptr_destructor(struct cli_smb_req_state **ptr)
42 struct cli_smb_req_state *state = *ptr;
43 void *parent = talloc_parent(state);
45 talloc_set_destructor(state, NULL);
47 talloc_reparent(state, parent, state->req);
48 talloc_free(state);
49 return 0;
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)
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;
66 state = talloc_zero(mem_ctx, struct cli_smb_req_state);
67 if (state == NULL) {
68 return NULL;
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;
77 *state->ptr = state;
79 if (cli->case_sensitive) {
80 clear_flags |= FLAG_CASELESS_PATHNAMES;
81 } else {
82 /* Default setting, case insensitive. */
83 additional_flags |= FLAG_CASELESS_PATHNAMES;
86 if ((smb1cli_conn_capabilities(cli->conn) & CAP_DFS) && cli->dfsroot) {
87 additional_flags2 |= FLAGS2_DFS_PATHNAMES;
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;
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);
107 return state->req;
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)
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;
124 state = talloc_zero(mem_ctx, struct cli_smb_req_state);
125 if (state == NULL) {
126 return NULL;
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;
135 *state->ptr = state;
137 if (cli->case_sensitive) {
138 clear_flags |= FLAG_CASELESS_PATHNAMES;
139 } else {
140 /* Default setting, case insensitive. */
141 additional_flags |= FLAG_CASELESS_PATHNAMES;
144 if ((smb1cli_conn_capabilities(cli->conn) & CAP_DFS) && cli->dfsroot) {
145 additional_flags2 |= FLAGS2_DFS_PATHNAMES;
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;
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);
165 return state->req;
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)
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;
187 if (pinbuf != NULL) {
188 *pinbuf = NULL;
190 if (pwct != NULL) {
191 *pwct = 0;
193 if (pvwv != NULL) {
194 *pvwv = NULL;
196 if (pnum_bytes != NULL) {
197 *pnum_bytes = 0;
199 if (pbytes != NULL) {
200 *pbytes = NULL;
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 */
215 if (state) {
216 if ((state->smb_command == SMBsesssetupX) &&
217 NT_STATUS_EQUAL(status,
218 NT_STATUS_MORE_PROCESSING_REQUIRED)) {
220 * NT_STATUS_MORE_PROCESSING_REQUIRED is a
221 * valid return code for session setup
223 is_expected = true;
226 map_dos_errors = state->cli->map_dos_errors;
227 state->cli->raw_status = status;
228 talloc_free(state->ptr);
229 state = NULL;
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);
236 * TODO: is it really a good idea to do a mapping here?
238 * The old cli_pull_error() also does it, so I do not change
239 * the behavior yet.
241 status = dos_to_ntstatus(eclass, ecode);
244 if (!NT_STATUS_IS_ERR(status)) {
245 is_expected = true;
248 if (!is_expected) {
249 TALLOC_FREE(recv_iov);
250 return status;
253 if (wct < min_wct) {
254 TALLOC_FREE(recv_iov);
255 return NT_STATUS_INVALID_NETWORK_RESPONSE;
258 if (pwct != NULL) {
259 *pwct = wct;
261 if (pvwv != NULL) {
262 *pvwv = vwv;
264 if (pnum_bytes != NULL) {
265 *pnum_bytes = num_bytes;
267 if (pbytes != NULL) {
268 *pbytes = bytes;
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;
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);
285 return status;