* 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.
This commit is contained in:
Corinna Vinschen 2014-10-11 12:14:29 +00:00
parent 9f64fd8081
commit 2483fa2719
4 changed files with 71 additions and 57 deletions

View File

@ -1,3 +1,16 @@
2014-10-11 Corinna Vinschen <corinna@vinschen.de>
* 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 <corinna@vinschen.de> 2014-10-11 Corinna Vinschen <corinna@vinschen.de>
* fhandler_socket.cc (fhandler_socket::evaluate_events): Slightly * fhandler_socket.cc (fhandler_socket::evaluate_events): Slightly

View File

@ -621,7 +621,26 @@ fhandler_socket::evaluate_events (const long event_mask, long &events,
wsock_events->events |= evts.lNetworkEvents; wsock_events->events |= evts.lNetworkEvents;
events_now = (wsock_events->events & event_mask); events_now = (wsock_events->events & event_mask);
if (evts.lNetworkEvents & FD_CONNECT) 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; UNLOCK_EVENTS;
if ((evts.lNetworkEvents & FD_OOB) && wsock_events->owner) if ((evts.lNetworkEvents & FD_OOB) && wsock_events->owner)
kill (wsock_events->owner, SIGURG); kill (wsock_events->owner, SIGURG);
@ -1084,21 +1103,35 @@ out:
int int
fhandler_socket::connect (const struct sockaddr *name, int namelen) fhandler_socket::connect (const struct sockaddr *name, int namelen)
{ {
bool in_progress = false;
struct sockaddr_storage sst; struct sockaddr_storage sst;
DWORD err;
int type; int type;
if (get_inet_addr (name, namelen, &sst, &namelen, &type, connect_secret) if (get_inet_addr (name, namelen, &sst, &namelen, &type, connect_secret)
== SOCKET_ERROR) == SOCKET_ERROR)
return SOCKET_ERROR; return SOCKET_ERROR;
if (get_addr_family () == AF_LOCAL && get_socket_type () != type) if (get_addr_family () == AF_LOCAL)
{ {
WSASetLastError (WSAEPROTOTYPE); if (get_socket_type () != type)
set_winsock_errno (); {
return SOCKET_ERROR; 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); int res = ::connect (get_socket (), (struct sockaddr *) &sst, namelen);
if (!is_nonblocking () if (!is_nonblocking ()
@ -1106,48 +1139,21 @@ fhandler_socket::connect (const struct sockaddr *name, int namelen)
&& WSAGetLastError () == WSAEWOULDBLOCK) && WSAGetLastError () == WSAEWOULDBLOCK)
res = wait_for_events (FD_CONNECT | FD_CLOSE, 0); res = wait_for_events (FD_CONNECT | FD_CLOSE, 0);
if (!res) if (res)
err = 0;
else
{ {
err = WSAGetLastError (); DWORD err = WSAGetLastError ();
/* Special handling for connect to return the correct error code
when called on a non-blocking socket. */ /* Winsock returns WSAEWOULDBLOCK if the non-blocking socket cannot be
if (is_nonblocking ()) conected immediately. Convert to POSIX/Linux compliant EINPROGRESS. */
{ if (is_nonblocking () && err == WSAEWOULDBLOCK)
if (err == WSAEWOULDBLOCK || err == WSAEALREADY) WSASetLastError (WSAEINPROGRESS);
in_progress = true; /* Winsock returns WSAEINVAL if the socket is already a listener.
Convert to POSIX/Linux compliant EISCONN. */
if (err == WSAEWOULDBLOCK) else if (err == WSAEINVAL)
WSASetLastError (err = WSAEINPROGRESS); WSASetLastError (WSAEISCONN);
}
if (err == WSAEINVAL)
WSASetLastError (err = WSAEISCONN);
set_winsock_errno (); 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; return res;
} }

View File

@ -115,8 +115,9 @@ poll (struct pollfd *fds, nfds_t nfds, int timeout)
So it looks like there's actually no good reason to So it looks like there's actually no good reason to
return POLLERR. */ return POLLERR. */
fds[i].revents |= POLLIN; fds[i].revents |= POLLIN;
/* Handle failed connect. */ /* Handle failed connect. A failed connect implicitly sets
if (FD_ISSET(fds[i].fd, write_fds) POLLOUT, if requested, but it doesn't set POLLIN. */
if ((fds[i].events & POLLIN)
&& (sock = cygheap->fdtab[fds[i].fd]->is_socket ()) && (sock = cygheap->fdtab[fds[i].fd]->is_socket ())
&& sock->connect_state () == connect_failed) && sock->connect_state () == connect_failed)
fds[i].revents |= (POLLIN | POLLERR); fds[i].revents |= (POLLIN | POLLERR);

View File

@ -480,16 +480,10 @@ set_bits (select_record *me, fd_set *readfds, fd_set *writefds,
UNIX_FD_SET (me->fd, writefds); UNIX_FD_SET (me->fd, writefds);
if (me->except_on_write && (sock = me->fh->is_socket ())) if (me->except_on_write && (sock = me->fh->is_socket ()))
{ {
/* Special AF_LOCAL handling. */ /* Set readfds entry in case of a failed connect. */
if (!me->read_ready && sock->connect_state () == connect_pending if (!me->read_ready && me->read_selected
&& sock->af_local_connect ()) && sock->connect_state () == connect_failed)
{ UNIX_FD_SET (me->fd, readfds);
if (me->read_selected)
UNIX_FD_SET (me->fd, readfds);
sock->connect_state (connect_failed);
}
else
sock->connect_state (connected);
} }
ready++; ready++;
} }