X-Git-Url: http://git.samba.org/samba.git/?a=blobdiff_plain;f=lib%2Ftsocket%2Ftsocket_bsd.c;h=bc7cfe3fe9e24aad603be171b79c3d6a0814d150;hb=4423aa59abda50c8b71815f922ea03e2009f9e50;hp=87586e08e3c672b1a17d3ad6dda24e782c79fa42;hpb=7f10bf980edef3ace1522d0c9c082b35e225c950;p=samba.git diff --git a/lib/tsocket/tsocket_bsd.c b/lib/tsocket/tsocket_bsd.c index 87586e08e3c..bc7cfe3fe9e 100644 --- a/lib/tsocket/tsocket_bsd.c +++ b/lib/tsocket/tsocket_bsd.c @@ -3,7 +3,7 @@ Copyright (C) Stefan Metzmacher 2009 - ** NOTE! The following LGPL license applies to the tevent + ** NOTE! The following LGPL license applies to the tsocket ** library. This does NOT imply that all of Samba is released ** under the LGPL @@ -24,7 +24,6 @@ #include "replace.h" #include "system/filesys.h" #include "system/network.h" -#include "system/filesys.h" #include "tsocket.h" #include "tsocket_internal.h" @@ -150,58 +149,48 @@ static int tsocket_bsd_common_prepare_fd(int fd, bool high_fd) static ssize_t tsocket_bsd_pending(int fd) { - int ret; + int ret, error; int value = 0; + socklen_t len; ret = ioctl(fd, FIONREAD, &value); if (ret == -1) { return ret; } - if (ret == 0) { - if (value == 0) { - int error=0; - socklen_t len = sizeof(error); - /* - * if no data is available check if the socket - * is in error state. For dgram sockets - * it's the way to return ICMP error messages - * of connected sockets to the caller. - */ - ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, - &error, &len); - if (ret == -1) { - return ret; - } - if (error != 0) { - errno = error; - return -1; - } - } + if (ret != 0) { + /* this should not be reached */ + errno = EIO; + return -1; + } + + if (value != 0) { return value; } - /* this should not be reached */ - errno = EIO; - return -1; + error = 0; + len = sizeof(error); + + /* + * if no data is available check if the socket is in error state. For + * dgram sockets it's the way to return ICMP error messages of + * connected sockets to the caller. + */ + ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len); + if (ret == -1) { + return ret; + } + if (error != 0) { + errno = error; + return -1; + } + return 0; } -static const struct tsocket_context_ops tsocket_context_bsd_ops; static const struct tsocket_address_ops tsocket_address_bsd_ops; -static int tsocket_context_bsd_set_option(const struct tsocket_context *sock, - const char *option, - bool force, - const char *value); - -struct tsocket_context_bsd { - bool close_on_disconnect; - int fd; - struct tevent_fd *fde; -}; - struct tsocket_address_bsd { - bool broadcast; + socklen_t sa_socklen; union { struct sockaddr sa; struct sockaddr_in in; @@ -213,34 +202,40 @@ struct tsocket_address_bsd { } u; }; -static int _tsocket_address_bsd_from_sockaddr(TALLOC_CTX *mem_ctx, - struct sockaddr *sa, - socklen_t sa_len, - struct tsocket_address **_addr, - const char *location) +int _tsocket_address_bsd_from_sockaddr(TALLOC_CTX *mem_ctx, + struct sockaddr *sa, + size_t sa_socklen, + struct tsocket_address **_addr, + const char *location) { struct tsocket_address *addr; struct tsocket_address_bsd *bsda; + if (sa_socklen < sizeof(sa->sa_family)) { + errno = EINVAL; + return -1; + } + switch (sa->sa_family) { case AF_UNIX: - if (sa_len < sizeof(struct sockaddr_un)) { - errno = EINVAL; - return -1; + if (sa_socklen > sizeof(struct sockaddr_un)) { + sa_socklen = sizeof(struct sockaddr_un); } break; case AF_INET: - if (sa_len < sizeof(struct sockaddr_in)) { + if (sa_socklen < sizeof(struct sockaddr_in)) { errno = EINVAL; return -1; } + sa_socklen = sizeof(struct sockaddr_in); break; #ifdef HAVE_IPV6 case AF_INET6: - if (sa_len < sizeof(struct sockaddr_in6)) { + if (sa_socklen < sizeof(struct sockaddr_in6)) { errno = EINVAL; return -1; } + sa_socklen = sizeof(struct sockaddr_in6); break; #endif default: @@ -248,7 +243,7 @@ static int _tsocket_address_bsd_from_sockaddr(TALLOC_CTX *mem_ctx, return -1; } - if (sa_len > sizeof(struct sockaddr_storage)) { + if (sa_socklen > sizeof(struct sockaddr_storage)) { errno = EINVAL; return -1; } @@ -265,12 +260,77 @@ static int _tsocket_address_bsd_from_sockaddr(TALLOC_CTX *mem_ctx, ZERO_STRUCTP(bsda); - memcpy(&bsda->u.ss, sa, sa_len); + memcpy(&bsda->u.ss, sa, sa_socklen); + + bsda->sa_socklen = sa_socklen; *_addr = addr; return 0; } +ssize_t tsocket_address_bsd_sockaddr(const struct tsocket_address *addr, + struct sockaddr *sa, + size_t sa_socklen) +{ + struct tsocket_address_bsd *bsda = talloc_get_type(addr->private_data, + struct tsocket_address_bsd); + + if (!bsda) { + errno = EINVAL; + return -1; + } + + if (sa_socklen < bsda->sa_socklen) { + errno = EINVAL; + return -1; + } + + if (sa_socklen > bsda->sa_socklen) { + memset(sa, 0, sa_socklen); + sa_socklen = bsda->sa_socklen; + } + + memcpy(sa, &bsda->u.ss, sa_socklen); + return sa_socklen; +} + +bool tsocket_address_is_inet(const struct tsocket_address *addr, const char *fam) +{ + struct tsocket_address_bsd *bsda = talloc_get_type(addr->private_data, + struct tsocket_address_bsd); + + if (!bsda) { + return false; + } + + switch (bsda->u.sa.sa_family) { + case AF_INET: + if (strcasecmp(fam, "ip") == 0) { + return true; + } + + if (strcasecmp(fam, "ipv4") == 0) { + return true; + } + + return false; +#ifdef HAVE_IPV6 + case AF_INET6: + if (strcasecmp(fam, "ip") == 0) { + return true; + } + + if (strcasecmp(fam, "ipv6") == 0) { + return true; + } + + return false; +#endif + } + + return false; +} + int _tsocket_address_inet_from_strings(TALLOC_CTX *mem_ctx, const char *fam, const char *addr, @@ -443,17 +503,21 @@ int tsocket_address_inet_set_port(struct tsocket_address *addr, return 0; } -void tsocket_address_inet_set_broadcast(struct tsocket_address *addr, - bool broadcast) +bool tsocket_address_is_unix(const struct tsocket_address *addr) { struct tsocket_address_bsd *bsda = talloc_get_type(addr->private_data, struct tsocket_address_bsd); if (!bsda) { - return; + return false; + } + + switch (bsda->u.sa.sa_family) { + case AF_UNIX: + return true; } - bsda->broadcast = broadcast; + return false; } int _tsocket_address_unix_from_path(TALLOC_CTX *mem_ctx, @@ -469,9 +533,14 @@ int _tsocket_address_unix_from_path(TALLOC_CTX *mem_ctx, path = ""; } + if (strlen(path) > sizeof(un.sun_path)-1) { + errno = ENAMETOOLONG; + return -1; + } + ZERO_STRUCT(un); un.sun_family = AF_UNIX; - strncpy(un.sun_path, path, sizeof(un.sun_path)); + strncpy(un.sun_path, path, sizeof(un.sun_path)-1); ret = _tsocket_address_bsd_from_sockaddr(mem_ctx, (struct sockaddr *)p, @@ -523,9 +592,11 @@ static char *tsocket_address_bsd_string(const struct tsocket_address *addr, case AF_INET: prefix = "ipv4"; break; +#ifdef HAVE_IPV6 case AF_INET6: prefix = "ipv6"; break; +#endif default: errno = EINVAL; return NULL; @@ -556,664 +627,766 @@ static struct tsocket_address *tsocket_address_bsd_copy(const struct tsocket_add ret = _tsocket_address_bsd_from_sockaddr(mem_ctx, &bsda->u.sa, - sizeof(bsda->u.ss), + bsda->sa_socklen, ©, location); if (ret != 0) { return NULL; } - tsocket_address_inet_set_broadcast(copy, bsda->broadcast); return copy; } -int _tsocket_context_bsd_wrap_existing(TALLOC_CTX *mem_ctx, - int fd, bool close_on_disconnect, - struct tsocket_context **_sock, - const char *location) -{ - struct tsocket_context *sock; - struct tsocket_context_bsd *bsds; - - sock = tsocket_context_create(mem_ctx, - &tsocket_context_bsd_ops, - &bsds, - struct tsocket_context_bsd, - location); - if (!sock) { - return -1; - } - - bsds->close_on_disconnect = close_on_disconnect; - bsds->fd = fd; - bsds->fde = NULL; - - *_sock = sock; - return 0; -} - -static int tsocket_address_bsd_create_socket(const struct tsocket_address *addr, - enum tsocket_type type, - TALLOC_CTX *mem_ctx, - struct tsocket_context **_sock, - const char *location) -{ - struct tsocket_address_bsd *bsda = talloc_get_type(addr->private_data, - struct tsocket_address_bsd); - struct tsocket_context *sock; - int bsd_type; - int fd; - int ret; - bool do_bind = false; - bool do_reuseaddr = false; - - switch (type) { - case TSOCKET_TYPE_STREAM: - if (bsda->broadcast) { - errno = EINVAL; - return -1; - } - bsd_type = SOCK_STREAM; - break; - default: - errno = EPROTONOSUPPORT; - return -1; - } - - switch (bsda->u.sa.sa_family) { - case AF_UNIX: - if (bsda->broadcast) { - errno = EINVAL; - return -1; - } - if (bsda->u.un.sun_path[0] != 0) { - do_bind = true; - } - break; - case AF_INET: - if (bsda->u.in.sin_port != 0) { - do_reuseaddr = true; - do_bind = true; - } - if (bsda->u.in.sin_addr.s_addr == INADDR_ANY) { - do_bind = true; - } - break; -#ifdef HAVE_IPV6 - case AF_INET6: - if (bsda->u.in6.sin6_port != 0) { - do_reuseaddr = true; - do_bind = true; - } - if (memcmp(&in6addr_any, - &bsda->u.in6.sin6_addr, - sizeof(in6addr_any)) != 0) { - do_bind = true; - } - break; -#endif - default: - errno = EINVAL; - return -1; - } - - fd = socket(bsda->u.sa.sa_family, bsd_type, 0); - if (fd < 0) { - return fd; - } - - fd = tsocket_common_prepare_fd(fd, true); - if (fd < 0) { - return fd; - } - - ret = _tsocket_context_bsd_wrap_existing(mem_ctx, fd, true, - &sock, location); - if (ret != 0) { - int saved_errno = errno; - close(fd); - errno = saved_errno; - return ret; - } - - if (bsda->broadcast) { - ret = tsocket_context_bsd_set_option(sock, "SO_BROADCAST", true, "1"); - if (ret != 0) { - int saved_errno = errno; - talloc_free(sock); - errno = saved_errno; - return ret; - } - } - - if (do_reuseaddr) { - ret = tsocket_context_bsd_set_option(sock, "SO_REUSEADDR", true, "1"); - if (ret != 0) { - int saved_errno = errno; - talloc_free(sock); - errno = saved_errno; - return ret; - } - } - - if (do_bind) { - ret = bind(fd, &bsda->u.sa, sizeof(bsda->u.ss)); - if (ret != 0) { - int saved_errno = errno; - talloc_free(sock); - errno = saved_errno; - return ret; - } - } - - *_sock = sock; - return 0; -} - static const struct tsocket_address_ops tsocket_address_bsd_ops = { .name = "bsd", .string = tsocket_address_bsd_string, .copy = tsocket_address_bsd_copy, - .create_socket = tsocket_address_bsd_create_socket }; -static void tsocket_context_bsd_fde_handler(struct tevent_context *ev, - struct tevent_fd *fde, - uint16_t flags, - void *private_data) +struct tdgram_bsd { + int fd; + + void *event_ptr; + struct tevent_fd *fde; + + void *readable_private; + void (*readable_handler)(void *private_data); + void *writeable_private; + void (*writeable_handler)(void *private_data); +}; + +static void tdgram_bsd_fde_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, + void *private_data) { - struct tsocket_context *sock = talloc_get_type(private_data, - struct tsocket_context); + struct tdgram_bsd *bsds = talloc_get_type_abort(private_data, + struct tdgram_bsd); if (flags & TEVENT_FD_WRITE) { - sock->event.write_handler(sock, sock->event.write_private); + bsds->writeable_handler(bsds->writeable_private); return; } if (flags & TEVENT_FD_READ) { - sock->event.read_handler(sock, sock->event.read_private); + if (!bsds->readable_handler) { + TEVENT_FD_NOT_READABLE(bsds->fde); + return; + } + bsds->readable_handler(bsds->readable_private); return; } } -static int tsocket_context_bsd_set_event_context(struct tsocket_context *sock, - struct tevent_context *ev) +static int tdgram_bsd_set_readable_handler(struct tdgram_bsd *bsds, + struct tevent_context *ev, + void (*handler)(void *private_data), + void *private_data) { - struct tsocket_context_bsd *bsds = talloc_get_type(sock->private_data, - struct tsocket_context_bsd); - - talloc_free(bsds->fde); - bsds->fde = NULL; - ZERO_STRUCT(sock->event); + if (ev == NULL) { + if (handler) { + errno = EINVAL; + return -1; + } + if (!bsds->readable_handler) { + return 0; + } + bsds->readable_handler = NULL; + bsds->readable_private = NULL; - if (!ev) { return 0; } - bsds->fde = tevent_add_fd(ev, bsds, - bsds->fd, - 0, - tsocket_context_bsd_fde_handler, - sock); - if (!bsds->fde) { - if (errno == 0) { - errno = ENOMEM; + /* read and write must use the same tevent_context */ + if (bsds->event_ptr != ev) { + if (bsds->readable_handler || bsds->writeable_handler) { + errno = EINVAL; + return -1; } - return -1; + bsds->event_ptr = NULL; + TALLOC_FREE(bsds->fde); } - sock->event.ctx = ev; - - return 0; -} + if (tevent_fd_get_flags(bsds->fde) == 0) { + TALLOC_FREE(bsds->fde); -static int tsocket_context_bsd_set_read_handler(struct tsocket_context *sock, - tsocket_event_handler_t handler, - void *private_data) -{ - struct tsocket_context_bsd *bsds = talloc_get_type(sock->private_data, - struct tsocket_context_bsd); + bsds->fde = tevent_add_fd(ev, bsds, + bsds->fd, TEVENT_FD_READ, + tdgram_bsd_fde_handler, + bsds); + if (!bsds->fde) { + errno = ENOMEM; + return -1; + } - if (sock->event.read_handler && !handler) { - TEVENT_FD_NOT_READABLE(bsds->fde); - } else if (!sock->event.read_handler && handler) { + /* cache the event context we're running on */ + bsds->event_ptr = ev; + } else if (!bsds->readable_handler) { TEVENT_FD_READABLE(bsds->fde); } - sock->event.read_handler = handler; - sock->event.read_private = private_data; + bsds->readable_handler = handler; + bsds->readable_private = private_data; return 0; } -static int tsocket_context_bsd_set_write_handler(struct tsocket_context *sock, - tsocket_event_handler_t handler, - void *private_data) +static int tdgram_bsd_set_writeable_handler(struct tdgram_bsd *bsds, + struct tevent_context *ev, + void (*handler)(void *private_data), + void *private_data) { - struct tsocket_context_bsd *bsds = talloc_get_type(sock->private_data, - struct tsocket_context_bsd); - - if (sock->event.write_handler && !handler) { - TEVENT_FD_NOT_WRITEABLE(bsds->fde); - } else if (!sock->event.write_handler && handler) { - TEVENT_FD_WRITEABLE(bsds->fde); - } + if (ev == NULL) { + if (handler) { + errno = EINVAL; + return -1; + } + if (!bsds->writeable_handler) { + return 0; + } + bsds->writeable_handler = NULL; + bsds->writeable_private = NULL; + TEVENT_FD_NOT_WRITEABLE(bsds->fde); + + return 0; + } + + /* read and write must use the same tevent_context */ + if (bsds->event_ptr != ev) { + if (bsds->readable_handler || bsds->writeable_handler) { + errno = EINVAL; + return -1; + } + bsds->event_ptr = NULL; + TALLOC_FREE(bsds->fde); + } - sock->event.write_handler = handler; - sock->event.write_private = private_data; + if (tevent_fd_get_flags(bsds->fde) == 0) { + TALLOC_FREE(bsds->fde); + + bsds->fde = tevent_add_fd(ev, bsds, + bsds->fd, TEVENT_FD_WRITE, + tdgram_bsd_fde_handler, + bsds); + if (!bsds->fde) { + errno = ENOMEM; + return -1; + } + + /* cache the event context we're running on */ + bsds->event_ptr = ev; + } else if (!bsds->writeable_handler) { + TEVENT_FD_WRITEABLE(bsds->fde); + } + + bsds->writeable_handler = handler; + bsds->writeable_private = private_data; return 0; } -static int tsocket_context_bsd_connect_to(struct tsocket_context *sock, - const struct tsocket_address *remote) +struct tdgram_bsd_recvfrom_state { + struct tdgram_context *dgram; + + uint8_t *buf; + size_t len; + struct tsocket_address *src; +}; + +static int tdgram_bsd_recvfrom_destructor(struct tdgram_bsd_recvfrom_state *state) { - struct tsocket_context_bsd *bsds = talloc_get_type(sock->private_data, - struct tsocket_context_bsd); - struct tsocket_address_bsd *bsda = talloc_get_type(remote->private_data, - struct tsocket_address_bsd); - int ret; + struct tdgram_bsd *bsds = tdgram_context_data(state->dgram, + struct tdgram_bsd); - ret = connect(bsds->fd, &bsda->u.sa, - sizeof(bsda->u.ss)); + tdgram_bsd_set_readable_handler(bsds, NULL, NULL, NULL); - return ret; + return 0; } -static int tsocket_context_bsd_listen_on(struct tsocket_context *sock, - int queue_size) +static void tdgram_bsd_recvfrom_handler(void *private_data); + +static struct tevent_req *tdgram_bsd_recvfrom_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tdgram_context *dgram) { - struct tsocket_context_bsd *bsds = talloc_get_type(sock->private_data, - struct tsocket_context_bsd); + struct tevent_req *req; + struct tdgram_bsd_recvfrom_state *state; + struct tdgram_bsd *bsds = tdgram_context_data(dgram, struct tdgram_bsd); int ret; - ret = listen(bsds->fd, queue_size); + req = tevent_req_create(mem_ctx, &state, + struct tdgram_bsd_recvfrom_state); + if (!req) { + return NULL; + } - return ret; -} + state->dgram = dgram; + state->buf = NULL; + state->len = 0; + state->src = NULL; -static int tsocket_context_bsd_accept_new(struct tsocket_context *sock, - TALLOC_CTX *mem_ctx, - struct tsocket_context **_new_sock, - const char *location) -{ - struct tsocket_context_bsd *bsds = talloc_get_type(sock->private_data, - struct tsocket_context_bsd); - int new_fd; - struct tsocket_context *new_sock; - struct tsocket_context_bsd *new_bsds; - struct sockaddr_storage ss; - void *p = &ss; - socklen_t ss_len = sizeof(ss); - - new_fd = accept(bsds->fd, (struct sockaddr *)p, &ss_len); - if (new_fd < 0) { - return new_fd; - } - - new_fd = tsocket_common_prepare_fd(new_fd, true); - if (new_fd < 0) { - return new_fd; - } - - new_sock = tsocket_context_create(mem_ctx, - &tsocket_context_bsd_ops, - &new_bsds, - struct tsocket_context_bsd, - location); - if (!new_sock) { - int saved_errno = errno; - close(new_fd); - errno = saved_errno; - return -1; + talloc_set_destructor(state, tdgram_bsd_recvfrom_destructor); + + if (bsds->fd == -1) { + tevent_req_error(req, ENOTCONN); + goto post; + } + + /* + * this is a fast path, not waiting for the + * socket to become explicit readable gains + * about 10%-20% performance in benchmark tests. + */ + tdgram_bsd_recvfrom_handler(req); + if (!tevent_req_is_in_progress(req)) { + goto post; + } + + ret = tdgram_bsd_set_readable_handler(bsds, ev, + tdgram_bsd_recvfrom_handler, + req); + if (ret == -1) { + tevent_req_error(req, errno); + goto post; } - new_bsds->close_on_disconnect = true; - new_bsds->fd = new_fd; - new_bsds->fde = NULL; + return req; - *_new_sock = new_sock; - return 0; + post: + tevent_req_post(req, ev); + return req; } -static ssize_t tsocket_context_bsd_pending_data(struct tsocket_context *sock) +static void tdgram_bsd_recvfrom_handler(void *private_data) { - struct tsocket_context_bsd *bsds = talloc_get_type(sock->private_data, - struct tsocket_context_bsd); - int ret; - int value = 0; + struct tevent_req *req = talloc_get_type_abort(private_data, + struct tevent_req); + struct tdgram_bsd_recvfrom_state *state = tevent_req_data(req, + struct tdgram_bsd_recvfrom_state); + struct tdgram_context *dgram = state->dgram; + struct tdgram_bsd *bsds = tdgram_context_data(dgram, struct tdgram_bsd); + struct tsocket_address_bsd *bsda; + ssize_t ret; + int err; + bool retry; - ret = ioctl(bsds->fd, FIONREAD, &value); - if (ret == -1) { - return ret; + ret = tsocket_bsd_pending(bsds->fd); + if (ret == 0) { + /* retry later */ + return; + } + err = tsocket_bsd_error_from_errno(ret, errno, &retry); + if (retry) { + /* retry later */ + return; + } + if (tevent_req_error(req, err)) { + return; } - if (ret == 0) { - if (value == 0) { - int error=0; - socklen_t len = sizeof(error); - /* - * if no data is available check if the socket - * is in error state. For dgram sockets - * it's the way to return ICMP error messages - * of connected sockets to the caller. - */ - ret = getsockopt(bsds->fd, SOL_SOCKET, SO_ERROR, - &error, &len); - if (ret == -1) { - return ret; - } - if (error != 0) { - errno = error; - return -1; - } - } - return value; + state->buf = talloc_array(state, uint8_t, ret); + if (tevent_req_nomem(state->buf, req)) { + return; } + state->len = ret; - /* this should not be reached */ - errno = EIO; - return -1; + state->src = tsocket_address_create(state, + &tsocket_address_bsd_ops, + &bsda, + struct tsocket_address_bsd, + __location__ "bsd_recvfrom"); + if (tevent_req_nomem(state->src, req)) { + return; + } + + ZERO_STRUCTP(bsda); + bsda->sa_socklen = sizeof(bsda->u.ss); + + ret = recvfrom(bsds->fd, state->buf, state->len, 0, + &bsda->u.sa, &bsda->sa_socklen); + err = tsocket_bsd_error_from_errno(ret, errno, &retry); + if (retry) { + /* retry later */ + return; + } + if (tevent_req_error(req, err)) { + return; + } + + /* + * Some systems (FreeBSD, see bug #7115) return too much + * bytes in tsocket_bsd_pending()/ioctl(fd, FIONREAD, ...), + * the return value includes some IP/UDP header bytes, + * while recvfrom() just returns the payload. + */ + state->buf = talloc_realloc(state, state->buf, uint8_t, ret); + if (tevent_req_nomem(state->buf, req)) { + return; + } + state->len = ret; + + tevent_req_done(req); } -static int tsocket_context_bsd_readv_data(struct tsocket_context *sock, - const struct iovec *vector, - size_t count) +static ssize_t tdgram_bsd_recvfrom_recv(struct tevent_req *req, + int *perrno, + TALLOC_CTX *mem_ctx, + uint8_t **buf, + struct tsocket_address **src) { - struct tsocket_context_bsd *bsds = talloc_get_type(sock->private_data, - struct tsocket_context_bsd); - int ret; + struct tdgram_bsd_recvfrom_state *state = tevent_req_data(req, + struct tdgram_bsd_recvfrom_state); + ssize_t ret; - ret = readv(bsds->fd, vector, count); + ret = tsocket_simple_int_recv(req, perrno); + if (ret == 0) { + *buf = talloc_move(mem_ctx, &state->buf); + ret = state->len; + if (src) { + *src = talloc_move(mem_ctx, &state->src); + } + } + tevent_req_received(req); return ret; } -static int tsocket_context_bsd_writev_data(struct tsocket_context *sock, - const struct iovec *vector, - size_t count) +struct tdgram_bsd_sendto_state { + struct tdgram_context *dgram; + + const uint8_t *buf; + size_t len; + const struct tsocket_address *dst; + + ssize_t ret; +}; + +static int tdgram_bsd_sendto_destructor(struct tdgram_bsd_sendto_state *state) { - struct tsocket_context_bsd *bsds = talloc_get_type(sock->private_data, - struct tsocket_context_bsd); - int ret; + struct tdgram_bsd *bsds = tdgram_context_data(state->dgram, + struct tdgram_bsd); - ret = writev(bsds->fd, vector, count); + tdgram_bsd_set_writeable_handler(bsds, NULL, NULL, NULL); - return ret; + return 0; } -static int tsocket_context_bsd_get_status(const struct tsocket_context *sock) +static void tdgram_bsd_sendto_handler(void *private_data); + +static struct tevent_req *tdgram_bsd_sendto_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tdgram_context *dgram, + const uint8_t *buf, + size_t len, + const struct tsocket_address *dst) { - struct tsocket_context_bsd *bsds = talloc_get_type(sock->private_data, - struct tsocket_context_bsd); + struct tevent_req *req; + struct tdgram_bsd_sendto_state *state; + struct tdgram_bsd *bsds = tdgram_context_data(dgram, struct tdgram_bsd); int ret; - int error=0; - socklen_t len = sizeof(error); + + req = tevent_req_create(mem_ctx, &state, + struct tdgram_bsd_sendto_state); + if (!req) { + return NULL; + } + + state->dgram = dgram; + state->buf = buf; + state->len = len; + state->dst = dst; + state->ret = -1; + + talloc_set_destructor(state, tdgram_bsd_sendto_destructor); if (bsds->fd == -1) { - errno = EPIPE; - return -1; + tevent_req_error(req, ENOTCONN); + goto post; } - ret = getsockopt(bsds->fd, SOL_SOCKET, SO_ERROR, &error, &len); - if (ret == -1) { - return ret; + /* + * this is a fast path, not waiting for the + * socket to become explicit writeable gains + * about 10%-20% performance in benchmark tests. + */ + tdgram_bsd_sendto_handler(req); + if (!tevent_req_is_in_progress(req)) { + goto post; } - if (error != 0) { - errno = error; - return -1; + + ret = tdgram_bsd_set_writeable_handler(bsds, ev, + tdgram_bsd_sendto_handler, + req); + if (ret == -1) { + tevent_req_error(req, errno); + goto post; } - return 0; + return req; + + post: + tevent_req_post(req, ev); + return req; } -static int tsocket_context_bsd_get_local_address(const struct tsocket_context *sock, - TALLOC_CTX *mem_ctx, - struct tsocket_address **_addr, - const char *location) +static void tdgram_bsd_sendto_handler(void *private_data) { - struct tsocket_context_bsd *bsds = talloc_get_type(sock->private_data, - struct tsocket_context_bsd); - struct tsocket_address *addr; - struct tsocket_address_bsd *bsda; + struct tevent_req *req = talloc_get_type_abort(private_data, + struct tevent_req); + struct tdgram_bsd_sendto_state *state = tevent_req_data(req, + struct tdgram_bsd_sendto_state); + struct tdgram_context *dgram = state->dgram; + struct tdgram_bsd *bsds = tdgram_context_data(dgram, struct tdgram_bsd); + struct sockaddr *sa = NULL; + socklen_t sa_socklen = 0; ssize_t ret; - socklen_t sa_len; + int err; + bool retry; - addr = tsocket_address_create(mem_ctx, - &tsocket_address_bsd_ops, - &bsda, - struct tsocket_address_bsd, - location); - if (!addr) { - return -1; - } + if (state->dst) { + struct tsocket_address_bsd *bsda = + talloc_get_type(state->dst->private_data, + struct tsocket_address_bsd); - ZERO_STRUCTP(bsda); + sa = &bsda->u.sa; + sa_socklen = bsda->sa_socklen; + } - sa_len = sizeof(bsda->u.ss); - ret = getsockname(bsds->fd, &bsda->u.sa, &sa_len); - if (ret < 0) { - int saved_errno = errno; - talloc_free(addr); - errno = saved_errno; - return ret; + ret = sendto(bsds->fd, state->buf, state->len, 0, sa, sa_socklen); + err = tsocket_bsd_error_from_errno(ret, errno, &retry); + if (retry) { + /* retry later */ + return; + } + if (tevent_req_error(req, err)) { + return; } - *_addr = addr; - return 0; + state->ret = ret; + + tevent_req_done(req); } -static int tsocket_context_bsd_get_remote_address(const struct tsocket_context *sock, - TALLOC_CTX *mem_ctx, - struct tsocket_address **_addr, - const char *location) +static ssize_t tdgram_bsd_sendto_recv(struct tevent_req *req, int *perrno) { - struct tsocket_context_bsd *bsds = talloc_get_type(sock->private_data, - struct tsocket_context_bsd); - struct tsocket_address *addr; - struct tsocket_address_bsd *bsda; + struct tdgram_bsd_sendto_state *state = tevent_req_data(req, + struct tdgram_bsd_sendto_state); ssize_t ret; - socklen_t sa_len; - addr = tsocket_address_create(mem_ctx, - &tsocket_address_bsd_ops, - &bsda, - struct tsocket_address_bsd, - location); - if (!addr) { - return -1; + ret = tsocket_simple_int_recv(req, perrno); + if (ret == 0) { + ret = state->ret; } - ZERO_STRUCTP(bsda); + tevent_req_received(req); + return ret; +} - sa_len = sizeof(bsda->u.ss); - ret = getpeername(bsds->fd, &bsda->u.sa, &sa_len); - if (ret < 0) { - int saved_errno = errno; - talloc_free(addr); - errno = saved_errno; - return ret; +struct tdgram_bsd_disconnect_state { + uint8_t __dummy; +}; + +static struct tevent_req *tdgram_bsd_disconnect_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tdgram_context *dgram) +{ + struct tdgram_bsd *bsds = tdgram_context_data(dgram, struct tdgram_bsd); + struct tevent_req *req; + struct tdgram_bsd_disconnect_state *state; + int ret; + int err; + bool dummy; + + req = tevent_req_create(mem_ctx, &state, + struct tdgram_bsd_disconnect_state); + if (req == NULL) { + return NULL; } - *_addr = addr; - return 0; + if (bsds->fd == -1) { + tevent_req_error(req, ENOTCONN); + goto post; + } + + TALLOC_FREE(bsds->fde); + ret = close(bsds->fd); + bsds->fd = -1; + err = tsocket_bsd_error_from_errno(ret, errno, &dummy); + if (tevent_req_error(req, err)) { + goto post; + } + + tevent_req_done(req); +post: + tevent_req_post(req, ev); + return req; } -static const struct tsocket_context_bsd_option { - const char *name; - int level; - int optnum; - int optval; -} tsocket_context_bsd_options[] = { -#define TSOCKET_OPTION(_level, _optnum, _optval) { \ - .name = #_optnum, \ - .level = _level, \ - .optnum = _optnum, \ - .optval = _optval \ +static int tdgram_bsd_disconnect_recv(struct tevent_req *req, + int *perrno) +{ + int ret; + + ret = tsocket_simple_int_recv(req, perrno); + + tevent_req_received(req); + return ret; } - TSOCKET_OPTION(SOL_SOCKET, SO_REUSEADDR, 0), - TSOCKET_OPTION(SOL_SOCKET, SO_BROADCAST, 0) + +static const struct tdgram_context_ops tdgram_bsd_ops = { + .name = "bsd", + + .recvfrom_send = tdgram_bsd_recvfrom_send, + .recvfrom_recv = tdgram_bsd_recvfrom_recv, + + .sendto_send = tdgram_bsd_sendto_send, + .sendto_recv = tdgram_bsd_sendto_recv, + + .disconnect_send = tdgram_bsd_disconnect_send, + .disconnect_recv = tdgram_bsd_disconnect_recv, }; -static int tsocket_context_bsd_get_option(const struct tsocket_context *sock, - const char *option, - TALLOC_CTX *mem_ctx, - char **_value) +static int tdgram_bsd_destructor(struct tdgram_bsd *bsds) { - struct tsocket_context_bsd *bsds = talloc_get_type(sock->private_data, - struct tsocket_context_bsd); - const struct tsocket_context_bsd_option *opt = NULL; - uint32_t i; - int optval; - socklen_t optval_len = sizeof(optval); - char *value; + TALLOC_FREE(bsds->fde); + if (bsds->fd != -1) { + close(bsds->fd); + bsds->fd = -1; + } + return 0; +} + +static int tdgram_bsd_dgram_socket(const struct tsocket_address *local, + const struct tsocket_address *remote, + bool broadcast, + TALLOC_CTX *mem_ctx, + struct tdgram_context **_dgram, + const char *location) +{ + struct tsocket_address_bsd *lbsda = + talloc_get_type_abort(local->private_data, + struct tsocket_address_bsd); + struct tsocket_address_bsd *rbsda = NULL; + struct tdgram_context *dgram; + struct tdgram_bsd *bsds; + int fd; int ret; + bool do_bind = false; + bool do_reuseaddr = false; + bool do_ipv6only = false; + bool is_inet = false; + int sa_fam = lbsda->u.sa.sa_family; - for (i=0; i < ARRAY_SIZE(tsocket_context_bsd_options); i++) { - if (strcmp(option, tsocket_context_bsd_options[i].name) != 0) { - continue; - } + if (remote) { + rbsda = talloc_get_type_abort(remote->private_data, + struct tsocket_address_bsd); + } - opt = &tsocket_context_bsd_options[i]; + switch (lbsda->u.sa.sa_family) { + case AF_UNIX: + if (broadcast) { + errno = EINVAL; + return -1; + } + if (lbsda->u.un.sun_path[0] != 0) { + do_reuseaddr = true; + do_bind = true; + } + break; + case AF_INET: + if (lbsda->u.in.sin_port != 0) { + do_reuseaddr = true; + do_bind = true; + } + if (lbsda->u.in.sin_addr.s_addr != INADDR_ANY) { + do_bind = true; + } + is_inet = true; + break; +#ifdef HAVE_IPV6 + case AF_INET6: + if (lbsda->u.in6.sin6_port != 0) { + do_reuseaddr = true; + do_bind = true; + } + if (memcmp(&in6addr_any, + &lbsda->u.in6.sin6_addr, + sizeof(in6addr_any)) != 0) { + do_bind = true; + } + is_inet = true; + do_ipv6only = true; break; +#endif + default: + errno = EINVAL; + return -1; } - if (!opt) { - goto nosys; + if (!do_bind && is_inet && rbsda) { + sa_fam = rbsda->u.sa.sa_family; + switch (sa_fam) { + case AF_INET: + do_ipv6only = false; + break; +#ifdef HAVE_IPV6 + case AF_INET6: + do_ipv6only = true; + break; +#endif + } } - ret = getsockopt(bsds->fd, opt->level, opt->optnum, - (void *)&optval, &optval_len); - if (ret != 0) { - return ret; + fd = socket(sa_fam, SOCK_DGRAM, 0); + if (fd < 0) { + return -1; } - if (optval_len != sizeof(optval)) { - value = NULL; - } if (opt->optval != 0) { - if (optval == opt->optval) { - value = talloc_strdup(mem_ctx, "1"); - } else { - value = talloc_strdup(mem_ctx, "0"); - } - if (!value) { - goto nomem; + fd = tsocket_bsd_common_prepare_fd(fd, true); + if (fd < 0) { + return -1; + } + + dgram = tdgram_context_create(mem_ctx, + &tdgram_bsd_ops, + &bsds, + struct tdgram_bsd, + location); + if (!dgram) { + int saved_errno = errno; + close(fd); + errno = saved_errno; + return -1; + } + ZERO_STRUCTP(bsds); + bsds->fd = fd; + talloc_set_destructor(bsds, tdgram_bsd_destructor); + +#ifdef HAVE_IPV6 + if (do_ipv6only) { + int val = 1; + + ret = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, + (const void *)&val, sizeof(val)); + if (ret == -1) { + int saved_errno = errno; + talloc_free(dgram); + errno = saved_errno; + return -1; } - } else { - value = talloc_asprintf(mem_ctx, "%d", optval); - if (!value) { - goto nomem; + } +#endif + + if (broadcast) { + int val = 1; + + ret = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, + (const void *)&val, sizeof(val)); + if (ret == -1) { + int saved_errno = errno; + talloc_free(dgram); + errno = saved_errno; + return -1; } } - *_value = value; - return 0; - - nomem: - errno = ENOMEM; - return -1; - nosys: - errno = ENOSYS; - return -1; -} - -static int tsocket_context_bsd_set_option(const struct tsocket_context *sock, - const char *option, - bool force, - const char *value) -{ - struct tsocket_context_bsd *bsds = talloc_get_type(sock->private_data, - struct tsocket_context_bsd); - const struct tsocket_context_bsd_option *opt = NULL; - uint32_t i; - int optval; - int ret; + if (do_reuseaddr) { + int val = 1; - for (i=0; i < ARRAY_SIZE(tsocket_context_bsd_options); i++) { - if (strcmp(option, tsocket_context_bsd_options[i].name) != 0) { - continue; + ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, + (const void *)&val, sizeof(val)); + if (ret == -1) { + int saved_errno = errno; + talloc_free(dgram); + errno = saved_errno; + return -1; } - - opt = &tsocket_context_bsd_options[i]; - break; } - if (!opt) { - goto nosys; + if (do_bind) { + ret = bind(fd, &lbsda->u.sa, lbsda->sa_socklen); + if (ret == -1) { + int saved_errno = errno; + talloc_free(dgram); + errno = saved_errno; + return -1; + } } - if (value) { - if (opt->optval != 0) { + if (rbsda) { + if (rbsda->u.sa.sa_family != sa_fam) { + talloc_free(dgram); errno = EINVAL; return -1; } - optval = atoi(value); - } else { - optval = opt->optval; - } - - ret = setsockopt(bsds->fd, opt->level, opt->optnum, - (const void *)&optval, sizeof(optval)); - if (ret != 0) { - if (!force) { - errno = 0; - return 0; + ret = connect(fd, &rbsda->u.sa, rbsda->sa_socklen); + if (ret == -1) { + int saved_errno = errno; + talloc_free(dgram); + errno = saved_errno; + return -1; } - return ret; } + *_dgram = dgram; return 0; - - nosys: - if (!force) { - return 0; - } - - errno = ENOSYS; - return -1; } -static void tsocket_context_bsd_disconnect(struct tsocket_context *sock) +int _tdgram_inet_udp_socket(const struct tsocket_address *local, + const struct tsocket_address *remote, + TALLOC_CTX *mem_ctx, + struct tdgram_context **dgram, + const char *location) { - struct tsocket_context_bsd *bsds = talloc_get_type(sock->private_data, - struct tsocket_context_bsd); - - tsocket_context_bsd_set_event_context(sock, NULL); + struct tsocket_address_bsd *lbsda = + talloc_get_type_abort(local->private_data, + struct tsocket_address_bsd); + int ret; - if (bsds->fd != -1) { - if (bsds->close_on_disconnect) { - close(bsds->fd); - } - bsds->fd = -1; + switch (lbsda->u.sa.sa_family) { + case AF_INET: + break; +#ifdef HAVE_IPV6 + case AF_INET6: + break; +#endif + default: + errno = EINVAL; + return -1; } -} -static const struct tsocket_context_ops tsocket_context_bsd_ops = { - .name = "bsd", - - .set_event_context = tsocket_context_bsd_set_event_context, - .set_read_handler = tsocket_context_bsd_set_read_handler, - .set_write_handler = tsocket_context_bsd_set_write_handler, + ret = tdgram_bsd_dgram_socket(local, remote, false, + mem_ctx, dgram, location); - .connect_to = tsocket_context_bsd_connect_to, - .listen_on = tsocket_context_bsd_listen_on, - .accept_new = tsocket_context_bsd_accept_new, + return ret; +} - .pending_data = tsocket_context_bsd_pending_data, - .readv_data = tsocket_context_bsd_readv_data, - .writev_data = tsocket_context_bsd_writev_data, +int _tdgram_unix_socket(const struct tsocket_address *local, + const struct tsocket_address *remote, + TALLOC_CTX *mem_ctx, + struct tdgram_context **dgram, + const char *location) +{ + struct tsocket_address_bsd *lbsda = + talloc_get_type_abort(local->private_data, + struct tsocket_address_bsd); + int ret; - .get_status = tsocket_context_bsd_get_status, - .get_local_address = tsocket_context_bsd_get_local_address, - .get_remote_address = tsocket_context_bsd_get_remote_address, + switch (lbsda->u.sa.sa_family) { + case AF_UNIX: + break; + default: + errno = EINVAL; + return -1; + } - .get_option = tsocket_context_bsd_get_option, - .set_option = tsocket_context_bsd_set_option, + ret = tdgram_bsd_dgram_socket(local, remote, false, + mem_ctx, dgram, location); - .disconnect = tsocket_context_bsd_disconnect -}; + return ret; +} -struct tdgram_bsd { +struct tstream_bsd { int fd; void *event_ptr; @@ -1223,18 +1396,15 @@ struct tdgram_bsd { void (*readable_handler)(void *private_data); void *writeable_private; void (*writeable_handler)(void *private_data); - - struct tevent_req *read_req; - struct tevent_req *write_req; }; -static void tdgram_bsd_fde_handler(struct tevent_context *ev, - struct tevent_fd *fde, - uint16_t flags, - void *private_data) +static void tstream_bsd_fde_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, + void *private_data) { - struct tdgram_bsd *bsds = talloc_get_type_abort(private_data, - struct tdgram_bsd); + struct tstream_bsd *bsds = talloc_get_type_abort(private_data, + struct tstream_bsd); if (flags & TEVENT_FD_WRITE) { bsds->writeable_handler(bsds->writeable_private); @@ -1242,6 +1412,10 @@ static void tdgram_bsd_fde_handler(struct tevent_context *ev, } if (flags & TEVENT_FD_READ) { if (!bsds->readable_handler) { + if (bsds->writeable_handler) { + bsds->writeable_handler(bsds->writeable_private); + return; + } TEVENT_FD_NOT_READABLE(bsds->fde); return; } @@ -1250,10 +1424,10 @@ static void tdgram_bsd_fde_handler(struct tevent_context *ev, } } -static int tdgram_bsd_set_readable_handler(struct tdgram_bsd *bsds, - struct tevent_context *ev, - void (*handler)(void *private_data), - void *private_data) +static int tstream_bsd_set_readable_handler(struct tstream_bsd *bsds, + struct tevent_context *ev, + void (*handler)(void *private_data), + void *private_data) { if (ev == NULL) { if (handler) { @@ -1279,12 +1453,15 @@ static int tdgram_bsd_set_readable_handler(struct tdgram_bsd *bsds, TALLOC_FREE(bsds->fde); } - if (bsds->fde == NULL) { + if (tevent_fd_get_flags(bsds->fde) == 0) { + TALLOC_FREE(bsds->fde); + bsds->fde = tevent_add_fd(ev, bsds, bsds->fd, TEVENT_FD_READ, - tdgram_bsd_fde_handler, + tstream_bsd_fde_handler, bsds); if (!bsds->fde) { + errno = ENOMEM; return -1; } @@ -1300,10 +1477,10 @@ static int tdgram_bsd_set_readable_handler(struct tdgram_bsd *bsds, return 0; } -static int tdgram_bsd_set_writeable_handler(struct tdgram_bsd *bsds, - struct tevent_context *ev, - void (*handler)(void *private_data), - void *private_data) +static int tstream_bsd_set_writeable_handler(struct tstream_bsd *bsds, + struct tevent_context *ev, + void (*handler)(void *private_data), + void *private_data) { if (ev == NULL) { if (handler) { @@ -1330,19 +1507,25 @@ static int tdgram_bsd_set_writeable_handler(struct tdgram_bsd *bsds, TALLOC_FREE(bsds->fde); } - if (bsds->fde == NULL) { + if (tevent_fd_get_flags(bsds->fde) == 0) { + TALLOC_FREE(bsds->fde); + bsds->fde = tevent_add_fd(ev, bsds, - bsds->fd, TEVENT_FD_WRITE, - tdgram_bsd_fde_handler, + bsds->fd, + TEVENT_FD_READ | TEVENT_FD_WRITE, + tstream_bsd_fde_handler, bsds); if (!bsds->fde) { + errno = ENOMEM; return -1; } /* cache the event context we're running on */ bsds->event_ptr = ev; } else if (!bsds->writeable_handler) { - TEVENT_FD_WRITEABLE(bsds->fde); + uint16_t flags = tevent_fd_get_flags(bsds->fde); + flags |= TEVENT_FD_READ | TEVENT_FD_WRITE; + tevent_fd_set_flags(bsds->fde, flags); } bsds->writeable_handler = handler; @@ -1351,54 +1534,71 @@ static int tdgram_bsd_set_writeable_handler(struct tdgram_bsd *bsds, return 0; } -struct tdgram_bsd_recvfrom_state { - struct tdgram_context *dgram; +static ssize_t tstream_bsd_pending_bytes(struct tstream_context *stream) +{ + struct tstream_bsd *bsds = tstream_context_data(stream, + struct tstream_bsd); + ssize_t ret; - uint8_t *buf; - size_t len; - struct tsocket_address *src; + if (bsds->fd == -1) { + errno = ENOTCONN; + return -1; + } + + ret = tsocket_bsd_pending(bsds->fd); + + return ret; +} + +struct tstream_bsd_readv_state { + struct tstream_context *stream; + + struct iovec *vector; + size_t count; + + int ret; }; -static int tdgram_bsd_recvfrom_destructor(struct tdgram_bsd_recvfrom_state *state) +static int tstream_bsd_readv_destructor(struct tstream_bsd_readv_state *state) { - struct tdgram_bsd *bsds = tdgram_context_data(state->dgram, - struct tdgram_bsd); + struct tstream_bsd *bsds = tstream_context_data(state->stream, + struct tstream_bsd); - bsds->read_req = NULL; - tdgram_bsd_set_readable_handler(bsds, NULL, NULL, NULL); + tstream_bsd_set_readable_handler(bsds, NULL, NULL, NULL); return 0; } -static void tdgram_bsd_recvfrom_handler(void *private_data); +static void tstream_bsd_readv_handler(void *private_data); -static struct tevent_req *tdgram_bsd_recvfrom_send(TALLOC_CTX *mem_ctx, +static struct tevent_req *tstream_bsd_readv_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, - struct tdgram_context *dgram) + struct tstream_context *stream, + struct iovec *vector, + size_t count) { struct tevent_req *req; - struct tdgram_bsd_recvfrom_state *state; - struct tdgram_bsd *bsds = tdgram_context_data(dgram, struct tdgram_bsd); + struct tstream_bsd_readv_state *state; + struct tstream_bsd *bsds = tstream_context_data(stream, struct tstream_bsd); int ret; req = tevent_req_create(mem_ctx, &state, - struct tdgram_bsd_recvfrom_state); + struct tstream_bsd_readv_state); if (!req) { return NULL; } - state->dgram = dgram; - state->buf = NULL; - state->len = 0; - state->src = NULL; - - if (bsds->read_req) { - tevent_req_error(req, EBUSY); + state->stream = stream; + /* we make a copy of the vector so that we can modify it */ + state->vector = talloc_array(state, struct iovec, count); + if (tevent_req_nomem(state->vector, req)) { goto post; } - bsds->read_req = req; + memcpy(state->vector, vector, sizeof(struct iovec)*count); + state->count = count; + state->ret = 0; - talloc_set_destructor(state, tdgram_bsd_recvfrom_destructor); + talloc_set_destructor(state, tstream_bsd_readv_destructor); if (bsds->fd == -1) { tevent_req_error(req, ENOTCONN); @@ -1410,13 +1610,13 @@ static struct tevent_req *tdgram_bsd_recvfrom_send(TALLOC_CTX *mem_ctx, * socket to become explicit readable gains * about 10%-20% performance in benchmark tests. */ - tdgram_bsd_recvfrom_handler(req); + tstream_bsd_readv_handler(req); if (!tevent_req_is_in_progress(req)) { goto post; } - ret = tdgram_bsd_set_readable_handler(bsds, ev, - tdgram_bsd_recvfrom_handler, + ret = tstream_bsd_set_readable_handler(bsds, ev, + tstream_bsd_readv_handler, req); if (ret == -1) { tevent_req_error(req, errno); @@ -1430,24 +1630,22 @@ static struct tevent_req *tdgram_bsd_recvfrom_send(TALLOC_CTX *mem_ctx, return req; } -static void tdgram_bsd_recvfrom_handler(void *private_data) +static void tstream_bsd_readv_handler(void *private_data) { struct tevent_req *req = talloc_get_type_abort(private_data, struct tevent_req); - struct tdgram_bsd_recvfrom_state *state = tevent_req_data(req, - struct tdgram_bsd_recvfrom_state); - struct tdgram_context *dgram = state->dgram; - struct tdgram_bsd *bsds = tdgram_context_data(dgram, struct tdgram_bsd); - struct tsocket_address_bsd *bsda; - ssize_t ret; - struct sockaddr *sa = NULL; - socklen_t sa_len = 0; + struct tstream_bsd_readv_state *state = tevent_req_data(req, + struct tstream_bsd_readv_state); + struct tstream_context *stream = state->stream; + struct tstream_bsd *bsds = tstream_context_data(stream, struct tstream_bsd); + int ret; int err; bool retry; - ret = tsocket_bsd_pending(bsds->fd); + ret = readv(bsds->fd, state->vector, state->count); if (ret == 0) { - /* retry later */ + /* propagate end of file */ + tevent_req_error(req, EPIPE); return; } err = tsocket_bsd_error_from_errno(ret, errno, &retry); @@ -1459,120 +1657,108 @@ static void tdgram_bsd_recvfrom_handler(void *private_data) return; } - state->buf = talloc_array(state, uint8_t, ret); - if (tevent_req_nomem(state->buf, req)) { - return; - } - state->len = ret; + state->ret += ret; - state->src = tsocket_address_create(state, - &tsocket_address_bsd_ops, - &bsda, - struct tsocket_address_bsd, - __location__ "bsd_recvfrom"); - if (tevent_req_nomem(state->src, req)) { - return; + while (ret > 0) { + if (ret < state->vector[0].iov_len) { + uint8_t *base; + base = (uint8_t *)state->vector[0].iov_base; + base += ret; + state->vector[0].iov_base = (void *)base; + state->vector[0].iov_len -= ret; + break; + } + ret -= state->vector[0].iov_len; + state->vector += 1; + state->count -= 1; } - ZERO_STRUCTP(bsda); - - sa = &bsda->u.sa; - sa_len = sizeof(bsda->u.ss); - - ret = recvfrom(bsds->fd, state->buf, state->len, 0, sa, &sa_len); - err = tsocket_error_from_errno(ret, errno, &retry); - if (retry) { - /* retry later */ - return; - } - if (tevent_req_error(req, err)) { - return; + /* + * there're maybe some empty vectors at the end + * which we need to skip, otherwise we would get + * ret == 0 from the readv() call and return EPIPE + */ + while (state->count > 0) { + if (state->vector[0].iov_len > 0) { + break; + } + state->vector += 1; + state->count -= 1; } - if (ret != state->len) { - tevent_req_error(req, EIO); + if (state->count > 0) { + /* we have more to read */ return; } tevent_req_done(req); } -static ssize_t tdgram_bsd_recvfrom_recv(struct tevent_req *req, - int *perrno, - TALLOC_CTX *mem_ctx, - uint8_t **buf, - struct tsocket_address **src) +static int tstream_bsd_readv_recv(struct tevent_req *req, + int *perrno) { - struct tdgram_bsd_recvfrom_state *state = tevent_req_data(req, - struct tdgram_bsd_recvfrom_state); - ssize_t ret; + struct tstream_bsd_readv_state *state = tevent_req_data(req, + struct tstream_bsd_readv_state); + int ret; ret = tsocket_simple_int_recv(req, perrno); if (ret == 0) { - *buf = talloc_move(mem_ctx, &state->buf); - ret = state->len; - if (src) { - *src = talloc_move(mem_ctx, &state->src); - } + ret = state->ret; } tevent_req_received(req); return ret; } -struct tdgram_bsd_sendto_state { - struct tdgram_context *dgram; +struct tstream_bsd_writev_state { + struct tstream_context *stream; - const uint8_t *buf; - size_t len; - const struct tsocket_address *dst; + struct iovec *vector; + size_t count; - ssize_t ret; + int ret; }; -static int tdgram_bsd_sendto_destructor(struct tdgram_bsd_sendto_state *state) +static int tstream_bsd_writev_destructor(struct tstream_bsd_writev_state *state) { - struct tdgram_bsd *bsds = tdgram_context_data(state->dgram, - struct tdgram_bsd); + struct tstream_bsd *bsds = tstream_context_data(state->stream, + struct tstream_bsd); + + tstream_bsd_set_writeable_handler(bsds, NULL, NULL, NULL); - bsds->write_req = NULL; - tdgram_bsd_set_writeable_handler(bsds, NULL, NULL, NULL); return 0; } -static void tdgram_bsd_sendto_handler(void *private_data); +static void tstream_bsd_writev_handler(void *private_data); -static struct tevent_req *tdgram_bsd_sendto_send(TALLOC_CTX *mem_ctx, +static struct tevent_req *tstream_bsd_writev_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, - struct tdgram_context *dgram, - const uint8_t *buf, - size_t len, - const struct tsocket_address *dst) + struct tstream_context *stream, + const struct iovec *vector, + size_t count) { struct tevent_req *req; - struct tdgram_bsd_sendto_state *state; - struct tdgram_bsd *bsds = tdgram_context_data(dgram, struct tdgram_bsd); + struct tstream_bsd_writev_state *state; + struct tstream_bsd *bsds = tstream_context_data(stream, struct tstream_bsd); int ret; req = tevent_req_create(mem_ctx, &state, - struct tdgram_bsd_sendto_state); + struct tstream_bsd_writev_state); if (!req) { return NULL; } - state->dgram = dgram; - state->buf = buf; - state->len = len; - state->dst = dst; - state->ret = -1; - - if (bsds->write_req) { - tevent_req_error(req, EBUSY); + state->stream = stream; + /* we make a copy of the vector so that we can modify it */ + state->vector = talloc_array(state, struct iovec, count); + if (tevent_req_nomem(state->vector, req)) { goto post; } - bsds->write_req = req; + memcpy(state->vector, vector, sizeof(struct iovec)*count); + state->count = count; + state->ret = 0; - talloc_set_destructor(state, tdgram_bsd_sendto_destructor); + talloc_set_destructor(state, tstream_bsd_writev_destructor); if (bsds->fd == -1) { tevent_req_error(req, ENOTCONN); @@ -1584,13 +1770,13 @@ static struct tevent_req *tdgram_bsd_sendto_send(TALLOC_CTX *mem_ctx, * socket to become explicit writeable gains * about 10%-20% performance in benchmark tests. */ - tdgram_bsd_sendto_handler(req); + tstream_bsd_writev_handler(req); if (!tevent_req_is_in_progress(req)) { goto post; } - ret = tdgram_bsd_set_writeable_handler(bsds, ev, - tdgram_bsd_sendto_handler, + ret = tstream_bsd_set_writeable_handler(bsds, ev, + tstream_bsd_writev_handler, req); if (ret == -1) { tevent_req_error(req, errno); @@ -1604,31 +1790,25 @@ static struct tevent_req *tdgram_bsd_sendto_send(TALLOC_CTX *mem_ctx, return req; } -static void tdgram_bsd_sendto_handler(void *private_data) +static void tstream_bsd_writev_handler(void *private_data) { struct tevent_req *req = talloc_get_type_abort(private_data, struct tevent_req); - struct tdgram_bsd_sendto_state *state = tevent_req_data(req, - struct tdgram_bsd_sendto_state); - struct tdgram_context *dgram = state->dgram; - struct tdgram_bsd *bsds = tdgram_context_data(dgram, struct tdgram_bsd); - struct sockaddr *sa = NULL; - socklen_t sa_len = 0; + struct tstream_bsd_writev_state *state = tevent_req_data(req, + struct tstream_bsd_writev_state); + struct tstream_context *stream = state->stream; + struct tstream_bsd *bsds = tstream_context_data(stream, struct tstream_bsd); ssize_t ret; - int err; - bool retry; - - if (state->dst) { - struct tsocket_address_bsd *bsda = - talloc_get_type(state->dst->private_data, - struct tsocket_address_bsd); + int err; + bool retry; - sa = &bsda->u.sa; - sa_len = sizeof(bsda->u.ss); + ret = writev(bsds->fd, state->vector, state->count); + if (ret == 0) { + /* propagate end of file */ + tevent_req_error(req, EPIPE); + return; } - - ret = sendto(bsds->fd, state->buf, state->len, 0, sa, sa_len); - err = tsocket_error_from_errno(ret, errno, &retry); + err = tsocket_bsd_error_from_errno(ret, errno, &retry); if (retry) { /* retry later */ return; @@ -1637,16 +1817,48 @@ static void tdgram_bsd_sendto_handler(void *private_data) return; } - state->ret = ret; + state->ret += ret; + + while (ret > 0) { + if (ret < state->vector[0].iov_len) { + uint8_t *base; + base = (uint8_t *)state->vector[0].iov_base; + base += ret; + state->vector[0].iov_base = (void *)base; + state->vector[0].iov_len -= ret; + break; + } + ret -= state->vector[0].iov_len; + state->vector += 1; + state->count -= 1; + } + + /* + * there're maybe some empty vectors at the end + * which we need to skip, otherwise we would get + * ret == 0 from the writev() call and return EPIPE + */ + while (state->count > 0) { + if (state->vector[0].iov_len > 0) { + break; + } + state->vector += 1; + state->count -= 1; + } + + if (state->count > 0) { + /* we have more to read */ + return; + } tevent_req_done(req); } -static ssize_t tdgram_bsd_sendto_recv(struct tevent_req *req, int *perrno) +static int tstream_bsd_writev_recv(struct tevent_req *req, int *perrno) { - struct tdgram_bsd_sendto_state *state = tevent_req_data(req, - struct tdgram_bsd_sendto_state); - ssize_t ret; + struct tstream_bsd_writev_state *state = tevent_req_data(req, + struct tstream_bsd_writev_state); + int ret; ret = tsocket_simple_int_recv(req, perrno); if (ret == 0) { @@ -1657,40 +1869,36 @@ static ssize_t tdgram_bsd_sendto_recv(struct tevent_req *req, int *perrno) return ret; } -struct tdgram_bsd_disconnect_state { - uint8_t __dummy; +struct tstream_bsd_disconnect_state { + void *__dummy; }; -static struct tevent_req *tdgram_bsd_disconnect_send(TALLOC_CTX *mem_ctx, +static struct tevent_req *tstream_bsd_disconnect_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, - struct tdgram_context *dgram) + struct tstream_context *stream) { - struct tdgram_bsd *bsds = tdgram_context_data(dgram, struct tdgram_bsd); + struct tstream_bsd *bsds = tstream_context_data(stream, struct tstream_bsd); struct tevent_req *req; - struct tdgram_bsd_disconnect_state *state; + struct tstream_bsd_disconnect_state *state; int ret; int err; bool dummy; req = tevent_req_create(mem_ctx, &state, - struct tdgram_bsd_disconnect_state); + struct tstream_bsd_disconnect_state); if (req == NULL) { return NULL; } - if (bsds->read_req || bsds->write_req) { - tevent_req_error(req, EBUSY); - goto post; - } - if (bsds->fd == -1) { tevent_req_error(req, ENOTCONN); goto post; } + TALLOC_FREE(bsds->fde); ret = close(bsds->fd); bsds->fd = -1; - err = tsocket_error_from_errno(ret, errno, &dummy); + err = tsocket_bsd_error_from_errno(ret, errno, &dummy); if (tevent_req_error(req, err)) { goto post; } @@ -1701,7 +1909,7 @@ post: return req; } -static int tdgram_bsd_disconnect_recv(struct tevent_req *req, +static int tstream_bsd_disconnect_recv(struct tevent_req *req, int *perrno) { int ret; @@ -1712,20 +1920,22 @@ static int tdgram_bsd_disconnect_recv(struct tevent_req *req, return ret; } -static const struct tdgram_context_ops tdgram_bsd_ops = { +static const struct tstream_context_ops tstream_bsd_ops = { .name = "bsd", - .recvfrom_send = tdgram_bsd_recvfrom_send, - .recvfrom_recv = tdgram_bsd_recvfrom_recv, + .pending_bytes = tstream_bsd_pending_bytes, - .sendto_send = tdgram_bsd_sendto_send, - .sendto_recv = tdgram_bsd_sendto_recv, + .readv_send = tstream_bsd_readv_send, + .readv_recv = tstream_bsd_readv_recv, - .disconnect_send = tdgram_bsd_disconnect_send, - .disconnect_recv = tdgram_bsd_disconnect_recv, + .writev_send = tstream_bsd_writev_send, + .writev_recv = tstream_bsd_writev_recv, + + .disconnect_send = tstream_bsd_disconnect_send, + .disconnect_recv = tstream_bsd_disconnect_recv, }; -static int tdgram_bsd_destructor(struct tdgram_bsd *bsds) +static int tstream_bsd_destructor(struct tstream_bsd *bsds) { TALLOC_FREE(bsds->fde); if (bsds->fd != -1) { @@ -1735,26 +1945,89 @@ static int tdgram_bsd_destructor(struct tdgram_bsd *bsds) return 0; } -static int tdgram_bsd_dgram_socket(const struct tsocket_address *local, - const struct tsocket_address *remote, - TALLOC_CTX *mem_ctx, - struct tdgram_context **_dgram, - const char *location) +int _tstream_bsd_existing_socket(TALLOC_CTX *mem_ctx, + int fd, + struct tstream_context **_stream, + const char *location) +{ + struct tstream_context *stream; + struct tstream_bsd *bsds; + + stream = tstream_context_create(mem_ctx, + &tstream_bsd_ops, + &bsds, + struct tstream_bsd, + location); + if (!stream) { + return -1; + } + ZERO_STRUCTP(bsds); + bsds->fd = fd; + talloc_set_destructor(bsds, tstream_bsd_destructor); + + *_stream = stream; + return 0; +} + +struct tstream_bsd_connect_state { + int fd; + struct tevent_fd *fde; + struct tstream_conext *stream; +}; + +static int tstream_bsd_connect_destructor(struct tstream_bsd_connect_state *state) +{ + TALLOC_FREE(state->fde); + if (state->fd != -1) { + close(state->fd); + state->fd = -1; + } + + return 0; +} + +static void tstream_bsd_connect_fde_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, + void *private_data); + +static struct tevent_req * tstream_bsd_connect_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + int sys_errno, + const struct tsocket_address *local, + const struct tsocket_address *remote) { + struct tevent_req *req; + struct tstream_bsd_connect_state *state; struct tsocket_address_bsd *lbsda = talloc_get_type_abort(local->private_data, struct tsocket_address_bsd); - struct tsocket_address_bsd *rbsda = NULL; - struct tdgram_context *dgram; - struct tdgram_bsd *bsds; - int fd; + struct tsocket_address_bsd *rbsda = + talloc_get_type_abort(remote->private_data, + struct tsocket_address_bsd); int ret; + int err; + bool retry; bool do_bind = false; bool do_reuseaddr = false; + bool do_ipv6only = false; + bool is_inet = false; + int sa_fam = lbsda->u.sa.sa_family; - if (remote) { - rbsda = talloc_get_type_abort(remote->private_data, - struct tsocket_address_bsd); + req = tevent_req_create(mem_ctx, &state, + struct tstream_bsd_connect_state); + if (!req) { + return NULL; + } + state->fd = -1; + state->fde = NULL; + + talloc_set_destructor(state, tstream_bsd_connect_destructor); + + /* give the wrappers a chance to report an error */ + if (sys_errno != 0) { + tevent_req_error(req, sys_errno); + goto post; } switch (lbsda->u.sa.sa_family) { @@ -1769,9 +2042,10 @@ static int tdgram_bsd_dgram_socket(const struct tsocket_address *local, do_reuseaddr = true; do_bind = true; } - if (lbsda->u.in.sin_addr.s_addr == INADDR_ANY) { + if (lbsda->u.in.sin_addr.s_addr != INADDR_ANY) { do_bind = true; } + is_inet = true; break; #ifdef HAVE_IPV6 case AF_INET6: @@ -1784,98 +2058,181 @@ static int tdgram_bsd_dgram_socket(const struct tsocket_address *local, sizeof(in6addr_any)) != 0) { do_bind = true; } + is_inet = true; + do_ipv6only = true; break; #endif default: - errno = EINVAL; - return -1; + tevent_req_error(req, EINVAL); + goto post; } - fd = socket(lbsda->u.sa.sa_family, SOCK_DGRAM, 0); - if (fd < 0) { - return fd; + if (!do_bind && is_inet) { + sa_fam = rbsda->u.sa.sa_family; + switch (sa_fam) { + case AF_INET: + do_ipv6only = false; + break; +#ifdef HAVE_IPV6 + case AF_INET6: + do_ipv6only = true; + break; +#endif + } } - fd = tsocket_bsd_common_prepare_fd(fd, true); - if (fd < 0) { - return fd; + state->fd = socket(sa_fam, SOCK_STREAM, 0); + if (state->fd == -1) { + tevent_req_error(req, errno); + goto post; } - dgram = tdgram_context_create(mem_ctx, - &tdgram_bsd_ops, - &bsds, - struct tdgram_bsd, - location); - if (!dgram) { - int saved_errno = errno; - close(fd); - errno = saved_errno; - return -1; + state->fd = tsocket_bsd_common_prepare_fd(state->fd, true); + if (state->fd == -1) { + tevent_req_error(req, errno); + goto post; } - ZERO_STRUCTP(bsds); - bsds->fd = fd; - talloc_set_destructor(bsds, tdgram_bsd_destructor); - if (lbsda->broadcast) { +#ifdef HAVE_IPV6 + if (do_ipv6only) { int val = 1; - ret = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, + ret = setsockopt(state->fd, IPPROTO_IPV6, IPV6_V6ONLY, (const void *)&val, sizeof(val)); if (ret == -1) { - int saved_errno = errno; - talloc_free(dgram); - errno = saved_errno; - return ret; + tevent_req_error(req, errno); + goto post; } } +#endif if (do_reuseaddr) { int val = 1; - ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, + ret = setsockopt(state->fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&val, sizeof(val)); if (ret == -1) { - int saved_errno = errno; - talloc_free(dgram); - errno = saved_errno; - return ret; + tevent_req_error(req, errno); + goto post; } } if (do_bind) { - ret = bind(fd, &lbsda->u.sa, sizeof(lbsda->u.ss)); + ret = bind(state->fd, &lbsda->u.sa, lbsda->sa_socklen); if (ret == -1) { - int saved_errno = errno; - talloc_free(dgram); - errno = saved_errno; - return ret; + tevent_req_error(req, errno); + goto post; } } - if (rbsda) { - ret = connect(fd, &rbsda->u.sa, sizeof(rbsda->u.ss)); + if (rbsda->u.sa.sa_family != sa_fam) { + tevent_req_error(req, EINVAL); + goto post; + } + + ret = connect(state->fd, &rbsda->u.sa, rbsda->sa_socklen); + err = tsocket_bsd_error_from_errno(ret, errno, &retry); + if (retry) { + /* retry later */ + goto async; + } + if (tevent_req_error(req, err)) { + goto post; + } + + tevent_req_done(req); + goto post; + + async: + state->fde = tevent_add_fd(ev, state, + state->fd, + TEVENT_FD_READ | TEVENT_FD_WRITE, + tstream_bsd_connect_fde_handler, + req); + if (tevent_req_nomem(state->fde, req)) { + goto post; + } + + return req; + + post: + tevent_req_post(req, ev); + return req; +} + +static void tstream_bsd_connect_fde_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, + void *private_data) +{ + struct tevent_req *req = talloc_get_type_abort(private_data, + struct tevent_req); + struct tstream_bsd_connect_state *state = tevent_req_data(req, + struct tstream_bsd_connect_state); + int ret; + int error=0; + socklen_t len = sizeof(error); + int err; + bool retry; + + ret = getsockopt(state->fd, SOL_SOCKET, SO_ERROR, &error, &len); + if (ret == 0) { + if (error != 0) { + errno = error; + ret = -1; + } + } + err = tsocket_bsd_error_from_errno(ret, errno, &retry); + if (retry) { + /* retry later */ + return; + } + if (tevent_req_error(req, err)) { + return; + } + + tevent_req_done(req); +} + +static int tstream_bsd_connect_recv(struct tevent_req *req, + int *perrno, + TALLOC_CTX *mem_ctx, + struct tstream_context **stream, + const char *location) +{ + struct tstream_bsd_connect_state *state = tevent_req_data(req, + struct tstream_bsd_connect_state); + int ret; + + ret = tsocket_simple_int_recv(req, perrno); + if (ret == 0) { + ret = _tstream_bsd_existing_socket(mem_ctx, + state->fd, + stream, + location); if (ret == -1) { - int saved_errno = errno; - talloc_free(dgram); - errno = saved_errno; - return ret; + *perrno = errno; + goto done; } + TALLOC_FREE(state->fde); + state->fd = -1; } - *_dgram = dgram; - return 0; +done: + tevent_req_received(req); + return ret; } -int _tdgram_inet_udp_socket(const struct tsocket_address *local, - const struct tsocket_address *remote, - TALLOC_CTX *mem_ctx, - struct tdgram_context **dgram, - const char *location) +struct tevent_req * tstream_inet_tcp_connect_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const struct tsocket_address *local, + const struct tsocket_address *remote) { struct tsocket_address_bsd *lbsda = talloc_get_type_abort(local->private_data, struct tsocket_address_bsd); - int ret; + struct tevent_req *req; + int sys_errno = 0; switch (lbsda->u.sa.sa_family) { case AF_INET: @@ -1885,36 +2242,119 @@ int _tdgram_inet_udp_socket(const struct tsocket_address *local, break; #endif default: - errno = EINVAL; - return -1; + sys_errno = EINVAL; + break; } - ret = tdgram_bsd_dgram_socket(local, remote, mem_ctx, dgram, location); + req = tstream_bsd_connect_send(mem_ctx, ev, sys_errno, local, remote); - return ret; + return req; +} + +int _tstream_inet_tcp_connect_recv(struct tevent_req *req, + int *perrno, + TALLOC_CTX *mem_ctx, + struct tstream_context **stream, + const char *location) +{ + return tstream_bsd_connect_recv(req, perrno, mem_ctx, stream, location); } -int _tdgram_unix_dgram_socket(const struct tsocket_address *local, - const struct tsocket_address *remote, - TALLOC_CTX *mem_ctx, - struct tdgram_context **dgram, - const char *location) +struct tevent_req * tstream_unix_connect_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const struct tsocket_address *local, + const struct tsocket_address *remote) { struct tsocket_address_bsd *lbsda = talloc_get_type_abort(local->private_data, struct tsocket_address_bsd); - int ret; + struct tevent_req *req; + int sys_errno = 0; switch (lbsda->u.sa.sa_family) { case AF_UNIX: break; default: - errno = EINVAL; + sys_errno = EINVAL; + break; + } + + req = tstream_bsd_connect_send(mem_ctx, ev, sys_errno, local, remote); + + return req; +} + +int _tstream_unix_connect_recv(struct tevent_req *req, + int *perrno, + TALLOC_CTX *mem_ctx, + struct tstream_context **stream, + const char *location) +{ + return tstream_bsd_connect_recv(req, perrno, mem_ctx, stream, location); +} + +int _tstream_unix_socketpair(TALLOC_CTX *mem_ctx1, + struct tstream_context **_stream1, + TALLOC_CTX *mem_ctx2, + struct tstream_context **_stream2, + const char *location) +{ + int ret; + int fds[2]; + int fd1; + int fd2; + struct tstream_context *stream1 = NULL; + struct tstream_context *stream2 = NULL; + + ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fds); + if (ret == -1) { + return -1; + } + fd1 = fds[0]; + fd2 = fds[1]; + + fd1 = tsocket_bsd_common_prepare_fd(fd1, true); + if (fd1 == -1) { + int sys_errno = errno; + close(fd2); + errno = sys_errno; return -1; } - ret = tdgram_bsd_dgram_socket(local, remote, mem_ctx, dgram, location); + fd2 = tsocket_bsd_common_prepare_fd(fd2, true); + if (fd2 == -1) { + int sys_errno = errno; + close(fd1); + errno = sys_errno; + return -1; + } - return ret; + ret = _tstream_bsd_existing_socket(mem_ctx1, + fd1, + &stream1, + location); + if (ret == -1) { + int sys_errno = errno; + close(fd1); + close(fd2); + errno = sys_errno; + return -1; + } + + ret = _tstream_bsd_existing_socket(mem_ctx2, + fd2, + &stream2, + location); + if (ret == -1) { + int sys_errno = errno; + talloc_free(stream1); + close(fd2); + errno = sys_errno; + return -1; + } + + *_stream1 = stream1; + *_stream2 = stream2; + return 0; }