diff options
author | Amir Masoud Abdol <amir.abdol@qt.io> | 2023-03-01 15:41:14 +0100 |
---|---|---|
committer | Thiago Macieira <thiago.macieira@intel.com> | 2023-05-11 17:05:25 -0700 |
commit | 68b625901f9eb7c34e3d7aa302e1c0a454d3190b (patch) | |
tree | 36faae7f3c3b7d573a82e08390c7f8c03900408e /src/network/kernel/qhostinfo_unix.cpp | |
parent | ff9da1db0b0963f967f45ab430ec40a3051b70b4 (diff) |
Network: link directly to libresolv instead of dlopen()ing it
There's little need for us to dynamically load it. The reasons why that
was necessary aren't in the public history (Qt 4.5 already had it[1]). I
remember writing the code in 2007-2008, I just don't remember why.
On modern Linux and FreeBSD, there's no libresolv.so any more and those
symbols have been rolled up into libc.so. It's still necessary on Darwin
systems, so this commit introduces WrapResolv.
It also resolves the unity build issues relating to libresolv symbols.
[1] https://code.qt.io/cgit/qt/qt.git/tree/src/network/kernel/qhostinfo_unix.cpp?h=v4.5.1
Task-number: QTBUG-109394
Change-Id: Ic5799e4d000b6c9395109e008780643bac52122b
Reviewed-by: MÃ¥rten Nordheim <marten.nordheim@qt.io>
Diffstat (limited to 'src/network/kernel/qhostinfo_unix.cpp')
-rw-r--r-- | src/network/kernel/qhostinfo_unix.cpp | 214 |
1 files changed, 67 insertions, 147 deletions
diff --git a/src/network/kernel/qhostinfo_unix.cpp b/src/network/kernel/qhostinfo_unix.cpp index 12d8c04d10..331c5fbf45 100644 --- a/src/network/kernel/qhostinfo_unix.cpp +++ b/src/network/kernel/qhostinfo_unix.cpp @@ -3,140 +3,75 @@ //#define QHOSTINFO_DEBUG -#include "qplatformdefs.h" - #include "qhostinfo_p.h" -#include "private/qnativesocketengine_p.h" -#include "qiodevice.h" + #include <qbytearray.h> -#if QT_CONFIG(library) -#include <qlibrary.h> -#endif -#include <qbasicatomic.h> -#include <qurl.h> #include <qfile.h> -#include <private/qnet_unix_p.h> - -#include "QtCore/qapplicationstatic.h" +#include <qplatformdefs.h> +#include <qurl.h> #include <sys/types.h> #include <netdb.h> -#include <arpa/inet.h> -#if defined(Q_OS_VXWORKS) -# include <hostLib.h> -#else -# include <resolv.h> -#endif - -#if defined(__GNU_LIBRARY__) && !defined(__UCLIBC__) -# include <gnu/lib-names.h> -#endif +#include <netinet/in.h> +#include <resolv.h> -#if defined(Q_OS_FREEBSD) || QT_CONFIG(dlopen) -# include <dlfcn.h> +#ifndef _PATH_RESCONF +# define _PATH_RESCONF "/etc/resolv.conf" #endif QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; -enum LibResolvFeature { - NeedResInit, - NeedResNInit -}; - -typedef struct __res_state *res_state_ptr; - -typedef int (*res_init_proto)(void); -static res_init_proto local_res_init = nullptr; -typedef int (*res_ninit_proto)(res_state_ptr); -static res_ninit_proto local_res_ninit = nullptr; -typedef void (*res_nclose_proto)(res_state_ptr); -static res_nclose_proto local_res_nclose = nullptr; -static res_state_ptr local_res = nullptr; - -#if QT_CONFIG(library) && !defined(Q_OS_QNX) -namespace { -struct LibResolv -{ - enum { -#ifdef RES_NORELOAD - // If RES_NORELOAD is defined, then the libc is capable of watching - // /etc/resolv.conf for changes and reloading as necessary. So accept - // whatever is configured. - ReinitNecessary = false -#else - ReinitNecessary = true -#endif - }; - - QLibrary lib; - LibResolv(); - ~LibResolv() { lib.unload(); } -}; -} - -static QFunctionPointer resolveSymbol(QLibrary &lib, const char *sym) -{ - if (lib.isLoaded()) - return lib.resolve(sym); - -#if defined(RTLD_DEFAULT) && (defined(Q_OS_FREEBSD) || QT_CONFIG(dlopen)) - return reinterpret_cast<QFunctionPointer>(dlsym(RTLD_DEFAULT, sym)); -#else - return nullptr; -#endif -} - -LibResolv::LibResolv() +static void maybeRefreshResolver() { -#ifdef LIBRESOLV_SO - lib.setFileName(QStringLiteral(LIBRESOLV_SO)); - if (!lib.load()) +#if defined(RES_NORELOAD) + // If RES_NORELOAD is defined, then the libc is capable of watching + // /etc/resolv.conf for changes and reloading as necessary. So accept + // whatever is configured. + return; +#elif defined(Q_OS_DARWIN) + // Apple's libsystem_info.dylib:getaddrinfo() uses the + // libsystem_dnssd.dylib to resolve hostnames. Using res_init() has no + // effect on it and is thread-unsafe. + return; +#elif defined(Q_OS_FREEBSD) + // FreeBSD automatically refreshes: + // https://github.com/freebsd/freebsd-src/blob/b3fe5d932264445cbf9a1c4eab01afb6179b499b/lib/libc/resolv/res_state.c#L69 + return; +#elif defined(Q_OS_OPENBSD) + // OpenBSD automatically refreshes: + // https://github.com/ligurio/openbsd-src/blob/b1ce0da17da254cc15b8aff25b3d55d3c7a82cec/lib/libc/asr/asr.c#L367 + return; +#elif defined(Q_OS_QNX) + // res_init() is not thread-safe; executing it leads to state corruption. + // Whether it reloads resolv.conf on its own is unknown. + return; #endif - { - lib.setFileName("resolv"_L1); - lib.load(); - } - - // res_ninit is required for localDomainName() - local_res_ninit = res_ninit_proto(resolveSymbol(lib, "__res_ninit")); - if (!local_res_ninit) - local_res_ninit = res_ninit_proto(resolveSymbol(lib, "res_ninit")); - if (local_res_ninit) { - // we must now find res_nclose - local_res_nclose = res_nclose_proto(resolveSymbol(lib, "res_nclose")); - if (!local_res_nclose) - local_res_nclose = res_nclose_proto(resolveSymbol(lib, "__res_nclose")); - if (!local_res_nclose) - local_res_ninit = nullptr; - } - - if (ReinitNecessary || !local_res_ninit) { - local_res_init = res_init_proto(resolveSymbol(lib, "__res_init")); - if (!local_res_init) - local_res_init = res_init_proto(resolveSymbol(lib, "res_init")); - if (local_res_init && !local_res_ninit) { - // if we can't get a thread-safe context, we have to use the global _res state - local_res = res_state_ptr(resolveSymbol(lib, "_res")); +#if QT_CONFIG(res_ninit) + // OSes known or thought to reach here: AIX, NetBSD, Solaris, + // Linux with MUSL (though res_init() does nothing and is unnecessary) + + Q_CONSTINIT static QT_STATBUF lastStat = {}; + Q_CONSTINIT static QBasicMutex mutex = {}; + if (QT_STATBUF st; QT_STAT(_PATH_RESCONF, &st) == 0) { + QMutexLocker locker(&mutex); + bool refresh = false; + if ((_res.options & RES_INIT) == 0) + refresh = true; + else if (lastStat.st_ctime != st.st_ctime) + refresh = true; // file was updated + else if (lastStat.st_dev != st.st_dev || lastStat.st_ino != st.st_ino) + refresh = true; // file was replaced + if (refresh) { + lastStat = st; + res_init(); } } +#endif } -Q_APPLICATION_STATIC(LibResolv, libResolv) - -static void resolveLibrary(LibResolvFeature f) -{ - if (LibResolv::ReinitNecessary || f == NeedResNInit) - libResolv(); -} -#else // QT_CONFIG(library) || Q_OS_QNX -static void resolveLibrary(LibResolvFeature) -{ -} -#endif // QT_CONFIG(library) || Q_OS_QNX - QHostInfo QHostInfoAgent::fromName(const QString &hostName) { QHostInfo results; @@ -146,12 +81,7 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) hostName.toLatin1().constData()); #endif - // Load res_init on demand. - resolveLibrary(NeedResInit); - - // If res_init is available, poll it. - if (local_res_init) - local_res_init(); + maybeRefreshResolver(); QHostAddress address; if (address.setAddress(hostName)) @@ -162,40 +92,30 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) QString QHostInfo::localDomainName() { -#if !defined(Q_OS_VXWORKS) && !defined(Q_OS_ANDROID) - resolveLibrary(NeedResNInit); - if (local_res_ninit) { - // using thread-safe version - res_state_ptr state = res_state_ptr(malloc(sizeof(*state))); - Q_CHECK_PTR(state); - memset(state, 0, sizeof(*state)); - local_res_ninit(state); - QString domainName = QUrl::fromAce(state->defdname); +#if QT_CONFIG(res_ninit) + auto domainNameFromRes = [](res_state r) { + QString domainName; + if (r->defdname[0]) + domainName = QUrl::fromAce(r->defdname); if (domainName.isEmpty()) - domainName = QUrl::fromAce(state->dnsrch[0]); - local_res_nclose(state); - free(state); - + domainName = QUrl::fromAce(r->dnsrch[0]); return domainName; + }; + std::remove_pointer_t<res_state> state = {}; + if (res_ninit(&state) == 0) { + // using thread-safe version + auto guard = qScopeGuard([&] { res_nclose(&state); }); + return domainNameFromRes(&state); } - if (local_res_init && local_res) { - // using thread-unsafe version + // using thread-unsafe version + maybeRefreshResolver(); + return domainNameFromRes(&_res); +#endif // !QT_CONFIG(res_ninit) - local_res_init(); - QString domainName = QUrl::fromAce(local_res->defdname); - if (domainName.isEmpty()) - domainName = QUrl::fromAce(local_res->dnsrch[0]); - return domainName; - } -#endif // nothing worked, try doing it by ourselves: QFile resolvconf; -#if defined(_PATH_RESCONF) - resolvconf.setFileName(QFile::decodeName(_PATH_RESCONF)); -#else - resolvconf.setFileName("/etc/resolv.conf"_L1); -#endif + resolvconf.setFileName(_PATH_RESCONF ""_L1); if (!resolvconf.open(QIODevice::ReadOnly)) return QString(); // failure |