summaryrefslogtreecommitdiffstats
path: root/src/network/kernel/qhostinfo_unix.cpp
diff options
context:
space:
mode:
authorAmir Masoud Abdol <amir.abdol@qt.io>2023-03-01 15:41:14 +0100
committerThiago Macieira <thiago.macieira@intel.com>2023-05-11 17:05:25 -0700
commit68b625901f9eb7c34e3d7aa302e1c0a454d3190b (patch)
tree36faae7f3c3b7d573a82e08390c7f8c03900408e /src/network/kernel/qhostinfo_unix.cpp
parentff9da1db0b0963f967f45ab430ec40a3051b70b4 (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.cpp214
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