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