s3-waf: add check for TIME_T_MAX.
[samba.git] / lib / tsocket / tsocket_bsd.c
index 13680ec0c54793d71cc8bbb0d777f3f84155a9db..9e800652270db19de021c90b930212ae3b9006c6 100644 (file)
@@ -190,6 +190,7 @@ static ssize_t tsocket_bsd_pending(int fd)
 static const struct tsocket_address_ops tsocket_address_bsd_ops;
 
 struct tsocket_address_bsd {
+       socklen_t sa_socklen;
        union {
                struct sockaddr sa;
                struct sockaddr_in in;
@@ -261,6 +262,11 @@ int _tsocket_address_bsd_from_sockaddr(TALLOC_CTX *mem_ctx,
 
        memcpy(&bsda->u.ss, sa, sa_socklen);
 
+       bsda->sa_socklen = sa_socklen;
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+       bsda->u.sa.sa_len = bsda->sa_socklen;
+#endif
+
        *_addr = addr;
        return 0;
 }
@@ -271,42 +277,64 @@ ssize_t tsocket_address_bsd_sockaddr(const struct tsocket_address *addr,
 {
        struct tsocket_address_bsd *bsda = talloc_get_type(addr->private_data,
                                           struct tsocket_address_bsd);
-       ssize_t rlen = 0;
 
        if (!bsda) {
                errno = EINVAL;
                return -1;
        }
 
-       switch (bsda->u.sa.sa_family) {
-       case AF_UNIX:
-               rlen = sizeof(struct sockaddr_un);
-               break;
-       case AF_INET:
-               rlen = sizeof(struct sockaddr_in);
-               break;
-#ifdef HAVE_IPV6
-       case AF_INET6:
-               rlen = sizeof(struct sockaddr_in6);
-               break;
-#endif
-       default:
-               errno = EAFNOSUPPORT;
-               return -1;
-       }
-
-       if (sa_socklen < rlen) {
+       if (sa_socklen < bsda->sa_socklen) {
                errno = EINVAL;
                return -1;
        }
 
-       if (sa_socklen > sizeof(struct sockaddr_storage)) {
+       if (sa_socklen > bsda->sa_socklen) {
                memset(sa, 0, sa_socklen);
-               sa_socklen = sizeof(struct sockaddr_storage);
+               sa_socklen = bsda->sa_socklen;
        }
 
        memcpy(sa, &bsda->u.ss, sa_socklen);
-       return rlen;
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+       sa->sa_len = sa_socklen;
+#endif
+       return sa_socklen;
+}
+
+bool tsocket_address_is_inet(const struct tsocket_address *addr, const char *fam)
+{
+       struct tsocket_address_bsd *bsda = talloc_get_type(addr->private_data,
+                                          struct tsocket_address_bsd);
+
+       if (!bsda) {
+               return false;
+       }
+
+       switch (bsda->u.sa.sa_family) {
+       case AF_INET:
+               if (strcasecmp(fam, "ip") == 0) {
+                       return true;
+               }
+
+               if (strcasecmp(fam, "ipv4") == 0) {
+                       return true;
+               }
+
+               return false;
+#ifdef HAVE_IPV6
+       case AF_INET6:
+               if (strcasecmp(fam, "ip") == 0) {
+                       return true;
+               }
+
+               if (strcasecmp(fam, "ipv6") == 0) {
+                       return true;
+               }
+
+               return false;
+#endif
+       }
+
+       return false;
 }
 
 int _tsocket_address_inet_from_strings(TALLOC_CTX *mem_ctx,
@@ -481,6 +509,23 @@ int tsocket_address_inet_set_port(struct tsocket_address *addr,
        return 0;
 }
 
+bool tsocket_address_is_unix(const struct tsocket_address *addr)
+{
+       struct tsocket_address_bsd *bsda = talloc_get_type(addr->private_data,
+                                          struct tsocket_address_bsd);
+
+       if (!bsda) {
+               return false;
+       }
+
+       switch (bsda->u.sa.sa_family) {
+       case AF_UNIX:
+               return true;
+       }
+
+       return false;
+}
+
 int _tsocket_address_unix_from_path(TALLOC_CTX *mem_ctx,
                                    const char *path,
                                    struct tsocket_address **_addr,
@@ -588,7 +633,7 @@ static struct tsocket_address *tsocket_address_bsd_copy(const struct tsocket_add
 
        ret = _tsocket_address_bsd_from_sockaddr(mem_ctx,
                                                 &bsda->u.sa,
-                                                sizeof(bsda->u.ss),
+                                                bsda->sa_socklen,
                                                 &copy,
                                                 location);
        if (ret != 0) {
@@ -827,8 +872,6 @@ static void tdgram_bsd_recvfrom_handler(void *private_data)
        struct tdgram_bsd *bsds = tdgram_context_data(dgram, struct tdgram_bsd);
        struct tsocket_address_bsd *bsda;
        ssize_t ret;
-       struct sockaddr *sa = NULL;
-       socklen_t sa_socklen = 0;
        int err;
        bool retry;
 
@@ -862,18 +905,13 @@ static void tdgram_bsd_recvfrom_handler(void *private_data)
        }
 
        ZERO_STRUCTP(bsda);
+       bsda->sa_socklen = sizeof(bsda->u.ss);
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+       bsda->u.sa.sa_len = bsda->sa_socklen;
+#endif
 
-       sa = &bsda->u.sa;
-       sa_socklen = sizeof(bsda->u.ss);
-       /*
-        * for unix sockets we can't use the size of sockaddr_storage
-        * we would get EINVAL
-        */
-       if (bsda->u.sa.sa_family == AF_UNIX) {
-               sa_socklen = sizeof(bsda->u.un);
-       }
-
-       ret = recvfrom(bsds->fd, state->buf, state->len, 0, sa, &sa_socklen);
+       ret = recvfrom(bsds->fd, state->buf, state->len, 0,
+                      &bsda->u.sa, &bsda->sa_socklen);
        err = tsocket_bsd_error_from_errno(ret, errno, &retry);
        if (retry) {
                /* retry later */
@@ -883,10 +921,17 @@ static void tdgram_bsd_recvfrom_handler(void *private_data)
                return;
        }
 
-       if (ret != state->len) {
-               tevent_req_error(req, EIO);
+       /*
+        * Some systems (FreeBSD, see bug #7115) return too much
+        * bytes in tsocket_bsd_pending()/ioctl(fd, FIONREAD, ...),
+        * the return value includes some IP/UDP header bytes,
+        * while recvfrom() just returns the payload.
+        */
+       state->buf = talloc_realloc(state, state->buf, uint8_t, ret);
+       if (tevent_req_nomem(state->buf, req)) {
                return;
        }
+       state->len = ret;
 
        tevent_req_done(req);
 }
@@ -1012,14 +1057,7 @@ static void tdgram_bsd_sendto_handler(void *private_data)
                        struct tsocket_address_bsd);
 
                sa = &bsda->u.sa;
-               sa_socklen = sizeof(bsda->u.ss);
-               /*
-                * for unix sockets we can't use the size of sockaddr_storage
-                * we would get EINVAL
-                */
-               if (bsda->u.sa.sa_family == AF_UNIX) {
-                       sa_socklen = sizeof(bsda->u.un);
-               }
+               sa_socklen = bsda->sa_socklen;
        }
 
        ret = sendto(bsds->fd, state->buf, state->len, 0, sa, sa_socklen);
@@ -1078,6 +1116,7 @@ static struct tevent_req *tdgram_bsd_disconnect_send(TALLOC_CTX *mem_ctx,
                goto post;
        }
 
+       TALLOC_FREE(bsds->fde);
        ret = close(bsds->fd);
        bsds->fd = -1;
        err = tsocket_bsd_error_from_errno(ret, errno, &dummy);
@@ -1145,7 +1184,6 @@ static int tdgram_bsd_dgram_socket(const struct tsocket_address *local,
        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) {
                rbsda = talloc_get_type_abort(remote->private_data,
@@ -1162,11 +1200,6 @@ static int tdgram_bsd_dgram_socket(const struct tsocket_address *local,
                        do_reuseaddr = true;
                        do_bind = true;
                }
-               /*
-                * for unix sockets we can't use the size of sockaddr_storage
-                * we would get EINVAL
-                */
-               sa_socklen = sizeof(lbsda->u.un);
                break;
        case AF_INET:
                if (lbsda->u.in.sin_port != 0) {
@@ -1177,7 +1210,6 @@ static int tdgram_bsd_dgram_socket(const struct tsocket_address *local,
                        do_bind = true;
                }
                is_inet = true;
-               sa_socklen = sizeof(rbsda->u.in);
                break;
 #ifdef HAVE_IPV6
        case AF_INET6:
@@ -1191,7 +1223,6 @@ static int tdgram_bsd_dgram_socket(const struct tsocket_address *local,
                        do_bind = true;
                }
                is_inet = true;
-               sa_socklen = sizeof(rbsda->u.in6);
                do_ipv6only = true;
                break;
 #endif
@@ -1204,12 +1235,10 @@ static int tdgram_bsd_dgram_socket(const struct tsocket_address *local,
                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
@@ -1218,12 +1247,12 @@ static int tdgram_bsd_dgram_socket(const struct tsocket_address *local,
 
        fd = socket(sa_fam, SOCK_DGRAM, 0);
        if (fd < 0) {
-               return fd;
+               return -1;
        }
 
        fd = tsocket_bsd_common_prepare_fd(fd, true);
        if (fd < 0) {
-               return fd;
+               return -1;
        }
 
        dgram = tdgram_context_create(mem_ctx,
@@ -1251,7 +1280,7 @@ static int tdgram_bsd_dgram_socket(const struct tsocket_address *local,
                        int saved_errno = errno;
                        talloc_free(dgram);
                        errno = saved_errno;
-                       return ret;
+                       return -1;
                }
        }
 #endif
@@ -1265,7 +1294,7 @@ static int tdgram_bsd_dgram_socket(const struct tsocket_address *local,
                        int saved_errno = errno;
                        talloc_free(dgram);
                        errno = saved_errno;
-                       return ret;
+                       return -1;
                }
        }
 
@@ -1278,17 +1307,17 @@ static int tdgram_bsd_dgram_socket(const struct tsocket_address *local,
                        int saved_errno = errno;
                        talloc_free(dgram);
                        errno = saved_errno;
-                       return ret;
+                       return -1;
                }
        }
 
        if (do_bind) {
-               ret = bind(fd, &lbsda->u.sa, sa_socklen);
+               ret = bind(fd, &lbsda->u.sa, lbsda->sa_socklen);
                if (ret == -1) {
                        int saved_errno = errno;
                        talloc_free(dgram);
                        errno = saved_errno;
-                       return ret;
+                       return -1;
                }
        }
 
@@ -1299,12 +1328,12 @@ static int tdgram_bsd_dgram_socket(const struct tsocket_address *local,
                        return -1;
                }
 
-               ret = connect(fd, &rbsda->u.sa, sa_socklen);
+               ret = connect(fd, &rbsda->u.sa, rbsda->sa_socklen);
                if (ret == -1) {
                        int saved_errno = errno;
                        talloc_free(dgram);
                        errno = saved_errno;
-                       return ret;
+                       return -1;
                }
        }
 
@@ -1644,7 +1673,7 @@ static void tstream_bsd_readv_handler(void *private_data)
                        uint8_t *base;
                        base = (uint8_t *)state->vector[0].iov_base;
                        base += ret;
-                       state->vector[0].iov_base = base;
+                       state->vector[0].iov_base = (void *)base;
                        state->vector[0].iov_len -= ret;
                        break;
                }
@@ -1804,7 +1833,7 @@ static void tstream_bsd_writev_handler(void *private_data)
                        uint8_t *base;
                        base = (uint8_t *)state->vector[0].iov_base;
                        base += ret;
-                       state->vector[0].iov_base = base;
+                       state->vector[0].iov_base = (void *)base;
                        state->vector[0].iov_len -= ret;
                        break;
                }
@@ -1875,6 +1904,7 @@ static struct tevent_req *tstream_bsd_disconnect_send(TALLOC_CTX *mem_ctx,
                goto post;
        }
 
+       TALLOC_FREE(bsds->fde);
        ret = close(bsds->fd);
        bsds->fd = -1;
        err = tsocket_bsd_error_from_errno(ret, errno, &dummy);
@@ -1952,6 +1982,7 @@ struct tstream_bsd_connect_state {
        int fd;
        struct tevent_fd *fde;
        struct tstream_conext *stream;
+       struct tsocket_address *local;
 };
 
 static int tstream_bsd_connect_destructor(struct tstream_bsd_connect_state *state)
@@ -1970,7 +2001,7 @@ static void tstream_bsd_connect_fde_handler(struct tevent_context *ev,
                                            uint16_t flags,
                                            void *private_data);
 
-static struct tevent_req * tstream_bsd_connect_send(TALLOC_CTX *mem_ctx,
+static struct tevent_req *tstream_bsd_connect_send(TALLOC_CTX *mem_ctx,
                                        struct tevent_context *ev,
                                        int sys_errno,
                                        const struct tsocket_address *local,
@@ -1981,6 +2012,7 @@ static struct tevent_req * tstream_bsd_connect_send(TALLOC_CTX *mem_ctx,
        struct tsocket_address_bsd *lbsda =
                talloc_get_type_abort(local->private_data,
                struct tsocket_address_bsd);
+       struct tsocket_address_bsd *lrbsda = NULL;
        struct tsocket_address_bsd *rbsda =
                talloc_get_type_abort(remote->private_data,
                struct tsocket_address_bsd);
@@ -1992,7 +2024,6 @@ static struct tevent_req * tstream_bsd_connect_send(TALLOC_CTX *mem_ctx,
        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,
                                struct tstream_bsd_connect_state);
@@ -2016,11 +2047,6 @@ static struct tevent_req * tstream_bsd_connect_send(TALLOC_CTX *mem_ctx,
                        do_reuseaddr = true;
                        do_bind = true;
                }
-               /*
-                * for unix sockets we can't use the size of sockaddr_storage
-                * we would get EINVAL
-                */
-               sa_socklen = sizeof(rbsda->u.un);
                break;
        case AF_INET:
                if (lbsda->u.in.sin_port != 0) {
@@ -2031,7 +2057,6 @@ static struct tevent_req * tstream_bsd_connect_send(TALLOC_CTX *mem_ctx,
                        do_bind = true;
                }
                is_inet = true;
-               sa_socklen = sizeof(rbsda->u.in);
                break;
 #ifdef HAVE_IPV6
        case AF_INET6:
@@ -2045,7 +2070,6 @@ static struct tevent_req * tstream_bsd_connect_send(TALLOC_CTX *mem_ctx,
                        do_bind = true;
                }
                is_inet = true;
-               sa_socklen = sizeof(rbsda->u.in6);
                do_ipv6only = true;
                break;
 #endif
@@ -2058,18 +2082,33 @@ static struct tevent_req * tstream_bsd_connect_send(TALLOC_CTX *mem_ctx,
                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
                }
        }
 
+       if (is_inet) {
+               state->local = tsocket_address_create(state,
+                                                     &tsocket_address_bsd_ops,
+                                                     &lrbsda,
+                                                     struct tsocket_address_bsd,
+                                                     __location__ "bsd_connect");
+               if (tevent_req_nomem(state->local, req)) {
+                       goto post;
+               }
+
+               ZERO_STRUCTP(lrbsda);
+               lrbsda->sa_socklen = sizeof(lrbsda->u.ss);
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+               lrbsda->u.sa.sa_len = lrbsda->sa_socklen;
+#endif
+       }
+
        state->fd = socket(sa_fam, SOCK_STREAM, 0);
        if (state->fd == -1) {
                tevent_req_error(req, errno);
@@ -2107,7 +2146,7 @@ static struct tevent_req * tstream_bsd_connect_send(TALLOC_CTX *mem_ctx,
        }
 
        if (do_bind) {
-               ret = bind(state->fd, &lbsda->u.sa, sa_socklen);
+               ret = bind(state->fd, &lbsda->u.sa, lbsda->sa_socklen);
                if (ret == -1) {
                        tevent_req_error(req, errno);
                        goto post;
@@ -2119,7 +2158,7 @@ static struct tevent_req * tstream_bsd_connect_send(TALLOC_CTX *mem_ctx,
                goto post;
        }
 
-       ret = connect(state->fd, &rbsda->u.sa, sa_socklen);
+       ret = connect(state->fd, &rbsda->u.sa, rbsda->sa_socklen);
        err = tsocket_bsd_error_from_errno(ret, errno, &retry);
        if (retry) {
                /* retry later */
@@ -2129,6 +2168,17 @@ static struct tevent_req * tstream_bsd_connect_send(TALLOC_CTX *mem_ctx,
                goto post;
        }
 
+       if (!state->local) {
+               tevent_req_done(req);
+               goto post;
+       }
+
+       ret = getsockname(state->fd, &lrbsda->u.sa, &lrbsda->sa_socklen);
+       if (ret == -1) {
+               tevent_req_error(req, errno);
+               goto post;
+       }
+
        tevent_req_done(req);
        goto post;
 
@@ -2158,6 +2208,7 @@ static void tstream_bsd_connect_fde_handler(struct tevent_context *ev,
                                 struct tevent_req);
        struct tstream_bsd_connect_state *state = tevent_req_data(req,
                                        struct tstream_bsd_connect_state);
+       struct tsocket_address_bsd *lrbsda = NULL;
        int ret;
        int error=0;
        socklen_t len = sizeof(error);
@@ -2180,6 +2231,20 @@ static void tstream_bsd_connect_fde_handler(struct tevent_context *ev,
                return;
        }
 
+       if (!state->local) {
+               tevent_req_done(req);
+               return;
+       }
+
+       lrbsda = talloc_get_type_abort(state->local->private_data,
+                                      struct tsocket_address_bsd);
+
+       ret = getsockname(state->fd, &lrbsda->u.sa, &lrbsda->sa_socklen);
+       if (ret == -1) {
+               tevent_req_error(req, errno);
+               return;
+       }
+
        tevent_req_done(req);
 }
 
@@ -2187,6 +2252,7 @@ static int tstream_bsd_connect_recv(struct tevent_req *req,
                                    int *perrno,
                                    TALLOC_CTX *mem_ctx,
                                    struct tstream_context **stream,
+                                   struct tsocket_address **local,
                                    const char *location)
 {
        struct tstream_bsd_connect_state *state = tevent_req_data(req,
@@ -2205,6 +2271,10 @@ static int tstream_bsd_connect_recv(struct tevent_req *req,
                }
                TALLOC_FREE(state->fde);
                state->fd = -1;
+
+               if (local) {
+                       *local = talloc_move(mem_ctx, &state->local);
+               }
        }
 
 done:
@@ -2244,9 +2314,12 @@ int _tstream_inet_tcp_connect_recv(struct tevent_req *req,
                                   int *perrno,
                                   TALLOC_CTX *mem_ctx,
                                   struct tstream_context **stream,
+                                  struct tsocket_address **local,
                                   const char *location)
 {
-       return tstream_bsd_connect_recv(req, perrno, mem_ctx, stream, location);
+       return tstream_bsd_connect_recv(req, perrno,
+                                       mem_ctx, stream, local,
+                                       location);
 }
 
 struct tevent_req * tstream_unix_connect_send(TALLOC_CTX *mem_ctx,
@@ -2279,7 +2352,9 @@ int _tstream_unix_connect_recv(struct tevent_req *req,
                                      struct tstream_context **stream,
                                      const char *location)
 {
-       return tstream_bsd_connect_recv(req, perrno, mem_ctx, stream, location);
+       return tstream_bsd_connect_recv(req, perrno,
+                                       mem_ctx, stream, NULL,
+                                       location);
 }
 
 int _tstream_unix_socketpair(TALLOC_CTX *mem_ctx1,