s3/torture: local_rbtree: avoid birthday collisions
[samba4-gss.git] / libcli / tstream_binding_handle / tstream_binding_handle.c
blob76f54a7f338af494a5626ee6f2a897985a72f912
1 /*
2 Unix SMB/CIFS implementation.
4 Copyright (C) Ralph Boehme 2016
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 <tevent.h>
22 #include "libcli/tstream_binding_handle/tstream_binding_handle.h"
23 #include "system/filesys.h"
24 #include "lib/util/tevent_ntstatus.h"
25 #include "lib/tsocket/tsocket.h"
26 #include "lib/util/debug.h"
27 #include "lib/util/tevent_ntstatus.h"
28 #include "libcli/smb/tstream_smbXcli_np.h"
30 struct tstream_bh_state {
31 struct tstream_context *stream;
32 struct tevent_queue *write_queue;
33 const struct ndr_interface_table *table;
34 uint32_t request_timeout;
35 size_t call_initial_read_size;
36 tstream_read_pdu_blob_full_fn_t *complete_pdu_fn;
37 void *complete_pdu_fn_private;
40 static bool tstream_bh_is_connected(struct dcerpc_binding_handle *h)
42 struct tstream_bh_state *hs = dcerpc_binding_handle_data(
43 h, struct tstream_bh_state);
44 ssize_t ret;
46 if (hs->stream == NULL) {
47 return false;
50 ret = tstream_pending_bytes(hs->stream);
51 if (ret == -1) {
52 return false;
55 return true;
58 static uint32_t tstream_bh_set_timeout(struct dcerpc_binding_handle *h,
59 uint32_t timeout)
61 struct tstream_bh_state *hs = dcerpc_binding_handle_data(
62 h, struct tstream_bh_state);
63 uint32_t old;
65 old = hs->request_timeout;
66 hs->request_timeout = timeout;
68 return old;
71 struct tstream_bh_disconnect_state {
72 struct tstream_bh_state *hs;
75 static void tstream_bh_disconnect_done(struct tevent_req *subreq);
77 static struct tevent_req *tstream_bh_disconnect_send(
78 TALLOC_CTX *mem_ctx,
79 struct tevent_context *ev,
80 struct dcerpc_binding_handle *h)
82 struct tstream_bh_state *hs = dcerpc_binding_handle_data(
83 h, struct tstream_bh_state);
84 struct tevent_req *req = NULL;
85 struct tstream_bh_disconnect_state *state = NULL;
86 struct tevent_req *subreq = NULL;
87 bool ok;
89 req = tevent_req_create(mem_ctx, &state,
90 struct tstream_bh_disconnect_state);
91 if (req == NULL) {
92 return NULL;
95 state->hs = hs;
97 ok = tstream_bh_is_connected(h);
98 if (!ok) {
99 tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED);
100 return tevent_req_post(req, ev);
103 subreq = tstream_disconnect_send(state, ev, hs->stream);
104 if (tevent_req_nomem(subreq, req)) {
105 tevent_req_post(req, ev);
106 return req;
108 tevent_req_set_callback(subreq, tstream_bh_disconnect_done, req);
110 return req;
113 static void tstream_bh_disconnect_done(struct tevent_req *subreq)
115 struct tevent_req *req = tevent_req_callback_data(
116 subreq, struct tevent_req);
117 struct tstream_bh_disconnect_state *state = tevent_req_data(
118 req, struct tstream_bh_disconnect_state);
119 int ret, err;
121 ret = tstream_disconnect_recv(subreq, &err);
122 TALLOC_FREE(subreq);
123 if (ret != 0) {
124 DBG_ERR("tstream_bh_disconnect failed [%s]\n", strerror(err));
125 tevent_req_nterror(req, map_nt_error_from_unix_common(err));
126 return;
129 state->hs->stream = NULL;
132 static NTSTATUS tstream_bh_disconnect_recv(struct tevent_req *req)
134 NTSTATUS status;
136 if (tevent_req_is_nterror(req, &status)) {
137 tevent_req_received(req);
138 return status;
141 tevent_req_received(req);
142 return NT_STATUS_OK;
145 struct tstream_bh_call_state {
146 struct tevent_context *ev;
147 struct tstream_context *stream;
148 struct tstream_bh_state *hs;
149 struct iovec out_data;
150 DATA_BLOB in_data;
153 static void tstream_bh_call_writev_done(struct tevent_req *subreq);
154 static void tstream_bh_call_read_pdu_done(struct tevent_req *subreq);
156 static struct tevent_req *tstream_bh_call_send(TALLOC_CTX *mem_ctx,
157 struct tevent_context *ev,
158 struct dcerpc_binding_handle *h,
159 const struct GUID *object,
160 uint32_t opnum,
161 uint32_t in_flags,
162 const uint8_t *out_data,
163 size_t out_length)
165 struct tstream_bh_state *hs = dcerpc_binding_handle_data(
166 h, struct tstream_bh_state);
167 struct tevent_req *req = NULL;
168 struct tevent_req *subreq = NULL;
169 struct tstream_bh_call_state* state = NULL;
170 struct timeval timeout;
171 bool ok;
173 req = tevent_req_create(mem_ctx, &state,
174 struct tstream_bh_call_state);
175 if (req == NULL) {
176 return NULL;
179 *state = (struct tstream_bh_call_state) {
180 .ev = ev,
181 .stream = hs->stream,
182 .hs = hs,
183 .out_data = {
184 .iov_base = discard_const_p(uint8_t, out_data),
185 .iov_len = out_length,
189 ok = tstream_bh_is_connected(h);
190 if (!ok) {
191 tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED);
192 return tevent_req_post(req, ev);
195 if (tstream_is_smbXcli_np(hs->stream)) {
196 tstream_smbXcli_np_use_trans(hs->stream);
198 if (tevent_queue_length(hs->write_queue) > 0) {
199 tevent_req_nterror(req, NT_STATUS_PIPE_BUSY);
202 timeout = timeval_current_ofs(hs->request_timeout, 0);
204 subreq = tstream_writev_queue_send(state, ev,
205 state->stream,
206 hs->write_queue,
207 &state->out_data, 1);
208 if (tevent_req_nomem(subreq, req)) {
209 return tevent_req_post(req, ev);
211 if (!tevent_req_set_endtime(subreq, ev, timeout)) {
212 return tevent_req_post(req, ev);
214 tevent_req_set_callback(subreq, tstream_bh_call_writev_done, req);
216 subreq = tstream_read_pdu_blob_send(state,
218 hs->stream,
219 hs->call_initial_read_size,
220 hs->complete_pdu_fn,
221 hs->complete_pdu_fn_private);
222 if (tevent_req_nomem(subreq, req)) {
223 return tevent_req_post(req, ev);
225 if (!tevent_req_set_endtime(subreq, ev, timeout)) {
226 return tevent_req_post(req, ev);
228 tevent_req_set_callback(subreq, tstream_bh_call_read_pdu_done, req);
230 return req;
233 static void tstream_bh_call_writev_done(struct tevent_req *subreq)
235 struct tevent_req *req = tevent_req_callback_data(
236 subreq, struct tevent_req);
237 struct tstream_bh_call_state *state = tevent_req_data(
238 req, struct tstream_bh_call_state);
239 int ret, err;
241 ret = tstream_writev_queue_recv(subreq, &err);
242 TALLOC_FREE(subreq);
243 if (ret == -1) {
244 state->hs->stream = NULL;
245 tevent_req_nterror(req, map_nt_error_from_unix_common(err));
246 return;
250 static void tstream_bh_call_read_pdu_done(struct tevent_req *subreq)
252 struct tevent_req *req = tevent_req_callback_data(
253 subreq, struct tevent_req);
254 struct tstream_bh_call_state *state = tevent_req_data(
255 req, struct tstream_bh_call_state);
256 NTSTATUS status;
258 status = tstream_read_pdu_blob_recv(subreq, state, &state->in_data);
259 TALLOC_FREE(subreq);
260 if (!NT_STATUS_IS_OK(status)) {
261 state->hs->stream = NULL;
262 tevent_req_nterror(req, status);
263 return;
266 tevent_req_done(req);
269 static NTSTATUS tstream_bh_call_recv(struct tevent_req *req,
270 TALLOC_CTX *mem_ctx,
271 uint8_t **in_data,
272 size_t *in_length,
273 uint32_t *in_flags)
275 NTSTATUS status;
276 struct tstream_bh_call_state *state = tevent_req_data(
277 req, struct tstream_bh_call_state);
279 if (tevent_req_is_nterror(req, &status)) {
280 tevent_req_received(req);
281 return status;
284 *in_data = talloc_move(mem_ctx, &state->in_data.data);
285 *in_length = state->in_data.length;
287 tevent_req_received(req);
288 return NT_STATUS_OK;
291 static const struct dcerpc_binding_handle_ops tstream_bh_ops = {
292 .name = "tstream_binding_handle",
293 .is_connected = tstream_bh_is_connected,
294 .set_timeout = tstream_bh_set_timeout,
295 .raw_call_send = tstream_bh_call_send,
296 .raw_call_recv = tstream_bh_call_recv,
297 .disconnect_send = tstream_bh_disconnect_send,
298 .disconnect_recv = tstream_bh_disconnect_recv,
301 struct dcerpc_binding_handle *tstream_binding_handle_create(
302 TALLOC_CTX *mem_ctx,
303 const struct ndr_interface_table *table,
304 struct tstream_context **stream,
305 size_t call_initial_read_size,
306 tstream_read_pdu_blob_full_fn_t *complete_pdu_fn,
307 void *complete_pdu_fn_private,
308 uint32_t max_data)
310 struct dcerpc_binding_handle *h = NULL;
311 struct tstream_bh_state *hs = NULL;
313 h = dcerpc_binding_handle_create(mem_ctx,
314 &tstream_bh_ops,
315 NULL,
316 table,
317 &hs,
318 struct tstream_bh_state,
319 __location__);
320 if (h == NULL) {
321 return NULL;
324 hs->table = table;
325 hs->stream = talloc_move(hs, stream);
326 hs->call_initial_read_size = call_initial_read_size;
327 hs->complete_pdu_fn = complete_pdu_fn;
328 hs->complete_pdu_fn_private = complete_pdu_fn_private;
330 hs->write_queue = tevent_queue_create(hs, "write_queue");
331 if (hs->write_queue == NULL) {
332 TALLOC_FREE(h);
333 return NULL;
336 if (max_data > 0) {
337 tstream_smbXcli_np_set_max_data(hs->stream, max_data);
340 return h;