* if it fails in a fatal way or needs to poll to go further, otherwise it
* returns non-zero and removes the CO_FL_WAIT_L4_CONN flag from the connection's
* flags. In case of error, it sets CO_FL_ERROR and leaves the error code in
- * errno. The error checking is done in two passes in order to limit the number
- * of syscalls in the normal case :
- * - if POLL_ERR was reported by the poller, we check for a pending error on
- * the socket before proceeding. If found, it's assigned to errno so that
- * upper layers can see it.
- * - otherwise connect() is used to check the connection state again, since
- * the getsockopt return cannot reliably be used to know if the connection
- * is still pending or ready. This one may often return an error as well,
- * since we don't always have POLL_ERR (eg: OSX or cached events).
+ * errno.
*/
int tcp_connect_probe(struct connection *conn)
{
struct sockaddr_storage *addr;
int fd = conn->handle.fd;
- socklen_t lskerr;
- int skerr;
if (conn->flags & CO_FL_ERROR)
return 0;
if (!fd_send_ready(fd))
return 0;
- /* we might be the first witness of FD_POLL_ERR. Note that FD_POLL_HUP
- * without FD_POLL_IN also indicates a hangup without input data meaning
- * there was no connection.
+ /* Here we have 2 cases :
+ * - modern pollers, able to report ERR/HUP. If these ones return any
+ * of these flags then it's likely a failure, otherwise it possibly
+ * is a success (i.e. there may have been data received just before
+ * the error was reported).
+ * - select, which doesn't report these and with which it's always
+ * necessary either to try connect() again or to check for SO_ERROR.
+ * In order to simplify everything, we double-check using connect() as
+ * soon as we meet either of these delicate situations. Note that
+ * SO_ERROR would clear the error after reporting it!
*/
- if (fdtab[fd].ev & FD_POLL_ERR ||
- (fdtab[fd].ev & (FD_POLL_IN|FD_POLL_HUP)) == FD_POLL_HUP) {
- skerr = 0;
- lskerr = sizeof(skerr);
- getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr);
- errno = skerr;
- if (errno == EAGAIN)
- errno = 0;
- if (errno)
- goto out_error;
- }
-
- /* Use connect() to check the state of the socket. This has the
- * advantage of giving us the following info :
+ if (cur_poller.flags & HAP_POLL_F_ERRHUP) {
+ /* modern poller, able to report ERR/HUP */
+ if ((fdtab[fd].ev & (FD_POLL_IN|FD_POLL_ERR|FD_POLL_HUP)) == FD_POLL_IN)
+ goto done;
+ if ((fdtab[fd].ev & (FD_POLL_OUT|FD_POLL_ERR|FD_POLL_HUP)) == FD_POLL_OUT)
+ goto done;
+ if (!(fdtab[fd].ev & (FD_POLL_ERR|FD_POLL_HUP)))
+ goto wait;
+ /* error present, fall through common error check path */
+ }
+
+ /* Use connect() to check the state of the socket. This has the double
+ * advantage of *not* clearing the error (so that health checks can
+ * still use getsockopt(SO_ERROR)) and giving us the following info :
* - error
* - connecting (EALREADY, EINPROGRESS)
* - connected (EISCONN, 0)
addr = &objt_server(conn->target)->socks4_addr;
if (connect(fd, (const struct sockaddr *)addr, get_addr_len(addr)) == -1) {
- if (errno == EALREADY || errno == EINPROGRESS) {
- __conn_xprt_want_send(conn);
- fd_cant_send(fd);
- return 0;
- }
+ if (errno == EALREADY || errno == EINPROGRESS)
+ goto wait;
if (errno && errno != EISCONN)
goto out_error;
-
- /* otherwise we're connected */
}
+ done:
/* The FD is ready now, we'll mark the connection as complete and
* forward the event to the transport layer which will notify the
* data layer.
conn->flags &= ~CO_FL_WAIT_L4_CONN;
fd_may_send(fd);
fd_cond_recv(fd);
+ errno = 0; // make health checks happy
return 1;
out_error:
conn->flags |= CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH;
__conn_xprt_stop_both(conn);
return 0;
+
+ wait:
+ __conn_xprt_want_send(conn);
+ fd_cant_send(fd);
+ return 0;
}
/* XXX: Should probably be elsewhere */