diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 4c558dd19..332bc33fd 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,16 @@ +2014-10-11 Corinna Vinschen + + * fhandler_socket.cc (fhandler_socket::evaluate_events): Handle + connect_state and af_local_connect connect call here, once, independent + of FD_CONNECT being requested. Add comment to explain why. + (fhandler_socket::connect): Drop connect_state handling and calling + af_local_connect. Move remaining AF_LOCAL stuff prior to calling + ::connect and explain why. Simplify error case. + * poll.cc (poll): Handle connect state independently of POLLOUT being + requested for the descriptor to allow setting POLLIN if connect failed. + Add comment. + * select.cc (set_bits): Drop connect_state and AF_LOCAL handling here. + 2014-10-11 Corinna Vinschen * fhandler_socket.cc (fhandler_socket::evaluate_events): Slightly diff --git a/winsup/cygwin/fhandler_socket.cc b/winsup/cygwin/fhandler_socket.cc index 93cfddf6a..f0258c31e 100644 --- a/winsup/cygwin/fhandler_socket.cc +++ b/winsup/cygwin/fhandler_socket.cc @@ -621,7 +621,26 @@ fhandler_socket::evaluate_events (const long event_mask, long &events, wsock_events->events |= evts.lNetworkEvents; events_now = (wsock_events->events & event_mask); if (evts.lNetworkEvents & FD_CONNECT) - wsock_events->connect_errorcode = evts.iErrorCode[FD_CONNECT_BIT]; + { + wsock_events->connect_errorcode = evts.iErrorCode[FD_CONNECT_BIT]; + /* Setting the connect_state and calling the AF_LOCAL handshake + here allows to handle this stuff from a single point. This + is independent of FD_CONNECT being requested. Consider a + server calling connect(2) and then immediately poll(2) with + only polling for POLLIN (example: postfix), or select(2) just + asking for descriptors ready to read). */ + if (wsock_events->connect_errorcode) + connect_state (connect_failed); + else if (get_addr_family () == AF_LOCAL + && get_socket_type () == SOCK_STREAM + && af_local_connect ()) + { + wsock_events->connect_errorcode = WSAGetLastError (); + connect_state (connect_failed); + } + else + connect_state (connected); + } UNLOCK_EVENTS; if ((evts.lNetworkEvents & FD_OOB) && wsock_events->owner) kill (wsock_events->owner, SIGURG); @@ -1084,21 +1103,35 @@ out: int fhandler_socket::connect (const struct sockaddr *name, int namelen) { - bool in_progress = false; struct sockaddr_storage sst; - DWORD err; int type; if (get_inet_addr (name, namelen, &sst, &namelen, &type, connect_secret) == SOCKET_ERROR) return SOCKET_ERROR; - if (get_addr_family () == AF_LOCAL && get_socket_type () != type) + if (get_addr_family () == AF_LOCAL) { - WSASetLastError (WSAEPROTOTYPE); - set_winsock_errno (); - return SOCKET_ERROR; + if (get_socket_type () != type) + { + WSASetLastError (WSAEPROTOTYPE); + set_winsock_errno (); + return SOCKET_ERROR; + } + + set_peer_sun_path (name->sa_data); + + /* Don't move af_local_set_cred into af_local_connect which may be called + via select, possibly running under another identity. Call early here, + because af_local_connect is called in wait_for_events. */ + if (get_socket_type () == SOCK_STREAM) + af_local_set_cred (); } + + /* Initialize connect state to "connect_pending". State is ultimately set + to "connected" or "connect_failed" in wait_for_events when the FD_CONNECT + event occurs. */ + connect_state (connect_pending); int res = ::connect (get_socket (), (struct sockaddr *) &sst, namelen); if (!is_nonblocking () @@ -1106,48 +1139,21 @@ fhandler_socket::connect (const struct sockaddr *name, int namelen) && WSAGetLastError () == WSAEWOULDBLOCK) res = wait_for_events (FD_CONNECT | FD_CLOSE, 0); - if (!res) - err = 0; - else + if (res) { - err = WSAGetLastError (); - /* Special handling for connect to return the correct error code - when called on a non-blocking socket. */ - if (is_nonblocking ()) - { - if (err == WSAEWOULDBLOCK || err == WSAEALREADY) - in_progress = true; - - if (err == WSAEWOULDBLOCK) - WSASetLastError (err = WSAEINPROGRESS); - } - if (err == WSAEINVAL) - WSASetLastError (err = WSAEISCONN); + DWORD err = WSAGetLastError (); + + /* Winsock returns WSAEWOULDBLOCK if the non-blocking socket cannot be + conected immediately. Convert to POSIX/Linux compliant EINPROGRESS. */ + if (is_nonblocking () && err == WSAEWOULDBLOCK) + WSASetLastError (WSAEINPROGRESS); + /* Winsock returns WSAEINVAL if the socket is already a listener. + Convert to POSIX/Linux compliant EISCONN. */ + else if (err == WSAEINVAL) + WSASetLastError (WSAEISCONN); set_winsock_errno (); } - if (get_addr_family () == AF_LOCAL && (!res || in_progress)) - set_peer_sun_path (name->sa_data); - - if (get_addr_family () == AF_LOCAL && get_socket_type () == SOCK_STREAM) - { - af_local_set_cred (); /* Don't move into af_local_connect since - af_local_connect is called from select, - possibly running under another identity. */ - if (!res && af_local_connect ()) - { - set_winsock_errno (); - return SOCKET_ERROR; - } - } - - if (err == WSAEINPROGRESS || err == WSAEALREADY) - connect_state (connect_pending); - else if (err) - connect_state (connect_failed); - else - connect_state (connected); - return res; } diff --git a/winsup/cygwin/poll.cc b/winsup/cygwin/poll.cc index 17411a098..2b6f8a90a 100644 --- a/winsup/cygwin/poll.cc +++ b/winsup/cygwin/poll.cc @@ -115,8 +115,9 @@ poll (struct pollfd *fds, nfds_t nfds, int timeout) So it looks like there's actually no good reason to return POLLERR. */ fds[i].revents |= POLLIN; - /* Handle failed connect. */ - if (FD_ISSET(fds[i].fd, write_fds) + /* Handle failed connect. A failed connect implicitly sets + POLLOUT, if requested, but it doesn't set POLLIN. */ + if ((fds[i].events & POLLIN) && (sock = cygheap->fdtab[fds[i].fd]->is_socket ()) && sock->connect_state () == connect_failed) fds[i].revents |= (POLLIN | POLLERR); diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc index 6a396852a..c72cd0c76 100644 --- a/winsup/cygwin/select.cc +++ b/winsup/cygwin/select.cc @@ -480,16 +480,10 @@ set_bits (select_record *me, fd_set *readfds, fd_set *writefds, UNIX_FD_SET (me->fd, writefds); if (me->except_on_write && (sock = me->fh->is_socket ())) { - /* Special AF_LOCAL handling. */ - if (!me->read_ready && sock->connect_state () == connect_pending - && sock->af_local_connect ()) - { - if (me->read_selected) - UNIX_FD_SET (me->fd, readfds); - sock->connect_state (connect_failed); - } - else - sock->connect_state (connected); + /* Set readfds entry in case of a failed connect. */ + if (!me->read_ready && me->read_selected + && sock->connect_state () == connect_failed) + UNIX_FD_SET (me->fd, readfds); } ready++; }