+static int
+timeout_connect(int sockfd, const struct sockaddr *serv_addr,
+ socklen_t addrlen, int timeout)
+{
+ fd_set *fdset;
+ struct timeval tv;
+ socklen_t optlen;
+ int fdsetsz, optval, rc, result = -1;
+
+ if (timeout <= 0)
+ return (connect(sockfd, serv_addr, addrlen));
+
+ set_nonblock(sockfd);
+ rc = connect(sockfd, serv_addr, addrlen);
+ if (rc == 0) {
+ unset_nonblock(sockfd);
+ return (0);
+ }
+ if (errno != EINPROGRESS)
+ return (-1);
+
+ fdsetsz = howmany(sockfd + 1, NFDBITS) * sizeof(fd_mask);
+ fdset = (fd_set *)xmalloc(fdsetsz);
+
+ memset(fdset, 0, fdsetsz);
+ FD_SET(sockfd, fdset);
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+
+ for(;;) {
+ rc = select(sockfd + 1, NULL, fdset, NULL, &tv);
+ if (rc != -1 || errno != EINTR)
+ break;
+ }
+
+ switch(rc) {
+ case 0:
+ /* Timed out */
+ errno = ETIMEDOUT;
+ break;
+ case -1:
+ /* Select error */
+ debug("select: %s", strerror(errno));
+ break;
+ case 1:
+ /* Completed or failed */
+ optval = 0;
+ optlen = sizeof(optval);
+ if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval,
+ &optlen) == -1) {
+ debug("getsockopt: %s", strerror(errno));
+ break;
+ }
+ if (optval != 0) {
+ errno = optval;
+ break;
+ }
+ result = 0;
+ unset_nonblock(sockfd);
+ break;
+ default:
+ /* Should not occur */
+ fatal("Bogus return (%d) from select()", rc);
+ }
+
+ xfree(fdset);
+ return (result);
+}
+