From 43a5e0cc3de25926e648d0c5d0c2fe2a2ba3aacd Mon Sep 17 00:00:00 2001 From: Ralf Habacker Date: Thu, 8 Mar 2018 17:02:15 +0100 Subject: [PATCH] Keep Windows implementation of _dbus_listen_tcp_socket in sync with unix - move inet_sockaddr_to_string, set_error_with_inet_sockaddr and combine_tcp_error to dbus-sysdeps.c to be usable on Windows - prefix the mentioned function with _dbus to indicate private api - use mentioned functions in dbus_listen_tcp_socket Bug: https://bugs.freedesktop.org/show_bug.cgi?id=61922 --- cmake/CMakeLists.txt | 4 + dbus/dbus-sysdeps-unix.c | 190 ++--------------------------------------------- dbus/dbus-sysdeps-win.c | 89 +++++++++++++++------- dbus/dbus-sysdeps.c | 182 +++++++++++++++++++++++++++++++++++++++++++++ dbus/dbus-sysdeps.h | 26 +++++++ 5 files changed, 283 insertions(+), 208 deletions(-) diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index cebf8169..a85c0e60 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -170,6 +170,10 @@ if(MSVC) SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /FIconfig.h") endif() +# set Windows Vista as mimimun requirement +if(WIN32 AND NOT WINCE) + add_definitions(-D_WIN32_WINNT=0x0600) +endif() # # setup warnings # diff --git a/dbus/dbus-sysdeps-unix.c b/dbus/dbus-sysdeps-unix.c index 33678158..22cf498a 100644 --- a/dbus/dbus-sysdeps-unix.c +++ b/dbus/dbus-sysdeps-unix.c @@ -1325,182 +1325,6 @@ _dbus_listen_systemd_sockets (DBusSocket **fds, #endif } -/* - * Try to convert the IPv4 or IPv6 address pointed to by - * sockaddr_pointer into a string. - * - * @param sockaddr_pointer A struct sockaddr_in or struct sockaddr_in6 - * @param len The length of the struct pointed to by sockaddr_pointer - * @param string An array to write the address into - * @param string_len Length of string (should usually be at least INET6_ADDRSTRLEN) - * @param family_name Used to return "ipv4" or "ipv6", or NULL to ignore - * @param port Used to return the port number, or NULL to ignore - * @returns #FALSE with errno set if the address family was not understood - */ -static dbus_bool_t -inet_sockaddr_to_string (const void *sockaddr_pointer, - size_t len, - char *string, - size_t string_len, - const char **family_name, - dbus_uint16_t *port) -{ - union - { - struct sockaddr sa; - struct sockaddr_storage storage; - struct sockaddr_in ipv4; - struct sockaddr_in6 ipv6; - } addr; - - if (len > sizeof (addr)) - return FALSE; - - _DBUS_ZERO (addr); - memcpy (&addr, sockaddr_pointer, len); - - switch (addr.sa.sa_family) - { - case AF_INET: - if (inet_ntop (AF_INET, &addr.ipv4.sin_addr, string, string_len) != NULL) - { - if (family_name != NULL) - *family_name = "ipv4"; - - if (port != NULL) - *port = ntohs (addr.ipv4.sin_port); - - return TRUE; - } - - return FALSE; - -#ifdef AF_INET6 - case AF_INET6: - if (inet_ntop (AF_INET6, &addr.ipv6.sin6_addr, string, string_len) != NULL) - { - if (family_name != NULL) - *family_name = "ipv6"; - - if (port != NULL) - *port = ntohs (addr.ipv6.sin6_port); - - return TRUE; - } - return FALSE; -#endif - - default: - errno = EAFNOSUPPORT; - return FALSE; - } -} - -/* - * Format an error appropriate for saved_errno for the IPv4 or IPv6 - * address pointed to by sockaddr_pointer of length sockaddr_len. - * - * @param error The error to set - * @param sockaddr_pointer A struct sockaddr_in or struct sockaddr_in6 - * @param len The length of the struct pointed to by sockaddr_pointer - * @param description A prefix like "Failed to listen on socket" - * @param saved_errno The OS-level error number to use - */ -static void -set_error_with_inet_sockaddr (DBusError *error, - const void *sockaddr_pointer, - socklen_t len, - const char *description, - int saved_errno) -{ - char string[INET6_ADDRSTRLEN]; - dbus_uint16_t port; - const struct sockaddr *addr = sockaddr_pointer; - - if (inet_sockaddr_to_string (sockaddr_pointer, len, string, sizeof (string), - NULL, &port)) - { - dbus_set_error (error, _dbus_error_from_errno (saved_errno), - "%s \"%s\" port %u: %s", - description, string, port, _dbus_strerror (saved_errno)); - } - else - { - dbus_set_error (error, _dbus_error_from_errno (saved_errno), - "%s
: %s", - description, addr->sa_family, - _dbus_strerror (saved_errno)); - } -} - -static void -combine_tcp_errors (DBusList **sources, - const char *summary, - const char *host, - const char *port, - DBusError *dest) -{ - DBusString message = _DBUS_STRING_INIT_INVALID; - - if (_dbus_list_length_is_one (sources)) - { - /* If there was exactly one error, just use it */ - dbus_move_error (_dbus_list_get_first (sources), dest); - } - else - { - DBusList *iter; - const char *name; - - /* If there was more than one error, concatenate all the - * errors' diagnostic messages, and use their common error - * name, or DBUS_ERROR_FAILED if more than one name is - * represented */ - if (!_dbus_string_init (&message)) - { - _DBUS_SET_OOM (dest); - goto out; - } - - for (iter = _dbus_list_get_first_link (sources); - iter != NULL; - iter = _dbus_list_get_next_link (sources, iter)) - { - DBusError *error = iter->data; - - if (name == NULL) - { - /* no error names known yet, try to use this one */ - name = error->name; - } - else if (strcmp (name, error->name) != 0) - { - /* errors of two different names exist, reconcile by - * using FAILED */ - name = DBUS_ERROR_FAILED; - } - - if ((_dbus_string_get_length (&message) > 0 && - !_dbus_string_append (&message, "; ")) || - !_dbus_string_append (&message, error->message)) - { - _DBUS_SET_OOM (dest); - goto out; - } - } - - if (name == NULL) - name = DBUS_ERROR_FAILED; - - dbus_set_error (dest, name, "%s to \"%s\":%s (%s)", - summary, host ? host : "*", port, - _dbus_string_get_const_data (&message)); - } - -out: - _dbus_string_free (&message); -} - /** * Creates a socket and connects to a socket at the given host * and port. The connection fd is returned, and is set up as @@ -1596,7 +1420,7 @@ _dbus_connect_tcp_socket_with_nonce (const char *host, } dbus_error_init (connect_error); - set_error_with_inet_sockaddr (connect_error, tmp->ai_addr, tmp->ai_addrlen, + _dbus_set_error_with_inet_sockaddr (connect_error, tmp->ai_addr, tmp->ai_addrlen, "Failed to connect to socket", saved_errno); if (!_dbus_list_append (&connect_errors, connect_error)) @@ -1617,7 +1441,7 @@ _dbus_connect_tcp_socket_with_nonce (const char *host, if (!_dbus_socket_is_valid (fd)) { - combine_tcp_errors (&connect_errors, "Failed to connect", + _dbus_combine_tcp_errors (&connect_errors, "Failed to connect", host, port, error); goto out; } @@ -1784,7 +1608,7 @@ _dbus_listen_tcp_socket (const char *host, } dbus_error_init (bind_error); - set_error_with_inet_sockaddr (bind_error, tmp->ai_addr, tmp->ai_addrlen, + _dbus_set_error_with_inet_sockaddr (bind_error, tmp->ai_addr, tmp->ai_addrlen, "Failed to bind socket", saved_errno); if (!_dbus_list_append (&bind_errors, bind_error)) @@ -1804,7 +1628,7 @@ _dbus_listen_tcp_socket (const char *host, { saved_errno = errno; _dbus_close (fd, NULL); - set_error_with_inet_sockaddr (error, tmp->ai_addr, tmp->ai_addrlen, + _dbus_set_error_with_inet_sockaddr (error, tmp->ai_addr, tmp->ai_addrlen, "Failed to listen on socket", saved_errno); goto failed; } @@ -1875,7 +1699,7 @@ _dbus_listen_tcp_socket (const char *host, if (!nlisten_fd) { - combine_tcp_errors (&bind_errors, "Failed to bind", host, port, error); + _dbus_combine_tcp_errors (&bind_errors, "Failed to bind", host, port, error); goto failed; } @@ -4951,8 +4775,8 @@ _dbus_append_address_from_socket (DBusSocket fd, #endif _dbus_string_init_const (&path_str, hostip); - if (inet_sockaddr_to_string (&socket, size, hostip, sizeof (hostip), - &family_name, &port)) + if (_dbus_inet_sockaddr_to_string (&socket, size, hostip, sizeof (hostip), + &family_name, &port)) { if (_dbus_string_append_printf (address, "tcp:family=%s,port=%u,host=", family_name, port) && diff --git a/dbus/dbus-sysdeps-win.c b/dbus/dbus-sysdeps-win.c index 4e71d1cf..b018bb13 100644 --- a/dbus/dbus-sysdeps-win.c +++ b/dbus/dbus-sysdeps-win.c @@ -30,12 +30,6 @@ #define STRSAFE_NO_DEPRECATE -#ifndef DBUS_WINCE -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0501 -#endif -#endif - #include "dbus-internals.h" #include "dbus-sha.h" #include "dbus-sysdeps.h" @@ -1646,6 +1640,9 @@ _dbus_listen_tcp_socket (const char *host, DBusSocket **fds_p, DBusError *error) { + int saved_errno; + DBusList *bind_errors = NULL; + DBusError *bind_error = NULL; DBusSocket *listen_fd = NULL; int nlisten_fd = 0, res, i, port_num = -1; struct addrinfo hints; @@ -1722,21 +1719,52 @@ _dbus_listen_tcp_socket (const char *host, if (bind (fd.sock, (struct sockaddr*) tmp->ai_addr, tmp->ai_addrlen) == SOCKET_ERROR) { DBUS_SOCKET_SET_ERRNO (); + saved_errno = errno; closesocket (fd.sock); - if (errno == WSAEADDRINUSE) - { - /* Calling this function with port=0 tries to - * bind the same port twice, so we should - * ignore the second bind. - */ - tmp = tmp->ai_next; - continue; - } - dbus_set_error (error, _dbus_error_from_errno (errno), - "Failed to bind socket \"%s:%s\": %s", - host ? host : "*", port, _dbus_strerror_from_errno ()); - goto failed; - } + + /* + * We don't treat this as a fatal error, because there might be + * other addresses that we can listen on. In particular: + * + * - If saved_errno is EADDRINUSE after we + * "goto redo_lookup_with_port" after binding a port on one of the + * possible addresses, we will try to bind that same port on + * every address, including the same address again for a second + * time, which will fail with EADDRINUSE. + * + * - If saved_errno is EADDRINUSE, it might be because binding to + * an IPv6 address implicitly binds to a corresponding IPv4 + * address or vice versa (e.g. Linux with bindv6only=0). + * + * - If saved_errno is EADDRNOTAVAIL when we asked for family + * AF_UNSPEC, it might be because IPv6 is disabled for this + * particular interface (e.g. Linux with + * /proc/sys/net/ipv6/conf/lo/disable_ipv6). + */ + bind_error = dbus_new0 (DBusError, 1); + + if (bind_error == NULL) + { + _DBUS_SET_OOM (error); + goto failed; + } + + dbus_error_init (bind_error); + _dbus_set_error_with_inet_sockaddr (bind_error, tmp->ai_addr, tmp->ai_addrlen, + "Failed to bind socket", saved_errno); + + if (!_dbus_list_append (&bind_errors, bind_error)) + { + dbus_error_free (bind_error); + dbus_free (bind_error); + _DBUS_SET_OOM (error); + goto failed; + } + + /* Try the next address, maybe it will work better */ + tmp = tmp->ai_next; + continue; + } if (listen (fd.sock, 30 /* backlog */) == SOCKET_ERROR) { @@ -1811,11 +1839,8 @@ _dbus_listen_tcp_socket (const char *host, if (!nlisten_fd) { - _dbus_win_set_errno (WSAEADDRINUSE); - dbus_set_error (error, _dbus_error_from_errno (errno), - "Failed to bind socket \"%s:%s\": %s", - host ? host : "*", port, _dbus_strerror_from_errno ()); - return -1; + _dbus_combine_tcp_errors (&bind_errors, "Failed to bind", host, port, error); + goto failed; } sscanf(_dbus_string_get_const_data(retport), "%d", &port_num); @@ -1831,6 +1856,13 @@ _dbus_listen_tcp_socket (const char *host, *fds_p = listen_fd; + /* This list might be non-empty even on success, because we might be + * ignoring EADDRINUSE or EADDRNOTAVAIL */ + while ((bind_error = _dbus_list_pop_first (&bind_errors))) + { + dbus_error_free (bind_error); + dbus_free (bind_error); + } return nlisten_fd; failed: @@ -1838,6 +1870,13 @@ _dbus_listen_tcp_socket (const char *host, freeaddrinfo(ai); for (i = 0 ; i < nlisten_fd ; i++) closesocket (listen_fd[i].sock); + + while ((bind_error = _dbus_list_pop_first (&bind_errors))) + { + dbus_error_free (bind_error); + dbus_free (bind_error); + } + dbus_free(listen_fd); return -1; } diff --git a/dbus/dbus-sysdeps.c b/dbus/dbus-sysdeps.c index 20bb8944..c4779b62 100644 --- a/dbus/dbus-sysdeps.c +++ b/dbus/dbus-sysdeps.c @@ -36,12 +36,17 @@ * * These are the standard ANSI C headers... */ + #if HAVE_LOCALE_H #include #endif #include #include #include +#ifdef DBUS_UNIX +#include +#include +#endif #ifdef HAVE_ERRNO_H #include @@ -49,6 +54,7 @@ #ifdef DBUS_WIN #include + #include #elif (defined __APPLE__) # include # define environ (*_NSGetEnviron()) @@ -773,6 +779,182 @@ _dbus_log (DBusSystemLogSeverity severity, va_end (args); } +/* + * Try to convert the IPv4 or IPv6 address pointed to by + * sockaddr_pointer into a string. + * + * @param sockaddr_pointer A struct sockaddr_in or struct sockaddr_in6 + * @param len The length of the struct pointed to by sockaddr_pointer + * @param string An array to write the address into + * @param string_len Length of string (should usually be at least INET6_ADDRSTRLEN) + * @param family_name Used to return "ipv4" or "ipv6", or NULL to ignore + * @param port Used to return the port number, or NULL to ignore + * @returns #FALSE with errno set if the address family was not understood + */ +dbus_bool_t +_dbus_inet_sockaddr_to_string (const void *sockaddr_pointer, + size_t len, + char *string, + size_t string_len, + const char **family_name, + dbus_uint16_t *port) +{ + union + { + struct sockaddr sa; + struct sockaddr_storage storage; + struct sockaddr_in ipv4; + struct sockaddr_in6 ipv6; + } addr; + + if (len > sizeof (addr)) + return FALSE; + + _DBUS_ZERO (addr); + memcpy (&addr, sockaddr_pointer, len); + + switch (addr.sa.sa_family) + { + case AF_INET: + if (inet_ntop (AF_INET, &addr.ipv4.sin_addr, string, string_len) != NULL) + { + if (family_name != NULL) + *family_name = "ipv4"; + + if (port != NULL) + *port = ntohs (addr.ipv4.sin_port); + + return TRUE; + } + + return FALSE; + +#ifdef AF_INET6 + case AF_INET6: + if (inet_ntop (AF_INET6, &addr.ipv6.sin6_addr, string, string_len) != NULL) + { + if (family_name != NULL) + *family_name = "ipv6"; + + if (port != NULL) + *port = ntohs (addr.ipv6.sin6_port); + + return TRUE; + } + return FALSE; +#endif + + default: + errno = EAFNOSUPPORT; + return FALSE; + } +} + +/* + * Format an error appropriate for saved_errno for the IPv4 or IPv6 + * address pointed to by sockaddr_pointer of length sockaddr_len. + * + * @param error The error to set + * @param sockaddr_pointer A struct sockaddr_in or struct sockaddr_in6 + * @param len The length of the struct pointed to by sockaddr_pointer + * @param description A prefix like "Failed to listen on socket" + * @param saved_errno The OS-level error number to use + */ +void +_dbus_set_error_with_inet_sockaddr (DBusError *error, + const void *sockaddr_pointer, + socklen_t len, + const char *description, + int saved_errno) +{ + char string[INET6_ADDRSTRLEN]; + dbus_uint16_t port; + const struct sockaddr *addr = sockaddr_pointer; + + if (_dbus_inet_sockaddr_to_string (sockaddr_pointer, len, string, sizeof (string), + NULL, &port)) + { + dbus_set_error (error, _dbus_error_from_errno (saved_errno), + "%s \"%s\" port %u: %s", + description, string, port, _dbus_strerror (saved_errno)); + } + else + { + dbus_set_error (error, _dbus_error_from_errno (saved_errno), + "%s
: %s", + description, addr->sa_family, + _dbus_strerror (saved_errno)); + } +} + +void +_dbus_combine_tcp_errors (DBusList **sources, + const char *summary, + const char *host, + const char *port, + DBusError *dest) +{ + DBusString message = _DBUS_STRING_INIT_INVALID; + + if (_dbus_list_length_is_one (sources)) + { + /* If there was exactly one error, just use it */ + dbus_move_error (_dbus_list_get_first (sources), dest); + } + else + { + DBusList *iter; + const char *name; + + /* If there was more than one error, concatenate all the + * errors' diagnostic messages, and use their common error + * name, or DBUS_ERROR_FAILED if more than one name is + * represented */ + if (!_dbus_string_init (&message)) + { + _DBUS_SET_OOM (dest); + goto out; + } + + for (iter = _dbus_list_get_first_link (sources); + iter != NULL; + iter = _dbus_list_get_next_link (sources, iter)) + { + DBusError *error = iter->data; + + if (name == NULL) + { + /* no error names known yet, try to use this one */ + name = error->name; + } + else if (strcmp (name, error->name) != 0) + { + /* errors of two different names exist, reconcile by + * using FAILED */ + name = DBUS_ERROR_FAILED; + } + + if ((_dbus_string_get_length (&message) > 0 && + !_dbus_string_append (&message, "; ")) || + !_dbus_string_append (&message, error->message)) + { + _DBUS_SET_OOM (dest); + goto out; + } + } + + if (name == NULL) + name = DBUS_ERROR_FAILED; + + dbus_set_error (dest, name, "%s to \"%s\":%s (%s)", + summary, host ? host : "*", port, + _dbus_string_get_const_data (&message)); + } + +out: + _dbus_string_free (&message); +} + /** @} end of sysdeps */ /* tests in dbus-sysdeps-util.c */ diff --git a/dbus/dbus-sysdeps.h b/dbus/dbus-sysdeps.h index 0d1ff7c4..c452e7cf 100644 --- a/dbus/dbus-sysdeps.h +++ b/dbus/dbus-sysdeps.h @@ -38,6 +38,10 @@ #include #endif +#ifdef HAVE_UNISTD_H +#include +#endif + #include #include #include @@ -692,6 +696,28 @@ void _dbus_daemon_report_reloading (void); void _dbus_daemon_report_reloaded (void); void _dbus_daemon_report_stopping (void); +DBUS_PRIVATE_EXPORT +void _dbus_set_error_with_inet_sockaddr (DBusError *error, + const void *sockaddr_pointer, + socklen_t len, + const char *description, + int saved_errno); + +DBUS_PRIVATE_EXPORT +void _dbus_combine_tcp_errors (DBusList **sources, + const char *summary, + const char *host, + const char *port, + DBusError *dest); + +DBUS_PRIVATE_EXPORT +dbus_bool_t _dbus_inet_sockaddr_to_string (const void *sockaddr_pointer, + size_t len, + char *string, + size_t string_len, + const char **family_name, + dbus_uint16_t *port); + /** @} */ DBUS_END_DECLS -- 2.12.3