diff options
author | Bram Moolenaar <Bram@vim.org> | 2016-02-28 20:51:49 +0100 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2016-02-28 20:51:49 +0100 |
commit | d42119fff228434fe57e88d501c744de0a9fb1b1 (patch) | |
tree | 70fbb85dcb32be278238ea2d0a8b62d195ee2990 /src | |
parent | 29e1951e14907b62797554ad0cc85cbbe75a1be4 (diff) | |
download | vim-git-d42119fff228434fe57e88d501c744de0a9fb1b1.tar.gz |
patch 7.4.1457v7.4.1457
Problem: Opening a channel with select() is not done properly.
Solution: Also used read-fds. Use getsockopt() to check for errors. (Ozaki
Kiichi)
Diffstat (limited to 'src')
-rw-r--r-- | src/channel.c | 69 | ||||
-rw-r--r-- | src/version.c | 2 |
2 files changed, 37 insertions, 34 deletions
diff --git a/src/channel.c b/src/channel.c index 9e406abdb..61156be38 100644 --- a/src/channel.c +++ b/src/channel.c @@ -28,6 +28,8 @@ # define ECONNREFUSED WSAECONNREFUSED # undef EWOULDBLOCK # define EWOULDBLOCK WSAEWOULDBLOCK +# undef EINPROGRESS +# define EINPROGRESS WSAEINPROGRESS # ifdef EINTR # undef EINTR # endif @@ -550,8 +552,6 @@ channel_open( #else int port = port_in; struct timeval start_tv; - int so_error; - socklen_t so_error_len = sizeof(so_error); #endif channel_T *channel; int ret; @@ -633,7 +633,6 @@ channel_open( { if (errno != EWOULDBLOCK && errno != ECONNREFUSED - #ifdef EINPROGRESS && errno != EINPROGRESS #endif @@ -653,14 +652,13 @@ channel_open( if (waittime >= 0 && ret < 0) { struct timeval tv; + fd_set rfds; fd_set wfds; -#if defined(__APPLE__) && __APPLE__ == 1 -# define PASS_RFDS - fd_set rfds; + int so_error = 0; + socklen_t so_error_len = sizeof(so_error); FD_ZERO(&rfds); FD_SET(sd, &rfds); -#endif FD_ZERO(&wfds); FD_SET(sd, &wfds); @@ -671,13 +669,7 @@ channel_open( #endif ch_logn(channel, "Waiting for connection (waittime %d msec)...", waittime); - ret = select((int)sd + 1, -#ifdef PASS_RFDS - &rfds, -#else - NULL, -#endif - &wfds, NULL, &tv); + ret = select((int)sd + 1, &rfds, &wfds, NULL, &tv); if (ret < 0) { @@ -689,30 +681,39 @@ channel_open( channel_free(channel); return NULL; } -#ifdef PASS_RFDS - if (ret == 0 && FD_ISSET(sd, &rfds) && FD_ISSET(sd, &wfds)) - { - /* For OS X, this implies error. See tcp(4). */ - ch_error(channel, "channel_open: Connect failed"); - EMSG(_(e_cannot_connect)); - sock_close(sd); - channel_free(channel); - return NULL; - } -#endif -#ifdef WIN32 - /* On Win32 select() is expected to work and wait for up to the - * waittime for the socket to be open. */ - if (!FD_ISSET(sd, &wfds) || ret == 0) -#else - /* See socket(7) for the behavior on Linux-like systems: + + /* On Win32: select() is expected to work and wait for up to the + * waittime for the socket to be open. + * On Linux-like systems: See socket(7) for the behavior * After putting the socket in non-blocking mode, connect() will * return EINPROGRESS, select() will not wait (as if writing is * possible), need to use getsockopt() to check if the socket is - * actually open. */ - getsockopt(sd, SOL_SOCKET, SO_ERROR, &so_error, &so_error_len); - if (!FD_ISSET(sd, &wfds) || ret == 0 || so_error != 0) + * actually connect. + * We detect an failure to connect when both read and write fds + * are set. Use getsockopt() to find out what kind of failure. */ + if (FD_ISSET(sd, &rfds) && FD_ISSET(sd, &wfds)) + { + ret = getsockopt(sd, + SOL_SOCKET, SO_ERROR, &so_error, &so_error_len); + if (ret < 0 || (so_error != 0 + && so_error != EWOULDBLOCK + && so_error != ECONNREFUSED +#ifdef EINPROGRESS + && so_error != EINPROGRESS #endif + )) + { + ch_errorn(channel, + "channel_open: Connect failed with errno %d", + so_error); + PERROR(_(e_cannot_connect)); + sock_close(sd); + channel_free(channel); + return NULL; + } + } + + if (!FD_ISSET(sd, &wfds) || so_error != 0) { #ifndef WIN32 struct timeval end_tv; diff --git a/src/version.c b/src/version.c index 923256b39..33640ce1b 100644 --- a/src/version.c +++ b/src/version.c @@ -744,6 +744,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1457, +/**/ 1456, /**/ 1455, |