librpc/rpc/pipe_handle.c
[metze/samba/wip.git] / librpc / rpc / pipe_handle.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    dcerpc pipe handle functions
5
6    Copyright (C) Stefan Metzmacher 2010,2013
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_pipe_handle_connection {
29         const char *chunk_struct_name;
30         size_t chunk_struct_size;
31         struct dcerpc_pipe_handle *p;
32         bool push;
33 };
34
35 struct dcerpc_pipe_handle {
36         void *private_data;
37         const struct dcerpc_pipe_handle_ops *ops;
38         const char *location;
39         struct dcerpc_pipe_handle_connection *pc;
40         struct tevent_req *current_req;
41 };
42
43 struct dcerpc_pipe_handle_connection *dcerpc_pipe_handle_connection_create(
44                                         TALLOC_CTX *mem_ctx,
45                                         const char *chunk_struct_name,
46                                         size_t chunk_struct_size)
47 {
48         struct dcerpc_pipe_handle_connection *pc;
49
50         pc = talloc_zero(mem_ctx, struct dcerpc_pipe_handle_connection);
51         if (pc == NULL) {
52                 return NULL;
53         }
54
55         pc->chunk_struct_name = talloc_strdup(pc, chunk_struct_name);
56         if (pc->chunk_struct_name == NULL) {
57                 TALLOC_FREE(pc);
58                 return NULL;
59         }
60
61         pc->chunk_struct_size = chunk_struct_size;
62
63         return pc;
64 }
65
66 bool _dcerpc_pipe_handle_connection_connect(
67                                 struct dcerpc_pipe_handle_connection *pc,
68                                 const char *chunk_struct_name,
69                                 size_t chunk_struct_size,
70                                 struct dcerpc_pipe_handle *p,
71                                 bool push)
72 {
73         if (pc->chunk_struct_size != chunk_struct_size) {
74                 return false;
75         }
76
77         if (strcmp(pc->chunk_struct_name, chunk_struct_name) != 0) {
78                 return false;
79         }
80
81         pc->p = p;
82         p->pc = pc;
83
84         pc->push = push;
85
86         return true;
87 }
88
89 void dcerpc_pipe_handle_connection_disconnect(
90                                 struct dcerpc_pipe_handle_connection *pc)
91 {
92         if (pc == NULL) {
93                 return;
94         }
95
96         if (pc->p != NULL) {
97                 pc->p->pc = NULL;
98         }
99
100         pc->p = NULL;
101 }
102
103 static int dcerpc_pipe_handle_destructor(struct dcerpc_pipe_handle *p)
104 {
105         dcerpc_pipe_handle_connection_disconnect(p->pc);
106
107         if (p->current_req) {
108                 tevent_req_received(p->current_req);
109         }
110
111         return 0;
112 }
113
114 struct dcerpc_pipe_handle *_dcerpc_pipe_handle_create(
115                                         TALLOC_CTX *mem_ctx,
116                                         const struct dcerpc_pipe_handle_ops *ops,
117                                         void *pstate,
118                                         size_t psize,
119                                         const char *type,
120                                         const char *location)
121 {
122         struct dcerpc_pipe_handle *p;
123         void **ppstate = (void **)pstate;
124         void *state;
125
126         p = talloc_zero(mem_ctx, struct dcerpc_pipe_handle);
127         if (p == NULL) {
128                 return NULL;
129         }
130         p->ops          = ops;
131         p->location     = location;
132
133         state = talloc_zero_size(p, psize);
134         if (state == NULL) {
135                 talloc_free(p);
136                 return NULL;
137         }
138         talloc_set_name_const(state, type);
139
140         p->private_data = state;
141
142         talloc_set_destructor(p, dcerpc_pipe_handle_destructor);
143
144         *ppstate = state;
145         return p;
146 }
147
148 void *_dcerpc_pipe_handle_data(struct dcerpc_pipe_handle *p)
149 {
150         return p->private_data;
151 }
152
153 struct dcerpc_pipe_handle_push_state {
154         const struct dcerpc_pipe_handle_ops *ops;
155         struct dcerpc_pipe_handle *p;
156 };
157
158 static int dcerpc_pipe_handle_push_state_destructor(
159         struct dcerpc_pipe_handle_push_state *state)
160 {
161         if (state->p != NULL) {
162                 state->p->current_req = NULL;
163         }
164
165         return 0;
166 }
167
168 static void dcerpc_pipe_handle_push_done(struct tevent_req *subreq);
169
170 struct tevent_req *dcerpc_pipe_handle_push_send(TALLOC_CTX *mem_ctx,
171                                         struct tevent_context *ev,
172                                         struct dcerpc_pipe_handle_connection *pc,
173                                         const void *chunk_ptr)
174 {
175         struct dcerpc_pipe_handle *p = pc->p;
176         struct tevent_req *req;
177         struct dcerpc_pipe_handle_push_state *state;
178         struct tevent_req *subreq;
179
180         req = tevent_req_create(mem_ctx, &state,
181                                 struct dcerpc_pipe_handle_push_state);
182         if (req == NULL) {
183                 return NULL;
184         }
185
186         if (p == NULL) {
187                 tevent_req_nterror(req, NT_STATUS_RPC_PIPE_DISCIPLINE_ERROR);
188                 return tevent_req_post(req, ev);
189         }
190
191         if (!pc->push) {
192                 tevent_req_nterror(req, NT_STATUS_RPC_PIPE_DISCIPLINE_ERROR);
193                 return tevent_req_post(req, ev);
194         }
195
196         if (p->current_req) {
197                 tevent_req_nterror(req, NT_STATUS_RPC_PIPE_DISCIPLINE_ERROR);
198                 return tevent_req_post(req, ev);
199         }
200
201         state->ops = p->ops;
202         state->p = p;
203
204         talloc_set_destructor(state, dcerpc_pipe_handle_push_state_destructor);
205         p->current_req = req;
206
207         subreq = p->ops->chunk_push_send(state, ev, p, chunk_ptr);
208         if (tevent_req_nomem(subreq, req)) {
209                 return tevent_req_post(req, ev);
210         }
211
212         tevent_req_set_callback(subreq, dcerpc_pipe_handle_push_done, req);
213
214         return req;
215 }
216
217 static void dcerpc_pipe_handle_push_done(struct tevent_req *subreq)
218 {
219         struct tevent_req *req =
220                 tevent_req_callback_data(subreq,
221                 struct tevent_req);
222         struct dcerpc_pipe_handle_push_state *state =
223                 tevent_req_data(req,
224                 struct dcerpc_pipe_handle_push_state);
225         NTSTATUS status;
226
227         status = state->ops->chunk_push_recv(subreq);
228         TALLOC_FREE(subreq);
229         state->p->current_req = NULL;
230         state->p = NULL;
231         if (tevent_req_nterror(req, status)) {
232                 return;
233         }
234
235         tevent_req_done(req);
236 }
237
238 NTSTATUS dcerpc_pipe_handle_push_recv(struct tevent_req *req)
239 {
240         return tevent_req_simple_recv_ntstatus(req);
241 }
242
243 struct dcerpc_pipe_handle_pull_state {
244         const struct dcerpc_pipe_handle_ops *ops;
245         struct dcerpc_pipe_handle *p;
246 };
247
248 static int dcerpc_pipe_handle_pull_state_destructor(
249         struct dcerpc_pipe_handle_pull_state *state)
250 {
251         if (state->p != NULL) {
252                 state->p->current_req = NULL;
253         }
254
255         return 0;
256 }
257
258 static void dcerpc_pipe_handle_pull_done(struct tevent_req *subreq);
259
260 struct tevent_req *dcerpc_pipe_handle_pull_send(TALLOC_CTX *mem_ctx,
261                                         struct tevent_context *ev,
262                                         struct dcerpc_pipe_handle_connection *pc,
263                                         void *chunk_mem,
264                                         void *chunk_ptr)
265 {
266         struct dcerpc_pipe_handle *p = pc->p;
267         struct tevent_req *req;
268         struct dcerpc_pipe_handle_pull_state *state;
269         struct tevent_req *subreq;
270
271         req = tevent_req_create(mem_ctx, &state,
272                                 struct dcerpc_pipe_handle_pull_state);
273         if (req == NULL) {
274                 return NULL;
275         }
276
277         if (p == NULL) {
278                 tevent_req_nterror(req, NT_STATUS_RPC_PIPE_DISCIPLINE_ERROR);
279                 return tevent_req_post(req, ev);
280         }
281
282         if (pc->push) {
283                 tevent_req_nterror(req, NT_STATUS_RPC_PIPE_DISCIPLINE_ERROR);
284                 return tevent_req_post(req, ev);
285         }
286
287         if (p->current_req) {
288                 tevent_req_nterror(req, NT_STATUS_RPC_PIPE_DISCIPLINE_ERROR);
289                 return tevent_req_post(req, ev);
290         }
291
292         state->ops = p->ops;
293         state->p = p;
294
295         talloc_set_destructor(state, dcerpc_pipe_handle_pull_state_destructor);
296         p->current_req = req;
297
298         subreq = p->ops->chunk_pull_send(state, ev, p, chunk_mem, chunk_ptr);
299         if (tevent_req_nomem(subreq, req)) {
300                 return tevent_req_post(req, ev);
301         }
302
303         tevent_req_set_callback(subreq, dcerpc_pipe_handle_pull_done, req);
304
305         return req;
306 }
307
308 static void dcerpc_pipe_handle_pull_done(struct tevent_req *subreq)
309 {
310         struct tevent_req *req =
311                 tevent_req_callback_data(subreq,
312                 struct tevent_req);
313         struct dcerpc_pipe_handle_pull_state *state =
314                 tevent_req_data(req,
315                 struct dcerpc_pipe_handle_pull_state);
316         NTSTATUS status;
317
318         status = state->ops->chunk_pull_recv(subreq);
319         TALLOC_FREE(subreq);
320         state->p->current_req = NULL;
321         state->p = NULL;
322         if (tevent_req_nterror(req, status)) {
323                 return;
324         }
325
326         tevent_req_done(req);
327 }
328
329 NTSTATUS dcerpc_pipe_handle_pull_recv(struct tevent_req *req)
330 {
331         return tevent_req_simple_recv_ntstatus(req);
332 }