tsocket/bsd: fix bug #7115 FreeBSD includes the UDP header in FIONREAD
[ira/wip.git] / lib / tsocket / tsocket_bsd.c
index d44525338c63bbe4f27fd23a1e006445c65949b6..9027bc97114f0b10286ede18b416f2ead919bc96 100644 (file)
@@ -3,7 +3,7 @@
 
    Copyright (C) Stefan Metzmacher 2009
 
-     ** NOTE! The following LGPL license applies to the tevent
+     ** NOTE! The following LGPL license applies to the tsocket
      ** library. This does NOT imply that all of Samba is released
      ** under the LGPL
 
@@ -210,11 +210,15 @@ int _tsocket_address_bsd_from_sockaddr(TALLOC_CTX *mem_ctx,
        struct tsocket_address *addr;
        struct tsocket_address_bsd *bsda;
 
+       if (sa_socklen < sizeof(sa->sa_family)) {
+               errno = EINVAL;
+               return -1;
+       }
+
        switch (sa->sa_family) {
        case AF_UNIX:
-               if (sa_socklen < sizeof(struct sockaddr_un)) {
-                       errno = EINVAL;
-                       return -1;
+               if (sa_socklen > sizeof(struct sockaddr_un)) {
+                       sa_socklen = sizeof(struct sockaddr_un);
                }
                break;
        case AF_INET:
@@ -222,6 +226,7 @@ int _tsocket_address_bsd_from_sockaddr(TALLOC_CTX *mem_ctx,
                        errno = EINVAL;
                        return -1;
                }
+               sa_socklen = sizeof(struct sockaddr_in);
                break;
 #ifdef HAVE_IPV6
        case AF_INET6:
@@ -229,6 +234,7 @@ int _tsocket_address_bsd_from_sockaddr(TALLOC_CTX *mem_ctx,
                        errno = EINVAL;
                        return -1;
                }
+               sa_socklen = sizeof(struct sockaddr_in6);
                break;
 #endif
        default:
@@ -877,10 +883,12 @@ static void tdgram_bsd_recvfrom_handler(void *private_data)
                return;
        }
 
-       if (ret != state->len) {
-               tevent_req_error(req, EIO);
-               return;
-       }
+       /*
+        * some systems too much bytes in tsocket_bsd_pending()
+        * the return value includes some IP/UDP header bytes
+        */
+       state->len = ret;
+       talloc_realloc(state, state->buf, uint8_t, ret);
 
        tevent_req_done(req);
 }
@@ -1136,6 +1144,9 @@ static int tdgram_bsd_dgram_socket(const struct tsocket_address *local,
        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) {
@@ -1164,9 +1175,11 @@ static int tdgram_bsd_dgram_socket(const struct tsocket_address *local,
                        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:
@@ -1179,6 +1192,9 @@ static int tdgram_bsd_dgram_socket(const struct tsocket_address *local,
                           sizeof(in6addr_any)) != 0) {
                        do_bind = true;
                }
+               is_inet = true;
+               sa_socklen = sizeof(rbsda->u.in6);
+               do_ipv6only = true;
                break;
 #endif
        default:
@@ -1186,7 +1202,23 @@ static int tdgram_bsd_dgram_socket(const struct tsocket_address *local,
                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;
        }
@@ -1211,6 +1243,21 @@ static int tdgram_bsd_dgram_socket(const struct tsocket_address *local,
        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;
 
@@ -1248,6 +1295,12 @@ static int tdgram_bsd_dgram_socket(const struct tsocket_address *local,
        }
 
        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;
@@ -1938,6 +1991,9 @@ static struct tevent_req * tstream_bsd_connect_send(TALLOC_CTX *mem_ctx,
        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,
@@ -1973,9 +2029,11 @@ static struct tevent_req * tstream_bsd_connect_send(TALLOC_CTX *mem_ctx,
                        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:
@@ -1988,6 +2046,9 @@ static struct tevent_req * tstream_bsd_connect_send(TALLOC_CTX *mem_ctx,
                           sizeof(in6addr_any)) != 0) {
                        do_bind = true;
                }
+               is_inet = true;
+               sa_socklen = sizeof(rbsda->u.in6);
+               do_ipv6only = true;
                break;
 #endif
        default:
@@ -1995,7 +2056,23 @@ static struct tevent_req * tstream_bsd_connect_send(TALLOC_CTX *mem_ctx,
                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;
@@ -2007,6 +2084,19 @@ static struct tevent_req * tstream_bsd_connect_send(TALLOC_CTX *mem_ctx,
                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;
 
@@ -2019,13 +2109,18 @@ static struct tevent_req * tstream_bsd_connect_send(TALLOC_CTX *mem_ctx,
        }
 
        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) {