diff --git a/bufferevent-internal.h b/bufferevent-internal.h index 81cadf1a..eac31831 100644 --- a/bufferevent-internal.h +++ b/bufferevent-internal.h @@ -137,6 +137,9 @@ struct bufferevent_private { unsigned writecb_pending : 1; /** Flag: set if we are currently busy connecting. */ unsigned connecting : 1; + /** Flag: set if a connect failed prematurely; this is a hack for + * getting around the bufferevent abstraction. */ + unsigned connection_refused : 1; /** Set to the events pending if we have deferred callbacks and * an events callback is pending. */ short eventcb_pending; diff --git a/bufferevent_sock.c b/bufferevent_sock.c index 2225a2a4..22e76032 100644 --- a/bufferevent_sock.c +++ b/bufferevent_sock.c @@ -213,6 +213,12 @@ bufferevent_writecb(evutil_socket_t fd, short event, void *arg) } if (bufev_p->connecting) { int c = evutil_socket_finished_connecting(fd); + /* we need to fake the error if the connection was refused + * immediately - usually connection to localhost on BSD */ + if (bufev_p->connection_refused) { + bufev_p->connection_refused = 0; + c = -1; + } if (c == 0) goto done; @@ -395,11 +401,19 @@ bufferevent_socket_connect(struct bufferevent *bev, result = 0; goto done; } - } else { + } if (r == 1) { /* The connect succeeded already. How very BSD of it. */ result = 0; bufev_p->connecting = 1; event_active(&bev->ev_write, EV_WRITE, 1); + } else { + /* The connect failed already. How very BSD of it. */ + if (! be_socket_enable(bev, EV_WRITE)) { + bufev_p->connection_refused = 1; + bufev_p->connecting = 1; + result = 0; + goto done; + } } goto done; diff --git a/evutil.c b/evutil.c index a4dc2ef6..7df02149 100644 --- a/evutil.c +++ b/evutil.c @@ -355,7 +355,7 @@ evutil_socket_geterror(evutil_socket_t sock) } #endif -/* 1 for connected, 0 for not yet, -1 for error. */ +/* 2 for connection refused, 1 for connected, 0 for not yet, -1 for error. */ int evutil_socket_connect(evutil_socket_t *fd_ptr, struct sockaddr *sa, int socklen) { @@ -374,6 +374,8 @@ evutil_socket_connect(evutil_socket_t *fd_ptr, struct sockaddr *sa, int socklen) int e = evutil_socket_geterror(*fd_ptr); if (EVUTIL_ERR_CONNECT_RETRIABLE(e)) return 0; + if (EVUTIL_ERR_CONNECT_REFUSED(e)) + return 2; goto err; } else { return 1; diff --git a/util-internal.h b/util-internal.h index 726b6939..6fa4bd63 100644 --- a/util-internal.h +++ b/util-internal.h @@ -66,13 +66,17 @@ extern "C" { /* True iff e is an error that means a read/write operation can be retried. */ #define EVUTIL_ERR_RW_RETRIABLE(e) \ ((e) == EINTR || (e) == EAGAIN) -/* True iff e is an error that means an accept can be retried. */ +/* True iff e is an error that means an connect can be retried. */ #define EVUTIL_ERR_CONNECT_RETRIABLE(e) \ ((e) == EINTR || (e) == EINPROGRESS) -/* True iff e is an error that means a connect can be retried. */ +/* True iff e is an error that means a accept can be retried. */ #define EVUTIL_ERR_ACCEPT_RETRIABLE(e) \ ((e) == EINTR || (e) == EAGAIN || (e) == ECONNABORTED) +/* True iff e is an error that means the connection was refused */ +#define EVUTIL_ERR_CONNECT_REFUSED(e) \ + ((e) == ECONNREFUSED) + #else #define EVUTIL_ERR_RW_RETRIABLE(e) \ @@ -88,6 +92,9 @@ extern "C" { #define EVUTIL_ERR_ACCEPT_RETRIABLE(e) \ EVUTIL_ERR_RW_RETRIABLE(e) +#define EVUTIL_ERR_CONNECT_REFUSED(e) \ + ((e) == WSAECONNREFUSED) + #endif #ifdef _EVENT_socklen_t