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                 if (pc->p->current_req != NULL) {
98                         tevent_req_nterror(pc->p->current_req,
99                                           NT_STATUS_RPC_PIPE_CLOSED);
100                 }
101                 pc->p->pc = NULL;
102         }
103
104         pc->p = NULL;
105 }
106
107 static int dcerpc_pipe_handle_destructor(struct dcerpc_pipe_handle *p)
108 {
109         dcerpc_pipe_handle_connection_disconnect(p->pc);
110         return 0;
111 }
112
113 struct dcerpc_pipe_handle *_dcerpc_pipe_handle_create(
114                                         TALLOC_CTX *mem_ctx,
115                                         const struct dcerpc_pipe_handle_ops *ops,
116                                         void *pstate,
117                                         size_t psize,
118                                         const char *type,
119                                         const char *location)
120 {
121         struct dcerpc_pipe_handle *p;
122         void **ppstate = (void **)pstate;
123         void *state;
124
125         p = talloc_zero(mem_ctx, struct dcerpc_pipe_handle);
126         if (p == NULL) {
127                 return NULL;
128         }
129         p->ops          = ops;
130         p->location     = location;
131
132         state = talloc_zero_size(p, psize);
133         if (state == NULL) {
134                 talloc_free(p);
135                 return NULL;
136         }
137         talloc_set_name_const(state, type);
138
139         p->private_data = state;
140
141         talloc_set_destructor(p, dcerpc_pipe_handle_destructor);
142
143         *ppstate = state;
144         return p;
145 }
146
147 void *_dcerpc_pipe_handle_data(struct dcerpc_pipe_handle *p)
148 {
149         return p->private_data;
150 }
151
152 struct dcerpc_pipe_handle_push_state {
153         const struct dcerpc_pipe_handle_ops *ops;
154         struct dcerpc_pipe_handle *p;
155 };
156
157 static int dcerpc_pipe_handle_push_state_destructor(
158         struct dcerpc_pipe_handle_push_state *state)
159 {
160         if (state->p != NULL) {
161                 state->p->current_req = NULL;
162         }
163
164         return 0;
165 }
166
167 static void dcerpc_pipe_handle_push_done(struct tevent_req *subreq);
168
169 struct tevent_req *dcerpc_pipe_handle_push_send(TALLOC_CTX *mem_ctx,
170                                         struct tevent_context *ev,
171                                         struct dcerpc_pipe_handle_connection *pc,
172                                         const void *chunk_ptr)
173 {
174         struct dcerpc_pipe_handle *p = pc->p;
175         struct tevent_req *req;
176         struct dcerpc_pipe_handle_push_state *state;
177         struct tevent_req *subreq;
178
179         req = tevent_req_create(mem_ctx, &state,
180                                 struct dcerpc_pipe_handle_push_state);
181         if (req == NULL) {
182                 return NULL;
183         }
184
185         if (p == NULL) {
186                 tevent_req_nterror(req, NT_STATUS_RPC_PIPE_DISCIPLINE_ERROR);
187                 return tevent_req_post(req, ev);
188         }
189
190         if (!pc->push) {
191                 tevent_req_nterror(req, NT_STATUS_RPC_PIPE_DISCIPLINE_ERROR);
192                 return tevent_req_post(req, ev);
193         }
194
195         if (p->current_req) {
196                 tevent_req_nterror(req, NT_STATUS_RPC_PIPE_DISCIPLINE_ERROR);
197                 return tevent_req_post(req, ev);
198         }
199
200         state->ops = p->ops;
201         state->p = p;
202
203         tevent_req_defer_callback(req, ev);
204
205         talloc_set_destructor(state, dcerpc_pipe_handle_push_state_destructor);
206         p->current_req = req;
207
208         subreq = p->ops->chunk_push_send(state, ev, p, chunk_ptr);
209         if (tevent_req_nomem(subreq, req)) {
210                 return tevent_req_post(req, ev);
211         }
212
213         tevent_req_set_callback(subreq, dcerpc_pipe_handle_push_done, req);
214
215         return req;
216 }
217
218 static void dcerpc_pipe_handle_push_done(struct tevent_req *subreq)
219 {
220         struct tevent_req *req =
221                 tevent_req_callback_data(subreq,
222                 struct tevent_req);
223         struct dcerpc_pipe_handle_push_state *state =
224                 tevent_req_data(req,
225                 struct dcerpc_pipe_handle_push_state);
226         NTSTATUS status;
227
228         status = state->ops->chunk_push_recv(subreq);
229         TALLOC_FREE(subreq);
230         state->p->current_req = NULL;
231         state->p = NULL;
232         if (tevent_req_nterror(req, status)) {
233                 return;
234         }
235
236         tevent_req_done(req);
237 }
238
239 NTSTATUS dcerpc_pipe_handle_push_recv(struct tevent_req *req)
240 {
241         return tevent_req_simple_recv_ntstatus(req);
242 }
243
244 struct dcerpc_pipe_handle_pull_state {
245         const struct dcerpc_pipe_handle_ops *ops;
246         struct dcerpc_pipe_handle *p;
247 };
248
249 static int dcerpc_pipe_handle_pull_state_destructor(
250         struct dcerpc_pipe_handle_pull_state *state)
251 {
252         if (state->p != NULL) {
253                 state->p->current_req = NULL;
254         }
255
256         return 0;
257 }
258
259 static void dcerpc_pipe_handle_pull_done(struct tevent_req *subreq);
260
261 struct tevent_req *dcerpc_pipe_handle_pull_send(TALLOC_CTX *mem_ctx,
262                                         struct tevent_context *ev,
263                                         struct dcerpc_pipe_handle_connection *pc,
264                                         void *chunk_mem,
265                                         void *chunk_ptr)
266 {
267         struct dcerpc_pipe_handle *p = pc->p;
268         struct tevent_req *req;
269         struct dcerpc_pipe_handle_pull_state *state;
270         struct tevent_req *subreq;
271
272         req = tevent_req_create(mem_ctx, &state,
273                                 struct dcerpc_pipe_handle_pull_state);
274         if (req == NULL) {
275                 return NULL;
276         }
277
278         if (p == NULL) {
279                 tevent_req_nterror(req, NT_STATUS_RPC_PIPE_DISCIPLINE_ERROR);
280                 return tevent_req_post(req, ev);
281         }
282
283         if (pc->push) {
284                 tevent_req_nterror(req, NT_STATUS_RPC_PIPE_DISCIPLINE_ERROR);
285                 return tevent_req_post(req, ev);
286         }
287
288         if (p->current_req) {
289                 tevent_req_nterror(req, NT_STATUS_RPC_PIPE_DISCIPLINE_ERROR);
290                 return tevent_req_post(req, ev);
291         }
292
293         state->ops = p->ops;
294         state->p = p;
295
296         tevent_req_defer_callback(req, ev);
297
298         talloc_set_destructor(state, dcerpc_pipe_handle_pull_state_destructor);
299         p->current_req = req;
300
301         subreq = p->ops->chunk_pull_send(state, ev, p, chunk_mem, chunk_ptr);
302         if (tevent_req_nomem(subreq, req)) {
303                 return tevent_req_post(req, ev);
304         }
305
306         tevent_req_set_callback(subreq, dcerpc_pipe_handle_pull_done, req);
307
308         return req;
309 }
310
311 static void dcerpc_pipe_handle_pull_done(struct tevent_req *subreq)
312 {
313         struct tevent_req *req =
314                 tevent_req_callback_data(subreq,
315                 struct tevent_req);
316         struct dcerpc_pipe_handle_pull_state *state =
317                 tevent_req_data(req,
318                 struct dcerpc_pipe_handle_pull_state);
319         NTSTATUS status;
320
321         status = state->ops->chunk_pull_recv(subreq);
322         TALLOC_FREE(subreq);
323         state->p->current_req = NULL;
324         state->p = NULL;
325         if (tevent_req_nterror(req, status)) {
326                 return;
327         }
328
329         tevent_req_done(req);
330 }
331
332 NTSTATUS dcerpc_pipe_handle_pull_recv(struct tevent_req *req)
333 {
334         return tevent_req_simple_recv_ntstatus(req);
335 }