From 8ec0b5ae2d9bce65d8246204b90a9d2552f337e7 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 4 Jun 2018 16:27:46 +0100 Subject: [PATCH] sysdeps-unix: Handle errors from getaddrinfo correctly getaddrinfo and getnameinfo have their own error-handling convention in which the library call returns either 0 or an EAI_* error code unrelated to errno. If the error code is not EAI_SYSTEM, then the value of errno is undefined (in particular it might be carried over from a previous system call or library call). Introduce a new helper function _dbus_error_from_gai() to handle this. The equivalent code paths in Windows appear to be OK: the Windows implementation of getaddrinfo() is documented to return a Winsock error code, which we seem to be handling correctly. Signed-off-by: Simon McVittie Reviewed-by: Philip Withnall Bug: https://bugs.freedesktop.org/show_bug.cgi?id=106395 (cherry picked from commit 60cedd0cfd775c9fcf7260e12af9b2ffeefc2bbe) --- dbus/dbus-sysdeps-unix.c | 70 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 65 insertions(+), 5 deletions(-) diff --git a/dbus/dbus-sysdeps-unix.c b/dbus/dbus-sysdeps-unix.c index b0ba899..222c8c8 100644 --- a/dbus/dbus-sysdeps-unix.c +++ b/dbus/dbus-sysdeps-unix.c @@ -1320,6 +1320,56 @@ _dbus_listen_systemd_sockets (DBusSocket **fds, #endif } +/* Convert an error code from getaddrinfo() or getnameinfo() into + * a D-Bus error name. */ +static const char * +_dbus_error_from_gai (int gai_res, + int saved_errno) +{ + switch (gai_res) + { +#ifdef EAI_FAMILY + case EAI_FAMILY: + /* ai_family not supported (at all) */ + return DBUS_ERROR_NOT_SUPPORTED; +#endif + +#ifdef EAI_SOCKTYPE + case EAI_SOCKTYPE: + /* ai_socktype not supported (at all) */ + return DBUS_ERROR_NOT_SUPPORTED; +#endif + +#ifdef EAI_MEMORY + case EAI_MEMORY: + /* Out of memory */ + return DBUS_ERROR_NO_MEMORY; +#endif + +#ifdef EAI_SYSTEM + case EAI_SYSTEM: + /* Unspecified system error, details in errno */ + return _dbus_error_from_errno (saved_errno); +#endif + + case 0: + /* It succeeded, but we didn't get any addresses? */ + return DBUS_ERROR_FAILED; + + /* EAI_AGAIN: Transient failure */ + /* EAI_BADFLAGS: invalid ai_flags (programming error) */ + /* EAI_FAIL: Non-recoverable failure */ + /* EAI_NODATA: host exists but has no addresses */ + /* EAI_NONAME: host does not exist */ + /* EAI_OVERFLOW: argument buffer overflow */ + /* EAI_SERVICE: service not available for specified socket + * type (we should never see this because we use numeric + * ports) */ + default: + return DBUS_ERROR_FAILED; + } +} + /** * Creates a socket and connects to a socket at the given host * and port. The connection fd is returned, and is set up as @@ -1379,7 +1429,7 @@ _dbus_connect_tcp_socket_with_nonce (const char *host, if ((res = getaddrinfo(host, port, &hints, &ai)) != 0) { dbus_set_error (error, - _dbus_error_from_errno (errno), + _dbus_error_from_gai (res, errno), "Failed to lookup host/port: \"%s:%s\": %s (%d)", host, port, gai_strerror(res), res); return _dbus_socket_get_invalid (); @@ -1501,7 +1551,7 @@ _dbus_listen_tcp_socket (const char *host, if ((res = getaddrinfo(host, port, &hints, &ai)) != 0 || !ai) { dbus_set_error (error, - _dbus_error_from_errno (errno), + _dbus_error_from_gai (res, errno), "Failed to lookup host/port: \"%s:%s\": %s (%d)", host ? host : "*", port, gai_strerror(res), res); goto failed; @@ -1601,16 +1651,26 @@ _dbus_listen_tcp_socket (const char *host, addrlen = sizeof(addr); result = getsockname(fd, (struct sockaddr*) &addr, &addrlen); - if (result == -1 || - (res = getnameinfo ((struct sockaddr*)&addr, addrlen, NULL, 0, + if (result == -1) + { + saved_errno = errno; + dbus_set_error (error, _dbus_error_from_errno (saved_errno), + "Failed to retrieve socket name for \"%s:%s\": %s", + host ? host : "*", port, _dbus_strerror (saved_errno)); + goto failed; + } + + if ((res = getnameinfo ((struct sockaddr*)&addr, addrlen, NULL, 0, portbuf, sizeof(portbuf), NI_NUMERICHOST | NI_NUMERICSERV)) != 0) { - dbus_set_error (error, _dbus_error_from_errno (errno), + saved_errno = errno; + dbus_set_error (error, _dbus_error_from_gai (res, saved_errno), "Failed to resolve port \"%s:%s\": %s (%d)", host ? host : "*", port, gai_strerror(res), res); goto failed; } + if (!_dbus_string_append(retport, portbuf)) { dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); -- 2.7.4