Spelling fixes in lib/zlib.
[ira/wip.git] / lib / socket_wrapper / socket_wrapper.c
index f9ef48e4d66f5766bf901ecadb0a734dd9d46182..9d732ee65299d7e18d7e9222c11776f1e7329eab 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * 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)
@@ -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);