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);
}
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;
socklen_t sa_socklen = sizeof(lbsda->u.ss);
if (remote) {
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;
+ sa_socklen = sizeof(rbsda->u.in);
break;
#ifdef HAVE_IPV6
case AF_INET6:
sizeof(in6addr_any)) != 0) {
do_bind = true;
}
+ is_inet = true;
+ sa_socklen = sizeof(rbsda->u.in6);
+ 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:
+ sa_socklen = sizeof(rbsda->u.in);
+ do_ipv6only = false;
+ break;
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ sa_socklen = sizeof(rbsda->u.in6);
+ do_ipv6only = true;
+ break;
+#endif
+ }
+ }
+
+ fd = socket(sa_fam, SOCK_DGRAM, 0);
if (fd < 0) {
return fd;
}
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 ret;
+ }
+ }
+#endif
+
if (broadcast) {
int val = 1;
}
if (rbsda) {
+ if (rbsda->u.sa.sa_family != sa_fam) {
+ talloc_free(dgram);
+ errno = EINVAL;
+ return -1;
+ }
+
ret = connect(fd, &rbsda->u.sa, sa_socklen);
if (ret == -1) {
int saved_errno = errno;
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;
socklen_t sa_socklen = sizeof(rbsda->u.ss);
req = tevent_req_create(mem_ctx, &state,
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;
+ sa_socklen = sizeof(rbsda->u.in);
break;
#ifdef HAVE_IPV6
case AF_INET6:
sizeof(in6addr_any)) != 0) {
do_bind = true;
}
+ is_inet = true;
+ sa_socklen = sizeof(rbsda->u.in6);
+ 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:
+ sa_socklen = sizeof(rbsda->u.in);
+ do_ipv6only = false;
+ break;
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ sa_socklen = sizeof(rbsda->u.in6);
+ 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, sa_socklen);
if (ret == -1) {
tevent_req_error(req, errno);
goto post;
}
}
+ if (rbsda->u.sa.sa_family != sa_fam) {
+ tevent_req_error(req, EINVAL);
+ goto post;
+ }
+
ret = connect(state->fd, &rbsda->u.sa, sa_socklen);
err = tsocket_bsd_error_from_errno(ret, errno, &retry);
if (retry) {