#define real_writev writev
#define real_socket socket
#define real_close close
+#define real_dup dup
+#define real_dup2 dup2
#endif
#ifdef HAVE_GETTIMEOFDAY_TZ
#define SOCKET_TYPE_CHAR_TCP_V6 'X'
#define SOCKET_TYPE_CHAR_UDP_V6 'Y'
-#define MAX_WRAPPED_INTERFACES 16
+/* This limit is to avoid broadcast sendto() needing to stat too many
+ * files. It may be raised (with a performance cost) to up to 254
+ * without changing the format above */
+#define MAX_WRAPPED_INTERFACES 32
#ifdef HAVE_IPV6
/*
return 0;
}
-
+struct socket_info_fd {
+ struct socket_info_fd *prev, *next;
+ int fd;
+};
struct socket_info
{
- int fd;
+ struct socket_info_fd *fds;
int family;
int type;
int connected;
int defer_connect;
- char *path;
char *tmp_path;
struct sockaddr *myname;
switch(type) {
case SOCKET_TYPE_CHAR_TCP:
case SOCKET_TYPE_CHAR_UDP: {
- struct sockaddr_in *in2 = (struct sockaddr_in *)in;
+ struct sockaddr_in *in2 = (struct sockaddr_in *)(void *)in;
if ((*len) < sizeof(*in2)) {
errno = EINVAL;
#ifdef HAVE_IPV6
case SOCKET_TYPE_CHAR_TCP_V6:
case SOCKET_TYPE_CHAR_UDP_V6: {
- struct sockaddr_in6 *in2 = (struct sockaddr_in6 *)in;
+ struct sockaddr_in6 *in2 = (struct sockaddr_in6 *)(void *)in;
if ((*len) < sizeof(*in2)) {
errno = EINVAL;
switch (inaddr->sa_family) {
case AF_INET: {
const struct sockaddr_in *in =
- (const struct sockaddr_in *)inaddr;
+ (const struct sockaddr_in *)(const void *)inaddr;
unsigned int addr = ntohl(in->sin_addr.s_addr);
char u_type = '\0';
char b_type = '\0';
#ifdef HAVE_IPV6
case AF_INET6: {
const struct sockaddr_in6 *in =
- (const struct sockaddr_in6 *)inaddr;
- struct in6_addr cmp;
+ (const struct sockaddr_in6 *)(const void *)inaddr;
+ struct in6_addr cmp1, cmp2;
switch (si->type) {
case SOCK_STREAM:
prt = ntohs(in->sin6_port);
- cmp = in->sin6_addr;
- cmp.s6_addr[15] = 0;
- if (IN6_ARE_ADDR_EQUAL(swrap_ipv6(), &cmp)) {
+ cmp1 = *swrap_ipv6();
+ cmp2 = in->sin6_addr;
+ cmp2.s6_addr[15] = 0;
+ if (IN6_ARE_ADDR_EQUAL(&cmp1, &cmp2)) {
iface = in->sin6_addr.s6_addr[15];
} else {
errno = ENETUNREACH;
switch (si->family) {
case AF_INET: {
const struct sockaddr_in *in =
- (const struct sockaddr_in *)inaddr;
+ (const struct sockaddr_in *)(const void *)inaddr;
unsigned int addr = ntohl(in->sin_addr.s_addr);
char u_type = '\0';
char d_type = '\0';
#ifdef HAVE_IPV6
case AF_INET6: {
const struct sockaddr_in6 *in =
- (const struct sockaddr_in6 *)inaddr;
- struct in6_addr cmp;
+ (const struct sockaddr_in6 *)(const void *)inaddr;
+ struct in6_addr cmp1, cmp2;
switch (si->type) {
case SOCK_STREAM:
prt = ntohs(in->sin6_port);
- cmp = in->sin6_addr;
- cmp.s6_addr[15] = 0;
+ cmp1 = *swrap_ipv6();
+ cmp2 = in->sin6_addr;
+ cmp2.s6_addr[15] = 0;
if (IN6_IS_ADDR_UNSPECIFIED(&in->sin6_addr)) {
iface = socket_wrapper_default_iface();
- } else if (IN6_ARE_ADDR_EQUAL(swrap_ipv6(), &cmp)) {
+ } else if (IN6_ARE_ADDR_EQUAL(&cmp1, &cmp2)) {
iface = in->sin6_addr.s6_addr[15];
} else {
errno = EADDRNOTAVAIL;
if (bcast) *bcast = is_bcast;
+ if (iface == 0 || iface > MAX_WRAPPED_INTERFACES) {
+ errno = EINVAL;
+ return -1;
+ }
+
if (prt == 0) {
/* handle auto-allocation of ephemeral ports */
for (prt = 5001; prt < 10000; prt++) {
{
struct socket_info *i;
for (i = sockets; i; i = i->next) {
- if (i->fd == fd)
- return i;
+ struct socket_info_fd *f;
+ for (f = i->fds; f; f = f->next) {
+ if (f->fd == fd) {
+ return i;
+ }
+ }
}
return NULL;
_PUBLIC_ int swrap_socket(int family, int type, int protocol)
{
struct socket_info *si;
+ struct socket_info_fd *fi;
int fd;
int real_type = type;
#ifdef SOCK_CLOEXEC
if (fd == -1) return -1;
si = (struct socket_info *)calloc(1, sizeof(struct socket_info));
+ if (si == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
si->family = family;
* the type, not the flags */
si->type = real_type;
si->protocol = protocol;
- si->fd = fd;
+ fi = (struct socket_info_fd *)calloc(1, sizeof(struct socket_info_fd));
+ if (fi == NULL) {
+ free(si);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ fi->fd = fd;
+
+ SWRAP_DLIST_ADD(si->fds, fi);
SWRAP_DLIST_ADD(sockets, si);
- return si->fd;
+ return fd;
}
_PUBLIC_ int swrap_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
{
struct socket_info *parent_si, *child_si;
+ struct socket_info_fd *child_fi;
int fd;
struct sockaddr_un un_addr;
socklen_t un_addrlen = sizeof(un_addr);
memset(&un_addr, 0, sizeof(un_addr));
memset(&un_my_addr, 0, sizeof(un_my_addr));
- ret = real_accept(s, (struct sockaddr *)&un_addr, &un_addrlen);
+ ret = real_accept(s, (struct sockaddr *)(void *)&un_addr, &un_addrlen);
if (ret == -1) {
free(my_addr);
return ret;
child_si = (struct socket_info *)malloc(sizeof(struct socket_info));
memset(child_si, 0, sizeof(*child_si));
- child_si->fd = fd;
+ child_fi = (struct socket_info_fd *)calloc(1, sizeof(struct socket_info_fd));
+ if (child_fi == NULL) {
+ free(child_si);
+ free(my_addr);
+ close(fd);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ child_fi->fd = fd;
+
+ SWRAP_DLIST_ADD(child_si->fds, child_fi);
+
child_si->family = parent_si->family;
child_si->type = parent_si->type;
child_si->protocol = parent_si->protocol;
child_si->peername = sockaddr_dup(my_addr, len);
if (addr != NULL && addrlen != NULL) {
- *addrlen = len;
- if (*addrlen >= len)
- memcpy(addr, my_addr, len);
- *addrlen = 0;
+ size_t copy_len = MIN(*addrlen, len);
+ if (copy_len > 0) {
+ memcpy(addr, my_addr, copy_len);
+ }
+ *addrlen = len;
}
- ret = real_getsockname(fd, (struct sockaddr *)&un_my_addr, &un_my_addrlen);
+ ret = real_getsockname(fd, (struct sockaddr *)(void *)&un_my_addr,
+ &un_my_addrlen);
if (ret == -1) {
+ free(child_fi);
free(child_si);
close(fd);
return ret;
ret = sockaddr_convert_from_un(child_si, &un_my_addr, un_my_addrlen,
child_si->family, my_addr, &len);
if (ret == -1) {
+ free(child_fi);
free(child_si);
free(my_addr);
close(fd);
assign it here.
Note: this might change the family from ipv6 to ipv4
*/
-static int swrap_auto_bind(struct socket_info *si, int family)
+static int swrap_auto_bind(int fd, struct socket_info *si, int family)
{
struct sockaddr_un un_addr;
int i;
type, socket_wrapper_default_iface(), port);
if (stat(un_addr.sun_path, &st) == 0) continue;
- ret = real_bind(si->fd, (struct sockaddr *)&un_addr, sizeof(un_addr));
+ ret = real_bind(fd, (struct sockaddr *)(void *)&un_addr,
+ sizeof(un_addr));
if (ret == -1) return ret;
si->tmp_path = strdup(un_addr.sun_path);
}
if (si->bound == 0) {
- ret = swrap_auto_bind(si, serv_addr->sa_family);
+ ret = swrap_auto_bind(s, si, serv_addr->sa_family);
if (ret == -1) return -1;
}
return -1;
}
- ret = sockaddr_convert_to_un(si, (const struct sockaddr *)serv_addr,
+ ret = sockaddr_convert_to_un(si, serv_addr,
addrlen, &un_addr, 0, &bcast);
if (ret == -1) return -1;
} else {
swrap_dump_packet(si, serv_addr, SWRAP_CONNECT_SEND, NULL, 0);
- ret = real_connect(s, (struct sockaddr *)&un_addr,
+ ret = real_connect(s, (struct sockaddr *)(void *)&un_addr,
sizeof(struct sockaddr_un));
}
si->myname_len = addrlen;
si->myname = sockaddr_dup(myaddr, addrlen);
- ret = sockaddr_convert_to_un(si, (const struct sockaddr *)myaddr, addrlen, &un_addr, 1, &si->bcast);
+ ret = sockaddr_convert_to_un(si, myaddr, addrlen, &un_addr, 1, &si->bcast);
if (ret == -1) return -1;
unlink(un_addr.sun_path);
- ret = real_bind(s, (struct sockaddr *)&un_addr,
+ ret = real_bind(s, (struct sockaddr *)(void *)&un_addr,
sizeof(struct sockaddr_un));
if (ret == 0) {
}
}
+_PUBLIC_ int swrap_ioctl(int s, int r, void *p)
+{
+ int ret;
+ struct socket_info *si = find_socket_info(s);
+ int value;
+
+ if (!si) {
+ return real_ioctl(s, r, p);
+ }
+
+ ret = real_ioctl(s, r, p);
+
+ switch (r) {
+ case FIONREAD:
+ value = *((int *)p);
+ if (ret == -1 && errno != EAGAIN && errno != ENOBUFS) {
+ swrap_dump_packet(si, NULL, SWRAP_PENDING_RST, NULL, 0);
+ } else if (value == 0) { /* END OF FILE */
+ swrap_dump_packet(si, NULL, SWRAP_PENDING_RST, NULL, 0);
+ }
+ break;
+ }
+
+ return ret;
+}
+
+static ssize_t swrap_sendmsg_before(int fd,
+ struct socket_info *si,
+ struct msghdr *msg,
+ struct iovec *tmp_iov,
+ struct sockaddr_un *tmp_un,
+ const struct sockaddr_un **to_un,
+ const struct sockaddr **to,
+ int *bcast)
+{
+ size_t i, len = 0;
+ ssize_t ret;
+
+ if (to_un) {
+ *to_un = NULL;
+ }
+ if (to) {
+ *to = NULL;
+ }
+ if (bcast) {
+ *bcast = 0;
+ }
+
+ switch (si->type) {
+ case SOCK_STREAM:
+ if (!si->connected) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ if (msg->msg_iovlen == 0) {
+ break;
+ }
+
+ /*
+ * 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)
+ */
+
+ for (i=0; i < msg->msg_iovlen; i++) {
+ size_t nlen;
+ nlen = len + msg->msg_iov[i].iov_len;
+ if (nlen > 1500) {
+ break;
+ }
+ }
+ msg->msg_iovlen = i;
+ if (msg->msg_iovlen == 0) {
+ *tmp_iov = msg->msg_iov[0];
+ tmp_iov->iov_len = MIN(tmp_iov->iov_len, 1500);
+ msg->msg_iov = tmp_iov;
+ msg->msg_iovlen = 1;
+ }
+ break;
+
+ case SOCK_DGRAM:
+ if (si->connected) {
+ if (msg->msg_name) {
+ errno = EISCONN;
+ return -1;
+ }
+ } else {
+ const struct sockaddr *msg_name;
+ msg_name = (const struct sockaddr *)msg->msg_name;
+
+ if (msg_name == NULL) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+
+ ret = sockaddr_convert_to_un(si, msg_name, msg->msg_namelen,
+ tmp_un, 0, bcast);
+ if (ret == -1) return -1;
+
+ if (to_un) {
+ *to_un = tmp_un;
+ }
+ if (to) {
+ *to = msg_name;
+ }
+ msg->msg_name = tmp_un;
+ msg->msg_namelen = sizeof(*tmp_un);
+ }
+
+ if (si->bound == 0) {
+ ret = swrap_auto_bind(fd, si, si->family);
+ if (ret == -1) return -1;
+ }
+
+ if (!si->defer_connect) {
+ break;
+ }
+
+ ret = sockaddr_convert_to_un(si, si->peername, si->peername_len,
+ tmp_un, 0, NULL);
+ if (ret == -1) return -1;
+
+ ret = real_connect(fd, (struct sockaddr *)(void *)tmp_un,
+ sizeof(*tmp_un));
+
+ /* to give better errors */
+ if (ret == -1 && errno == ENOENT) {
+ errno = EHOSTUNREACH;
+ }
+
+ if (ret == -1) {
+ return ret;
+ }
+
+ si->defer_connect = 0;
+ break;
+ default:
+ errno = EHOSTUNREACH;
+ return -1;
+ }
+
+ return 0;
+}
+
+static void swrap_sendmsg_after(struct socket_info *si,
+ struct msghdr *msg,
+ const struct sockaddr *to,
+ ssize_t ret)
+{
+ int saved_errno = errno;
+ size_t i, len = 0;
+ uint8_t *buf;
+ off_t ofs = 0;
+ size_t avail = 0;
+ size_t remain;
+
+ /* to give better errors */
+ if (ret == -1 && saved_errno == ENOENT) {
+ saved_errno = EHOSTUNREACH;
+ }
+
+ for (i=0; i < msg->msg_iovlen; i++) {
+ avail += msg->msg_iov[i].iov_len;
+ }
+
+ if (ret == -1) {
+ remain = MIN(80, avail);
+ } else {
+ remain = ret;
+ }
+
+ /* we capture it as one single packet */
+ buf = (uint8_t *)malloc(remain);
+ if (!buf) {
+ /* we just not capture the packet */
+ errno = saved_errno;
+ return;
+ }
+
+ 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;
+ }
+ len = ofs;
+
+ switch (si->type) {
+ case SOCK_STREAM:
+ if (ret == -1) {
+ swrap_dump_packet(si, NULL, SWRAP_SEND, buf, len);
+ swrap_dump_packet(si, NULL, SWRAP_SEND_RST, NULL, 0);
+ } else {
+ swrap_dump_packet(si, NULL, SWRAP_SEND, buf, len);
+ }
+ break;
+
+ case SOCK_DGRAM:
+ if (si->connected) {
+ to = si->peername;
+ }
+ if (ret == -1) {
+ swrap_dump_packet(si, to, SWRAP_SENDTO, buf, len);
+ swrap_dump_packet(si, to, SWRAP_SENDTO_UNREACH, buf, len);
+ } else {
+ swrap_dump_packet(si, to, SWRAP_SENDTO, buf, len);
+ }
+ break;
+ }
+
+ free(buf);
+ errno = saved_errno;
+}
+
_PUBLIC_ ssize_t swrap_recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen)
{
struct sockaddr_un un_addr;
}
if (!from) {
- from = (struct sockaddr *)&ss;
+ from = (struct sockaddr *)(void *)&ss;
fromlen = &ss_len;
}
/* irix 6.4 forgets to null terminate the sun_path string :-( */
memset(&un_addr, 0, sizeof(un_addr));
- ret = real_recvfrom(s, buf, len, flags, (struct sockaddr *)&un_addr, &un_addrlen);
+ ret = real_recvfrom(s, buf, len, flags,
+ (struct sockaddr *)(void *)&un_addr, &un_addrlen);
if (ret == -1)
return ret;
_PUBLIC_ ssize_t swrap_sendto(int s, const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen)
{
+ struct msghdr msg;
+ struct iovec tmp;
struct sockaddr_un un_addr;
- int ret;
+ const struct sockaddr_un *to_un = NULL;
+ ssize_t ret;
struct socket_info *si = find_socket_info(s);
int bcast = 0;
return real_sendto(s, buf, len, flags, to, tolen);
}
- 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);
+ tmp.iov_base = discard_const_p(char, buf);
+ tmp.iov_len = len;
- ret = real_send(s, buf, len, flags);
- break;
- case SOCK_DGRAM:
- if (si->bound == 0) {
- ret = swrap_auto_bind(si, si->family);
- if (ret == -1) return -1;
- }
-
- ret = sockaddr_convert_to_un(si, to, tolen, &un_addr, 0, &bcast);
- if (ret == -1) return -1;
-
- if (bcast) {
- struct stat st;
- unsigned int iface;
- unsigned int prt = ntohs(((const struct sockaddr_in *)to)->sin_port);
- char type;
-
- type = SOCKET_TYPE_CHAR_UDP;
-
- for(iface=0; iface <= MAX_WRAPPED_INTERFACES; iface++) {
- snprintf(un_addr.sun_path, sizeof(un_addr.sun_path), "%s/"SOCKET_FORMAT,
- socket_wrapper_dir(), type, iface, prt);
- if (stat(un_addr.sun_path, &st) != 0) continue;
-
- /* ignore the any errors in broadcast sends */
- real_sendto(s, buf, len, flags, (struct sockaddr *)&un_addr, sizeof(un_addr));
- }
+ ZERO_STRUCT(msg);
+ msg.msg_name = discard_const_p(struct sockaddr, to); /* optional address */
+ msg.msg_namelen = tolen; /* size of address */
+ msg.msg_iov = &tmp; /* scatter/gather array */
+ msg.msg_iovlen = 1; /* # elements in msg_iov */
+#if 0 /* not available on solaris */
+ msg.msg_control = NULL; /* ancillary data, see below */
+ msg.msg_controllen = 0; /* ancillary data buffer len */
+ msg.msg_flags = 0; /* flags on received message */
+#endif
- swrap_dump_packet(si, to, SWRAP_SENDTO, buf, len);
+ ret = swrap_sendmsg_before(s, si, &msg, &tmp, &un_addr, &to_un, &to, &bcast);
+ if (ret == -1) return -1;
- return len;
- }
+ buf = msg.msg_iov[0].iov_base;
+ len = msg.msg_iov[0].iov_len;
- if (si->defer_connect) {
- ret = real_connect(s, (struct sockaddr *)&un_addr,
- sizeof(un_addr));
+ if (bcast) {
+ struct stat st;
+ unsigned int iface;
+ unsigned int prt = ntohs(((const struct sockaddr_in *)to)->sin_port);
+ char type;
- /* to give better errors */
- if (ret == -1 && errno == ENOENT) {
- errno = EHOSTUNREACH;
- }
+ type = SOCKET_TYPE_CHAR_UDP;
- if (ret == -1) {
- return ret;
- }
- si->defer_connect = 0;
- }
+ for(iface=0; iface <= MAX_WRAPPED_INTERFACES; iface++) {
+ snprintf(un_addr.sun_path, sizeof(un_addr.sun_path), "%s/"SOCKET_FORMAT,
+ socket_wrapper_dir(), type, iface, prt);
+ if (stat(un_addr.sun_path, &st) != 0) continue;
- /* Man page for Linux says:
- * "the error EISONN may be returned when they are not NULL and 0"
- * But in practice it's not on x86/amd64, but on other unix it is
- * (ie. freebsd)
- * So if we are already connected we send NULL/0
- */
- if (si->connected) {
- ret = real_sendto(s, buf, len, flags, NULL, 0);
- } else {
- ret = real_sendto(s, buf, len, flags, (struct sockaddr *)&un_addr, sizeof(un_addr));
+ /* ignore the any errors in broadcast sends */
+ real_sendto(s, buf, len, flags,
+ (struct sockaddr *)(void *)&un_addr,
+ sizeof(un_addr));
}
- break;
- default:
- ret = -1;
- errno = EHOSTUNREACH;
- break;
- }
-
- /* to give better errors */
- if (ret == -1 && errno == ENOENT) {
- errno = EHOSTUNREACH;
- }
- if (ret == -1) {
swrap_dump_packet(si, to, SWRAP_SENDTO, buf, len);
- swrap_dump_packet(si, to, SWRAP_SENDTO_UNREACH, buf, len);
- } else {
- swrap_dump_packet(si, to, SWRAP_SENDTO, buf, ret);
- }
- return ret;
-}
-
-_PUBLIC_ int swrap_ioctl(int s, int r, void *p)
-{
- int ret;
- struct socket_info *si = find_socket_info(s);
- int value;
-
- if (!si) {
- return real_ioctl(s, r, p);
+ return len;
}
- ret = real_ioctl(s, r, p);
+ ret = real_sendto(s, buf, len, flags, (struct sockaddr *)msg.msg_name,
+ msg.msg_namelen);
- switch (r) {
- case FIONREAD:
- value = *((int *)p);
- if (ret == -1 && errno != EAGAIN && errno != ENOBUFS) {
- swrap_dump_packet(si, NULL, SWRAP_PENDING_RST, NULL, 0);
- } else if (value == 0) { /* END OF FILE */
- swrap_dump_packet(si, NULL, SWRAP_PENDING_RST, NULL, 0);
- }
- break;
- }
+ swrap_sendmsg_after(si, &msg, to, ret);
return ret;
}
_PUBLIC_ ssize_t swrap_send(int s, const void *buf, size_t len, int flags)
{
- int ret;
+ struct msghdr msg;
+ struct iovec tmp;
+ struct sockaddr_un un_addr;
+ ssize_t ret;
struct socket_info *si = find_socket_info(s);
if (!si) {
return real_send(s, buf, len, flags);
}
- 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;
- }
+ tmp.iov_base = discard_const_p(char, buf);
+ tmp.iov_len = len;
- 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));
+ ZERO_STRUCT(msg);
+ msg.msg_name = NULL; /* optional address */
+ msg.msg_namelen = 0; /* size of address */
+ msg.msg_iov = &tmp; /* scatter/gather array */
+ msg.msg_iovlen = 1; /* # elements in msg_iov */
+#if 0 /* not available on solaris */
+ msg.msg_control = NULL; /* ancillary data, see below */
+ msg.msg_controllen = 0; /* ancillary data buffer len */
+ msg.msg_flags = 0; /* flags on received message */
+#endif
- /* to give better errors */
- if (ret == -1 && errno == ENOENT) {
- errno = EHOSTUNREACH;
- }
+ ret = swrap_sendmsg_before(s, si, &msg, &tmp, &un_addr, NULL, NULL, NULL);
+ if (ret == -1) return -1;
- if (ret == -1) {
- return ret;
- }
- si->defer_connect = 0;
- }
+ buf = msg.msg_iov[0].iov_base;
+ len = msg.msg_iov[0].iov_len;
ret = real_send(s, buf, len, flags);
- if (ret == -1) {
- swrap_dump_packet(si, NULL, SWRAP_SEND, buf, len);
- swrap_dump_packet(si, NULL, SWRAP_SEND_RST, NULL, 0);
- } else {
- swrap_dump_packet(si, NULL, SWRAP_SEND, buf, ret);
- }
+ swrap_sendmsg_after(si, &msg, NULL, ret);
return ret;
}
-_PUBLIC_ ssize_t swrap_sendmsg(int s, const struct msghdr *msg, int flags)
+_PUBLIC_ ssize_t swrap_sendmsg(int s, const struct msghdr *omsg, int flags)
{
- int ret;
- uint8_t *buf;
- off_t ofs = 0;
- size_t i;
- size_t remain;
-
+ struct msghdr msg;
+ struct iovec tmp;
+ struct sockaddr_un un_addr;
+ const struct sockaddr_un *to_un = NULL;
+ const struct sockaddr *to = NULL;
+ ssize_t ret;
struct socket_info *si = find_socket_info(s);
+ int bcast = 0;
if (!si) {
- return real_sendmsg(s, msg, flags);
- }
+ return real_sendmsg(s, omsg, flags);
+ }
+
+ tmp.iov_base = NULL;
+ tmp.iov_len = 0;
+
+ msg = *omsg;
+#if 0
+ msg.msg_name = omsg->msg_name; /* optional address */
+ msg.msg_namelen = omsg->msg_namelen; /* size of address */
+ msg.msg_iov = omsg->msg_iov; /* scatter/gather array */
+ msg.msg_iovlen = omsg->msg_iovlen; /* # elements in msg_iov */
+ /* the following is not available on solaris */
+ msg.msg_control = omsg->msg_control; /* ancillary data, see below */
+ msg.msg_controllen = omsg->msg_controllen; /* ancillary data buffer len */
+ msg.msg_flags = omsg->msg_flags; /* flags on received message */
+#endif
- if (si->defer_connect) {
- struct sockaddr_un un_addr;
- int bcast = 0;
+ ret = swrap_sendmsg_before(s, si, &msg, &tmp, &un_addr, &to_un, &to, &bcast);
+ if (ret == -1) return -1;
- if (si->bound == 0) {
- ret = swrap_auto_bind(si, si->family);
- if (ret == -1) return -1;
+ if (bcast) {
+ struct stat st;
+ unsigned int iface;
+ unsigned int prt = ntohs(((const struct sockaddr_in *)to)->sin_port);
+ char type;
+ size_t i, len = 0;
+ uint8_t *buf;
+ off_t ofs = 0;
+ size_t avail = 0;
+ size_t remain;
+
+ for (i=0; i < msg.msg_iovlen; i++) {
+ avail += msg.msg_iov[i].iov_len;
}
- ret = sockaddr_convert_to_un(si, si->peername, si->peername_len,
- &un_addr, 0, &bcast);
- if (ret == -1) return -1;
+ len = avail;
+ remain = avail;
- ret = real_connect(s, (struct sockaddr *)&un_addr,
- sizeof(un_addr));
+ /* we capture it as one single packet */
+ buf = (uint8_t *)malloc(remain);
+ if (!buf) {
+ return -1;
+ }
- /* to give better errors */
- if (ret == -1 && errno == ENOENT) {
- errno = EHOSTUNREACH;
+ 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;
}
- if (ret == -1) {
- return ret;
+ type = SOCKET_TYPE_CHAR_UDP;
+
+ for(iface=0; iface <= MAX_WRAPPED_INTERFACES; iface++) {
+ snprintf(un_addr.sun_path, sizeof(un_addr.sun_path), "%s/"SOCKET_FORMAT,
+ socket_wrapper_dir(), type, iface, prt);
+ if (stat(un_addr.sun_path, &st) != 0) continue;
+
+ msg.msg_name = &un_addr; /* optional address */
+ msg.msg_namelen = sizeof(un_addr); /* size of address */
+
+ /* ignore the any errors in broadcast sends */
+ real_sendmsg(s, &msg, flags);
}
- si->defer_connect = 0;
- }
- ret = real_sendmsg(s, msg, flags);
- remain = ret;
+ swrap_dump_packet(si, to, SWRAP_SENDTO, buf, len);
+ free(buf);
- /* we capture it as one single packet */
- buf = (uint8_t *)malloc(ret);
- if (!buf) {
- /* we just not capture the packet */
- errno = 0;
- return ret;
+ return len;
}
- 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;
- }
+ ret = real_sendmsg(s, &msg, flags);
- swrap_dump_packet(si, NULL, SWRAP_SEND, buf, ret);
- free(buf);
- if (ret == -1) {
- swrap_dump_packet(si, NULL, SWRAP_SEND_RST, NULL, 0);
- }
+ swrap_sendmsg_after(si, &msg, to, ret);
return ret;
}
return real_readv(s, vector, count);
}
+ if (!si->connected) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
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
int swrap_writev(int s, const struct iovec *vector, size_t count)
{
- int ret;
+ struct msghdr msg;
+ struct iovec tmp;
+ struct sockaddr_un un_addr;
+ ssize_t 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;
+ tmp.iov_base = NULL;
+ tmp.iov_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;
+ ZERO_STRUCT(msg);
+ msg.msg_name = NULL; /* optional address */
+ msg.msg_namelen = 0; /* size of address */
+ msg.msg_iov = discard_const_p(struct iovec, vector); /* scatter/gather array */
+ msg.msg_iovlen = count; /* # elements in msg_iov */
+#if 0 /* not available on solaris */
+ msg.msg_control = NULL; /* ancillary data, see below */
+ msg.msg_controllen = 0; /* ancillary data buffer len */
+ msg.msg_flags = 0; /* flags on received message */
+#endif
- /* we capture it as one single packet */
- buf = (uint8_t *)malloc(ret);
- if (!buf) {
- /* we just not capture the packet */
- errno = 0;
- return ret;
- }
+ ret = swrap_sendmsg_before(s, si, &msg, &tmp, &un_addr, NULL, NULL, NULL);
+ if (ret == -1) return -1;
- 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;
- }
+ ret = real_writev(s, msg.msg_iov, msg.msg_iovlen);
- swrap_dump_packet(si, NULL, SWRAP_SEND, buf, ret);
- free(buf);
- }
+ swrap_sendmsg_after(si, &msg, NULL, ret);
return ret;
}
_PUBLIC_ int swrap_close(int fd)
{
struct socket_info *si = find_socket_info(fd);
+ struct socket_info_fd *fi;
int ret;
if (!si) {
return real_close(fd);
}
+ for (fi = si->fds; fi; fi = fi->next) {
+ if (fi->fd == fd) {
+ SWRAP_DLIST_REMOVE(si->fds, fi);
+ free(fi);
+ break;
+ }
+ }
+
+ if (si->fds) {
+ /* there are still references left */
+ return real_close(fd);
+ }
+
SWRAP_DLIST_REMOVE(sockets, si);
if (si->myname && si->peername) {
swrap_dump_packet(si, NULL, SWRAP_CLOSE_ACK, NULL, 0);
}
- if (si->path) free(si->path);
if (si->myname) free(si->myname);
if (si->peername) free(si->peername);
if (si->tmp_path) {
return ret;
}
+
+_PUBLIC_ int swrap_dup(int fd)
+{
+ struct socket_info *si;
+ struct socket_info_fd *fi;
+
+ si = find_socket_info(fd);
+
+ if (!si) {
+ return real_dup(fd);
+ }
+
+ fi = (struct socket_info_fd *)calloc(1, sizeof(struct socket_info_fd));
+ if (fi == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ fi->fd = real_dup(fd);
+ if (fi->fd == -1) {
+ int saved_errno = errno;
+ free(fi);
+ errno = saved_errno;
+ return -1;
+ }
+
+ SWRAP_DLIST_ADD(si->fds, fi);
+ return fi->fd;
+}
+
+_PUBLIC_ int swrap_dup2(int fd, int newfd)
+{
+ struct socket_info *si;
+ struct socket_info_fd *fi;
+
+ si = find_socket_info(fd);
+
+ if (!si) {
+ return real_dup2(fd, newfd);
+ }
+
+ if (find_socket_info(newfd)) {
+ /* dup2() does an implicit close of newfd, which we
+ * need to emulate */
+ swrap_close(newfd);
+ }
+
+ fi = (struct socket_info_fd *)calloc(1, sizeof(struct socket_info_fd));
+ if (fi == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ fi->fd = real_dup2(fd, newfd);
+ if (fi->fd == -1) {
+ int saved_errno = errno;
+ free(fi);
+ errno = saved_errno;
+ return -1;
+ }
+
+ SWRAP_DLIST_ADD(si->fds, fi);
+ return fi->fd;
+}