From 28c42273d1467a16cdc719ce9f7badf20b46b043 Mon Sep 17 00:00:00 2001 From: Pavel Strashkin Date: Mon, 23 Jan 2012 22:49:42 -0800 Subject: [PATCH] Add support for connect timeout for TCP/IP transport --- dbus/dbus-sysdeps-unix.c | 74 ++++++++++++++++++++++++++++++++++---- dbus/dbus-sysdeps-win.c | 5 ++- dbus/dbus-sysdeps.h | 2 + dbus/dbus-transport-protected.h | 3 +- dbus/dbus-transport-socket.c | 38 +++++++++++++++++--- dbus/dbus-transport-socket.h | 1 + dbus/dbus-transport-win.c | 22 +++++++++++- dbus/dbus-transport.c | 4 ++ 8 files changed, 133 insertions(+), 16 deletions(-) diff --git a/dbus/dbus-sysdeps-unix.c b/dbus/dbus-sysdeps-unix.c index 22868e2..534ad5f 100644 --- a/dbus/dbus-sysdeps-unix.c +++ b/dbus/dbus-sysdeps-unix.c @@ -1158,6 +1158,7 @@ _dbus_listen_systemd_sockets (int **fds, * @param host the host name to connect to * @param port the port to connect to * @param family the address family to listen on, NULL for all + * @param timeout_milliseconds the connect timeout or -1 for an infinite timeout * @param error return location for error code * @returns connection file descriptor or -1 on error */ @@ -1165,20 +1166,24 @@ int _dbus_connect_tcp_socket (const char *host, const char *port, const char *family, + int timeout_milliseconds, DBusError *error) { - return _dbus_connect_tcp_socket_with_nonce (host, port, family, (const char*)NULL, error); + return _dbus_connect_tcp_socket_with_nonce (host, port, family, timeout_milliseconds, (const char*)NULL, error); } int _dbus_connect_tcp_socket_with_nonce (const char *host, const char *port, const char *family, + int timeout_milliseconds, const char *noncefile, DBusError *error) { int saved_errno = 0; + socklen_t saved_errno_len = sizeof(saved_errno); int fd = -1, res; + DBusPollFD pollfd; struct addrinfo hints; struct addrinfo *ai, *tmp; @@ -1223,16 +1228,75 @@ _dbus_connect_tcp_socket_with_nonce (const char *host, } _DBUS_ASSERT_ERROR_IS_CLEAR(error); - if (connect (fd, (struct sockaddr*) tmp->ai_addr, tmp->ai_addrlen) < 0) + if (!_dbus_set_fd_nonblocking (fd, error)) + { + freeaddrinfo (ai); + _DBUS_ASSERT_ERROR_IS_SET (error); + + _dbus_close (fd, NULL); + fd = -1; + + return -1; + } + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + if (connect (fd, (struct sockaddr*) tmp->ai_addr, tmp->ai_addrlen) < 0 && errno != EINPROGRESS) { saved_errno = errno; + _dbus_close(fd, NULL); fd = -1; + tmp = tmp->ai_next; continue; } + pollfd.fd = fd; + pollfd.events = _DBUS_POLLOUT; + pollfd.revents = 0; + + res = _dbus_poll (&pollfd, 1, timeout_milliseconds); + if (res == 1) + { + if (getsockopt (fd, SOL_SOCKET, SO_ERROR, &saved_errno, &saved_errno_len) < 0) + { + saved_errno = errno; + + _dbus_close (fd, NULL); + fd = -1; + + tmp = tmp->ai_next; + continue; + } + + if (saved_errno) + { + _dbus_close (fd, NULL); + fd = -1; + + tmp = tmp->ai_next; + continue; + } + + break; + } + + /* we process remaining _dbus_poll result at this point + * to not lose errno during _dbus_close call + */ + if (res == 0) + saved_errno = ETIMEDOUT; + else + saved_errno = errno; + + _dbus_close (fd, NULL); + fd = -1; + + if (saved_errno == ETIMEDOUT) break; + + tmp = tmp->ai_next; + continue; } freeaddrinfo(ai); @@ -1260,12 +1324,6 @@ _dbus_connect_tcp_socket_with_nonce (const char *host, } } - if (!_dbus_set_fd_nonblocking (fd, error)) - { - _dbus_close (fd, NULL); - return -1; - } - return fd; } diff --git a/dbus/dbus-sysdeps-win.c b/dbus/dbus-sysdeps-win.c index e30e92f..e630050 100644 --- a/dbus/dbus-sysdeps-win.c +++ b/dbus/dbus-sysdeps-win.c @@ -1235,6 +1235,7 @@ _dbus_exit (int code) * @param host the host name to connect to * @param port the port to connect to * @param family the address family to listen on, NULL for all + * @param timeout_milliseconds the connect timeout -1 for an infinite timeout * @param error return location for error code * @returns connection file descriptor or -1 on error */ @@ -1242,15 +1243,17 @@ int _dbus_connect_tcp_socket (const char *host, const char *port, const char *family, + int timeout_milliseconds, DBusError *error) { - return _dbus_connect_tcp_socket_with_nonce (host, port, family, (const char*)NULL, error); + return _dbus_connect_tcp_socket_with_nonce (host, port, family, timeout_milliseconds, (const char*)NULL, error); } int _dbus_connect_tcp_socket_with_nonce (const char *host, const char *port, const char *family, + int timeout_milliseconds, const char *noncefile, DBusError *error) { diff --git a/dbus/dbus-sysdeps.h b/dbus/dbus-sysdeps.h index f8438f4..8990810 100644 --- a/dbus/dbus-sysdeps.h +++ b/dbus/dbus-sysdeps.h @@ -168,10 +168,12 @@ dbus_bool_t _dbus_socket_is_invalid (int fd); int _dbus_connect_tcp_socket (const char *host, const char *port, const char *family, + int timeout_milliseconds, DBusError *error); int _dbus_connect_tcp_socket_with_nonce (const char *host, const char *port, const char *family, + int timeout_milliseconds, const char *noncefile, DBusError *error); int _dbus_listen_tcp_socket (const char *host, diff --git a/dbus/dbus-transport-protected.h b/dbus/dbus-transport-protected.h index 44b9d78..2ab1f01 100644 --- a/dbus/dbus-transport-protected.h +++ b/dbus/dbus-transport-protected.h @@ -131,7 +131,8 @@ typedef enum DBUS_TRANSPORT_OPEN_NOT_HANDLED, /**< we aren't in charge of this address type */ DBUS_TRANSPORT_OPEN_OK, /**< we set up the listen */ DBUS_TRANSPORT_OPEN_BAD_ADDRESS, /**< malformed address */ - DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT /**< well-formed address but failed to set it up */ + DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT, /**< well-formed address but failed to set it up */ + DBUS_TRANSPORT_OPEN_BAD_TIMEOUT /**< malformed timeout (for TCP/IP) */ } DBusTransportOpenResult; DBusTransportOpenResult _dbus_transport_open_platform_specific (DBusAddressEntry *entry, diff --git a/dbus/dbus-transport-socket.c b/dbus/dbus-transport-socket.c index 0673a8c..95d2439 100644 --- a/dbus/dbus-transport-socket.c +++ b/dbus/dbus-transport-socket.c @@ -1299,6 +1299,7 @@ _dbus_transport_new_for_socket (int fd, * @param host the host to connect to * @param port the port to connect to * @param family the address family to connect to + * @param timeout_milliseconds the connect timeout -1 for an infinite timeout * @param path to nonce file * @param error location to store reason for failure. * @returns a new transport, or #NULL on failure. @@ -1307,6 +1308,7 @@ DBusTransport* _dbus_transport_new_for_tcp_socket (const char *host, const char *port, const char *family, + int timeout_milliseconds, const char *noncefile, DBusError *error) { @@ -1337,16 +1339,21 @@ _dbus_transport_new_for_tcp_socket (const char *host, goto error; if (family != NULL && - (!_dbus_string_append (&address, "family=") || + (!_dbus_string_append (&address, ",family=") || !_dbus_string_append (&address, family))) goto error; + /* don't append invalid or infinite timeout */ + if (timeout_milliseconds > 0 && + (!_dbus_string_append_printf(&address, ",timeout=%d", timeout_milliseconds))) + goto error; + if (noncefile != NULL && - (!_dbus_string_append (&address, "noncefile=") || + (!_dbus_string_append (&address, ",noncefile=") || !_dbus_string_append (&address, noncefile))) goto error; - fd = _dbus_connect_tcp_socket_with_nonce (host, port, family, noncefile, error); + fd = _dbus_connect_tcp_socket_with_nonce (host, port, family, timeout_milliseconds, noncefile, error); if (fd < 0) { _DBUS_ASSERT_ERROR_IS_SET (error); @@ -1403,8 +1410,11 @@ _dbus_transport_open_socket(DBusAddressEntry *entry, const char *port = dbus_address_entry_get_value (entry, "port"); const char *family = dbus_address_entry_get_value (entry, "family"); const char *noncefile = dbus_address_entry_get_value (entry, "noncefile"); + const char *timeout = dbus_address_entry_get_value (entry, "timeout"); + long timeout_milliseconds = -1; - if ((isNonceTcp == TRUE) != (noncefile != NULL)) { + if ((isNonceTcp == TRUE) != (noncefile != NULL)) + { _dbus_set_bad_address (error, method, "noncefile", NULL); return DBUS_TRANSPORT_OPEN_BAD_ADDRESS; } @@ -1415,7 +1425,25 @@ _dbus_transport_open_socket(DBusAddressEntry *entry, return DBUS_TRANSPORT_OPEN_BAD_ADDRESS; } - *transport_p = _dbus_transport_new_for_tcp_socket (host, port, family, noncefile, error); + if (timeout) + { + DBusString timeout_str; + _dbus_string_init_const (&timeout_str, timeout); + + if (!_dbus_string_parse_int (&timeout_str, 0, &timeout_milliseconds, NULL)) + { + _dbus_set_bad_address (error, NULL, NULL, "timeout argument is malformed (must be positive integer)"); + return DBUS_TRANSPORT_OPEN_BAD_TIMEOUT; + } + + if (timeout_milliseconds <= 0) + { + _dbus_set_bad_address (error, NULL, NULL, "timeout argument is invalid (must be greater than 0)"); + return DBUS_TRANSPORT_OPEN_BAD_TIMEOUT; + } + } + + *transport_p = _dbus_transport_new_for_tcp_socket (host, port, family, timeout_milliseconds, noncefile, error); if (*transport_p == NULL) { _DBUS_ASSERT_ERROR_IS_SET (error); diff --git a/dbus/dbus-transport-socket.h b/dbus/dbus-transport-socket.h index 8aefae3..0dc8066 100644 --- a/dbus/dbus-transport-socket.h +++ b/dbus/dbus-transport-socket.h @@ -33,6 +33,7 @@ DBusTransport* _dbus_transport_new_for_socket (int f DBusTransport* _dbus_transport_new_for_tcp_socket (const char *host, const char *port, const char *family, + int timeout_milliseconds, const char *noncefile, DBusError *error); DBusTransportOpenResult _dbus_transport_open_socket (DBusAddressEntry *entry, diff --git a/dbus/dbus-transport-win.c b/dbus/dbus-transport-win.c index faaf1bd..6f8104c 100644 --- a/dbus/dbus-transport-win.c +++ b/dbus/dbus-transport-win.c @@ -57,6 +57,8 @@ _dbus_transport_open_platform_specific (DBusAddressEntry *entry, const char *port = dbus_address_entry_get_value (entry, "port"); const char *family = dbus_address_entry_get_value (entry, "family"); const char *noncefile = dbus_address_entry_get_value (entry, "noncefile"); + const char *timeout = dbus_address_entry_get_value (entry, "timeout"); + long timeout_milliseconds = -1; method = dbus_address_entry_get_method (entry); _dbus_assert (method != NULL); @@ -73,7 +75,25 @@ _dbus_transport_open_platform_specific (DBusAddressEntry *entry, return DBUS_TRANSPORT_OPEN_BAD_ADDRESS; } - *transport_p = _dbus_transport_new_for_tcp_socket (host, port, family, noncefile, error); + if (timeout) + { + DBusString timeout_str; + _dbus_string_init_const (&timeout_str, timeout); + + if (!_dbus_string_parse_int (&timeout_str, 0, &timeout_milliseconds, NULL)) + { + _dbus_set_bad_address (error, NULL, NULL, "timeout argument is malformed (must be positive integer)"); + return DBUS_TRANSPORT_OPEN_BAD_TIMEOUT; + } + + if (timeout_milliseconds <= 0) + { + _dbus_set_bad_address (error, NULL, NULL, "timeout argument is invalid (must be greater than 0)"); + return DBUS_TRANSPORT_OPEN_BAD_TIMEOUT; + } + } + + *transport_p = _dbus_transport_new_for_tcp_socket (host, port, family, timeout_milliseconds, noncefile, error); if (*transport_p == NULL) { _DBUS_ASSERT_ERROR_IS_SET (error); diff --git a/dbus/dbus-transport.c b/dbus/dbus-transport.c index f743d01..cb62304 100644 --- a/dbus/dbus-transport.c +++ b/dbus/dbus-transport.c @@ -410,6 +410,10 @@ _dbus_transport_open (DBusAddressEntry *entry, _DBUS_ASSERT_ERROR_IS_SET (&tmp_error); goto out; break; + case DBUS_TRANSPORT_OPEN_BAD_TIMEOUT: + _DBUS_ASSERT_ERROR_IS_SET (&tmp_error); + goto out; + break; } } -- 1.7.4.1