tsocket: add tstream_context infrastructure similar to tdgram_context
[ira/wip.git] / lib / tsocket / tsocket_bsd.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Copyright (C) Stefan Metzmacher 2009
5
6      ** NOTE! The following LGPL license applies to the tevent
7      ** library. This does NOT imply that all of Samba is released
8      ** under the LGPL
9
10    This library is free software; you can redistribute it and/or
11    modify it under the terms of the GNU Lesser General Public
12    License as published by the Free Software Foundation; either
13    version 3 of the License, or (at your option) any later version.
14
15    This library is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18    Lesser General Public License for more details.
19
20    You should have received a copy of the GNU Lesser General Public
21    License along with this library; if not, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include "replace.h"
25 #include "system/filesys.h"
26 #include "system/network.h"
27 #include "tsocket.h"
28 #include "tsocket_internal.h"
29
30 static int tsocket_bsd_error_from_errno(int ret,
31                                         int sys_errno,
32                                         bool *retry)
33 {
34         *retry = false;
35
36         if (ret >= 0) {
37                 return 0;
38         }
39
40         if (ret != -1) {
41                 return EIO;
42         }
43
44         if (sys_errno == 0) {
45                 return EIO;
46         }
47
48         if (sys_errno == EINTR) {
49                 *retry = true;
50                 return sys_errno;
51         }
52
53         if (sys_errno == EINPROGRESS) {
54                 *retry = true;
55                 return sys_errno;
56         }
57
58         if (sys_errno == EAGAIN) {
59                 *retry = true;
60                 return sys_errno;
61         }
62
63 #ifdef EWOULDBLOCK
64         if (sys_errno == EWOULDBLOCK) {
65                 *retry = true;
66                 return sys_errno;
67         }
68 #endif
69
70         return sys_errno;
71 }
72
73 static int tsocket_bsd_common_prepare_fd(int fd, bool high_fd)
74 {
75         int i;
76         int sys_errno = 0;
77         int fds[3];
78         int num_fds = 0;
79
80         int result, flags;
81
82         if (fd == -1) {
83                 return -1;
84         }
85
86         /* first make a fd >= 3 */
87         if (high_fd) {
88                 while (fd < 3) {
89                         fds[num_fds++] = fd;
90                         fd = dup(fd);
91                         if (fd == -1) {
92                                 sys_errno = errno;
93                                 break;
94                         }
95                 }
96                 for (i=0; i<num_fds; i++) {
97                         close(fds[i]);
98                 }
99                 if (fd == -1) {
100                         errno = sys_errno;
101                         return fd;
102                 }
103         }
104
105         /* fd should be nonblocking. */
106
107 #ifdef O_NONBLOCK
108 #define FLAG_TO_SET O_NONBLOCK
109 #else
110 #ifdef SYSV
111 #define FLAG_TO_SET O_NDELAY
112 #else /* BSD */
113 #define FLAG_TO_SET FNDELAY
114 #endif
115 #endif
116
117         if ((flags = fcntl(fd, F_GETFL)) == -1) {
118                 goto fail;
119         }
120
121         flags |= FLAG_TO_SET;
122         if (fcntl(fd, F_SETFL, flags) == -1) {
123                 goto fail;
124         }
125
126 #undef FLAG_TO_SET
127
128         /* fd should be closed on exec() */
129 #ifdef FD_CLOEXEC
130         result = flags = fcntl(fd, F_GETFD, 0);
131         if (flags >= 0) {
132                 flags |= FD_CLOEXEC;
133                 result = fcntl(fd, F_SETFD, flags);
134         }
135         if (result < 0) {
136                 goto fail;
137         }
138 #endif
139         return fd;
140
141  fail:
142         if (fd != -1) {
143                 sys_errno = errno;
144                 close(fd);
145                 errno = sys_errno;
146         }
147         return -1;
148 }
149
150 static ssize_t tsocket_bsd_pending(int fd)
151 {
152         int ret;
153         int value = 0;
154
155         ret = ioctl(fd, FIONREAD, &value);
156         if (ret == -1) {
157                 return ret;
158         }
159
160         if (ret == 0) {
161                 if (value == 0) {
162                         int error=0;
163                         socklen_t len = sizeof(error);
164                         /*
165                          * if no data is available check if the socket
166                          * is in error state. For dgram sockets
167                          * it's the way to return ICMP error messages
168                          * of connected sockets to the caller.
169                          */
170                         ret = getsockopt(fd, SOL_SOCKET, SO_ERROR,
171                                          &error, &len);
172                         if (ret == -1) {
173                                 return ret;
174                         }
175                         if (error != 0) {
176                                 errno = error;
177                                 return -1;
178                         }
179                 }
180                 return value;
181         }
182
183         /* this should not be reached */
184         errno = EIO;
185         return -1;
186 }
187
188 static const struct tsocket_address_ops tsocket_address_bsd_ops;
189
190 struct tsocket_address_bsd {
191         union {
192                 struct sockaddr sa;
193                 struct sockaddr_in in;
194 #ifdef HAVE_IPV6
195                 struct sockaddr_in6 in6;
196 #endif
197                 struct sockaddr_un un;
198                 struct sockaddr_storage ss;
199         } u;
200 };
201
202 static int _tsocket_address_bsd_from_sockaddr(TALLOC_CTX *mem_ctx,
203                                               struct sockaddr *sa,
204                                               socklen_t sa_len,
205                                               struct tsocket_address **_addr,
206                                               const char *location)
207 {
208         struct tsocket_address *addr;
209         struct tsocket_address_bsd *bsda;
210
211         switch (sa->sa_family) {
212         case AF_UNIX:
213                 if (sa_len < sizeof(struct sockaddr_un)) {
214                         errno = EINVAL;
215                         return -1;
216                 }
217                 break;
218         case AF_INET:
219                 if (sa_len < sizeof(struct sockaddr_in)) {
220                         errno = EINVAL;
221                         return -1;
222                 }
223                 break;
224 #ifdef HAVE_IPV6
225         case AF_INET6:
226                 if (sa_len < sizeof(struct sockaddr_in6)) {
227                         errno = EINVAL;
228                         return -1;
229                 }
230                 break;
231 #endif
232         default:
233                 errno = EAFNOSUPPORT;
234                 return -1;
235         }
236
237         if (sa_len > sizeof(struct sockaddr_storage)) {
238                 errno = EINVAL;
239                 return -1;
240         }
241
242         addr = tsocket_address_create(mem_ctx,
243                                       &tsocket_address_bsd_ops,
244                                       &bsda,
245                                       struct tsocket_address_bsd,
246                                       location);
247         if (!addr) {
248                 errno = ENOMEM;
249                 return -1;
250         }
251
252         ZERO_STRUCTP(bsda);
253
254         memcpy(&bsda->u.ss, sa, sa_len);
255
256         *_addr = addr;
257         return 0;
258 }
259
260 int _tsocket_address_inet_from_strings(TALLOC_CTX *mem_ctx,
261                                        const char *fam,
262                                        const char *addr,
263                                        uint16_t port,
264                                        struct tsocket_address **_addr,
265                                        const char *location)
266 {
267         struct addrinfo hints;
268         struct addrinfo *result = NULL;
269         char port_str[6];
270         int ret;
271
272         ZERO_STRUCT(hints);
273         /*
274          * we use SOCKET_STREAM here to get just one result
275          * back from getaddrinfo().
276          */
277         hints.ai_socktype = SOCK_STREAM;
278         hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
279
280         if (strcasecmp(fam, "ip") == 0) {
281                 hints.ai_family = AF_UNSPEC;
282                 if (!addr) {
283 #ifdef HAVE_IPV6
284                         addr = "::";
285 #else
286                         addr = "0.0.0.0";
287 #endif
288                 }
289         } else if (strcasecmp(fam, "ipv4") == 0) {
290                 hints.ai_family = AF_INET;
291                 if (!addr) {
292                         addr = "0.0.0.0";
293                 }
294 #ifdef HAVE_IPV6
295         } else if (strcasecmp(fam, "ipv6") == 0) {
296                 hints.ai_family = AF_INET6;
297                 if (!addr) {
298                         addr = "::";
299                 }
300 #endif
301         } else {
302                 errno = EAFNOSUPPORT;
303                 return -1;
304         }
305
306         snprintf(port_str, sizeof(port_str) - 1, "%u", port);
307
308         ret = getaddrinfo(addr, port_str, &hints, &result);
309         if (ret != 0) {
310                 switch (ret) {
311                 case EAI_FAIL:
312                         errno = EINVAL;
313                         break;
314                 }
315                 ret = -1;
316                 goto done;
317         }
318
319         if (result->ai_socktype != SOCK_STREAM) {
320                 errno = EINVAL;
321                 ret = -1;
322                 goto done;
323         }
324
325         ret = _tsocket_address_bsd_from_sockaddr(mem_ctx,
326                                                   result->ai_addr,
327                                                   result->ai_addrlen,
328                                                   _addr,
329                                                   location);
330
331 done:
332         if (result) {
333                 freeaddrinfo(result);
334         }
335         return ret;
336 }
337
338 char *tsocket_address_inet_addr_string(const struct tsocket_address *addr,
339                                        TALLOC_CTX *mem_ctx)
340 {
341         struct tsocket_address_bsd *bsda = talloc_get_type(addr->private_data,
342                                            struct tsocket_address_bsd);
343         char addr_str[INET6_ADDRSTRLEN+1];
344         const char *str;
345
346         if (!bsda) {
347                 errno = EINVAL;
348                 return NULL;
349         }
350
351         switch (bsda->u.sa.sa_family) {
352         case AF_INET:
353                 str = inet_ntop(bsda->u.in.sin_family,
354                                 &bsda->u.in.sin_addr,
355                                 addr_str, sizeof(addr_str));
356                 break;
357 #ifdef HAVE_IPV6
358         case AF_INET6:
359                 str = inet_ntop(bsda->u.in6.sin6_family,
360                                 &bsda->u.in6.sin6_addr,
361                                 addr_str, sizeof(addr_str));
362                 break;
363 #endif
364         default:
365                 errno = EINVAL;
366                 return NULL;
367         }
368
369         if (!str) {
370                 return NULL;
371         }
372
373         return talloc_strdup(mem_ctx, str);
374 }
375
376 uint16_t tsocket_address_inet_port(const struct tsocket_address *addr)
377 {
378         struct tsocket_address_bsd *bsda = talloc_get_type(addr->private_data,
379                                            struct tsocket_address_bsd);
380         uint16_t port = 0;
381
382         if (!bsda) {
383                 errno = EINVAL;
384                 return 0;
385         }
386
387         switch (bsda->u.sa.sa_family) {
388         case AF_INET:
389                 port = ntohs(bsda->u.in.sin_port);
390                 break;
391 #ifdef HAVE_IPV6
392         case AF_INET6:
393                 port = ntohs(bsda->u.in6.sin6_port);
394                 break;
395 #endif
396         default:
397                 errno = EINVAL;
398                 return 0;
399         }
400
401         return port;
402 }
403
404 int tsocket_address_inet_set_port(struct tsocket_address *addr,
405                                   uint16_t port)
406 {
407         struct tsocket_address_bsd *bsda = talloc_get_type(addr->private_data,
408                                            struct tsocket_address_bsd);
409
410         if (!bsda) {
411                 errno = EINVAL;
412                 return -1;
413         }
414
415         switch (bsda->u.sa.sa_family) {
416         case AF_INET:
417                 bsda->u.in.sin_port = htons(port);
418                 break;
419 #ifdef HAVE_IPV6
420         case AF_INET6:
421                 bsda->u.in6.sin6_port = htons(port);
422                 break;
423 #endif
424         default:
425                 errno = EINVAL;
426                 return -1;
427         }
428
429         return 0;
430 }
431
432 int _tsocket_address_unix_from_path(TALLOC_CTX *mem_ctx,
433                                     const char *path,
434                                     struct tsocket_address **_addr,
435                                     const char *location)
436 {
437         struct sockaddr_un un;
438         void *p = &un;
439         int ret;
440
441         if (!path) {
442                 path = "";
443         }
444
445         ZERO_STRUCT(un);
446         un.sun_family = AF_UNIX;
447         strncpy(un.sun_path, path, sizeof(un.sun_path));
448
449         ret = _tsocket_address_bsd_from_sockaddr(mem_ctx,
450                                                  (struct sockaddr *)p,
451                                                  sizeof(un),
452                                                  _addr,
453                                                  location);
454
455         return ret;
456 }
457
458 char *tsocket_address_unix_path(const struct tsocket_address *addr,
459                                 TALLOC_CTX *mem_ctx)
460 {
461         struct tsocket_address_bsd *bsda = talloc_get_type(addr->private_data,
462                                            struct tsocket_address_bsd);
463         const char *str;
464
465         if (!bsda) {
466                 errno = EINVAL;
467                 return NULL;
468         }
469
470         switch (bsda->u.sa.sa_family) {
471         case AF_UNIX:
472                 str = bsda->u.un.sun_path;
473                 break;
474         default:
475                 errno = EINVAL;
476                 return NULL;
477         }
478
479         return talloc_strdup(mem_ctx, str);
480 }
481
482 static char *tsocket_address_bsd_string(const struct tsocket_address *addr,
483                                         TALLOC_CTX *mem_ctx)
484 {
485         struct tsocket_address_bsd *bsda = talloc_get_type(addr->private_data,
486                                            struct tsocket_address_bsd);
487         char *str;
488         char *addr_str;
489         const char *prefix = NULL;
490         uint16_t port;
491
492         switch (bsda->u.sa.sa_family) {
493         case AF_UNIX:
494                 return talloc_asprintf(mem_ctx, "unix:%s",
495                                        bsda->u.un.sun_path);
496         case AF_INET:
497                 prefix = "ipv4";
498                 break;
499 #ifdef HAVE_IPV6
500         case AF_INET6:
501                 prefix = "ipv6";
502                 break;
503 #endif
504         default:
505                 errno = EINVAL;
506                 return NULL;
507         }
508
509         addr_str = tsocket_address_inet_addr_string(addr, mem_ctx);
510         if (!addr_str) {
511                 return NULL;
512         }
513
514         port = tsocket_address_inet_port(addr);
515
516         str = talloc_asprintf(mem_ctx, "%s:%s:%u",
517                               prefix, addr_str, port);
518         talloc_free(addr_str);
519
520         return str;
521 }
522
523 static struct tsocket_address *tsocket_address_bsd_copy(const struct tsocket_address *addr,
524                                                          TALLOC_CTX *mem_ctx,
525                                                          const char *location)
526 {
527         struct tsocket_address_bsd *bsda = talloc_get_type(addr->private_data,
528                                            struct tsocket_address_bsd);
529         struct tsocket_address *copy;
530         int ret;
531
532         ret = _tsocket_address_bsd_from_sockaddr(mem_ctx,
533                                                  &bsda->u.sa,
534                                                  sizeof(bsda->u.ss),
535                                                  &copy,
536                                                  location);
537         if (ret != 0) {
538                 return NULL;
539         }
540
541         return copy;
542 }
543
544 static const struct tsocket_address_ops tsocket_address_bsd_ops = {
545         .name           = "bsd",
546         .string         = tsocket_address_bsd_string,
547         .copy           = tsocket_address_bsd_copy,
548 };
549
550 struct tdgram_bsd {
551         int fd;
552
553         void *event_ptr;
554         struct tevent_fd *fde;
555
556         void *readable_private;
557         void (*readable_handler)(void *private_data);
558         void *writeable_private;
559         void (*writeable_handler)(void *private_data);
560 };
561
562 static void tdgram_bsd_fde_handler(struct tevent_context *ev,
563                                    struct tevent_fd *fde,
564                                    uint16_t flags,
565                                    void *private_data)
566 {
567         struct tdgram_bsd *bsds = talloc_get_type_abort(private_data,
568                                   struct tdgram_bsd);
569
570         if (flags & TEVENT_FD_WRITE) {
571                 bsds->writeable_handler(bsds->writeable_private);
572                 return;
573         }
574         if (flags & TEVENT_FD_READ) {
575                 if (!bsds->readable_handler) {
576                         TEVENT_FD_NOT_READABLE(bsds->fde);
577                         return;
578                 }
579                 bsds->readable_handler(bsds->readable_private);
580                 return;
581         }
582 }
583
584 static int tdgram_bsd_set_readable_handler(struct tdgram_bsd *bsds,
585                                            struct tevent_context *ev,
586                                            void (*handler)(void *private_data),
587                                            void *private_data)
588 {
589         if (ev == NULL) {
590                 if (handler) {
591                         errno = EINVAL;
592                         return -1;
593                 }
594                 if (!bsds->readable_handler) {
595                         return 0;
596                 }
597                 bsds->readable_handler = NULL;
598                 bsds->readable_private = NULL;
599
600                 return 0;
601         }
602
603         /* read and write must use the same tevent_context */
604         if (bsds->event_ptr != ev) {
605                 if (bsds->readable_handler || bsds->writeable_handler) {
606                         errno = EINVAL;
607                         return -1;
608                 }
609                 bsds->event_ptr = NULL;
610                 TALLOC_FREE(bsds->fde);
611         }
612
613         if (bsds->fde == NULL) {
614                 bsds->fde = tevent_add_fd(ev, bsds,
615                                           bsds->fd, TEVENT_FD_READ,
616                                           tdgram_bsd_fde_handler,
617                                           bsds);
618                 if (!bsds->fde) {
619                         return -1;
620                 }
621
622                 /* cache the event context we're running on */
623                 bsds->event_ptr = ev;
624         } else if (!bsds->readable_handler) {
625                 TEVENT_FD_READABLE(bsds->fde);
626         }
627
628         bsds->readable_handler = handler;
629         bsds->readable_private = private_data;
630
631         return 0;
632 }
633
634 static int tdgram_bsd_set_writeable_handler(struct tdgram_bsd *bsds,
635                                             struct tevent_context *ev,
636                                             void (*handler)(void *private_data),
637                                             void *private_data)
638 {
639         if (ev == NULL) {
640                 if (handler) {
641                         errno = EINVAL;
642                         return -1;
643                 }
644                 if (!bsds->writeable_handler) {
645                         return 0;
646                 }
647                 bsds->writeable_handler = NULL;
648                 bsds->writeable_private = NULL;
649                 TEVENT_FD_NOT_WRITEABLE(bsds->fde);
650
651                 return 0;
652         }
653
654         /* read and write must use the same tevent_context */
655         if (bsds->event_ptr != ev) {
656                 if (bsds->readable_handler || bsds->writeable_handler) {
657                         errno = EINVAL;
658                         return -1;
659                 }
660                 bsds->event_ptr = NULL;
661                 TALLOC_FREE(bsds->fde);
662         }
663
664         if (bsds->fde == NULL) {
665                 bsds->fde = tevent_add_fd(ev, bsds,
666                                           bsds->fd, TEVENT_FD_WRITE,
667                                           tdgram_bsd_fde_handler,
668                                           bsds);
669                 if (!bsds->fde) {
670                         return -1;
671                 }
672
673                 /* cache the event context we're running on */
674                 bsds->event_ptr = ev;
675         } else if (!bsds->writeable_handler) {
676                 TEVENT_FD_WRITEABLE(bsds->fde);
677         }
678
679         bsds->writeable_handler = handler;
680         bsds->writeable_private = private_data;
681
682         return 0;
683 }
684
685 struct tdgram_bsd_recvfrom_state {
686         struct tdgram_context *dgram;
687
688         uint8_t *buf;
689         size_t len;
690         struct tsocket_address *src;
691 };
692
693 static int tdgram_bsd_recvfrom_destructor(struct tdgram_bsd_recvfrom_state *state)
694 {
695         struct tdgram_bsd *bsds = tdgram_context_data(state->dgram,
696                                   struct tdgram_bsd);
697
698         tdgram_bsd_set_readable_handler(bsds, NULL, NULL, NULL);
699
700         return 0;
701 }
702
703 static void tdgram_bsd_recvfrom_handler(void *private_data);
704
705 static struct tevent_req *tdgram_bsd_recvfrom_send(TALLOC_CTX *mem_ctx,
706                                         struct tevent_context *ev,
707                                         struct tdgram_context *dgram)
708 {
709         struct tevent_req *req;
710         struct tdgram_bsd_recvfrom_state *state;
711         struct tdgram_bsd *bsds = tdgram_context_data(dgram, struct tdgram_bsd);
712         int ret;
713
714         req = tevent_req_create(mem_ctx, &state,
715                                 struct tdgram_bsd_recvfrom_state);
716         if (!req) {
717                 return NULL;
718         }
719
720         state->dgram    = dgram;
721         state->buf      = NULL;
722         state->len      = 0;
723         state->src      = NULL;
724
725         talloc_set_destructor(state, tdgram_bsd_recvfrom_destructor);
726
727         if (bsds->fd == -1) {
728                 tevent_req_error(req, ENOTCONN);
729                 goto post;
730         }
731
732         /*
733          * this is a fast path, not waiting for the
734          * socket to become explicit readable gains
735          * about 10%-20% performance in benchmark tests.
736          */
737         tdgram_bsd_recvfrom_handler(req);
738         if (!tevent_req_is_in_progress(req)) {
739                 goto post;
740         }
741
742         ret = tdgram_bsd_set_readable_handler(bsds, ev,
743                                               tdgram_bsd_recvfrom_handler,
744                                               req);
745         if (ret == -1) {
746                 tevent_req_error(req, errno);
747                 goto post;
748         }
749
750         return req;
751
752  post:
753         tevent_req_post(req, ev);
754         return req;
755 }
756
757 static void tdgram_bsd_recvfrom_handler(void *private_data)
758 {
759         struct tevent_req *req = talloc_get_type_abort(private_data,
760                                  struct tevent_req);
761         struct tdgram_bsd_recvfrom_state *state = tevent_req_data(req,
762                                         struct tdgram_bsd_recvfrom_state);
763         struct tdgram_context *dgram = state->dgram;
764         struct tdgram_bsd *bsds = tdgram_context_data(dgram, struct tdgram_bsd);
765         struct tsocket_address_bsd *bsda;
766         ssize_t ret;
767         struct sockaddr *sa = NULL;
768         socklen_t sa_len = 0;
769         int err;
770         bool retry;
771
772         ret = tsocket_bsd_pending(bsds->fd);
773         if (ret == 0) {
774                 /* retry later */
775                 return;
776         }
777         err = tsocket_bsd_error_from_errno(ret, errno, &retry);
778         if (retry) {
779                 /* retry later */
780                 return;
781         }
782         if (tevent_req_error(req, err)) {
783                 return;
784         }
785
786         state->buf = talloc_array(state, uint8_t, ret);
787         if (tevent_req_nomem(state->buf, req)) {
788                 return;
789         }
790         state->len = ret;
791
792         state->src = tsocket_address_create(state,
793                                             &tsocket_address_bsd_ops,
794                                             &bsda,
795                                             struct tsocket_address_bsd,
796                                             __location__ "bsd_recvfrom");
797         if (tevent_req_nomem(state->src, req)) {
798                 return;
799         }
800
801         ZERO_STRUCTP(bsda);
802
803         sa = &bsda->u.sa;
804         sa_len = sizeof(bsda->u.ss);
805         /*
806          * for unix sockets we can't use the size of sockaddr_storage
807          * we would get EINVAL
808          */
809         if (bsda->u.sa.sa_family == AF_UNIX) {
810                 sa_len = sizeof(bsda->u.un);
811         }
812
813         ret = recvfrom(bsds->fd, state->buf, state->len, 0, sa, &sa_len);
814         err = tsocket_bsd_error_from_errno(ret, errno, &retry);
815         if (retry) {
816                 /* retry later */
817                 return;
818         }
819         if (tevent_req_error(req, err)) {
820                 return;
821         }
822
823         if (ret != state->len) {
824                 tevent_req_error(req, EIO);
825                 return;
826         }
827
828         tevent_req_done(req);
829 }
830
831 static ssize_t tdgram_bsd_recvfrom_recv(struct tevent_req *req,
832                                         int *perrno,
833                                         TALLOC_CTX *mem_ctx,
834                                         uint8_t **buf,
835                                         struct tsocket_address **src)
836 {
837         struct tdgram_bsd_recvfrom_state *state = tevent_req_data(req,
838                                         struct tdgram_bsd_recvfrom_state);
839         ssize_t ret;
840
841         ret = tsocket_simple_int_recv(req, perrno);
842         if (ret == 0) {
843                 *buf = talloc_move(mem_ctx, &state->buf);
844                 ret = state->len;
845                 if (src) {
846                         *src = talloc_move(mem_ctx, &state->src);
847                 }
848         }
849
850         tevent_req_received(req);
851         return ret;
852 }
853
854 struct tdgram_bsd_sendto_state {
855         struct tdgram_context *dgram;
856
857         const uint8_t *buf;
858         size_t len;
859         const struct tsocket_address *dst;
860
861         ssize_t ret;
862 };
863
864 static int tdgram_bsd_sendto_destructor(struct tdgram_bsd_sendto_state *state)
865 {
866         struct tdgram_bsd *bsds = tdgram_context_data(state->dgram,
867                                   struct tdgram_bsd);
868
869         tdgram_bsd_set_writeable_handler(bsds, NULL, NULL, NULL);
870
871         return 0;
872 }
873
874 static void tdgram_bsd_sendto_handler(void *private_data);
875
876 static struct tevent_req *tdgram_bsd_sendto_send(TALLOC_CTX *mem_ctx,
877                                                  struct tevent_context *ev,
878                                                  struct tdgram_context *dgram,
879                                                  const uint8_t *buf,
880                                                  size_t len,
881                                                  const struct tsocket_address *dst)
882 {
883         struct tevent_req *req;
884         struct tdgram_bsd_sendto_state *state;
885         struct tdgram_bsd *bsds = tdgram_context_data(dgram, struct tdgram_bsd);
886         int ret;
887
888         req = tevent_req_create(mem_ctx, &state,
889                                 struct tdgram_bsd_sendto_state);
890         if (!req) {
891                 return NULL;
892         }
893
894         state->dgram    = dgram;
895         state->buf      = buf;
896         state->len      = len;
897         state->dst      = dst;
898         state->ret      = -1;
899
900         talloc_set_destructor(state, tdgram_bsd_sendto_destructor);
901
902         if (bsds->fd == -1) {
903                 tevent_req_error(req, ENOTCONN);
904                 goto post;
905         }
906
907         /*
908          * this is a fast path, not waiting for the
909          * socket to become explicit writeable gains
910          * about 10%-20% performance in benchmark tests.
911          */
912         tdgram_bsd_sendto_handler(req);
913         if (!tevent_req_is_in_progress(req)) {
914                 goto post;
915         }
916
917         ret = tdgram_bsd_set_writeable_handler(bsds, ev,
918                                                tdgram_bsd_sendto_handler,
919                                                req);
920         if (ret == -1) {
921                 tevent_req_error(req, errno);
922                 goto post;
923         }
924
925         return req;
926
927  post:
928         tevent_req_post(req, ev);
929         return req;
930 }
931
932 static void tdgram_bsd_sendto_handler(void *private_data)
933 {
934         struct tevent_req *req = talloc_get_type_abort(private_data,
935                                  struct tevent_req);
936         struct tdgram_bsd_sendto_state *state = tevent_req_data(req,
937                                         struct tdgram_bsd_sendto_state);
938         struct tdgram_context *dgram = state->dgram;
939         struct tdgram_bsd *bsds = tdgram_context_data(dgram, struct tdgram_bsd);
940         struct sockaddr *sa = NULL;
941         socklen_t sa_len = 0;
942         ssize_t ret;
943         int err;
944         bool retry;
945
946         if (state->dst) {
947                 struct tsocket_address_bsd *bsda =
948                         talloc_get_type(state->dst->private_data,
949                         struct tsocket_address_bsd);
950
951                 sa = &bsda->u.sa;
952                 sa_len = sizeof(bsda->u.ss);
953                 /*
954                  * for unix sockets we can't use the size of sockaddr_storage
955                  * we would get EINVAL
956                  */
957                 if (bsda->u.sa.sa_family == AF_UNIX) {
958                         sa_len = sizeof(bsda->u.un);
959                 }
960         }
961
962         ret = sendto(bsds->fd, state->buf, state->len, 0, sa, sa_len);
963         err = tsocket_bsd_error_from_errno(ret, errno, &retry);
964         if (retry) {
965                 /* retry later */
966                 return;
967         }
968         if (tevent_req_error(req, err)) {
969                 return;
970         }
971
972         state->ret = ret;
973
974         tevent_req_done(req);
975 }
976
977 static ssize_t tdgram_bsd_sendto_recv(struct tevent_req *req, int *perrno)
978 {
979         struct tdgram_bsd_sendto_state *state = tevent_req_data(req,
980                                         struct tdgram_bsd_sendto_state);
981         ssize_t ret;
982
983         ret = tsocket_simple_int_recv(req, perrno);
984         if (ret == 0) {
985                 ret = state->ret;
986         }
987
988         tevent_req_received(req);
989         return ret;
990 }
991
992 struct tdgram_bsd_disconnect_state {
993         uint8_t __dummy;
994 };
995
996 static struct tevent_req *tdgram_bsd_disconnect_send(TALLOC_CTX *mem_ctx,
997                                                      struct tevent_context *ev,
998                                                      struct tdgram_context *dgram)
999 {
1000         struct tdgram_bsd *bsds = tdgram_context_data(dgram, struct tdgram_bsd);
1001         struct tevent_req *req;
1002         struct tdgram_bsd_disconnect_state *state;
1003         int ret;
1004         int err;
1005         bool dummy;
1006
1007         req = tevent_req_create(mem_ctx, &state,
1008                                 struct tdgram_bsd_disconnect_state);
1009         if (req == NULL) {
1010                 return NULL;
1011         }
1012
1013         if (bsds->fd == -1) {
1014                 tevent_req_error(req, ENOTCONN);
1015                 goto post;
1016         }
1017
1018         ret = close(bsds->fd);
1019         bsds->fd = -1;
1020         err = tsocket_bsd_error_from_errno(ret, errno, &dummy);
1021         if (tevent_req_error(req, err)) {
1022                 goto post;
1023         }
1024
1025         tevent_req_done(req);
1026 post:
1027         tevent_req_post(req, ev);
1028         return req;
1029 }
1030
1031 static int tdgram_bsd_disconnect_recv(struct tevent_req *req,
1032                                       int *perrno)
1033 {
1034         int ret;
1035
1036         ret = tsocket_simple_int_recv(req, perrno);
1037
1038         tevent_req_received(req);
1039         return ret;
1040 }
1041
1042 static const struct tdgram_context_ops tdgram_bsd_ops = {
1043         .name                   = "bsd",
1044
1045         .recvfrom_send          = tdgram_bsd_recvfrom_send,
1046         .recvfrom_recv          = tdgram_bsd_recvfrom_recv,
1047
1048         .sendto_send            = tdgram_bsd_sendto_send,
1049         .sendto_recv            = tdgram_bsd_sendto_recv,
1050
1051         .disconnect_send        = tdgram_bsd_disconnect_send,
1052         .disconnect_recv        = tdgram_bsd_disconnect_recv,
1053 };
1054
1055 static int tdgram_bsd_destructor(struct tdgram_bsd *bsds)
1056 {
1057         TALLOC_FREE(bsds->fde);
1058         if (bsds->fd != -1) {
1059                 close(bsds->fd);
1060                 bsds->fd = -1;
1061         }
1062         return 0;
1063 }
1064
1065 static int tdgram_bsd_dgram_socket(const struct tsocket_address *local,
1066                                    const struct tsocket_address *remote,
1067                                    bool broadcast,
1068                                    TALLOC_CTX *mem_ctx,
1069                                    struct tdgram_context **_dgram,
1070                                    const char *location)
1071 {
1072         struct tsocket_address_bsd *lbsda =
1073                 talloc_get_type_abort(local->private_data,
1074                 struct tsocket_address_bsd);
1075         struct tsocket_address_bsd *rbsda = NULL;
1076         struct tdgram_context *dgram;
1077         struct tdgram_bsd *bsds;
1078         int fd;
1079         int ret;
1080         bool do_bind = false;
1081         bool do_reuseaddr = false;
1082         socklen_t sa_len = sizeof(lbsda->u.ss);
1083
1084         if (remote) {
1085                 rbsda = talloc_get_type_abort(remote->private_data,
1086                         struct tsocket_address_bsd);
1087         }
1088
1089         switch (lbsda->u.sa.sa_family) {
1090         case AF_UNIX:
1091                 if (broadcast) {
1092                         errno = EINVAL;
1093                         return -1;
1094                 }
1095                 if (lbsda->u.un.sun_path[0] != 0) {
1096                         do_reuseaddr = true;
1097                         do_bind = true;
1098                 }
1099                 /*
1100                  * for unix sockets we can't use the size of sockaddr_storage
1101                  * we would get EINVAL
1102                  */
1103                 sa_len = sizeof(lbsda->u.un);
1104                 break;
1105         case AF_INET:
1106                 if (lbsda->u.in.sin_port != 0) {
1107                         do_reuseaddr = true;
1108                         do_bind = true;
1109                 }
1110                 if (lbsda->u.in.sin_addr.s_addr == INADDR_ANY) {
1111                         do_bind = true;
1112                 }
1113                 break;
1114 #ifdef HAVE_IPV6
1115         case AF_INET6:
1116                 if (lbsda->u.in6.sin6_port != 0) {
1117                         do_reuseaddr = true;
1118                         do_bind = true;
1119                 }
1120                 if (memcmp(&in6addr_any,
1121                            &lbsda->u.in6.sin6_addr,
1122                            sizeof(in6addr_any)) != 0) {
1123                         do_bind = true;
1124                 }
1125                 break;
1126 #endif
1127         default:
1128                 errno = EINVAL;
1129                 return -1;
1130         }
1131
1132         fd = socket(lbsda->u.sa.sa_family, SOCK_DGRAM, 0);
1133         if (fd < 0) {
1134                 return fd;
1135         }
1136
1137         fd = tsocket_bsd_common_prepare_fd(fd, true);
1138         if (fd < 0) {
1139                 return fd;
1140         }
1141
1142         dgram = tdgram_context_create(mem_ctx,
1143                                       &tdgram_bsd_ops,
1144                                       &bsds,
1145                                       struct tdgram_bsd,
1146                                       location);
1147         if (!dgram) {
1148                 int saved_errno = errno;
1149                 close(fd);
1150                 errno = saved_errno;
1151                 return -1;
1152         }
1153         ZERO_STRUCTP(bsds);
1154         bsds->fd = fd;
1155         talloc_set_destructor(bsds, tdgram_bsd_destructor);
1156
1157         if (broadcast) {
1158                 int val = 1;
1159
1160                 ret = setsockopt(fd, SOL_SOCKET, SO_BROADCAST,
1161                                  (const void *)&val, sizeof(val));
1162                 if (ret == -1) {
1163                         int saved_errno = errno;
1164                         talloc_free(dgram);
1165                         errno = saved_errno;
1166                         return ret;
1167                 }
1168         }
1169
1170         if (do_reuseaddr) {
1171                 int val = 1;
1172
1173                 ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
1174                                  (const void *)&val, sizeof(val));
1175                 if (ret == -1) {
1176                         int saved_errno = errno;
1177                         talloc_free(dgram);
1178                         errno = saved_errno;
1179                         return ret;
1180                 }
1181         }
1182
1183         if (do_bind) {
1184                 ret = bind(fd, &lbsda->u.sa, sa_len);
1185                 if (ret == -1) {
1186                         int saved_errno = errno;
1187                         talloc_free(dgram);
1188                         errno = saved_errno;
1189                         return ret;
1190                 }
1191         }
1192
1193         if (rbsda) {
1194                 ret = connect(fd, &rbsda->u.sa, sa_len);
1195                 if (ret == -1) {
1196                         int saved_errno = errno;
1197                         talloc_free(dgram);
1198                         errno = saved_errno;
1199                         return ret;
1200                 }
1201         }
1202
1203         *_dgram = dgram;
1204         return 0;
1205 }
1206
1207 int _tdgram_inet_udp_socket(const struct tsocket_address *local,
1208                             const struct tsocket_address *remote,
1209                             TALLOC_CTX *mem_ctx,
1210                             struct tdgram_context **dgram,
1211                             const char *location)
1212 {
1213         struct tsocket_address_bsd *lbsda =
1214                 talloc_get_type_abort(local->private_data,
1215                 struct tsocket_address_bsd);
1216         int ret;
1217
1218         switch (lbsda->u.sa.sa_family) {
1219         case AF_INET:
1220                 break;
1221 #ifdef HAVE_IPV6
1222         case AF_INET6:
1223                 break;
1224 #endif
1225         default:
1226                 errno = EINVAL;
1227                 return -1;
1228         }
1229
1230         ret = tdgram_bsd_dgram_socket(local, remote, false,
1231                                       mem_ctx, dgram, location);
1232
1233         return ret;
1234 }
1235
1236 int _tdgram_unix_socket(const struct tsocket_address *local,
1237                         const struct tsocket_address *remote,
1238                         TALLOC_CTX *mem_ctx,
1239                         struct tdgram_context **dgram,
1240                         const char *location)
1241 {
1242         struct tsocket_address_bsd *lbsda =
1243                 talloc_get_type_abort(local->private_data,
1244                 struct tsocket_address_bsd);
1245         int ret;
1246
1247         switch (lbsda->u.sa.sa_family) {
1248         case AF_UNIX:
1249                 break;
1250         default:
1251                 errno = EINVAL;
1252                 return -1;
1253         }
1254
1255         ret = tdgram_bsd_dgram_socket(local, remote, false,
1256                                       mem_ctx, dgram, location);
1257
1258         return ret;
1259 }
1260