d299914c7326eac0f6fd0b41c216a341a6744ff1
[ira/wip.git] / source3 / lib / wbclient.c
1 /*
2    Unix SMB/CIFS implementation.
3    Infrastructure for async winbind requests
4    Copyright (C) Volker Lendecke 2008
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "wbc_async.h"
22
23 static int make_nonstd_fd(int fd)
24 {
25         int i;
26         int sys_errno = 0;
27         int fds[3];
28         int num_fds = 0;
29
30         if (fd == -1) {
31                 return -1;
32         }
33         while (fd < 3) {
34                 fds[num_fds++] = fd;
35                 fd = dup(fd);
36                 if (fd == -1) {
37                         sys_errno = errno;
38                         break;
39                 }
40         }
41         for (i=0; i<num_fds; i++) {
42                 close(fds[i]);
43         }
44         if (fd == -1) {
45                 errno = sys_errno;
46         }
47         return fd;
48 }
49
50 /****************************************************************************
51  Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available,
52  else
53  if SYSV use O_NDELAY
54  if BSD use FNDELAY
55  Set close on exec also.
56 ****************************************************************************/
57
58 static int make_safe_fd(int fd)
59 {
60         int result, flags;
61         int new_fd = make_nonstd_fd(fd);
62
63         if (new_fd == -1) {
64                 goto fail;
65         }
66
67         /* Socket should be nonblocking. */
68
69 #ifdef O_NONBLOCK
70 #define FLAG_TO_SET O_NONBLOCK
71 #else
72 #ifdef SYSV
73 #define FLAG_TO_SET O_NDELAY
74 #else /* BSD */
75 #define FLAG_TO_SET FNDELAY
76 #endif
77 #endif
78
79         if ((flags = fcntl(new_fd, F_GETFL)) == -1) {
80                 goto fail;
81         }
82
83         flags |= FLAG_TO_SET;
84         if (fcntl(new_fd, F_SETFL, flags) == -1) {
85                 goto fail;
86         }
87
88 #undef FLAG_TO_SET
89
90         /* Socket should be closed on exec() */
91 #ifdef FD_CLOEXEC
92         result = flags = fcntl(new_fd, F_GETFD, 0);
93         if (flags >= 0) {
94                 flags |= FD_CLOEXEC;
95                 result = fcntl( new_fd, F_SETFD, flags );
96         }
97         if (result < 0) {
98                 goto fail;
99         }
100 #endif
101         return new_fd;
102
103  fail:
104         if (new_fd != -1) {
105                 int sys_errno = errno;
106                 close(new_fd);
107                 errno = sys_errno;
108         }
109         return -1;
110 }
111
112 static bool winbind_closed_fd(int fd)
113 {
114         struct timeval tv;
115         fd_set r_fds;
116
117         if (fd == -1) {
118                 return true;
119         }
120
121         FD_ZERO(&r_fds);
122         FD_SET(fd, &r_fds);
123         ZERO_STRUCT(tv);
124
125         if ((select(fd+1, &r_fds, NULL, NULL, &tv) == -1)
126             || FD_ISSET(fd, &r_fds)) {
127                 return true;
128         }
129
130         return false;
131 }
132
133 struct wb_context *wb_context_init(TALLOC_CTX *mem_ctx)
134 {
135         struct wb_context *result;
136
137         result = talloc(mem_ctx, struct wb_context);
138         if (result == NULL) {
139                 return NULL;
140         }
141         result->queue = async_req_queue_init(result);
142         if (result->queue == NULL) {
143                 TALLOC_FREE(result);
144                 return NULL;
145         }
146         result->fd = -1;
147         return result;
148 }
149
150 struct wb_connect_state {
151         int dummy;
152 };
153
154 static void wbc_connect_connected(struct tevent_req *subreq);
155
156 static struct async_req *wb_connect_send(TALLOC_CTX *mem_ctx,
157                                           struct tevent_context *ev,
158                                           struct wb_context *wb_ctx,
159                                           const char *dir)
160 {
161         struct async_req *result;
162         struct tevent_req *subreq;
163         struct wb_connect_state *state;
164         struct sockaddr_un sunaddr;
165         struct stat st;
166         char *path = NULL;
167         wbcErr wbc_err;
168
169         if (!async_req_setup(mem_ctx, &result, &state,
170                              struct wb_connect_state)) {
171                 return NULL;
172         }
173
174         if (wb_ctx->fd != -1) {
175                 close(wb_ctx->fd);
176                 wb_ctx->fd = -1;
177         }
178
179         /* Check permissions on unix socket directory */
180
181         if (lstat(dir, &st) == -1) {
182                 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
183                 goto post_status;
184         }
185
186         if (!S_ISDIR(st.st_mode) ||
187             (st.st_uid != 0 && st.st_uid != geteuid())) {
188                 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
189                 goto post_status;
190         }
191
192         /* Connect to socket */
193
194         path = talloc_asprintf(talloc_tos(), "%s/%s", dir,
195                                WINBINDD_SOCKET_NAME);
196         if (path == NULL) {
197                 goto nomem;
198         }
199
200         sunaddr.sun_family = AF_UNIX;
201         strlcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path));
202         TALLOC_FREE(path);
203
204         /* If socket file doesn't exist, don't bother trying to connect
205            with retry.  This is an attempt to make the system usable when
206            the winbindd daemon is not running. */
207
208         if ((lstat(sunaddr.sun_path, &st) == -1)
209             || !S_ISSOCK(st.st_mode)
210             || (st.st_uid != 0 && st.st_uid != geteuid())) {
211                 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
212                 goto post_status;
213         }
214
215         wb_ctx->fd = make_safe_fd(socket(AF_UNIX, SOCK_STREAM, 0));
216         if (wb_ctx->fd == -1) {
217                 wbc_err = map_wbc_err_from_errno(errno);
218                 goto post_status;
219         }
220
221         subreq = async_connect_send(mem_ctx, ev, wb_ctx->fd,
222                                     (struct sockaddr *)&sunaddr,
223                                     sizeof(sunaddr));
224         if (subreq == NULL) {
225                 goto nomem;
226         }
227         tevent_req_set_callback(subreq, wbc_connect_connected, result);
228
229         if (!tevent_req_set_endtime(subreq, ev, timeval_current_ofs(30, 0))) {
230                 goto nomem;
231         }
232
233         return result;
234
235  nomem:
236         wbc_err = WBC_ERR_NO_MEMORY;
237  post_status:
238         if (async_post_error(result, ev, wbc_err)) {
239                 return result;
240         }
241         TALLOC_FREE(result);
242         return NULL;
243 }
244
245 static void wbc_connect_connected(struct tevent_req *subreq)
246 {
247         struct async_req *req =
248                 tevent_req_callback_data(subreq, struct async_req);
249         int res, err;
250
251         res = async_connect_recv(subreq, &err);
252         TALLOC_FREE(subreq);
253         if (res == -1) {
254                 async_req_error(req, map_wbc_err_from_errno(err));
255                 return;
256         }
257         async_req_done(req);
258 }
259
260 static wbcErr wb_connect_recv(struct async_req *req)
261 {
262         return async_req_simple_recv_wbcerr(req);
263 }
264
265 static struct winbindd_request *winbindd_request_copy(
266         TALLOC_CTX *mem_ctx,
267         const struct winbindd_request *req)
268 {
269         struct winbindd_request *result;
270
271         result = (struct winbindd_request *)TALLOC_MEMDUP(
272                 mem_ctx, req, sizeof(struct winbindd_request));
273         if (result == NULL) {
274                 return NULL;
275         }
276
277         if (result->extra_len == 0) {
278                 return result;
279         }
280
281         result->extra_data.data = (char *)TALLOC_MEMDUP(
282                 result, result->extra_data.data, result->extra_len);
283         if (result->extra_data.data == NULL) {
284                 TALLOC_FREE(result);
285                 return NULL;
286         }
287         return result;
288 }
289
290 struct wb_int_trans_state {
291         struct tevent_context *ev;
292         int fd;
293         struct winbindd_request *wb_req;
294         struct winbindd_response *wb_resp;
295 };
296
297 static void wb_int_trans_write_done(struct tevent_req *subreq);
298 static void wb_int_trans_read_done(struct tevent_req *subreq);
299
300 static struct async_req *wb_int_trans_send(TALLOC_CTX *mem_ctx,
301                                            struct tevent_context *ev, int fd,
302                                            struct winbindd_request *wb_req)
303 {
304         struct async_req *result;
305         struct tevent_req *subreq;
306         struct wb_int_trans_state *state;
307
308         if (!async_req_setup(mem_ctx, &result, &state,
309                              struct wb_int_trans_state)) {
310                 return NULL;
311         }
312
313         if (winbind_closed_fd(fd)) {
314                 if (!async_post_error(result, ev,
315                                       WBC_ERR_WINBIND_NOT_AVAILABLE)) {
316                         goto fail;
317                 }
318                 return result;
319         }
320
321         state->ev = ev;
322         state->fd = fd;
323         state->wb_req = wb_req;
324
325         state->wb_req->length = sizeof(struct winbindd_request);
326         state->wb_req->pid = getpid();
327
328         subreq = wb_req_write_send(state, state->ev, NULL, state->fd,
329                                    state->wb_req);
330         if (subreq == NULL) {
331                 goto fail;
332         }
333         tevent_req_set_callback(subreq, wb_int_trans_write_done, result);
334
335         return result;
336
337  fail:
338         TALLOC_FREE(result);
339         return NULL;
340 }
341
342 static void wb_int_trans_write_done(struct tevent_req *subreq)
343 {
344         struct async_req *req = tevent_req_callback_data(
345                 subreq, struct async_req);
346         struct wb_int_trans_state *state = talloc_get_type_abort(
347                 req->private_data, struct wb_int_trans_state);
348         wbcErr wbc_err;
349
350         wbc_err = wb_req_write_recv(subreq);
351         TALLOC_FREE(subreq);
352         if (!WBC_ERROR_IS_OK(wbc_err)) {
353                 async_req_error(req, wbc_err);
354                 return;
355         }
356
357         subreq = wb_resp_read_send(state, state->ev, state->fd);
358         if (async_req_nomem(subreq, req)) {
359                 return;
360         }
361         tevent_req_set_callback(subreq, wb_int_trans_read_done, req);
362 }
363
364 static void wb_int_trans_read_done(struct tevent_req *subreq)
365 {
366         struct async_req *req = tevent_req_callback_data(
367                 subreq, struct async_req);
368         struct wb_int_trans_state *state = talloc_get_type_abort(
369                 req->private_data, struct wb_int_trans_state);
370         wbcErr wbc_err;
371
372         wbc_err = wb_resp_read_recv(subreq, state, &state->wb_resp);
373         TALLOC_FREE(subreq);
374         if (!WBC_ERROR_IS_OK(wbc_err)) {
375                 async_req_error(req, wbc_err);
376                 return;
377         }
378
379         async_req_done(req);
380 }
381
382 static wbcErr wb_int_trans_recv(struct async_req *req,
383                                 TALLOC_CTX *mem_ctx,
384                                 struct winbindd_response **presponse)
385 {
386         struct wb_int_trans_state *state = talloc_get_type_abort(
387                 req->private_data, struct wb_int_trans_state);
388         wbcErr wbc_err;
389
390         if (async_req_is_wbcerr(req, &wbc_err)) {
391                 return wbc_err;
392         }
393
394         *presponse = talloc_move(mem_ctx, &state->wb_resp);
395         return WBC_ERR_SUCCESS;
396 }
397
398 static const char *winbindd_socket_dir(void)
399 {
400 #ifdef SOCKET_WRAPPER
401         const char *env_dir;
402
403         env_dir = getenv(WINBINDD_SOCKET_DIR_ENVVAR);
404         if (env_dir) {
405                 return env_dir;
406         }
407 #endif
408
409         return WINBINDD_SOCKET_DIR;
410 }
411
412 struct wb_open_pipe_state {
413         struct wb_context *wb_ctx;
414         struct tevent_context *ev;
415         bool need_priv;
416         struct winbindd_request wb_req;
417 };
418
419 static void wb_open_pipe_connect_nonpriv_done(struct async_req *subreq);
420 static void wb_open_pipe_ping_done(struct async_req *subreq);
421 static void wb_open_pipe_getpriv_done(struct async_req *subreq);
422 static void wb_open_pipe_connect_priv_done(struct async_req *subreq);
423
424 static struct async_req *wb_open_pipe_send(TALLOC_CTX *mem_ctx,
425                                            struct tevent_context *ev,
426                                            struct wb_context *wb_ctx,
427                                            bool need_priv)
428 {
429         struct async_req *result;
430         struct async_req *subreq;
431         struct wb_open_pipe_state *state;
432
433         if (!async_req_setup(mem_ctx, &result, &state,
434                              struct wb_open_pipe_state)) {
435                 return NULL;
436         }
437         state->wb_ctx = wb_ctx;
438         state->ev = ev;
439         state->need_priv = need_priv;
440
441         if (wb_ctx->fd != -1) {
442                 close(wb_ctx->fd);
443                 wb_ctx->fd = -1;
444         }
445
446         subreq = wb_connect_send(state, ev, wb_ctx, winbindd_socket_dir());
447         if (subreq == NULL) {
448                 goto fail;
449         }
450
451         subreq->async.fn = wb_open_pipe_connect_nonpriv_done;
452         subreq->async.priv = result;
453         return result;
454
455  fail:
456         TALLOC_FREE(result);
457         return NULL;
458 }
459
460 static void wb_open_pipe_connect_nonpriv_done(struct async_req *subreq)
461 {
462         struct async_req *req = talloc_get_type_abort(
463                 subreq->async.priv, struct async_req);
464         struct wb_open_pipe_state *state = talloc_get_type_abort(
465                 req->private_data, struct wb_open_pipe_state);
466         wbcErr wbc_err;
467
468         wbc_err = wb_connect_recv(subreq);
469         TALLOC_FREE(subreq);
470         if (!WBC_ERROR_IS_OK(wbc_err)) {
471                 state->wb_ctx->is_priv = true;
472                 async_req_error(req, wbc_err);
473                 return;
474         }
475
476         ZERO_STRUCT(state->wb_req);
477         state->wb_req.cmd = WINBINDD_INTERFACE_VERSION;
478
479         subreq = wb_int_trans_send(state, state->ev, state->wb_ctx->fd,
480                                    &state->wb_req);
481         if (async_req_nomem(subreq, req)) {
482                 return;
483         }
484
485         subreq->async.fn = wb_open_pipe_ping_done;
486         subreq->async.priv = req;
487 }
488
489 static void wb_open_pipe_ping_done(struct async_req *subreq)
490 {
491         struct async_req *req = talloc_get_type_abort(
492                 subreq->async.priv, struct async_req);
493         struct wb_open_pipe_state *state = talloc_get_type_abort(
494                 req->private_data, struct wb_open_pipe_state);
495         struct winbindd_response *wb_resp;
496         wbcErr wbc_err;
497
498         wbc_err = wb_int_trans_recv(subreq, state, &wb_resp);
499         TALLOC_FREE(subreq);
500         if (!WBC_ERROR_IS_OK(wbc_err)) {
501                 async_req_error(req, wbc_err);
502                 return;
503         }
504
505         if (!state->need_priv) {
506                 async_req_done(req);
507                 return;
508         }
509
510         state->wb_req.cmd = WINBINDD_PRIV_PIPE_DIR;
511
512         subreq = wb_int_trans_send(state, state->ev, state->wb_ctx->fd,
513                                    &state->wb_req);
514         if (async_req_nomem(subreq, req)) {
515                 return;
516         }
517
518         subreq->async.fn = wb_open_pipe_getpriv_done;
519         subreq->async.priv = req;
520 }
521
522 static void wb_open_pipe_getpriv_done(struct async_req *subreq)
523 {
524         struct async_req *req = talloc_get_type_abort(
525                 subreq->async.priv, struct async_req);
526         struct wb_open_pipe_state *state = talloc_get_type_abort(
527                 req->private_data, struct wb_open_pipe_state);
528         struct winbindd_response *wb_resp = NULL;
529         wbcErr wbc_err;
530
531         wbc_err = wb_int_trans_recv(subreq, state, &wb_resp);
532         TALLOC_FREE(subreq);
533         if (!WBC_ERROR_IS_OK(wbc_err)) {
534                 async_req_error(req, wbc_err);
535                 return;
536         }
537
538         close(state->wb_ctx->fd);
539         state->wb_ctx->fd = -1;
540
541         subreq = wb_connect_send(state, state->ev, state->wb_ctx,
542                                  (char *)wb_resp->extra_data.data);
543         TALLOC_FREE(wb_resp);
544         if (async_req_nomem(subreq, req)) {
545                 return;
546         }
547
548         subreq->async.fn = wb_open_pipe_connect_priv_done;
549         subreq->async.priv = req;
550 }
551
552 static void wb_open_pipe_connect_priv_done(struct async_req *subreq)
553 {
554         struct async_req *req = talloc_get_type_abort(
555                 subreq->async.priv, struct async_req);
556         struct wb_open_pipe_state *state = talloc_get_type_abort(
557                 req->private_data, struct wb_open_pipe_state);
558         wbcErr wbc_err;
559
560         wbc_err = wb_connect_recv(subreq);
561         TALLOC_FREE(subreq);
562         if (!WBC_ERROR_IS_OK(wbc_err)) {
563                 async_req_error(req, wbc_err);
564                 return;
565         }
566         state->wb_ctx->is_priv = true;
567         async_req_done(req);
568 }
569
570 static wbcErr wb_open_pipe_recv(struct async_req *req)
571 {
572         return async_req_simple_recv_wbcerr(req);
573 }
574
575 struct wb_trans_state {
576         struct wb_trans_state *prev, *next;
577         struct wb_context *wb_ctx;
578         struct tevent_context *ev;
579         struct winbindd_request *wb_req;
580         struct winbindd_response *wb_resp;
581         int num_retries;
582         bool need_priv;
583 };
584
585 static void wb_trans_connect_done(struct async_req *subreq);
586 static void wb_trans_done(struct async_req *subreq);
587 static void wb_trans_retry_wait_done(struct async_req *subreq);
588
589 static void wb_trigger_trans(struct async_req *req)
590 {
591         struct wb_trans_state *state = talloc_get_type_abort(
592                 req->private_data, struct wb_trans_state);
593         struct async_req *subreq;
594
595         if ((state->wb_ctx->fd == -1)
596             || (state->need_priv && !state->wb_ctx->is_priv)) {
597
598                 subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
599                                            state->need_priv);
600                 if (async_req_nomem(subreq, req)) {
601                         return;
602                 }
603                 subreq->async.fn = wb_trans_connect_done;
604                 subreq->async.priv = req;
605                 return;
606         }
607
608         subreq = wb_int_trans_send(state, state->ev, state->wb_ctx->fd,
609                                    state->wb_req);
610         if (async_req_nomem(subreq, req)) {
611                 return;
612         }
613         subreq->async.fn = wb_trans_done;
614         subreq->async.priv = req;
615 }
616
617 struct async_req *wb_trans_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
618                                 struct wb_context *wb_ctx, bool need_priv,
619                                 const struct winbindd_request *wb_req)
620 {
621         struct async_req *result;
622         struct wb_trans_state *state;
623
624         if (!async_req_setup(mem_ctx, &result, &state,
625                              struct wb_trans_state)) {
626                 return NULL;
627         }
628         state->wb_ctx = wb_ctx;
629         state->ev = ev;
630         state->wb_req = winbindd_request_copy(state, wb_req);
631         if (state->wb_req == NULL) {
632                 goto fail;
633         }
634         state->num_retries = 10;
635         state->need_priv = need_priv;
636
637         if (!async_req_enqueue(wb_ctx->queue, ev, result, wb_trigger_trans)) {
638                 goto fail;
639         }
640         return result;
641
642  fail:
643         TALLOC_FREE(result);
644         return NULL;
645 }
646
647 static bool wb_trans_retry(struct async_req *req,
648                            struct wb_trans_state *state,
649                            wbcErr wbc_err)
650 {
651         struct async_req *subreq;
652
653         if (WBC_ERROR_IS_OK(wbc_err)) {
654                 return false;
655         }
656
657         if (wbc_err == WBC_ERR_WINBIND_NOT_AVAILABLE) {
658                 /*
659                  * Winbind not around or we can't connect to the pipe. Fail
660                  * immediately.
661                  */
662                 async_req_error(req, wbc_err);
663                 return true;
664         }
665
666         state->num_retries -= 1;
667         if (state->num_retries == 0) {
668                 async_req_error(req, wbc_err);
669                 return true;
670         }
671
672         /*
673          * The transfer as such failed, retry after one second
674          */
675
676         if (state->wb_ctx->fd != -1) {
677                 close(state->wb_ctx->fd);
678                 state->wb_ctx->fd = -1;
679         }
680
681         subreq = async_wait_send(state, state->ev, timeval_set(1, 0));
682         if (async_req_nomem(subreq, req)) {
683                 return true;
684         }
685
686         subreq->async.fn = wb_trans_retry_wait_done;
687         subreq->async.priv = req;
688         return true;
689 }
690
691 static void wb_trans_retry_wait_done(struct async_req *subreq)
692 {
693         struct async_req *req = talloc_get_type_abort(
694                 subreq->async.priv, struct async_req);
695         struct wb_trans_state *state = talloc_get_type_abort(
696                 req->private_data, struct wb_trans_state);
697         bool ret;
698
699         ret = async_wait_recv(subreq);
700         TALLOC_FREE(subreq);
701         if (ret) {
702                 async_req_error(req, WBC_ERR_UNKNOWN_FAILURE);
703                 return;
704         }
705
706         subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
707                                    state->need_priv);
708         if (async_req_nomem(subreq, req)) {
709                 return;
710         }
711         subreq->async.fn = wb_trans_connect_done;
712         subreq->async.priv = req;
713 }
714
715 static void wb_trans_connect_done(struct async_req *subreq)
716 {
717         struct async_req *req = talloc_get_type_abort(
718                 subreq->async.priv, struct async_req);
719         struct wb_trans_state *state = talloc_get_type_abort(
720                 req->private_data, struct wb_trans_state);
721         wbcErr wbc_err;
722
723         wbc_err = wb_open_pipe_recv(subreq);
724         TALLOC_FREE(subreq);
725
726         if (wb_trans_retry(req, state, wbc_err)) {
727                 return;
728         }
729
730         subreq = wb_int_trans_send(state, state->ev, state->wb_ctx->fd,
731                                    state->wb_req);
732         if (async_req_nomem(subreq, req)) {
733                 return;
734         }
735
736         subreq->async.fn = wb_trans_done;
737         subreq->async.priv = req;
738 }
739
740 static void wb_trans_done(struct async_req *subreq)
741 {
742         struct async_req *req = talloc_get_type_abort(
743                 subreq->async.priv, struct async_req);
744         struct wb_trans_state *state = talloc_get_type_abort(
745                 req->private_data, struct wb_trans_state);
746         wbcErr wbc_err;
747
748         wbc_err = wb_int_trans_recv(subreq, state, &state->wb_resp);
749         TALLOC_FREE(subreq);
750
751         if (wb_trans_retry(req, state, wbc_err)) {
752                 return;
753         }
754
755         async_req_done(req);
756 }
757
758 wbcErr wb_trans_recv(struct async_req *req, TALLOC_CTX *mem_ctx,
759                      struct winbindd_response **presponse)
760 {
761         struct wb_trans_state *state = talloc_get_type_abort(
762                 req->private_data, struct wb_trans_state);
763         wbcErr wbc_err;
764
765         if (async_req_is_wbcerr(req, &wbc_err)) {
766                 return wbc_err;
767         }
768
769         *presponse = talloc_move(mem_ctx, &state->wb_resp);
770         return WBC_ERR_SUCCESS;
771 }