Add "err_on_readability" to writev_send
[kai/samba-autobuild/.git] / source3 / lib / 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    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 "wbc_async.h"
24
25 #undef DBGC_CLASS
26 #define DBGC_CLASS DBGC_WINBIND
27
28 struct req_read_state {
29         struct winbindd_request *wb_req;
30         size_t max_extra_data;
31         ssize_t ret;
32 };
33
34 static ssize_t wb_req_more(uint8_t *buf, size_t buflen, void *private_data);
35 static void wb_req_read_done(struct tevent_req *subreq);
36
37 struct tevent_req *wb_req_read_send(TALLOC_CTX *mem_ctx,
38                                     struct tevent_context *ev,
39                                     int fd, size_t max_extra_data)
40 {
41         struct tevent_req *req, *subreq;
42         struct req_read_state *state;
43
44         req = tevent_req_create(mem_ctx, &state, struct req_read_state);
45         if (req == NULL) {
46                 return NULL;
47         }
48         state->max_extra_data = max_extra_data;
49
50         subreq = read_packet_send(state, ev, fd, 4, wb_req_more, state);
51         if (tevent_req_nomem(subreq, req)) {
52                 return tevent_req_post(req, ev);
53         }
54         tevent_req_set_callback(subreq, wb_req_read_done, req);
55         return req;
56 }
57
58 static ssize_t wb_req_more(uint8_t *buf, size_t buflen, void *private_data)
59 {
60         struct req_read_state *state = talloc_get_type_abort(
61                 private_data, struct req_read_state);
62         struct winbindd_request *req = (struct winbindd_request *)buf;
63
64         if (buflen == 4) {
65                 if (req->length != sizeof(struct winbindd_request)) {
66                         DEBUG(0, ("wb_req_read_len: Invalid request size "
67                                   "received: %d (expected %d)\n",
68                                   (int)req->length,
69                                   (int)sizeof(struct winbindd_request)));
70                         return -1;
71                 }
72                 return sizeof(struct winbindd_request) - 4;
73         }
74
75         if ((state->max_extra_data != 0)
76             && (req->extra_len > state->max_extra_data)) {
77                 DEBUG(3, ("Got request with %d bytes extra data on "
78                           "unprivileged socket\n", (int)req->extra_len));
79                 return -1;
80         }
81
82         return req->extra_len;
83 }
84
85 static void wb_req_read_done(struct tevent_req *subreq)
86 {
87         struct tevent_req *req = tevent_req_callback_data(
88                 subreq, struct tevent_req);
89         struct req_read_state *state = tevent_req_data(
90                 req, struct req_read_state);
91         int err;
92         uint8_t *buf;
93
94         state->ret = read_packet_recv(subreq, state, &buf, &err);
95         TALLOC_FREE(subreq);
96         if (state->ret == -1) {
97                 tevent_req_error(req, err);
98                 return;
99         }
100
101         state->wb_req = (struct winbindd_request *)buf;
102
103         if (state->wb_req->extra_len != 0) {
104                 state->wb_req->extra_data.data =
105                         (char *)buf + sizeof(struct winbindd_request);
106         } else {
107                 state->wb_req->extra_data.data = NULL;
108         }
109         tevent_req_done(req);
110 }
111
112 ssize_t wb_req_read_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
113                          struct winbindd_request **preq, int *err)
114 {
115         struct req_read_state *state = tevent_req_data(
116                 req, struct req_read_state);
117
118         if (tevent_req_is_unix_error(req, err)) {
119                 return -1;
120         }
121         *preq = talloc_move(mem_ctx, &state->wb_req);
122         return state->ret;
123 }
124
125 struct req_write_state {
126         struct iovec iov[2];
127         ssize_t ret;
128 };
129
130 static void wb_req_write_done(struct tevent_req *subreq);
131
132 struct tevent_req *wb_req_write_send(TALLOC_CTX *mem_ctx,
133                                      struct tevent_context *ev,
134                                      struct tevent_queue *queue, int fd,
135                                      struct winbindd_request *wb_req)
136 {
137         struct tevent_req *req, *subreq;
138         struct req_write_state *state;
139         int count = 1;
140
141         req = tevent_req_create(mem_ctx, &state, struct req_write_state);
142         if (req == NULL) {
143                 return NULL;
144         }
145
146         state->iov[0].iov_base = (void *)wb_req;
147         state->iov[0].iov_len = sizeof(struct winbindd_request);
148
149         if (wb_req->extra_len != 0) {
150                 state->iov[1].iov_base = (void *)wb_req->extra_data.data;
151                 state->iov[1].iov_len = wb_req->extra_len;
152                 count = 2;
153         }
154
155         subreq = writev_send(state, ev, queue, fd, true, state->iov, count);
156         if (tevent_req_nomem(subreq, req)) {
157                 return tevent_req_post(req, ev);
158         }
159         tevent_req_set_callback(subreq, wb_req_write_done, req);
160         return req;
161 }
162
163 static void wb_req_write_done(struct tevent_req *subreq)
164 {
165         struct tevent_req *req = tevent_req_callback_data(
166                 subreq, struct tevent_req);
167         struct req_write_state *state = tevent_req_data(
168                 req, struct req_write_state);
169         int err;
170
171         state->ret = writev_recv(subreq, &err);
172         /*
173          * We do not TALLOC_FREE(subreq) here, as this would trigger the next
174          * write of a client. The winbind protocol is purely request/response
175          * without multiplex ID's, so having multiple requeusts on the fly
176          * would confuse sequencing.
177          *
178          * Eventually the writev_req will be freed, "subreq" a child of "req"
179          */
180         if (state->ret < 0) {
181                 tevent_req_error(req, err);
182                 return;
183         }
184         tevent_req_done(req);
185 }
186
187 ssize_t wb_req_write_recv(struct tevent_req *req, int *err)
188 {
189         struct req_write_state *state = tevent_req_data(
190                 req, struct req_write_state);
191
192         if (tevent_req_is_unix_error(req, err)) {
193                 return -1;
194         }
195         return state->ret;
196 }
197
198 struct resp_read_state {
199         struct winbindd_response *wb_resp;
200         ssize_t ret;
201 };
202
203 static ssize_t wb_resp_more(uint8_t *buf, size_t buflen, void *private_data);
204 static void wb_resp_read_done(struct tevent_req *subreq);
205
206 struct tevent_req *wb_resp_read_send(TALLOC_CTX *mem_ctx,
207                                      struct tevent_context *ev, int fd)
208 {
209         struct tevent_req *req, *subreq;
210         struct resp_read_state *state;
211
212         req = tevent_req_create(mem_ctx, &state, struct resp_read_state);
213         if (req == NULL) {
214                 return NULL;
215         }
216
217         subreq = read_packet_send(state, ev, fd, 4, wb_resp_more, state);
218         if (tevent_req_nomem(subreq, req)) {
219                 return tevent_req_post(req, ev);
220         }
221         tevent_req_set_callback(subreq, wb_resp_read_done, req);
222         return req;
223 }
224
225 static ssize_t wb_resp_more(uint8_t *buf, size_t buflen, void *private_data)
226 {
227         struct winbindd_response *resp = (struct winbindd_response *)buf;
228
229         if (buflen == 4) {
230                 if (resp->length < sizeof(struct winbindd_response)) {
231                         DEBUG(0, ("wb_resp_read_len: Invalid response size "
232                                   "received: %d (expected at least%d)\n",
233                                   (int)resp->length,
234                                   (int)sizeof(struct winbindd_response)));
235                         return -1;
236                 }
237         }
238         return resp->length - buflen;
239 }
240
241 static void wb_resp_read_done(struct tevent_req *subreq)
242 {
243         struct tevent_req *req = tevent_req_callback_data(
244                 subreq, struct tevent_req);
245         struct resp_read_state *state = tevent_req_data(
246                 req, struct resp_read_state);
247         uint8_t *buf;
248         int err;
249
250         state->ret = read_packet_recv(subreq, state, &buf, &err);
251         TALLOC_FREE(subreq);
252         if (state->ret == -1) {
253                 tevent_req_error(req, err);
254                 return;
255         }
256
257         state->wb_resp = (struct winbindd_response *)buf;
258
259         if (state->wb_resp->length > sizeof(struct winbindd_response)) {
260                 state->wb_resp->extra_data.data =
261                         (char *)buf + sizeof(struct winbindd_response);
262         } else {
263                 state->wb_resp->extra_data.data = NULL;
264         }
265         tevent_req_done(req);
266 }
267
268 ssize_t wb_resp_read_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
269                           struct winbindd_response **presp, int *err)
270 {
271         struct resp_read_state *state = tevent_req_data(
272                 req, struct resp_read_state);
273
274         if (tevent_req_is_unix_error(req, err)) {
275                 return -1;
276         }
277         *presp = talloc_move(mem_ctx, &state->wb_resp);
278         return state->ret;
279 }
280
281 struct resp_write_state {
282         struct iovec iov[2];
283         ssize_t ret;
284 };
285
286 static void wb_resp_write_done(struct tevent_req *subreq);
287
288 struct tevent_req *wb_resp_write_send(TALLOC_CTX *mem_ctx,
289                                       struct tevent_context *ev,
290                                       struct tevent_queue *queue, int fd,
291                                       struct winbindd_response *wb_resp)
292 {
293         struct tevent_req *req, *subreq;
294         struct resp_write_state *state;
295         int count = 1;
296
297         req = tevent_req_create(mem_ctx, &state, struct resp_write_state);
298         if (req == NULL) {
299                 return NULL;
300         }
301
302         state->iov[0].iov_base = (void *)wb_resp;
303         state->iov[0].iov_len = sizeof(struct winbindd_response);
304
305         if (wb_resp->length > sizeof(struct winbindd_response)) {
306                 state->iov[1].iov_base = (void *)wb_resp->extra_data.data;
307                 state->iov[1].iov_len =
308                         wb_resp->length - sizeof(struct winbindd_response);
309                 count = 2;
310         }
311
312         subreq = writev_send(state, ev, queue, fd, true, state->iov, count);
313         if (tevent_req_nomem(subreq, req)) {
314                 return tevent_req_post(req, ev);
315         }
316         tevent_req_set_callback(subreq, wb_resp_write_done, req);
317         return req;
318 }
319
320 static void wb_resp_write_done(struct tevent_req *subreq)
321 {
322         struct tevent_req *req = tevent_req_callback_data(
323                 subreq, struct tevent_req);
324         struct resp_write_state *state = tevent_req_data(
325                 req, struct resp_write_state);
326         int err;
327
328         state->ret = writev_recv(subreq, &err);
329         TALLOC_FREE(subreq);
330         if (state->ret < 0) {
331                 tevent_req_error(req, err);
332                 return;
333         }
334         tevent_req_done(req);
335 }
336
337 ssize_t wb_resp_write_recv(struct tevent_req *req, int *err)
338 {
339         struct resp_write_state *state = tevent_req_data(
340                 req, struct resp_write_state);
341
342         if (tevent_req_is_unix_error(req, err)) {
343                 return -1;
344         }
345         return state->ret;
346 }
347
348 static bool closed_fd(int fd)
349 {
350         struct timeval tv;
351         fd_set r_fds;
352
353         if (fd == -1) {
354                 return true;
355         }
356
357         FD_ZERO(&r_fds);
358         FD_SET(fd, &r_fds);
359         ZERO_STRUCT(tv);
360
361         if ((select(fd+1, &r_fds, NULL, NULL, &tv) == -1)
362             || FD_ISSET(fd, &r_fds)) {
363                 return true;
364         }
365
366         return false;
367 }
368
369 struct wb_simple_trans_state {
370         struct tevent_context *ev;
371         int fd;
372         struct winbindd_response *wb_resp;
373 };
374
375 static void wb_simple_trans_write_done(struct tevent_req *subreq);
376 static void wb_simple_trans_read_done(struct tevent_req *subreq);
377
378 struct tevent_req *wb_simple_trans_send(TALLOC_CTX *mem_ctx,
379                                         struct tevent_context *ev,
380                                         struct tevent_queue *queue, int fd,
381                                         struct winbindd_request *wb_req)
382 {
383         struct tevent_req *req, *subreq;
384         struct wb_simple_trans_state *state;
385
386         req = tevent_req_create(mem_ctx, &state, struct wb_simple_trans_state);
387         if (req == NULL) {
388                 return NULL;
389         }
390
391         if (closed_fd(fd)) {
392                 tevent_req_error(req, EPIPE);
393                 return tevent_req_post(req, ev);
394         }
395
396         wb_req->length = sizeof(struct winbindd_request);
397
398         state->ev = ev;
399         state->fd = fd;
400
401         subreq = wb_req_write_send(state, ev, queue, fd, wb_req);
402         if (tevent_req_nomem(subreq, req)) {
403                 return tevent_req_post(req, ev);
404         }
405         tevent_req_set_callback(subreq, wb_simple_trans_write_done, req);
406
407         return req;
408 }
409
410 static void wb_simple_trans_write_done(struct tevent_req *subreq)
411 {
412         struct tevent_req *req = tevent_req_callback_data(
413                 subreq, struct tevent_req);
414         struct wb_simple_trans_state *state = tevent_req_data(
415                 req, struct wb_simple_trans_state);
416         ssize_t ret;
417         int err;
418
419         ret = wb_req_write_recv(subreq, &err);
420         /*
421          * We do not TALLOC_FREE(subreq) here, as this would trigger the next
422          * write of a client. The winbind protocol is purely request/response
423          * without multiplex ID's, so having multiple requeusts on the fly
424          * would confuse sequencing.
425          *
426          * Eventually the "subreq" will be freed, it is a child of "req"
427          */
428         if (ret == -1) {
429                 tevent_req_error(req, err);
430                 return;
431         }
432         subreq = wb_resp_read_send(state, state->ev, state->fd);
433         if (tevent_req_nomem(subreq, req)) {
434                 return;
435         }
436         tevent_req_set_callback(subreq, wb_simple_trans_read_done, req);
437 }
438
439 static void wb_simple_trans_read_done(struct tevent_req *subreq)
440 {
441         struct tevent_req *req = tevent_req_callback_data(
442                 subreq, struct tevent_req);
443         struct wb_simple_trans_state *state = tevent_req_data(
444                 req, struct wb_simple_trans_state);
445         ssize_t ret;
446         int err;
447
448         ret = wb_resp_read_recv(subreq, state, &state->wb_resp, &err);
449         if (ret == -1) {
450                 tevent_req_error(req, err);
451                 return;
452         }
453
454         tevent_req_done(req);
455 }
456
457 int wb_simple_trans_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
458                          struct winbindd_response **presponse, int *err)
459 {
460         struct wb_simple_trans_state *state = tevent_req_data(
461                 req, struct wb_simple_trans_state);
462
463         if (tevent_req_is_unix_error(req, err)) {
464                 return -1;
465         }
466         *presponse = talloc_move(mem_ctx, &state->wb_resp);
467         return 0;
468 }