Merge branch 'nodiscard' of /home/jelmer/samba4
[ira/wip.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 bool async_req_is_wbcerr(struct async_req *req, wbcErr *pwbc_err)
29 {
30         enum async_req_state state;
31         uint64_t error;
32         if (!async_req_is_error(req, &state, &error)) {
33                 *pwbc_err = WBC_ERR_SUCCESS;
34                 return false;
35         }
36
37         switch (state) {
38         case ASYNC_REQ_USER_ERROR:
39                 *pwbc_err = error;
40                 break;
41         case ASYNC_REQ_TIMED_OUT:
42                 *pwbc_err = WBC_ERR_UNKNOWN_FAILURE;
43                 break;
44         case ASYNC_REQ_NO_MEMORY:
45                 *pwbc_err = WBC_ERR_NO_MEMORY;
46                 break;
47         default:
48                 *pwbc_err = WBC_ERR_UNKNOWN_FAILURE;
49                 break;
50         }
51         return true;
52 }
53
54 wbcErr map_wbc_err_from_errno(int error)
55 {
56         switch(error) {
57         case EPERM:
58         case EACCES:
59                 return WBC_ERR_AUTH_ERROR;
60         case ENOMEM:
61                 return WBC_ERR_NO_MEMORY;
62         case EIO:
63         default:
64                 return WBC_ERR_UNKNOWN_FAILURE;
65         }
66 }
67
68 wbcErr async_req_simple_recv_wbcerr(struct async_req *req)
69 {
70         wbcErr wbc_err;
71
72         if (async_req_is_wbcerr(req, &wbc_err)) {
73                 return wbc_err;
74         }
75
76         return WBC_ERR_SUCCESS;
77 }
78
79 bool tevent_req_is_wbcerr(struct tevent_req *req, wbcErr *pwbc_err)
80 {
81         enum tevent_req_state state;
82         uint64_t error;
83         if (!tevent_req_is_error(req, &state, &error)) {
84                 *pwbc_err = WBC_ERR_SUCCESS;
85                 return false;
86         }
87
88         switch (state) {
89         case TEVENT_REQ_USER_ERROR:
90                 *pwbc_err = error;
91                 break;
92         case TEVENT_REQ_TIMED_OUT:
93                 *pwbc_err = WBC_ERR_UNKNOWN_FAILURE;
94                 break;
95         case TEVENT_REQ_NO_MEMORY:
96                 *pwbc_err = WBC_ERR_NO_MEMORY;
97                 break;
98         default:
99                 *pwbc_err = WBC_ERR_UNKNOWN_FAILURE;
100                 break;
101         }
102         return true;
103 }
104
105 wbcErr tevent_req_simple_recv_wbcerr(struct tevent_req *req)
106 {
107         wbcErr wbc_err;
108
109         if (tevent_req_is_wbcerr(req, &wbc_err)) {
110                 return wbc_err;
111         }
112
113         return WBC_ERR_SUCCESS;
114 }
115
116 struct req_read_state {
117         struct winbindd_request *wb_req;
118         size_t max_extra_data;
119 };
120
121 static ssize_t wb_req_more(uint8_t *buf, size_t buflen, void *private_data);
122 static void wb_req_read_done(struct tevent_req *subreq);
123
124 struct tevent_req *wb_req_read_send(TALLOC_CTX *mem_ctx,
125                                     struct tevent_context *ev,
126                                     int fd, size_t max_extra_data)
127 {
128         struct tevent_req *result, *subreq;
129         struct req_read_state *state;
130
131         result = tevent_req_create(mem_ctx, &state, struct req_read_state);
132         if (result == NULL) {
133                 return NULL;
134         }
135         state->max_extra_data = max_extra_data;
136
137         subreq = read_packet_send(state, ev, fd, 4, wb_req_more, state);
138         if (subreq == NULL) {
139                 goto nomem;
140         }
141
142         tevent_req_set_callback(subreq, wb_req_read_done, result);
143         return result;
144  nomem:
145         TALLOC_FREE(result);
146         return NULL;
147 }
148
149 static ssize_t wb_req_more(uint8_t *buf, size_t buflen, void *private_data)
150 {
151         struct req_read_state *state = talloc_get_type_abort(
152                 private_data, struct req_read_state);
153         struct winbindd_request *req = (struct winbindd_request *)buf;
154
155         if (buflen == 4) {
156                 if (req->length != sizeof(struct winbindd_request)) {
157                         DEBUG(0, ("wb_req_read_len: Invalid request size "
158                                   "received: %d (expected %d)\n",
159                                   (int)req->length,
160                                   (int)sizeof(struct winbindd_request)));
161                         return -1;
162                 }
163                 return sizeof(struct winbindd_request) - 4;
164         }
165
166         if ((state->max_extra_data != 0)
167             && (req->extra_len > state->max_extra_data)) {
168                 DEBUG(3, ("Got request with %d bytes extra data on "
169                           "unprivileged socket\n", (int)req->extra_len));
170                 return -1;
171         }
172
173         return req->extra_len;
174 }
175
176 static void wb_req_read_done(struct tevent_req *subreq)
177 {
178         struct tevent_req *req = tevent_req_callback_data(
179                 subreq, struct tevent_req);
180         struct req_read_state *state = tevent_req_data(
181                 req, struct req_read_state);
182         int err;
183         ssize_t ret;
184         uint8_t *buf;
185
186         ret = read_packet_recv(subreq, state, &buf, &err);
187         TALLOC_FREE(subreq);
188         if (ret == -1) {
189                 tevent_req_error(req, map_wbc_err_from_errno(err));
190                 return;
191         }
192
193         state->wb_req = (struct winbindd_request *)buf;
194
195         if (state->wb_req->extra_len != 0) {
196                 state->wb_req->extra_data.data =
197                         (char *)buf + sizeof(struct winbindd_request);
198         } else {
199                 state->wb_req->extra_data.data = NULL;
200         }
201         tevent_req_done(req);
202 }
203
204 wbcErr wb_req_read_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
205                         struct winbindd_request **preq)
206 {
207         struct req_read_state *state = tevent_req_data(
208                 req, struct req_read_state);
209         wbcErr wbc_err;
210
211         if (tevent_req_is_wbcerr(req, &wbc_err)) {
212                 return wbc_err;
213         }
214         *preq = talloc_move(mem_ctx, &state->wb_req);
215         return WBC_ERR_SUCCESS;
216 }
217
218 struct req_write_state {
219         struct iovec iov[2];
220 };
221
222 static void wb_req_write_done(struct tevent_req *subreq);
223
224 struct tevent_req *wb_req_write_send(TALLOC_CTX *mem_ctx,
225                                      struct tevent_context *ev,
226                                      struct tevent_queue *queue, int fd,
227                                      struct winbindd_request *wb_req)
228 {
229         struct tevent_req *result, *subreq;
230         struct req_write_state *state;
231         int count = 1;
232
233         result = tevent_req_create(mem_ctx, &state, struct req_write_state);
234         if (result == NULL) {
235                 return NULL;
236         }
237
238         state->iov[0].iov_base = wb_req;
239         state->iov[0].iov_len = sizeof(struct winbindd_request);
240
241         if (wb_req->extra_len != 0) {
242                 state->iov[1].iov_base = wb_req->extra_data.data;
243                 state->iov[1].iov_len = wb_req->extra_len;
244                 count = 2;
245         }
246
247         subreq = writev_send(state, ev, queue, fd, state->iov, count);
248         if (subreq == NULL) {
249                 goto fail;
250         }
251         tevent_req_set_callback(subreq, wb_req_write_done, result);
252         return result;
253
254  fail:
255         TALLOC_FREE(result);
256         return NULL;
257 }
258
259 static void wb_req_write_done(struct tevent_req *subreq)
260 {
261         struct tevent_req *req = tevent_req_callback_data(
262                 subreq, struct tevent_req);
263         int err;
264         ssize_t ret;
265
266         ret = writev_recv(subreq, &err);
267         TALLOC_FREE(subreq);
268         if (ret < 0) {
269                 tevent_req_error(req, map_wbc_err_from_errno(err));
270                 return;
271         }
272         tevent_req_done(req);
273 }
274
275 wbcErr wb_req_write_recv(struct tevent_req *req)
276 {
277         return tevent_req_simple_recv_wbcerr(req);
278 }
279
280 struct resp_read_state {
281         struct winbindd_response *wb_resp;
282 };
283
284 static ssize_t wb_resp_more(uint8_t *buf, size_t buflen, void *private_data);
285 static void wb_resp_read_done(struct tevent_req *subreq);
286
287 struct tevent_req *wb_resp_read_send(TALLOC_CTX *mem_ctx,
288                                      struct tevent_context *ev, int fd)
289 {
290         struct tevent_req *result, *subreq;
291         struct resp_read_state *state;
292
293         result = tevent_req_create(mem_ctx, &state, struct resp_read_state);
294         if (result == NULL) {
295                 return NULL;
296         }
297
298         subreq = read_packet_send(state, ev, fd, 4, wb_resp_more, state);
299         if (subreq == NULL) {
300                 goto nomem;
301         }
302         tevent_req_set_callback(subreq, wb_resp_read_done, result);
303         return result;
304
305  nomem:
306         TALLOC_FREE(result);
307         return NULL;
308 }
309
310 static ssize_t wb_resp_more(uint8_t *buf, size_t buflen, void *private_data)
311 {
312         struct winbindd_response *resp = (struct winbindd_response *)buf;
313
314         if (buflen == 4) {
315                 if (resp->length < sizeof(struct winbindd_response)) {
316                         DEBUG(0, ("wb_resp_read_len: Invalid response size "
317                                   "received: %d (expected at least%d)\n",
318                                   (int)resp->length,
319                                   (int)sizeof(struct winbindd_response)));
320                         return -1;
321                 }
322         }
323         return resp->length - buflen;
324 }
325
326 static void wb_resp_read_done(struct tevent_req *subreq)
327 {
328         struct tevent_req *req = tevent_req_callback_data(
329                 subreq, struct tevent_req);
330         struct resp_read_state *state = tevent_req_data(
331                 req, struct resp_read_state);
332         uint8_t *buf;
333         int err;
334         ssize_t ret;
335
336         ret = read_packet_recv(subreq, state, &buf, &err);
337         TALLOC_FREE(subreq);
338         if (ret == -1) {
339                 tevent_req_error(req, map_wbc_err_from_errno(err));
340                 return;
341         }
342
343         state->wb_resp = (struct winbindd_response *)buf;
344
345         if (state->wb_resp->length > sizeof(struct winbindd_response)) {
346                 state->wb_resp->extra_data.data =
347                         (char *)buf + sizeof(struct winbindd_response);
348         } else {
349                 state->wb_resp->extra_data.data = NULL;
350         }
351         tevent_req_done(req);
352 }
353
354 wbcErr wb_resp_read_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
355                          struct winbindd_response **presp)
356 {
357         struct resp_read_state *state = tevent_req_data(
358                 req, struct resp_read_state);
359         wbcErr wbc_err;
360
361         if (tevent_req_is_wbcerr(req, &wbc_err)) {
362                 return wbc_err;
363         }
364         *presp = talloc_move(mem_ctx, &state->wb_resp);
365         return WBC_ERR_SUCCESS;
366 }
367
368 struct resp_write_state {
369         struct iovec iov[2];
370 };
371
372 static void wb_resp_write_done(struct tevent_req *subreq);
373
374 struct tevent_req *wb_resp_write_send(TALLOC_CTX *mem_ctx,
375                                       struct tevent_context *ev,
376                                       struct tevent_queue *queue, int fd,
377                                       struct winbindd_response *wb_resp)
378 {
379         struct tevent_req *result, *subreq;
380         struct resp_write_state *state;
381         int count = 1;
382
383         result = tevent_req_create(mem_ctx, &state, struct resp_write_state);
384         if (result == NULL) {
385                 return NULL;
386         }
387
388         state->iov[0].iov_base = wb_resp;
389         state->iov[0].iov_len = sizeof(struct winbindd_response);
390
391         if (wb_resp->length > sizeof(struct winbindd_response)) {
392                 state->iov[1].iov_base = wb_resp->extra_data.data;
393                 state->iov[1].iov_len =
394                         wb_resp->length - sizeof(struct winbindd_response);
395                 count = 2;
396         }
397
398         subreq = writev_send(state, ev, queue, fd, state->iov, count);
399         if (subreq == NULL) {
400                 goto fail;
401         }
402         tevent_req_set_callback(subreq, wb_resp_write_done, result);
403         return result;
404
405  fail:
406         TALLOC_FREE(result);
407         return NULL;
408 }
409
410 static void wb_resp_write_done(struct tevent_req *subreq)
411 {
412         struct tevent_req *req = tevent_req_callback_data(
413                 subreq, struct tevent_req);
414         int err;
415         ssize_t ret;
416
417         ret = writev_recv(subreq, &err);
418         TALLOC_FREE(subreq);
419         if (ret < 0) {
420                 tevent_req_error(req, map_wbc_err_from_errno(err));
421                 return;
422         }
423         tevent_req_done(req);
424 }
425
426 wbcErr wb_resp_write_recv(struct tevent_req *req)
427 {
428         return tevent_req_simple_recv_wbcerr(req);
429 }