libwbclient: Add async call framework.
[ira/wip.git] / nsswitch / libwbclient / wb_reqtrans.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Async transfer of winbindd_request and _response structs
5
6    Copyright (C) Volker Lendecke 2008
7
8      ** NOTE! The following LGPL license applies to the wbclient
9      ** library. This does NOT imply that all of Samba is released
10      ** under the LGPL
11
12    This library is free software; you can redistribute it and/or
13    modify it under the terms of the GNU Lesser General Public
14    License as published by the Free Software Foundation; either
15    version 3 of the License, or (at your option) any later version.
16
17    This library is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20    Library General Public License for more details.
21
22    You should have received a copy of the GNU Lesser General Public License
23    along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 */
25
26 #include "replace.h"
27 #include "system/filesys.h"
28 #include "system/network.h"
29 #include <talloc.h>
30 #include <tevent.h>
31 struct fd_event;
32 struct event_context;
33 #include "lib/async_req/async_sock.h"
34 #include "lib/util/tevent_unix.h"
35 #include "nsswitch/winbind_struct_protocol.h"
36 #include "nsswitch/libwbclient/wbclient.h"
37 #include "nsswitch/libwbclient/wbc_async.h"
38
39 #ifdef DBGC_CLASS
40 #undef DBGC_CLASS
41 #define DBGC_CLASS DBGC_WINBIND
42 #endif
43
44 struct req_read_state {
45         struct winbindd_request *wb_req;
46         size_t max_extra_data;
47         ssize_t ret;
48 };
49
50 static ssize_t wb_req_more(uint8_t *buf, size_t buflen, void *private_data);
51 static void wb_req_read_done(struct tevent_req *subreq);
52
53 struct tevent_req *wb_req_read_send(TALLOC_CTX *mem_ctx,
54                                     struct tevent_context *ev,
55                                     int fd, size_t max_extra_data)
56 {
57         struct tevent_req *req, *subreq;
58         struct req_read_state *state;
59
60         req = tevent_req_create(mem_ctx, &state, struct req_read_state);
61         if (req == NULL) {
62                 return NULL;
63         }
64         state->max_extra_data = max_extra_data;
65
66         subreq = read_packet_send(state, ev, fd, 4, wb_req_more, state);
67         if (tevent_req_nomem(subreq, req)) {
68                 return tevent_req_post(req, ev);
69         }
70         tevent_req_set_callback(subreq, wb_req_read_done, req);
71         return req;
72 }
73
74 static ssize_t wb_req_more(uint8_t *buf, size_t buflen, void *private_data)
75 {
76         struct req_read_state *state = talloc_get_type_abort(
77                 private_data, struct req_read_state);
78         struct winbindd_request *req = (struct winbindd_request *)buf;
79
80         if (buflen == 4) {
81                 if (req->length != sizeof(struct winbindd_request)) {
82                         DEBUG(0, ("wb_req_read_len: Invalid request size "
83                                   "received: %d (expected %d)\n",
84                                   (int)req->length,
85                                   (int)sizeof(struct winbindd_request)));
86                         return -1;
87                 }
88                 return sizeof(struct winbindd_request) - 4;
89         }
90
91         if ((state->max_extra_data != 0)
92             && (req->extra_len > state->max_extra_data)) {
93                 DEBUG(3, ("Got request with %d bytes extra data on "
94                           "unprivileged socket\n", (int)req->extra_len));
95                 return -1;
96         }
97
98         return req->extra_len;
99 }
100
101 static void wb_req_read_done(struct tevent_req *subreq)
102 {
103         struct tevent_req *req = tevent_req_callback_data(
104                 subreq, struct tevent_req);
105         struct req_read_state *state = tevent_req_data(
106                 req, struct req_read_state);
107         int err;
108         uint8_t *buf;
109
110         state->ret = read_packet_recv(subreq, state, &buf, &err);
111         TALLOC_FREE(subreq);
112         if (state->ret == -1) {
113                 tevent_req_error(req, err);
114                 return;
115         }
116
117         state->wb_req = (struct winbindd_request *)buf;
118
119         if (state->wb_req->extra_len != 0) {
120                 state->wb_req->extra_data.data =
121                         (char *)buf + sizeof(struct winbindd_request);
122         } else {
123                 state->wb_req->extra_data.data = NULL;
124         }
125         tevent_req_done(req);
126 }
127
128 ssize_t wb_req_read_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
129                          struct winbindd_request **preq, int *err)
130 {
131         struct req_read_state *state = tevent_req_data(
132                 req, struct req_read_state);
133
134         if (tevent_req_is_unix_error(req, err)) {
135                 return -1;
136         }
137         *preq = talloc_move(mem_ctx, &state->wb_req);
138         return state->ret;
139 }
140
141 struct req_write_state {
142         struct iovec iov[2];
143         ssize_t ret;
144 };
145
146 static void wb_req_write_done(struct tevent_req *subreq);
147
148 struct tevent_req *wb_req_write_send(TALLOC_CTX *mem_ctx,
149                                      struct tevent_context *ev,
150                                      struct tevent_queue *queue, int fd,
151                                      struct winbindd_request *wb_req)
152 {
153         struct tevent_req *req, *subreq;
154         struct req_write_state *state;
155         int count = 1;
156
157         req = tevent_req_create(mem_ctx, &state, struct req_write_state);
158         if (req == NULL) {
159                 return NULL;
160         }
161
162         state->iov[0].iov_base = (void *)wb_req;
163         state->iov[0].iov_len = sizeof(struct winbindd_request);
164
165         if (wb_req->extra_len != 0) {
166                 state->iov[1].iov_base = (void *)wb_req->extra_data.data;
167                 state->iov[1].iov_len = wb_req->extra_len;
168                 count = 2;
169         }
170
171         subreq = writev_send(state, ev, queue, fd, true, state->iov, count);
172         if (tevent_req_nomem(subreq, req)) {
173                 return tevent_req_post(req, ev);
174         }
175         tevent_req_set_callback(subreq, wb_req_write_done, req);
176         return req;
177 }
178
179 static void wb_req_write_done(struct tevent_req *subreq)
180 {
181         struct tevent_req *req = tevent_req_callback_data(
182                 subreq, struct tevent_req);
183         struct req_write_state *state = tevent_req_data(
184                 req, struct req_write_state);
185         int err;
186
187         state->ret = writev_recv(subreq, &err);
188         TALLOC_FREE(subreq);
189         if (state->ret < 0) {
190                 tevent_req_error(req, err);
191                 return;
192         }
193         tevent_req_done(req);
194 }
195
196 ssize_t wb_req_write_recv(struct tevent_req *req, int *err)
197 {
198         struct req_write_state *state = tevent_req_data(
199                 req, struct req_write_state);
200
201         if (tevent_req_is_unix_error(req, err)) {
202                 return -1;
203         }
204         return state->ret;
205 }
206
207 struct resp_read_state {
208         struct winbindd_response *wb_resp;
209         ssize_t ret;
210 };
211
212 static ssize_t wb_resp_more(uint8_t *buf, size_t buflen, void *private_data);
213 static void wb_resp_read_done(struct tevent_req *subreq);
214
215 struct tevent_req *wb_resp_read_send(TALLOC_CTX *mem_ctx,
216                                      struct tevent_context *ev, int fd)
217 {
218         struct tevent_req *req, *subreq;
219         struct resp_read_state *state;
220
221         req = tevent_req_create(mem_ctx, &state, struct resp_read_state);
222         if (req == NULL) {
223                 return NULL;
224         }
225
226         subreq = read_packet_send(state, ev, fd, 4, wb_resp_more, state);
227         if (tevent_req_nomem(subreq, req)) {
228                 return tevent_req_post(req, ev);
229         }
230         tevent_req_set_callback(subreq, wb_resp_read_done, req);
231         return req;
232 }
233
234 static ssize_t wb_resp_more(uint8_t *buf, size_t buflen, void *private_data)
235 {
236         struct winbindd_response *resp = (struct winbindd_response *)buf;
237
238         if (buflen == 4) {
239                 if (resp->length < sizeof(struct winbindd_response)) {
240                         DEBUG(0, ("wb_resp_read_len: Invalid response size "
241                                   "received: %d (expected at least%d)\n",
242                                   (int)resp->length,
243                                   (int)sizeof(struct winbindd_response)));
244                         return -1;
245                 }
246         }
247         return resp->length - buflen;
248 }
249
250 static void wb_resp_read_done(struct tevent_req *subreq)
251 {
252         struct tevent_req *req = tevent_req_callback_data(
253                 subreq, struct tevent_req);
254         struct resp_read_state *state = tevent_req_data(
255                 req, struct resp_read_state);
256         uint8_t *buf;
257         int err;
258
259         state->ret = read_packet_recv(subreq, state, &buf, &err);
260         TALLOC_FREE(subreq);
261         if (state->ret == -1) {
262                 tevent_req_error(req, err);
263                 return;
264         }
265
266         state->wb_resp = (struct winbindd_response *)buf;
267
268         if (state->wb_resp->length > sizeof(struct winbindd_response)) {
269                 state->wb_resp->extra_data.data =
270                         (char *)buf + sizeof(struct winbindd_response);
271         } else {
272                 state->wb_resp->extra_data.data = NULL;
273         }
274         tevent_req_done(req);
275 }
276
277 ssize_t wb_resp_read_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
278                           struct winbindd_response **presp, int *err)
279 {
280         struct resp_read_state *state = tevent_req_data(
281                 req, struct resp_read_state);
282
283         if (tevent_req_is_unix_error(req, err)) {
284                 return -1;
285         }
286         *presp = talloc_move(mem_ctx, &state->wb_resp);
287         return state->ret;
288 }
289
290 struct resp_write_state {
291         struct iovec iov[2];
292         ssize_t ret;
293 };
294
295 static void wb_resp_write_done(struct tevent_req *subreq);
296
297 struct tevent_req *wb_resp_write_send(TALLOC_CTX *mem_ctx,
298                                       struct tevent_context *ev,
299                                       struct tevent_queue *queue, int fd,
300                                       struct winbindd_response *wb_resp)
301 {
302         struct tevent_req *req, *subreq;
303         struct resp_write_state *state;
304         int count = 1;
305
306         req = tevent_req_create(mem_ctx, &state, struct resp_write_state);
307         if (req == NULL) {
308                 return NULL;
309         }
310
311         state->iov[0].iov_base = (void *)wb_resp;
312         state->iov[0].iov_len = sizeof(struct winbindd_response);
313
314         if (wb_resp->length > sizeof(struct winbindd_response)) {
315                 state->iov[1].iov_base = (void *)wb_resp->extra_data.data;
316                 state->iov[1].iov_len =
317                         wb_resp->length - sizeof(struct winbindd_response);
318                 count = 2;
319         }
320
321         subreq = writev_send(state, ev, queue, fd, true, state->iov, count);
322         if (tevent_req_nomem(subreq, req)) {
323                 return tevent_req_post(req, ev);
324         }
325         tevent_req_set_callback(subreq, wb_resp_write_done, req);
326         return req;
327 }
328
329 static void wb_resp_write_done(struct tevent_req *subreq)
330 {
331         struct tevent_req *req = tevent_req_callback_data(
332                 subreq, struct tevent_req);
333         struct resp_write_state *state = tevent_req_data(
334                 req, struct resp_write_state);
335         int err;
336
337         state->ret = writev_recv(subreq, &err);
338         TALLOC_FREE(subreq);
339         if (state->ret < 0) {
340                 tevent_req_error(req, err);
341                 return;
342         }
343         tevent_req_done(req);
344 }
345
346 ssize_t wb_resp_write_recv(struct tevent_req *req, int *err)
347 {
348         struct resp_write_state *state = tevent_req_data(
349                 req, struct resp_write_state);
350
351         if (tevent_req_is_unix_error(req, err)) {
352                 return -1;
353         }
354         return state->ret;
355 }
356
357 struct wb_simple_trans_state {
358         struct tevent_context *ev;
359         int fd;
360         struct winbindd_response *wb_resp;
361 };
362
363 static void wb_simple_trans_write_done(struct tevent_req *subreq);
364 static void wb_simple_trans_read_done(struct tevent_req *subreq);
365
366 struct tevent_req *wb_simple_trans_send(TALLOC_CTX *mem_ctx,
367                                         struct tevent_context *ev,
368                                         struct tevent_queue *queue, int fd,
369                                         struct winbindd_request *wb_req)
370 {
371         struct tevent_req *req, *subreq;
372         struct wb_simple_trans_state *state;
373
374         req = tevent_req_create(mem_ctx, &state, struct wb_simple_trans_state);
375         if (req == NULL) {
376                 return NULL;
377         }
378
379         wb_req->length = sizeof(struct winbindd_request);
380
381         state->ev = ev;
382         state->fd = fd;
383
384         subreq = wb_req_write_send(state, ev, queue, fd, wb_req);
385         if (tevent_req_nomem(subreq, req)) {
386                 return tevent_req_post(req, ev);
387         }
388         tevent_req_set_callback(subreq, wb_simple_trans_write_done, req);
389
390         return req;
391 }
392
393 static void wb_simple_trans_write_done(struct tevent_req *subreq)
394 {
395         struct tevent_req *req = tevent_req_callback_data(
396                 subreq, struct tevent_req);
397         struct wb_simple_trans_state *state = tevent_req_data(
398                 req, struct wb_simple_trans_state);
399         ssize_t ret;
400         int err;
401
402         ret = wb_req_write_recv(subreq, &err);
403         TALLOC_FREE(subreq);
404         if (ret == -1) {
405                 tevent_req_error(req, err);
406                 return;
407         }
408         subreq = wb_resp_read_send(state, state->ev, state->fd);
409         if (tevent_req_nomem(subreq, req)) {
410                 return;
411         }
412         tevent_req_set_callback(subreq, wb_simple_trans_read_done, req);
413 }
414
415 static void wb_simple_trans_read_done(struct tevent_req *subreq)
416 {
417         struct tevent_req *req = tevent_req_callback_data(
418                 subreq, struct tevent_req);
419         struct wb_simple_trans_state *state = tevent_req_data(
420                 req, struct wb_simple_trans_state);
421         ssize_t ret;
422         int err;
423
424         ret = wb_resp_read_recv(subreq, state, &state->wb_resp, &err);
425         TALLOC_FREE(subreq);
426         if (ret == -1) {
427                 tevent_req_error(req, err);
428                 return;
429         }
430
431         tevent_req_done(req);
432 }
433
434 int wb_simple_trans_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
435                          struct winbindd_response **presponse, int *err)
436 {
437         struct wb_simple_trans_state *state = tevent_req_data(
438                 req, struct wb_simple_trans_state);
439
440         if (tevent_req_is_unix_error(req, err)) {
441                 return -1;
442         }
443         *presponse = talloc_move(mem_ctx, &state->wb_resp);
444         return 0;
445 }