/*
* Copyright (C) Jelmer Vernooij 2005,2008 <jelmer@samba.org>
- * Copyright (C) Stefan Metzmacher 2006 <metze@samba.org>
+ * Copyright (C) Stefan Metzmacher 2006-2009 <metze@samba.org>
*
* All rights reserved.
*
#define real_setsockopt setsockopt
#define real_recvfrom recvfrom
#define real_sendto sendto
+#define real_sendmsg sendmsg
#define real_ioctl ioctl
#define real_recv recv
+#define real_read read
#define real_send send
+#define real_readv readv
+#define real_writev writev
#define real_socket socket
#define real_close close
#endif
/*
* FD00::5357:5FXX
*/
-static const struct in6_addr swrap_ipv6 =
-{ { {
-0xFD,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x53,0x57,0x5F,0x00
-} } };
+static const struct in6_addr *swrap_ipv6(void)
+{
+ static struct in6_addr v;
+ static int initialized;
+ int ret;
+
+ if (initialized) {
+ return &v;
+ }
+ initialized = 1;
+
+ ret = inet_pton(AF_INET6, "FD00::5357:5F00", &v);
+ if (ret <= 0) {
+ abort();
+ }
+
+ return &v;
+}
#endif
static struct sockaddr *sockaddr_dup(const void *data, socklen_t len)
int bound;
int bcast;
int is_server;
+ int connected;
+ int defer_connect;
char *path;
char *tmp_path;
memset(in2, 0, sizeof(*in2));
in2->sin6_family = AF_INET6;
- in2->sin6_addr = swrap_ipv6;
+ in2->sin6_addr = *swrap_ipv6();
in2->sin6_addr.s6_addr[15] = iface;
in2->sin6_port = htons(prt);
if (bcast) *bcast = 0;
- switch (si->family) {
+ switch (inaddr->sa_family) {
case AF_INET: {
const struct sockaddr_in *in =
(const struct sockaddr_in *)inaddr;
cmp = in->sin6_addr;
cmp.s6_addr[15] = 0;
- if (IN6_ARE_ADDR_EQUAL(&swrap_ipv6, &cmp)) {
+ if (IN6_ARE_ADDR_EQUAL(swrap_ipv6(), &cmp)) {
iface = in->sin6_addr.s6_addr[15];
} else {
errno = ENETUNREACH;
cmp = in->sin6_addr;
cmp.s6_addr[15] = 0;
- if (IN6_ARE_ADDR_EQUAL(&swrap_ipv6, &cmp)) {
+ if (IN6_IS_ADDR_UNSPECIFIED(&in->sin6_addr)) {
+ iface = socket_wrapper_default_iface();
+ } else if (IN6_ARE_ADDR_EQUAL(swrap_ipv6(), &cmp)) {
iface = in->sin6_addr.s6_addr[15];
} else {
errno = EADDRNOTAVAIL;
SWRAP_SEND_RST,
SWRAP_CLOSE_SEND,
SWRAP_CLOSE_RECV,
- SWRAP_CLOSE_ACK
+ SWRAP_CLOSE_ACK,
};
struct swrap_file_hdr {
size_t icmp_hdr_len = 0;
size_t icmp_truncate_len = 0;
uint8_t protocol = 0, icmp_protocol = 0;
- const struct sockaddr_in *src_in;
- const struct sockaddr_in *dest_in;
+ const struct sockaddr_in *src_in = NULL;
+ const struct sockaddr_in *dest_in = NULL;
#ifdef HAVE_IPV6
- const struct sockaddr_in6 *src_in6;
- const struct sockaddr_in6 *dest_in6;
+ const struct sockaddr_in6 *src_in6 = NULL;
+ const struct sockaddr_in6 *dest_in6 = NULL;
#endif
uint16_t src_port;
uint16_t dest_port;
switch (si->family) {
case AF_INET:
break;
+#ifdef HAVE_IPV6
case AF_INET6:
break;
+#endif
default:
return NULL;
}
{
struct socket_info *si;
int fd;
+ int real_type = type;
+#ifdef SOCK_CLOEXEC
+ real_type &= ~SOCK_CLOEXEC;
+#endif
+#ifdef SOCK_NONBLOCK
+ real_type &= ~SOCK_NONBLOCK;
+#endif
if (!socket_wrapper_dir()) {
return real_socket(family, type, protocol);
return -1;
}
- switch (type) {
+ switch (real_type) {
case SOCK_STREAM:
break;
case SOCK_DGRAM:
case 0:
break;
case 6:
- if (type == SOCK_STREAM) {
+ if (real_type == SOCK_STREAM) {
break;
}
/*fall through*/
case 17:
- if (type == SOCK_DGRAM) {
+ if (real_type == SOCK_DGRAM) {
break;
}
/*fall through*/
return -1;
}
+ /* We must call real_socket with type, from the caller, not the version we removed
+ SOCK_CLOEXEC and SOCK_NONBLOCK from */
fd = real_socket(AF_UNIX, type, 0);
if (fd == -1) return -1;
si = (struct socket_info *)calloc(1, sizeof(struct socket_info));
si->family = family;
- si->type = type;
+
+ /* however, the rest of the socket_wrapper code expects just
+ * the type, not the flags */
+ si->type = real_type;
si->protocol = protocol;
si->fd = fd;
child_si->protocol = parent_si->protocol;
child_si->bound = 1;
child_si->is_server = 1;
+ child_si->connected = 1;
child_si->peername_len = len;
child_si->peername = sockaddr_dup(my_addr, len);
/* using sendto() or connect() on an unbound socket would give the
recipient no way to reply, as unlike UDP and TCP, a unix domain
socket can't auto-assign emphemeral port numbers, so we need to
- assign it here */
-static int swrap_auto_bind(struct socket_info *si)
+ assign it here.
+ Note: this might change the family from ipv6 to ipv4
+*/
+static int swrap_auto_bind(struct socket_info *si, int family)
{
struct sockaddr_un un_addr;
int i;
un_addr.sun_family = AF_UNIX;
- switch (si->family) {
+ switch (family) {
case AF_INET: {
struct sockaddr_in in;
case AF_INET6: {
struct sockaddr_in6 in6;
+ if (si->family != family) {
+ errno = ENETUNREACH;
+ return -1;
+ }
+
switch (si->type) {
case SOCK_STREAM:
type = SOCKET_TYPE_CHAR_TCP_V6;
memset(&in6, 0, sizeof(in6));
in6.sin6_family = AF_INET6;
- in6.sin6_addr = swrap_ipv6;
+ in6.sin6_addr = *swrap_ipv6();
in6.sin6_addr.s6_addr[15] = socket_wrapper_default_iface();
si->myname_len = sizeof(in6);
si->myname = sockaddr_dup(&in6, si->myname_len);
return -1;
}
+ si->family = family;
set_port(si->family, port, si->myname);
return 0;
}
if (si->bound == 0) {
- ret = swrap_auto_bind(si);
+ ret = swrap_auto_bind(si, serv_addr->sa_family);
if (ret == -1) return -1;
}
ret = sockaddr_convert_to_un(si, (const struct sockaddr *)serv_addr, addrlen, &un_addr, 0, NULL);
if (ret == -1) return -1;
- swrap_dump_packet(si, serv_addr, SWRAP_CONNECT_SEND, NULL, 0);
+ if (si->type == SOCK_DGRAM) {
+ si->defer_connect = 1;
+ ret = 0;
+ } else {
+ swrap_dump_packet(si, serv_addr, SWRAP_CONNECT_SEND, NULL, 0);
- ret = real_connect(s, (struct sockaddr *)&un_addr,
- sizeof(struct sockaddr_un));
+ ret = real_connect(s, (struct sockaddr *)&un_addr,
+ sizeof(struct sockaddr_un));
+ }
/* to give better errors */
if (ret == -1 && errno == ENOENT) {
if (ret == 0) {
si->peername_len = addrlen;
si->peername = sockaddr_dup(serv_addr, addrlen);
+ si->connected = 1;
swrap_dump_packet(si, serv_addr, SWRAP_CONNECT_RECV, NULL, 0);
swrap_dump_packet(si, serv_addr, SWRAP_CONNECT_ACK, NULL, 0);
switch (si->family) {
case AF_INET:
return 0;
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ return 0;
+#endif
default:
errno = ENOPROTOOPT;
return -1;
socklen_t un_addrlen = sizeof(un_addr);
int ret;
struct socket_info *si = find_socket_info(s);
+ struct sockaddr_storage ss;
+ socklen_t ss_len = sizeof(ss);
if (!si) {
return real_recvfrom(s, buf, len, flags, from, fromlen);
}
- len = MIN(len, 1500);
+ if (!from) {
+ from = (struct sockaddr *)&ss;
+ fromlen = &ss_len;
+ }
+
+ if (si->type == SOCK_STREAM) {
+ /* cut down to 1500 byte packets for stream sockets,
+ * which makes it easier to format PCAP capture files
+ * (as the caller will simply continue from here) */
+ len = MIN(len, 1500);
+ }
/* irix 6.4 forgets to null terminate the sun_path string :-( */
memset(&un_addr, 0, sizeof(un_addr));
return real_sendto(s, buf, len, flags, to, tolen);
}
- len = MIN(len, 1500);
+ if (si->connected) {
+ if (to) {
+ errno = EISCONN;
+ return -1;
+ }
+
+ to = si->peername;
+ tolen = si->peername_len;
+ }
switch (si->type) {
case SOCK_STREAM:
+ /* cut down to 1500 byte packets for stream sockets,
+ * which makes it easier to format PCAP capture files
+ * (as the caller will simply continue from here) */
+ len = MIN(len, 1500);
+
ret = real_send(s, buf, len, flags);
break;
case SOCK_DGRAM:
if (si->bound == 0) {
- ret = swrap_auto_bind(si);
+ ret = swrap_auto_bind(si, si->family);
if (ret == -1) return -1;
}
return len;
}
-
+
+ if (si->defer_connect) {
+ ret = real_connect(s, (struct sockaddr *)&un_addr,
+ sizeof(un_addr));
+
+ /* to give better errors */
+ if (ret == -1 && errno == ENOENT) {
+ errno = EHOSTUNREACH;
+ }
+
+ if (ret == -1) {
+ return ret;
+ }
+ si->defer_connect = 0;
+ }
+
ret = real_sendto(s, buf, len, flags, (struct sockaddr *)&un_addr, sizeof(un_addr));
break;
default:
return real_recv(s, buf, len, flags);
}
- len = MIN(len, 1500);
+ if (si->type == SOCK_STREAM) {
+ /* cut down to 1500 byte packets for stream sockets,
+ * which makes it easier to format PCAP capture files
+ * (as the caller will simply continue from here) */
+ len = MIN(len, 1500);
+ }
ret = real_recv(s, buf, len, flags);
if (ret == -1 && errno != EAGAIN && errno != ENOBUFS) {
return ret;
}
+_PUBLIC_ ssize_t swrap_read(int s, void *buf, size_t len)
+{
+ int ret;
+ struct socket_info *si = find_socket_info(s);
+
+ if (!si) {
+ return real_read(s, buf, len);
+ }
+
+ if (si->type == SOCK_STREAM) {
+ /* cut down to 1500 byte packets for stream sockets,
+ * which makes it easier to format PCAP capture files
+ * (as the caller will simply continue from here) */
+ len = MIN(len, 1500);
+ }
+
+ ret = real_read(s, buf, len);
+ if (ret == -1 && errno != EAGAIN && errno != ENOBUFS) {
+ swrap_dump_packet(si, NULL, SWRAP_RECV_RST, NULL, 0);
+ } else if (ret == 0) { /* END OF FILE */
+ swrap_dump_packet(si, NULL, SWRAP_RECV_RST, NULL, 0);
+ } else if (ret > 0) {
+ swrap_dump_packet(si, NULL, SWRAP_RECV, buf, ret);
+ }
+
+ return ret;
+}
+
_PUBLIC_ ssize_t swrap_send(int s, const void *buf, size_t len, int flags)
{
return real_send(s, buf, len, flags);
}
- len = MIN(len, 1500);
+ if (si->type == SOCK_STREAM) {
+ /* cut down to 1500 byte packets for stream sockets,
+ * which makes it easier to format PCAP capture files
+ * (as the caller will simply continue from here) */
+ len = MIN(len, 1500);
+ }
+
+ if (si->defer_connect) {
+ struct sockaddr_un un_addr;
+ int bcast = 0;
+
+ if (si->bound == 0) {
+ ret = swrap_auto_bind(si, si->family);
+ if (ret == -1) return -1;
+ }
+
+ ret = sockaddr_convert_to_un(si, si->peername, si->peername_len,
+ &un_addr, 0, &bcast);
+ if (ret == -1) return -1;
+
+ ret = real_connect(s, (struct sockaddr *)&un_addr,
+ sizeof(un_addr));
+
+ /* to give better errors */
+ if (ret == -1 && errno == ENOENT) {
+ errno = EHOSTUNREACH;
+ }
+
+ if (ret == -1) {
+ return ret;
+ }
+ si->defer_connect = 0;
+ }
ret = real_send(s, buf, len, flags);
return ret;
}
+_PUBLIC_ ssize_t swrap_sendmsg(int s, const struct msghdr *msg, int flags)
+{
+ int ret;
+ uint8_t *buf;
+ off_t ofs = 0;
+ size_t i;
+ size_t remain;
+
+ struct socket_info *si = find_socket_info(s);
+
+ if (!si) {
+ return real_sendmsg(s, msg, flags);
+ }
+
+ if (si->defer_connect) {
+ struct sockaddr_un un_addr;
+ int bcast = 0;
+
+ if (si->bound == 0) {
+ ret = swrap_auto_bind(si, si->family);
+ if (ret == -1) return -1;
+ }
+
+ ret = sockaddr_convert_to_un(si, si->peername, si->peername_len,
+ &un_addr, 0, &bcast);
+ if (ret == -1) return -1;
+
+ ret = real_connect(s, (struct sockaddr *)&un_addr,
+ sizeof(un_addr));
+
+ /* to give better errors */
+ if (ret == -1 && errno == ENOENT) {
+ errno = EHOSTUNREACH;
+ }
+
+ if (ret == -1) {
+ return ret;
+ }
+ si->defer_connect = 0;
+ }
+
+ ret = real_sendmsg(s, msg, flags);
+ remain = ret;
+
+ /* we capture it as one single packet */
+ buf = (uint8_t *)malloc(ret);
+ if (!buf) {
+ /* we just not capture the packet */
+ errno = 0;
+ return ret;
+ }
+
+ for (i=0; i < msg->msg_iovlen; i++) {
+ size_t this_time = MIN(remain, msg->msg_iov[i].iov_len);
+ memcpy(buf + ofs,
+ msg->msg_iov[i].iov_base,
+ this_time);
+ ofs += this_time;
+ remain -= this_time;
+ }
+
+ swrap_dump_packet(si, NULL, SWRAP_SEND, buf, ret);
+ free(buf);
+ if (ret == -1) {
+ swrap_dump_packet(si, NULL, SWRAP_SEND_RST, NULL, 0);
+ }
+
+ return ret;
+}
+
+int swrap_readv(int s, const struct iovec *vector, size_t count)
+{
+ int ret;
+ struct socket_info *si = find_socket_info(s);
+ struct iovec v;
+
+ if (!si) {
+ return real_readv(s, vector, count);
+ }
+
+ if (si->type == SOCK_STREAM && count > 0) {
+ /* cut down to 1500 byte packets for stream sockets,
+ * which makes it easier to format PCAP capture files
+ * (as the caller will simply continue from here) */
+ size_t i, len = 0;
+
+ for (i=0; i < count; i++) {
+ size_t nlen;
+ nlen = len + vector[i].iov_len;
+ if (nlen > 1500) {
+ break;
+ }
+ }
+ count = i;
+ if (count == 0) {
+ v = vector[0];
+ v.iov_len = MIN(v.iov_len, 1500);
+ vector = &v;
+ count = 1;
+ }
+ }
+
+ ret = real_readv(s, vector, count);
+ if (ret == -1 && errno != EAGAIN && errno != ENOBUFS) {
+ swrap_dump_packet(si, NULL, SWRAP_RECV_RST, NULL, 0);
+ } else if (ret == 0) { /* END OF FILE */
+ swrap_dump_packet(si, NULL, SWRAP_RECV_RST, NULL, 0);
+ } else if (ret > 0) {
+ uint8_t *buf;
+ off_t ofs = 0;
+ size_t i;
+ size_t remain = ret;
+
+ /* we capture it as one single packet */
+ buf = (uint8_t *)malloc(ret);
+ if (!buf) {
+ /* we just not capture the packet */
+ errno = 0;
+ return ret;
+ }
+
+ for (i=0; i < count; i++) {
+ size_t this_time = MIN(remain, vector[i].iov_len);
+ memcpy(buf + ofs,
+ vector[i].iov_base,
+ this_time);
+ ofs += this_time;
+ remain -= this_time;
+ }
+
+ swrap_dump_packet(si, NULL, SWRAP_RECV, buf, ret);
+ free(buf);
+ }
+
+ return ret;
+}
+
+int swrap_writev(int s, const struct iovec *vector, size_t count)
+{
+ int ret;
+ struct socket_info *si = find_socket_info(s);
+ struct iovec v;
+
+ if (!si) {
+ return real_writev(s, vector, count);
+ }
+
+ if (si->type == SOCK_STREAM && count > 0) {
+ /* cut down to 1500 byte packets for stream sockets,
+ * which makes it easier to format PCAP capture files
+ * (as the caller will simply continue from here) */
+ size_t i, len = 0;
+
+ for (i=0; i < count; i++) {
+ size_t nlen;
+ nlen = len + vector[i].iov_len;
+ if (nlen > 1500) {
+ break;
+ }
+ }
+ count = i;
+ if (count == 0) {
+ v = vector[0];
+ v.iov_len = MIN(v.iov_len, 1500);
+ vector = &v;
+ count = 1;
+ }
+ }
+
+ ret = real_writev(s, vector, count);
+ if (ret == -1) {
+ swrap_dump_packet(si, NULL, SWRAP_SEND_RST, NULL, 0);
+ } else {
+ uint8_t *buf;
+ off_t ofs = 0;
+ size_t i;
+ size_t remain = ret;
+
+ /* we capture it as one single packet */
+ buf = (uint8_t *)malloc(ret);
+ if (!buf) {
+ /* we just not capture the packet */
+ errno = 0;
+ return ret;
+ }
+
+ for (i=0; i < count; i++) {
+ size_t this_time = MIN(remain, vector[i].iov_len);
+ memcpy(buf + ofs,
+ vector[i].iov_base,
+ this_time);
+ ofs += this_time;
+ remain -= this_time;
+ }
+
+ swrap_dump_packet(si, NULL, SWRAP_SEND, buf, ret);
+ free(buf);
+ }
+
+ return ret;
+}
+
_PUBLIC_ int swrap_close(int fd)
{
struct socket_info *si = find_socket_info(fd);