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
static const struct tsocket_address_ops tsocket_address_bsd_ops;
struct tsocket_address_bsd {
+ socklen_t sa_socklen;
union {
struct sockaddr sa;
struct sockaddr_in in;
} 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:
return -1;
}
- if (sa_len > sizeof(struct sockaddr_storage)) {
+ if (sa_socklen > sizeof(struct sockaddr_storage)) {
errno = EINVAL;
return -1;
}
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,
return 0;
}
+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 false;
+ }
+
+ switch (bsda->u.sa.sa_family) {
+ case AF_UNIX:
+ return true;
+ }
+
+ return false;
+}
+
int _tsocket_address_unix_from_path(TALLOC_CTX *mem_ctx,
const char *path,
struct tsocket_address **_addr,
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,
ret = _tsocket_address_bsd_from_sockaddr(mem_ctx,
&bsda->u.sa,
- sizeof(bsda->u.ss),
+ bsda->sa_socklen,
©,
location);
if (ret != 0) {
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,
bsds);
if (!bsds->fde) {
+ errno = ENOMEM;
return -1;
}
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);
if (!bsds->fde) {
+ errno = ENOMEM;
return -1;
}
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;
int err;
bool retry;
}
ZERO_STRUCTP(bsda);
+ bsda->sa_socklen = sizeof(bsda->u.ss);
- sa = &bsda->u.sa;
- sa_len = sizeof(bsda->u.ss);
- /*
- * for unix sockets we can't use the size of sockaddr_storage
- * we would get EINVAL
- */
- if (bsda->u.sa.sa_family == AF_UNIX) {
- sa_len = sizeof(bsda->u.un);
- }
-
- ret = recvfrom(bsds->fd, state->buf, state->len, 0, sa, &sa_len);
+ 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 (ret != state->len) {
- tevent_req_error(req, EIO);
+ /*
+ * 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);
}
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;
+ socklen_t sa_socklen = 0;
ssize_t ret;
int err;
bool retry;
struct tsocket_address_bsd);
sa = &bsda->u.sa;
- sa_len = sizeof(bsda->u.ss);
- /*
- * for unix sockets we can't use the size of sockaddr_storage
- * we would get EINVAL
- */
- if (bsda->u.sa.sa_family == AF_UNIX) {
- sa_len = sizeof(bsda->u.un);
- }
+ sa_socklen = bsda->sa_socklen;
}
- ret = sendto(bsds->fd, state->buf, state->len, 0, sa, sa_len);
+ 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 */
goto post;
}
+ TALLOC_FREE(bsds->fde);
ret = close(bsds->fd);
bsds->fd = -1;
err = tsocket_bsd_error_from_errno(ret, errno, &dummy);
int ret;
bool do_bind = false;
bool do_reuseaddr = false;
- socklen_t sa_len = sizeof(lbsda->u.ss);
+ 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,
do_reuseaddr = true;
do_bind = true;
}
- /*
- * for unix sockets we can't use the size of sockaddr_storage
- * we would get EINVAL
- */
- sa_len = sizeof(lbsda->u.un);
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) {
+ if (lbsda->u.in.sin_addr.s_addr != INADDR_ANY) {
do_bind = true;
}
+ is_inet = true;
break;
#ifdef HAVE_IPV6
case AF_INET6:
sizeof(in6addr_any)) != 0) {
do_bind = true;
}
+ is_inet = true;
+ do_ipv6only = true;
break;
#endif
default:
return -1;
}
- fd = socket(lbsda->u.sa.sa_family, SOCK_DGRAM, 0);
+ 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
+ }
+ }
+
+ fd = socket(sa_fam, SOCK_DGRAM, 0);
if (fd < 0) {
- return fd;
+ return -1;
}
fd = tsocket_bsd_common_prepare_fd(fd, true);
if (fd < 0) {
- return fd;
+ return -1;
}
dgram = tdgram_context_create(mem_ctx,
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;
+ }
+ }
+#endif
+
if (broadcast) {
int val = 1;
int saved_errno = errno;
talloc_free(dgram);
errno = saved_errno;
- return ret;
+ return -1;
}
}
int saved_errno = errno;
talloc_free(dgram);
errno = saved_errno;
- return ret;
+ return -1;
}
}
if (do_bind) {
- ret = bind(fd, &lbsda->u.sa, sa_len);
+ ret = bind(fd, &lbsda->u.sa, lbsda->sa_socklen);
if (ret == -1) {
int saved_errno = errno;
talloc_free(dgram);
errno = saved_errno;
- return ret;
+ return -1;
}
}
if (rbsda) {
- ret = connect(fd, &rbsda->u.sa, sa_len);
+ if (rbsda->u.sa.sa_family != sa_fam) {
+ talloc_free(dgram);
+ errno = EINVAL;
+ return -1;
+ }
+
+ ret = connect(fd, &rbsda->u.sa, rbsda->sa_socklen);
if (ret == -1) {
int saved_errno = errno;
talloc_free(dgram);
errno = saved_errno;
- return ret;
+ return -1;
}
}
}
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;
}
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,
tstream_bsd_fde_handler,
bsds);
if (!bsds->fde) {
+ errno = ENOMEM;
return -1;
}
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,
+ 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;
uint8_t *base;
base = (uint8_t *)state->vector[0].iov_base;
base += ret;
- state->vector[0].iov_base = base;
+ state->vector[0].iov_base = (void *)base;
state->vector[0].iov_len -= ret;
break;
}
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 readv() 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;
uint8_t *base;
base = (uint8_t *)state->vector[0].iov_base;
base += ret;
- state->vector[0].iov_base = base;
+ state->vector[0].iov_base = (void *)base;
state->vector[0].iov_len -= ret;
break;
}
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;
goto post;
}
+ TALLOC_FREE(bsds->fde);
ret = close(bsds->fd);
bsds->fd = -1;
err = tsocket_bsd_error_from_errno(ret, errno, &dummy);
bool retry;
bool do_bind = false;
bool do_reuseaddr = false;
- socklen_t sa_len = sizeof(rbsda->u.ss);
+ bool do_ipv6only = false;
+ bool is_inet = false;
+ int sa_fam = lbsda->u.sa.sa_family;
req = tevent_req_create(mem_ctx, &state,
struct tstream_bsd_connect_state);
do_reuseaddr = true;
do_bind = true;
}
- /*
- * for unix sockets we can't use the size of sockaddr_storage
- * we would get EINVAL
- */
- sa_len = sizeof(rbsda->u.un);
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) {
+ if (lbsda->u.in.sin_addr.s_addr != INADDR_ANY) {
do_bind = true;
}
+ is_inet = true;
break;
#ifdef HAVE_IPV6
case AF_INET6:
sizeof(in6addr_any)) != 0) {
do_bind = true;
}
+ is_inet = true;
+ do_ipv6only = true;
break;
#endif
default:
goto post;
}
- state->fd = socket(lbsda->u.sa.sa_family, SOCK_STREAM, 0);
+ 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
+ }
+ }
+
+ state->fd = socket(sa_fam, SOCK_STREAM, 0);
if (state->fd == -1) {
tevent_req_error(req, errno);
goto post;
goto post;
}
+#ifdef HAVE_IPV6
+ if (do_ipv6only) {
+ int val = 1;
+
+ ret = setsockopt(state->fd, IPPROTO_IPV6, IPV6_V6ONLY,
+ (const void *)&val, sizeof(val));
+ if (ret == -1) {
+ tevent_req_error(req, errno);
+ goto post;
+ }
+ }
+#endif
+
if (do_reuseaddr) {
int val = 1;
}
if (do_bind) {
- ret = bind(state->fd, &lbsda->u.sa, sizeof(lbsda->u.ss));
+ ret = bind(state->fd, &lbsda->u.sa, lbsda->sa_socklen);
if (ret == -1) {
tevent_req_error(req, errno);
goto post;
}
}
- ret = connect(state->fd, &rbsda->u.sa, sa_len);
+ 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 */