X-Git-Url: http://git.samba.org/samba.git/?p=ira%2Fwip.git;a=blobdiff_plain;f=lib%2Fsocket_wrapper%2Fsocket_wrapper.c;h=9d732ee65299d7e18d7e9222c11776f1e7329eab;hp=f9ef48e4d66f5766bf901ecadb0a734dd9d46182;hb=e4c2f4dc75ad38bfeb7c8b8015a48b6b49b22b90;hpb=27cf23958b02b05becce6e7c68347f6fea5b7845 diff --git a/lib/socket_wrapper/socket_wrapper.c b/lib/socket_wrapper/socket_wrapper.c index f9ef48e4d66..9d732ee6529 100644 --- a/lib/socket_wrapper/socket_wrapper.c +++ b/lib/socket_wrapper/socket_wrapper.c @@ -1,6 +1,6 @@ /* * Copyright (C) Jelmer Vernooij 2005,2008 - * Copyright (C) Stefan Metzmacher 2006 + * Copyright (C) Stefan Metzmacher 2006-2009 * * All rights reserved. * @@ -118,9 +118,13 @@ #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 @@ -149,11 +153,24 @@ /* * 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) @@ -202,6 +219,8 @@ struct socket_info int bound; int bcast; int is_server; + int connected; + int defer_connect; char *path; char *tmp_path; @@ -304,7 +323,7 @@ static int convert_un_in(const struct sockaddr_un *un, struct sockaddr *in, sock 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); @@ -330,7 +349,7 @@ static int convert_in_un_remote(struct socket_info *si, const struct sockaddr *i if (bcast) *bcast = 0; - switch (si->family) { + switch (inaddr->sa_family) { case AF_INET: { const struct sockaddr_in *in = (const struct sockaddr_in *)inaddr; @@ -394,7 +413,7 @@ static int convert_in_un_remote(struct socket_info *si, const struct sockaddr *i 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; @@ -510,7 +529,9 @@ static int convert_in_un_alloc(struct socket_info *si, const struct sockaddr *in 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; @@ -649,7 +670,7 @@ enum swrap_packet_type { SWRAP_SEND_RST, SWRAP_CLOSE_SEND, SWRAP_CLOSE_RECV, - SWRAP_CLOSE_ACK + SWRAP_CLOSE_ACK, }; struct swrap_file_hdr { @@ -831,11 +852,11 @@ static uint8_t *swrap_packet_init(struct timeval *tval, 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; @@ -1083,8 +1104,10 @@ static uint8_t *swrap_marshall_packet(struct socket_info *si, switch (si->family) { case AF_INET: break; +#ifdef HAVE_IPV6 case AF_INET6: break; +#endif default: return NULL; } @@ -1362,6 +1385,13 @@ _PUBLIC_ int swrap_socket(int family, int type, int protocol) { 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); @@ -1380,7 +1410,7 @@ _PUBLIC_ int swrap_socket(int family, int type, int protocol) return -1; } - switch (type) { + switch (real_type) { case SOCK_STREAM: break; case SOCK_DGRAM: @@ -1394,12 +1424,12 @@ _PUBLIC_ int swrap_socket(int family, int type, int protocol) 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*/ @@ -1408,6 +1438,8 @@ _PUBLIC_ int swrap_socket(int family, int type, int protocol) 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; @@ -1415,7 +1447,10 @@ _PUBLIC_ int swrap_socket(int family, int type, int protocol) 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; @@ -1485,6 +1520,7 @@ _PUBLIC_ int swrap_accept(int s, struct sockaddr *addr, socklen_t *addrlen) 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); @@ -1532,8 +1568,10 @@ static int autobind_start; /* 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; @@ -1551,7 +1589,7 @@ static int swrap_auto_bind(struct socket_info *si) un_addr.sun_family = AF_UNIX; - switch (si->family) { + switch (family) { case AF_INET: { struct sockaddr_in in; @@ -1580,6 +1618,11 @@ static int swrap_auto_bind(struct socket_info *si) 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; @@ -1594,7 +1637,7 @@ static int swrap_auto_bind(struct socket_info *si) 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); @@ -1630,6 +1673,7 @@ static int swrap_auto_bind(struct socket_info *si) return -1; } + si->family = family; set_port(si->family, port, si->myname); return 0; @@ -1647,7 +1691,7 @@ _PUBLIC_ int swrap_connect(int s, const struct sockaddr *serv_addr, socklen_t ad } if (si->bound == 0) { - ret = swrap_auto_bind(si); + ret = swrap_auto_bind(si, serv_addr->sa_family); if (ret == -1) return -1; } @@ -1659,10 +1703,15 @@ _PUBLIC_ int swrap_connect(int s, const struct sockaddr *serv_addr, socklen_t ad 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) { @@ -1672,6 +1721,7 @@ _PUBLIC_ int swrap_connect(int s, const struct sockaddr *serv_addr, socklen_t ad 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); @@ -1789,6 +1839,10 @@ _PUBLIC_ int swrap_setsockopt(int s, int level, int optname, const void *o switch (si->family) { case AF_INET: return 0; +#ifdef HAVE_IPV6 + case AF_INET6: + return 0; +#endif default: errno = ENOPROTOOPT; return -1; @@ -1801,12 +1855,24 @@ _PUBLIC_ ssize_t swrap_recvfrom(int s, void *buf, size_t len, int flags, struct 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)); @@ -1836,15 +1902,28 @@ _PUBLIC_ ssize_t swrap_sendto(int s, const void *buf, size_t len, int flags, con 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; } @@ -1872,7 +1951,22 @@ _PUBLIC_ ssize_t swrap_sendto(int s, const void *buf, size_t len, int flags, con 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: @@ -1931,7 +2025,12 @@ _PUBLIC_ ssize_t swrap_recv(int s, void *buf, size_t len, int flags) 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) { @@ -1945,6 +2044,34 @@ _PUBLIC_ ssize_t swrap_recv(int s, void *buf, size_t len, int flags) 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) { @@ -1955,7 +2082,39 @@ _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); @@ -1969,6 +2128,208 @@ _PUBLIC_ ssize_t swrap_send(int s, const void *buf, size_t len, int 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);