to remote host was refused. FreeBSD connect(2) call returns EINVAL in that case and clears the error code, so there's no way to determine what happened. Reimplement ruby_connect via select call instead of polling the status by connect(2). This may also reduce overhead (though, not verified). Reported by: Saku Ytti <saku@ytti.fi>
106 lines
2.2 KiB
C
106 lines
2.2 KiB
C
--- ext/socket/socket.c.orig 2009-02-23 00:54:12.000000000 +0300
|
|
+++ ext/socket/socket.c 2009-02-23 01:27:13.000000000 +0300
|
|
@@ -1111,81 +1111,33 @@
|
|
fcntl(fd, F_SETFL, mode|NONBLOCKING);
|
|
#endif /* HAVE_FCNTL */
|
|
|
|
- for (;;) {
|
|
#if defined(SOCKS) && !defined(SOCKS5)
|
|
- if (socks) {
|
|
- status = Rconnect(fd, sockaddr, len);
|
|
- }
|
|
- else
|
|
-#endif
|
|
- {
|
|
- status = connect(fd, sockaddr, len);
|
|
- }
|
|
- if (status < 0) {
|
|
- switch (errno) {
|
|
- case EAGAIN:
|
|
-#ifdef EINPROGRESS
|
|
- case EINPROGRESS:
|
|
-#endif
|
|
-#if WAIT_IN_PROGRESS > 0
|
|
- sockerrlen = sizeof(sockerr);
|
|
- status = getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&sockerr, &sockerrlen);
|
|
- if (status) break;
|
|
- if (sockerr) {
|
|
- status = -1;
|
|
- errno = sockerr;
|
|
- break;
|
|
- }
|
|
-#endif
|
|
-#ifdef EALREADY
|
|
- case EALREADY:
|
|
-#endif
|
|
-#if WAIT_IN_PROGRESS > 0
|
|
- wait_in_progress = WAIT_IN_PROGRESS;
|
|
-#endif
|
|
- status = wait_connectable(fd);
|
|
- if (status) {
|
|
- break;
|
|
- }
|
|
- errno = 0;
|
|
- continue;
|
|
-
|
|
-#if WAIT_IN_PROGRESS > 0
|
|
- case EINVAL:
|
|
- if (wait_in_progress-- > 0) {
|
|
- /*
|
|
- * connect() after EINPROGRESS returns EINVAL on
|
|
- * some platforms, need to check true error
|
|
- * status.
|
|
- */
|
|
- sockerrlen = sizeof(sockerr);
|
|
- status = getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&sockerr, &sockerrlen);
|
|
- if (!status && !sockerr) {
|
|
- struct timeval tv = {0, 100000};
|
|
- rb_thread_wait_for(tv);
|
|
- continue;
|
|
- }
|
|
- status = -1;
|
|
- errno = sockerr;
|
|
- }
|
|
- break;
|
|
-#endif
|
|
-
|
|
-#ifdef EISCONN
|
|
- case EISCONN:
|
|
- status = 0;
|
|
- errno = 0;
|
|
- break;
|
|
+ if (socks) {
|
|
+ status = Rconnect(fd, sockaddr, len);
|
|
+ }
|
|
+ else
|
|
#endif
|
|
- default:
|
|
- break;
|
|
+ {
|
|
+ status = connect(fd, sockaddr, len);
|
|
+ }
|
|
+
|
|
+ if (status < 0 && (errno == EINPROGRESS || errno == EWOULDBLOCK)) {
|
|
+ status = wait_connectable(fd);
|
|
+ if (status == 0) {
|
|
+ int buf;
|
|
+ char c;
|
|
+ int len = sizeof(buf);
|
|
+ status = getpeername(fd, (struct sockaddr *)&buf, &len);
|
|
+ if (status == -1) {
|
|
+ read(fd, &c, 1); /* set errno. */
|
|
}
|
|
}
|
|
+ }
|
|
+
|
|
#ifdef HAVE_FCNTL
|
|
- fcntl(fd, F_SETFL, mode);
|
|
+ fcntl(fd, F_SETFL, mode);
|
|
#endif
|
|
- return status;
|
|
- }
|
|
+ return status;
|
|
}
|
|
|
|
struct inetsock_arg
|