diff options
Diffstat (limited to 'src/network/kernel/qhostinfo_unix.cpp')
-rw-r--r-- | src/network/kernel/qhostinfo_unix.cpp | 284 |
1 files changed, 80 insertions, 204 deletions
diff --git a/src/network/kernel/qhostinfo_unix.cpp b/src/network/kernel/qhostinfo_unix.cpp index 9b0a2ee669..80d386a13d 100644 --- a/src/network/kernel/qhostinfo_unix.cpp +++ b/src/network/kernel/qhostinfo_unix.cpp @@ -1,192 +1,80 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtNetwork module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only //#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 <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 +#include <netinet/in.h> -#if defined(__GNU_LIBRARY__) && !defined(__UCLIBC__) -# include <gnu/lib-names.h> +#if QT_CONFIG(libresolv) +# include <resolv.h> #endif -#if defined(Q_OS_FREEBSD) || QT_CONFIG(dlopen) -# include <dlfcn.h> +#ifndef _PATH_RESCONF +# define _PATH_RESCONF "/etc/resolv.conf" #endif QT_BEGIN_NAMESPACE -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 -} +using namespace Qt::StringLiterals; -LibResolv::LibResolv() +static void maybeRefreshResolver() { - QLibrary lib; -#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(QLatin1String("resolv")); - 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(libresolv) + // 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 } -LibResolv* libResolv() -{ - static LibResolv* theLibResolv = nullptr; - static QBasicMutex theMutex; - - const QMutexLocker locker(&theMutex); - if (theLibResolv == nullptr) { - theLibResolv = new LibResolv(); - Q_ASSERT(QCoreApplication::instance()); - QObject::connect(QCoreApplication::instance(), &QCoreApplication::destroyed, [] { - const QMutexLocker locker(&theMutex); - delete theLibResolv; - theLibResolv = nullptr; - }); - } - - return theLibResolv; -} - -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; @@ -196,12 +84,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)) @@ -212,56 +95,49 @@ 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(libresolv) + 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(libresolv) - 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(QLatin1String("/etc/resolv.conf")); -#endif + resolvconf.setFileName(_PATH_RESCONF ""_L1); if (!resolvconf.open(QIODevice::ReadOnly)) return QString(); // failure QString domainName; while (!resolvconf.atEnd()) { - QByteArray line = resolvconf.readLine().trimmed(); - if (line.startsWith("domain ")) - return QUrl::fromAce(line.mid(sizeof "domain " - 1).trimmed()); + const QByteArray lineArray = resolvconf.readLine(); + QByteArrayView line = QByteArrayView(lineArray).trimmed(); + constexpr QByteArrayView domainWithSpace = "domain "; + if (line.startsWith(domainWithSpace)) + return QUrl::fromAce(line.mid(domainWithSpace.size()).trimmed().toByteArray()); // in case there's no "domain" line, fall back to the first "search" entry - if (domainName.isEmpty() && line.startsWith("search ")) { - QByteArray searchDomain = line.mid(sizeof "search " - 1).trimmed(); + constexpr QByteArrayView searchWithSpace = "search "; + if (domainName.isEmpty() && line.startsWith(searchWithSpace)) { + QByteArrayView searchDomain = line.mid(searchWithSpace.size()).trimmed(); int pos = searchDomain.indexOf(' '); if (pos != -1) searchDomain.truncate(pos); - domainName = QUrl::fromAce(searchDomain); + domainName = QUrl::fromAce(searchDomain.toByteArray()); } } |