librpc/rpc: make the dcerpc_binding_handle_raw_call_send/recv() wrapper more thin
[metze/samba/wip.git] / librpc / rpc / binding_handle.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    dcerpc binding handle functions
5
6    Copyright (C) Stefan Metzmacher 2010
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include <tevent.h>
24 #include "../lib/util/tevent_ntstatus.h"
25 #include "librpc/rpc/dcerpc.h"
26 #include "rpc_common.h"
27
28 struct dcerpc_binding_handle {
29         void *private_data;
30         const struct dcerpc_binding_handle_ops *ops;
31         const char *location;
32         const struct GUID *object;
33         const struct ndr_interface_table *table;
34         struct tevent_context *sync_ev;
35 };
36
37 static int dcerpc_binding_handle_destructor(struct dcerpc_binding_handle *b)
38 {
39         return 0;
40 }
41
42 struct dcerpc_binding_handle *_dcerpc_binding_handle_create(TALLOC_CTX *mem_ctx,
43                                         const struct dcerpc_binding_handle_ops *ops,
44                                         const struct GUID *object,
45                                         const struct ndr_interface_table *table,
46                                         void *pstate,
47                                         size_t psize,
48                                         const char *type,
49                                         const char *location)
50 {
51         struct dcerpc_binding_handle *h;
52         void **ppstate = (void **)pstate;
53         void *state;
54
55         h = talloc_zero(mem_ctx, struct dcerpc_binding_handle);
56         if (h == NULL) {
57                 return NULL;
58         }
59         h->ops          = ops;
60         h->location     = location;
61         h->object       = object;
62         h->table        = table;
63
64         state = talloc_zero_size(h, psize);
65         if (state == NULL) {
66                 talloc_free(h);
67                 return NULL;
68         }
69         talloc_set_name_const(state, type);
70
71         h->private_data = state;
72
73         talloc_set_destructor(h, dcerpc_binding_handle_destructor);
74
75         *ppstate = state;
76         return h;
77 }
78
79 void *_dcerpc_binding_handle_data(struct dcerpc_binding_handle *h)
80 {
81         return h->private_data;
82 }
83
84 void dcerpc_binding_handle_set_sync_ev(struct dcerpc_binding_handle *h,
85                                        struct tevent_context *ev)
86 {
87         h->sync_ev = ev;
88 }
89
90 bool dcerpc_binding_handle_is_connected(struct dcerpc_binding_handle *h)
91 {
92         return h->ops->is_connected(h);
93 }
94
95 uint32_t dcerpc_binding_handle_set_timeout(struct dcerpc_binding_handle *h,
96                                            uint32_t timeout)
97 {
98         return h->ops->set_timeout(h, timeout);
99 }
100
101 void dcerpc_binding_handle_auth_info(struct dcerpc_binding_handle *h,
102                                      enum dcerpc_AuthType *auth_type,
103                                      enum dcerpc_AuthLevel *auth_level)
104 {
105         enum dcerpc_AuthType _auth_type;
106         enum dcerpc_AuthLevel _auth_level;
107
108         if (auth_type == NULL) {
109                 auth_type = &_auth_type;
110         }
111
112         if (auth_level == NULL) {
113                 auth_level = &_auth_level;
114         }
115
116         *auth_type = DCERPC_AUTH_TYPE_NONE;
117         *auth_level = DCERPC_AUTH_LEVEL_NONE;
118
119         if (h->ops->auth_info == NULL) {
120                 return;
121         }
122
123         h->ops->auth_info(h, auth_type, auth_level);
124 }
125
126 struct dcerpc_binding_handle_raw_call_state {
127         const struct dcerpc_binding_handle_ops *ops;
128         struct tevent_context *ev;
129         struct tevent_req *subreq;
130 };
131
132 static void dcerpc_binding_handle_raw_call_done(struct tevent_req *subreq);
133
134 struct tevent_req *dcerpc_binding_handle_raw_call_send(TALLOC_CTX *mem_ctx,
135                                                 struct tevent_context *ev,
136                                                 struct dcerpc_binding_handle *h,
137                                                 const struct GUID *object,
138                                                 uint32_t opnum,
139                                                 uint32_t in_flags,
140                                                 const uint8_t *in_data,
141                                                 size_t in_length)
142 {
143         struct tevent_req *req;
144         struct dcerpc_binding_handle_raw_call_state *state;
145
146         req = tevent_req_create(mem_ctx, &state,
147                                 struct dcerpc_binding_handle_raw_call_state);
148         if (req == NULL) {
149                 return NULL;
150         }
151         state->ops = h->ops;
152         state->ev = ev;
153
154         if (h->object != NULL) {
155                 /*
156                  * If an object is set on the binding handle,
157                  * per request object passing is not allowed.
158                  */
159                 if (object != NULL) {
160                         tevent_req_nterror(req, NT_STATUS_INVALID_HANDLE);
161                         return tevent_req_post(req, ev);
162                 }
163
164                 /*
165                  * We use the object from the binding handle
166                  */
167                 object = h->object;
168         }
169
170         state->subreq = state->ops->raw_call_send(state, ev, h,
171                                                   object, opnum,
172                                                   in_flags, in_data, in_length);
173         if (tevent_req_nomem(state->subreq, req)) {
174                 return tevent_req_post(req, ev);
175         }
176         tevent_req_set_callback(state->subreq,
177                                 dcerpc_binding_handle_raw_call_done,
178                                 req);
179
180         return req;
181 }
182
183 static void dcerpc_binding_handle_raw_call_done(struct tevent_req *subreq)
184 {
185         struct tevent_req *req =
186                 tevent_req_callback_data(subreq,
187                 struct tevent_req);
188
189         if (tevent_req_is_in_progress(subreq)) {
190                 tevent_req_notify_callback(req);
191                 return;
192         }
193
194         tevent_req_done(req);
195 }
196
197 NTSTATUS dcerpc_binding_handle_raw_call_recv(struct tevent_req *req,
198                                              TALLOC_CTX *mem_ctx,
199                                              uint8_t **out_data,
200                                              size_t *out_length,
201                                              uint32_t *out_flags)
202 {
203         struct dcerpc_binding_handle_raw_call_state *state =
204                 tevent_req_data(req,
205                 struct dcerpc_binding_handle_raw_call_state);
206         NTSTATUS error;
207
208         if (!tevent_req_is_in_progress(req)) {
209                 if (tevent_req_is_nterror(req, &error)) {
210                         tevent_req_received(req);
211                         return error;
212                 }
213         }
214
215         error = state->ops->raw_call_recv(state->subreq,
216                                           mem_ctx,
217                                           out_data,
218                                           out_length,
219                                           out_flags);
220         if (!NT_STATUS_IS_OK(error)) {
221                 tevent_req_received(req);
222                 return error;
223         }
224
225         if (tevent_req_is_in_progress(state->subreq)) {
226                 return NT_STATUS_OK;
227         }
228
229         tevent_req_received(req);
230         return NT_STATUS_OK;
231 }
232
233 NTSTATUS dcerpc_binding_handle_raw_call(struct dcerpc_binding_handle *h,
234                                         const struct GUID *object,
235                                         uint32_t opnum,
236                                         uint32_t in_flags,
237                                         const uint8_t *in_data,
238                                         size_t in_length,
239                                         TALLOC_CTX *mem_ctx,
240                                         uint8_t **out_data,
241                                         size_t *out_length,
242                                         uint32_t *out_flags)
243 {
244         TALLOC_CTX *frame = talloc_stackframe();
245         struct tevent_context *ev;
246         struct tevent_req *subreq;
247         NTSTATUS status = NT_STATUS_NO_MEMORY;
248
249         /*
250          * TODO: allow only one sync call
251          */
252
253         if (h->sync_ev) {
254                 ev = h->sync_ev;
255         } else {
256                 ev = samba_tevent_context_init(frame);
257         }
258         if (ev == NULL) {
259                 goto fail;
260         }
261
262         subreq = dcerpc_binding_handle_raw_call_send(frame, ev,
263                                                      h, object, opnum,
264                                                      in_flags,
265                                                      in_data,
266                                                      in_length);
267         if (subreq == NULL) {
268                 goto fail;
269         }
270
271         if (!tevent_req_poll_ntstatus(subreq, ev, &status)) {
272                 goto fail;
273         }
274
275         status = dcerpc_binding_handle_raw_call_recv(subreq,
276                                                      mem_ctx,
277                                                      out_data,
278                                                      out_length,
279                                                      out_flags);
280 fail:
281         TALLOC_FREE(frame);
282         return status;
283 }
284
285 struct dcerpc_binding_handle_disconnect_state {
286         const struct dcerpc_binding_handle_ops *ops;
287 };
288
289 static void dcerpc_binding_handle_disconnect_done(struct tevent_req *subreq);
290
291 struct tevent_req *dcerpc_binding_handle_disconnect_send(TALLOC_CTX *mem_ctx,
292                                                 struct tevent_context *ev,
293                                                 struct dcerpc_binding_handle *h)
294 {
295         struct tevent_req *req;
296         struct dcerpc_binding_handle_disconnect_state *state;
297         struct tevent_req *subreq;
298
299         req = tevent_req_create(mem_ctx, &state,
300                                 struct dcerpc_binding_handle_disconnect_state);
301         if (req == NULL) {
302                 return NULL;
303         }
304
305         state->ops = h->ops;
306
307         subreq = state->ops->disconnect_send(state, ev, h);
308         if (tevent_req_nomem(subreq, req)) {
309                 return tevent_req_post(req, ev);
310         }
311         tevent_req_set_callback(subreq, dcerpc_binding_handle_disconnect_done, req);
312
313         return req;
314 }
315
316 static void dcerpc_binding_handle_disconnect_done(struct tevent_req *subreq)
317 {
318         struct tevent_req *req = tevent_req_callback_data(subreq,
319                                  struct tevent_req);
320         struct dcerpc_binding_handle_disconnect_state *state =
321                 tevent_req_data(req,
322                 struct dcerpc_binding_handle_disconnect_state);
323         NTSTATUS error;
324
325         error = state->ops->disconnect_recv(subreq);
326         TALLOC_FREE(subreq);
327         if (tevent_req_nterror(req, error)) {
328                 return;
329         }
330
331         tevent_req_done(req);
332 }
333
334 NTSTATUS dcerpc_binding_handle_disconnect_recv(struct tevent_req *req)
335 {
336         NTSTATUS error;
337
338         if (tevent_req_is_nterror(req, &error)) {
339                 tevent_req_received(req);
340                 return error;
341         }
342
343         tevent_req_received(req);
344         return NT_STATUS_OK;
345 }
346
347 struct dcerpc_binding_handle_call_state {
348         struct dcerpc_binding_handle *h;
349         const struct ndr_interface_call *call;
350         TALLOC_CTX *r_mem;
351         void *r_ptr;
352         struct ndr_push *push;
353         DATA_BLOB request;
354         DATA_BLOB response;
355         struct ndr_pull *pull;
356 };
357
358 static void dcerpc_binding_handle_call_done(struct tevent_req *subreq);
359
360 struct tevent_req *dcerpc_binding_handle_call_send(TALLOC_CTX *mem_ctx,
361                                         struct tevent_context *ev,
362                                         struct dcerpc_binding_handle *h,
363                                         const struct GUID *object,
364                                         const struct ndr_interface_table *table,
365                                         uint32_t opnum,
366                                         TALLOC_CTX *r_mem,
367                                         void *r_ptr)
368 {
369         struct tevent_req *req;
370         struct dcerpc_binding_handle_call_state *state;
371         struct tevent_req *subreq;
372         enum ndr_err_code ndr_err;
373
374         req = tevent_req_create(mem_ctx, &state,
375                                 struct dcerpc_binding_handle_call_state);
376         if (req == NULL) {
377                 return NULL;
378         }
379
380         if (table != h->table) {
381                 tevent_req_nterror(req, NT_STATUS_INVALID_HANDLE);
382                 return tevent_req_post(req, ev);
383         }
384
385         if (opnum >= table->num_calls) {
386                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
387                 return tevent_req_post(req, ev);
388         }
389
390         state->h = h;
391         state->call = &table->calls[opnum];
392
393         state->r_mem = r_mem;
394         state->r_ptr = r_ptr;
395
396         /* setup for a ndr_push_* call */
397         state->push = ndr_push_init_ctx(state);
398         if (tevent_req_nomem(state->push, req)) {
399                 return tevent_req_post(req, ev);
400         }
401
402         if (h->ops->ref_alloc && h->ops->ref_alloc(h)) {
403                 state->push->flags |= LIBNDR_FLAG_REF_ALLOC;
404         }
405
406         if (h->ops->push_bigendian && h->ops->push_bigendian(h)) {
407                 state->push->flags |= LIBNDR_FLAG_BIGENDIAN;
408         }
409
410         if (h->ops->use_ndr64 && h->ops->use_ndr64(h)) {
411                 state->push->flags |= LIBNDR_FLAG_NDR64;
412         }
413
414         if (h->ops->do_ndr_print) {
415                 h->ops->do_ndr_print(h, NDR_IN | NDR_SET_VALUES,
416                                      state->r_ptr, state->call);
417         }
418
419         /* push the structure into a blob */
420         ndr_err = state->call->ndr_push(state->push, NDR_IN, state->r_ptr);
421         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
422                 NTSTATUS error;
423                 error = ndr_map_error2ntstatus(ndr_err);
424                 if (h->ops->ndr_push_failed) {
425                         h->ops->ndr_push_failed(h, error,
426                                                 state->r_ptr,
427                                                 state->call);
428                 }
429                 tevent_req_nterror(req, error);
430                 return tevent_req_post(req, ev);
431         }
432
433         /* retrieve the blob */
434         state->request = ndr_push_blob(state->push);
435
436         if (h->ops->ndr_validate_in) {
437                 NTSTATUS error;
438                 error = h->ops->ndr_validate_in(h, state,
439                                                 &state->request,
440                                                 state->call);
441                 if (!NT_STATUS_IS_OK(error)) {
442                         tevent_req_nterror(req, error);
443                         return tevent_req_post(req, ev);
444                 }
445         }
446
447         subreq = dcerpc_binding_handle_raw_call_send(state, ev,
448                                                      h, object, opnum,
449                                                      state->push->flags,
450                                                      state->request.data,
451                                                      state->request.length);
452         if (tevent_req_nomem(subreq, req)) {
453                 return tevent_req_post(req, ev);
454         }
455         tevent_req_set_callback(subreq, dcerpc_binding_handle_call_done, req);
456
457         return req;
458 }
459
460 static void dcerpc_binding_handle_call_done(struct tevent_req *subreq)
461 {
462         struct tevent_req *req = tevent_req_callback_data(subreq,
463                                  struct tevent_req);
464         struct dcerpc_binding_handle_call_state *state =
465                 tevent_req_data(req,
466                 struct dcerpc_binding_handle_call_state);
467         struct dcerpc_binding_handle *h = state->h;
468         NTSTATUS error;
469         uint32_t out_flags = 0;
470         enum ndr_err_code ndr_err;
471
472         error = dcerpc_binding_handle_raw_call_recv(subreq, state,
473                                                     &state->response.data,
474                                                     &state->response.length,
475                                                     &out_flags);
476         TALLOC_FREE(subreq);
477         if (tevent_req_nterror(req, error)) {
478                 return;
479         }
480
481         state->pull = ndr_pull_init_blob(&state->response, state);
482         if (tevent_req_nomem(state->pull, req)) {
483                 return;
484         }
485         state->pull->flags = state->push->flags;
486
487         if (out_flags & LIBNDR_FLAG_BIGENDIAN) {
488                 state->pull->flags |= LIBNDR_FLAG_BIGENDIAN;
489         } else {
490                 state->pull->flags &= ~LIBNDR_FLAG_BIGENDIAN;
491         }
492
493         state->pull->current_mem_ctx = state->r_mem;
494
495         /* pull the structure from the blob */
496         ndr_err = state->call->ndr_pull(state->pull, NDR_OUT, state->r_ptr);
497         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
498                 error = ndr_map_error2ntstatus(ndr_err);
499                 if (h->ops->ndr_pull_failed) {
500                         h->ops->ndr_pull_failed(h, error,
501                                                 &state->response,
502                                                 state->call);
503                 }
504                 tevent_req_nterror(req, error);
505                 return;
506         }
507
508         if (h->ops->do_ndr_print) {
509                 h->ops->do_ndr_print(h, NDR_OUT,
510                                      state->r_ptr, state->call);
511         }
512
513         if (h->ops->ndr_validate_out) {
514                 error = h->ops->ndr_validate_out(h,
515                                                  state->pull,
516                                                  state->r_ptr,
517                                                  state->call);
518                 if (!NT_STATUS_IS_OK(error)) {
519                         tevent_req_nterror(req, error);
520                         return;
521                 }
522         }
523
524         tevent_req_done(req);
525 }
526
527 NTSTATUS dcerpc_binding_handle_call_recv(struct tevent_req *req)
528 {
529         return tevent_req_simple_recv_ntstatus(req);
530 }
531
532 NTSTATUS dcerpc_binding_handle_call(struct dcerpc_binding_handle *h,
533                                     const struct GUID *object,
534                                     const struct ndr_interface_table *table,
535                                     uint32_t opnum,
536                                     TALLOC_CTX *r_mem,
537                                     void *r_ptr)
538 {
539         TALLOC_CTX *frame = talloc_stackframe();
540         struct tevent_context *ev;
541         struct tevent_req *subreq;
542         NTSTATUS status = NT_STATUS_NO_MEMORY;
543
544         /*
545          * TODO: allow only one sync call
546          */
547
548         if (h->sync_ev) {
549                 ev = h->sync_ev;
550         } else {
551                 ev = samba_tevent_context_init(frame);
552         }
553         if (ev == NULL) {
554                 goto fail;
555         }
556
557         subreq = dcerpc_binding_handle_call_send(frame, ev,
558                                                  h, object, table,
559                                                  opnum, r_mem, r_ptr);
560         if (subreq == NULL) {
561                 goto fail;
562         }
563
564         if (!tevent_req_poll_ntstatus(subreq, ev, &status)) {
565                 goto fail;
566         }
567
568         status = dcerpc_binding_handle_call_recv(subreq);
569 fail:
570         TALLOC_FREE(frame);
571         return status;
572 }