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>
This commit is contained in:
parent
ff9da1db0b
commit
68b625901f
49
cmake/FindWrapResolv.cmake
Normal file
49
cmake/FindWrapResolv.cmake
Normal file
@ -0,0 +1,49 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# Copyright (C) 2023 Intel Corpotation.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
# We can't create the same interface imported target multiple times, CMake will complain if we do
|
||||
# that. This can happen if the find_package call is done in multiple different subdirectories.
|
||||
if(TARGET WrapResolv::WrapResolv)
|
||||
set(WrapResolv_FOUND ON)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(WrapResolv_FOUND OFF)
|
||||
|
||||
include(CheckCXXSourceCompiles)
|
||||
include(CMakePushCheckState)
|
||||
|
||||
if(QNX)
|
||||
find_library(LIBRESOLV socket)
|
||||
else()
|
||||
find_library(LIBRESOLV resolv)
|
||||
endif()
|
||||
|
||||
cmake_push_check_state()
|
||||
if(LIBRESOLV)
|
||||
list(APPEND CMAKE_REQUIRED_LIBRARIES "${LIBRESOLV}")
|
||||
endif()
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include <netinet/in.h>
|
||||
#include <resolv.h>
|
||||
|
||||
int main(int, char **)
|
||||
{
|
||||
res_init();
|
||||
}
|
||||
" HAVE_RES_INIT)
|
||||
|
||||
cmake_pop_check_state()
|
||||
|
||||
if(HAVE_RES_INIT)
|
||||
set(WrapResolv_FOUND ON)
|
||||
add_library(WrapResolv::WrapResolv INTERFACE IMPORTED)
|
||||
if(LIBRESOLV)
|
||||
target_link_libraries(WrapResolv::WrapResolv INTERFACE "${LIBRESOLV}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(WrapResolv DEFAULT_MSG WrapResolv_FOUND)
|
@ -174,15 +174,6 @@ qt_internal_extend_target(Network CONDITION UNIX
|
||||
kernel/qhostinfo_unix.cpp
|
||||
socket/qnativesocketengine_unix.cpp
|
||||
socket/qnet_unix_p.h
|
||||
NO_UNITY_BUILD_SOURCES
|
||||
kernel/qhostinfo_unix.cpp # Temporary exclusion until, this,
|
||||
# https://codereview.qt-project.org/c/qt/qtbase/+/463670
|
||||
# is merged.
|
||||
)
|
||||
|
||||
qt_internal_extend_target(Network CONDITION QT_FEATURE_dlopen AND UNIX
|
||||
LIBRARIES
|
||||
${CMAKE_DL_LIBS}
|
||||
)
|
||||
|
||||
qt_internal_extend_target(Network CONDITION QT_FEATURE_linux_netlink AND UNIX
|
||||
@ -195,11 +186,6 @@ qt_internal_extend_target(Network CONDITION UNIX AND NOT QT_FEATURE_linux_netlin
|
||||
kernel/qnetworkinterface_unix.cpp
|
||||
)
|
||||
|
||||
qt_internal_extend_target(Network CONDITION ANDROID AND QT_FEATURE_dnslookup
|
||||
SOURCES
|
||||
kernel/qdnslookup_android.cpp
|
||||
)
|
||||
|
||||
qt_internal_extend_target(Network CONDITION WIN32
|
||||
SOURCES
|
||||
kernel/qhostinfo_win.cpp
|
||||
@ -214,11 +200,6 @@ qt_internal_extend_target(Network CONDITION WIN32
|
||||
winhttp
|
||||
)
|
||||
|
||||
qt_internal_extend_target(Network CONDITION QT_FEATURE_dnslookup AND WIN32
|
||||
SOURCES
|
||||
kernel/qdnslookup_win.cpp
|
||||
)
|
||||
|
||||
qt_internal_extend_target(Network CONDITION APPLE AND NOT UIKIT
|
||||
LIBRARIES
|
||||
${FWCoreServices}
|
||||
@ -236,6 +217,22 @@ qt_internal_extend_target(Network CONDITION APPLE
|
||||
${FWCFNetwork}
|
||||
)
|
||||
|
||||
qt_internal_extend_target(Network CONDITION QT_FEATURE_dnslookup AND TEST_res_ninit
|
||||
SOURCES
|
||||
kernel/qdnslookup_unix.cpp
|
||||
LIBRARIES
|
||||
WrapResolv::WrapResolv
|
||||
)
|
||||
|
||||
qt_internal_extend_target(Network CONDITION QT_FEATURE_dnslookup AND WIN32
|
||||
SOURCES
|
||||
kernel/qdnslookup_win.cpp
|
||||
)
|
||||
|
||||
qt_internal_extend_target(Network CONDITION QT_FEATURE_dnslookup AND NOT TEST_res_ninit AND NOT WIN32
|
||||
SOURCES
|
||||
kernel/qdnslookup_dummy.cpp
|
||||
)
|
||||
|
||||
qt_internal_extend_target(Network CONDITION IOS OR MACOS
|
||||
SOURCES
|
||||
@ -369,10 +366,6 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_ocsp AND QT_FEATURE_opens
|
||||
ssl/qocsp_p.h
|
||||
)
|
||||
|
||||
qt_internal_extend_target(Network CONDITION QT_FEATURE_dnslookup AND UNIX AND NOT ANDROID
|
||||
SOURCES
|
||||
kernel/qdnslookup_unix.cpp
|
||||
)
|
||||
qt_internal_add_docs(Network
|
||||
doc/qtnetwork.qdocconf
|
||||
)
|
||||
|
@ -12,7 +12,7 @@ qt_find_package(Libproxy PROVIDED_TARGETS PkgConfig::Libproxy MODULE_NAME networ
|
||||
qt_find_package(GSSAPI PROVIDED_TARGETS GSSAPI::GSSAPI MODULE_NAME network QMAKE_LIB gssapi)
|
||||
qt_find_package(GLIB2 OPTIONAL_COMPONENTS GOBJECT PROVIDED_TARGETS GLIB2::GOBJECT MODULE_NAME core QMAKE_LIB gobject)
|
||||
qt_find_package(GLIB2 OPTIONAL_COMPONENTS GIO PROVIDED_TARGETS GLIB2::GIO MODULE_NAME core QMAKE_LIB gio)
|
||||
|
||||
qt_find_package(WrapResolv PROVIDED_TARGETS WrapResolv::WrapResolv MODULE_NAME network QMAKE_LIB libresolv)
|
||||
|
||||
#### Tests
|
||||
|
||||
@ -100,6 +100,25 @@ ci.ifa_prefered = ci.ifa_valid = 0;
|
||||
}
|
||||
")
|
||||
|
||||
# res_ninit
|
||||
qt_config_compile_test(res_ninit
|
||||
LABEL "res_ninit()"
|
||||
LIBRARIES
|
||||
WrapResolv::WrapResolv
|
||||
CODE
|
||||
"#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#include <resolv.h>
|
||||
int main()
|
||||
{
|
||||
res_state state;
|
||||
res_ninit(state);
|
||||
res_nclose(state);
|
||||
return 0;
|
||||
}
|
||||
"
|
||||
)
|
||||
|
||||
# sctp
|
||||
qt_config_compile_test(sctp
|
||||
LABEL "SCTP support"
|
||||
@ -211,6 +230,11 @@ qt_feature("linux-netlink" PRIVATE
|
||||
LABEL "Linux AF_NETLINK"
|
||||
CONDITION LINUX AND NOT ANDROID AND TEST_linux_netlink
|
||||
)
|
||||
qt_feature("res_ninit" PRIVATE
|
||||
LABEL "res_ninit()"
|
||||
CONDITION TEST_res_ninit
|
||||
AUTODETECT UNIX
|
||||
)
|
||||
qt_feature("securetransport" PUBLIC
|
||||
LABEL "SecureTransport"
|
||||
CONDITION APPLE
|
||||
|
@ -13,7 +13,7 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN
|
||||
Q_UNUSED(reply);
|
||||
qWarning("Not yet supported on Android");
|
||||
reply->error = QDnsLookup::ResolverError;
|
||||
reply->errorString = tr("Not yet supported on Android");
|
||||
reply->errorString = tr("Not yet supported on this OS");
|
||||
return;
|
||||
}
|
||||
|
@ -3,130 +3,36 @@
|
||||
|
||||
#include "qdnslookup_p.h"
|
||||
|
||||
#if QT_CONFIG(library)
|
||||
#include <qlibrary.h>
|
||||
#endif
|
||||
#include <qvarlengtharray.h>
|
||||
#include <qscopedpointer.h>
|
||||
#include <qurl.h>
|
||||
#include <private/qnativesocketengine_p.h>
|
||||
#include <qvarlengtharray.h>
|
||||
#include <private/qnativesocketengine_p.h> // for SetSALen
|
||||
#include <private/qtnetwork-config_p.h>
|
||||
|
||||
QT_REQUIRE_CONFIG(res_ninit);
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/nameser.h>
|
||||
#if !defined(Q_OS_OPENBSD)
|
||||
#if __has_include(<arpa/nameser_compat.h>)
|
||||
# include <arpa/nameser_compat.h>
|
||||
#endif
|
||||
#include <resolv.h>
|
||||
|
||||
#if defined(__GNU_LIBRARY__) && !defined(__UCLIBC__)
|
||||
# include <gnu/lib-names.h>
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_FREEBSD) || QT_CONFIG(dlopen)
|
||||
# include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
#if QT_CONFIG(library)
|
||||
|
||||
#if defined(Q_OS_OPENBSD)
|
||||
typedef struct __res_state* res_state;
|
||||
#endif
|
||||
typedef int (*dn_expand_proto)(const unsigned char *, const unsigned char *, const unsigned char *, char *, int);
|
||||
static dn_expand_proto local_dn_expand = nullptr;
|
||||
typedef void (*res_nclose_proto)(res_state);
|
||||
static res_nclose_proto local_res_nclose = nullptr;
|
||||
typedef int (*res_ninit_proto)(res_state);
|
||||
static res_ninit_proto local_res_ninit = nullptr;
|
||||
typedef int (*res_nquery_proto)(res_state, const char *, int, int, unsigned char *, int);
|
||||
static res_nquery_proto local_res_nquery = nullptr;
|
||||
|
||||
// Custom deleter to close resolver state.
|
||||
|
||||
struct QDnsLookupStateDeleter
|
||||
{
|
||||
static inline void cleanup(struct __res_state *pointer)
|
||||
{
|
||||
local_res_nclose(pointer);
|
||||
}
|
||||
};
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
static bool resolveLibraryInternal()
|
||||
{
|
||||
QLibrary lib;
|
||||
#ifdef LIBRESOLV_SO
|
||||
lib.setFileName(QStringLiteral(LIBRESOLV_SO));
|
||||
if (!lib.load())
|
||||
#endif
|
||||
{
|
||||
lib.setFileName("resolv"_L1);
|
||||
lib.load();
|
||||
}
|
||||
|
||||
local_dn_expand = dn_expand_proto(resolveSymbol(lib, "__dn_expand"));
|
||||
if (!local_dn_expand)
|
||||
local_dn_expand = dn_expand_proto(resolveSymbol(lib, "dn_expand"));
|
||||
|
||||
local_res_nclose = res_nclose_proto(resolveSymbol(lib, "__res_nclose"));
|
||||
if (!local_res_nclose)
|
||||
local_res_nclose = res_nclose_proto(resolveSymbol(lib, "res_9_nclose"));
|
||||
if (!local_res_nclose)
|
||||
local_res_nclose = res_nclose_proto(resolveSymbol(lib, "res_nclose"));
|
||||
|
||||
local_res_ninit = res_ninit_proto(resolveSymbol(lib, "__res_ninit"));
|
||||
if (!local_res_ninit)
|
||||
local_res_ninit = res_ninit_proto(resolveSymbol(lib, "res_9_ninit"));
|
||||
if (!local_res_ninit)
|
||||
local_res_ninit = res_ninit_proto(resolveSymbol(lib, "res_ninit"));
|
||||
|
||||
local_res_nquery = res_nquery_proto(resolveSymbol(lib, "__res_nquery"));
|
||||
if (!local_res_nquery)
|
||||
local_res_nquery = res_nquery_proto(resolveSymbol(lib, "res_9_nquery"));
|
||||
if (!local_res_nquery)
|
||||
local_res_nquery = res_nquery_proto(resolveSymbol(lib, "res_nquery"));
|
||||
|
||||
return true;
|
||||
}
|
||||
Q_GLOBAL_STATIC_WITH_ARGS(bool, resolveLibrary, (resolveLibraryInternal()))
|
||||
|
||||
void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, const QHostAddress &nameserver, QDnsLookupReply *reply)
|
||||
{
|
||||
// Load dn_expand, res_ninit and res_nquery on demand.
|
||||
resolveLibrary();
|
||||
|
||||
// If dn_expand, res_ninit or res_nquery is missing, fail.
|
||||
if (!local_dn_expand || !local_res_nclose || !local_res_ninit || !local_res_nquery) {
|
||||
reply->error = QDnsLookup::ResolverError;
|
||||
reply->errorString = tr("Resolver functions not found");
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize state.
|
||||
struct __res_state state;
|
||||
std::memset(&state, 0, sizeof(state));
|
||||
if (local_res_ninit(&state) < 0) {
|
||||
std::remove_pointer_t<res_state> state = {};
|
||||
if (res_ninit(&state) < 0) {
|
||||
reply->error = QDnsLookup::ResolverError;
|
||||
reply->errorString = tr("Resolver initialization failed");
|
||||
return;
|
||||
}
|
||||
auto guard = qScopeGuard([&] { res_nclose(&state); });
|
||||
|
||||
//Check if a nameserver was set. If so, use it
|
||||
if (!nameserver.isNull()) {
|
||||
@ -170,16 +76,15 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN
|
||||
#ifdef QDNSLOOKUP_DEBUG
|
||||
state.options |= RES_DEBUG;
|
||||
#endif
|
||||
QScopedPointer<struct __res_state, QDnsLookupStateDeleter> state_ptr(&state);
|
||||
|
||||
// Perform DNS query.
|
||||
QVarLengthArray<unsigned char, PACKETSZ> buffer(PACKETSZ);
|
||||
std::memset(buffer.data(), 0, buffer.size());
|
||||
int responseLength = local_res_nquery(&state, requestName, C_IN, requestType, buffer.data(), buffer.size());
|
||||
int responseLength = res_nquery(&state, requestName, C_IN, requestType, buffer.data(), buffer.size());
|
||||
if (Q_UNLIKELY(responseLength > PACKETSZ)) {
|
||||
buffer.resize(responseLength);
|
||||
std::memset(buffer.data(), 0, buffer.size());
|
||||
responseLength = local_res_nquery(&state, requestName, C_IN, requestType, buffer.data(), buffer.size());
|
||||
responseLength = res_nquery(&state, requestName, C_IN, requestType, buffer.data(), buffer.size());
|
||||
if (Q_UNLIKELY(responseLength > buffer.size())) {
|
||||
// Ok, we give up.
|
||||
reply->error = QDnsLookup::ResolverError;
|
||||
@ -229,7 +134,7 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN
|
||||
// Skip the query host, type (2 bytes) and class (2 bytes).
|
||||
char host[PACKETSZ], answer[PACKETSZ];
|
||||
unsigned char *p = response + sizeof(HEADER);
|
||||
int status = local_dn_expand(response, response + responseLength, p, host, sizeof(host));
|
||||
int status = dn_expand(response, response + responseLength, p, host, sizeof(host));
|
||||
if (status < 0) {
|
||||
reply->error = QDnsLookup::InvalidReplyError;
|
||||
reply->errorString = tr("Could not expand domain name");
|
||||
@ -240,7 +145,7 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN
|
||||
// Extract results.
|
||||
int answerIndex = 0;
|
||||
while ((p < response + responseLength) && (answerIndex < answerCount)) {
|
||||
status = local_dn_expand(response, response + responseLength, p, host, sizeof(host));
|
||||
status = dn_expand(response, response + responseLength, p, host, sizeof(host));
|
||||
if (status < 0) {
|
||||
reply->error = QDnsLookup::InvalidReplyError;
|
||||
reply->errorString = tr("Could not expand domain name");
|
||||
@ -281,7 +186,7 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN
|
||||
record.d->value = QHostAddress(p);
|
||||
reply->hostAddressRecords.append(record);
|
||||
} else if (type == QDnsLookup::CNAME) {
|
||||
status = local_dn_expand(response, response + responseLength, p, answer, sizeof(answer));
|
||||
status = dn_expand(response, response + responseLength, p, answer, sizeof(answer));
|
||||
if (status < 0) {
|
||||
reply->error = QDnsLookup::InvalidReplyError;
|
||||
reply->errorString = tr("Invalid canonical name record");
|
||||
@ -293,7 +198,7 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN
|
||||
record.d->value = QUrl::fromAce(answer);
|
||||
reply->canonicalNameRecords.append(record);
|
||||
} else if (type == QDnsLookup::NS) {
|
||||
status = local_dn_expand(response, response + responseLength, p, answer, sizeof(answer));
|
||||
status = dn_expand(response, response + responseLength, p, answer, sizeof(answer));
|
||||
if (status < 0) {
|
||||
reply->error = QDnsLookup::InvalidReplyError;
|
||||
reply->errorString = tr("Invalid name server record");
|
||||
@ -305,7 +210,7 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN
|
||||
record.d->value = QUrl::fromAce(answer);
|
||||
reply->nameServerRecords.append(record);
|
||||
} else if (type == QDnsLookup::PTR) {
|
||||
status = local_dn_expand(response, response + responseLength, p, answer, sizeof(answer));
|
||||
status = dn_expand(response, response + responseLength, p, answer, sizeof(answer));
|
||||
if (status < 0) {
|
||||
reply->error = QDnsLookup::InvalidReplyError;
|
||||
reply->errorString = tr("Invalid pointer record");
|
||||
@ -318,7 +223,7 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN
|
||||
reply->pointerRecords.append(record);
|
||||
} else if (type == QDnsLookup::MX) {
|
||||
const quint16 preference = (p[0] << 8) | p[1];
|
||||
status = local_dn_expand(response, response + responseLength, p + 2, answer, sizeof(answer));
|
||||
status = dn_expand(response, response + responseLength, p + 2, answer, sizeof(answer));
|
||||
if (status < 0) {
|
||||
reply->error = QDnsLookup::InvalidReplyError;
|
||||
reply->errorString = tr("Invalid mail exchange record");
|
||||
@ -334,7 +239,7 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN
|
||||
const quint16 priority = (p[0] << 8) | p[1];
|
||||
const quint16 weight = (p[2] << 8) | p[3];
|
||||
const quint16 port = (p[4] << 8) | p[5];
|
||||
status = local_dn_expand(response, response + responseLength, p + 6, answer, sizeof(answer));
|
||||
status = dn_expand(response, response + responseLength, p + 6, answer, sizeof(answer));
|
||||
if (status < 0) {
|
||||
reply->error = QDnsLookup::InvalidReplyError;
|
||||
reply->errorString = tr("Invalid service record");
|
||||
@ -371,17 +276,4 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, const QHostAddress &nameserver, QDnsLookupReply *reply)
|
||||
{
|
||||
Q_UNUSED(requestType);
|
||||
Q_UNUSED(requestName);
|
||||
Q_UNUSED(nameserver);
|
||||
reply->error = QDnsLookup::ResolverError;
|
||||
reply->errorString = tr("Resolver library can't be loaded: No runtime library loading support");
|
||||
return;
|
||||
}
|
||||
|
||||
#endif /* QT_CONFIG(library) */
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -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 <qbytearray.h>
|
||||
#include <qfile.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>
|
||||
#include <resolv.h>
|
||||
|
||||
#if defined(__GNU_LIBRARY__) && !defined(__UCLIBC__)
|
||||
# include <gnu/lib-names.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
|
||||
|
||||
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
|
||||
static void maybeRefreshResolver()
|
||||
{
|
||||
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
|
||||
#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
|
||||
};
|
||||
|
||||
QLibrary lib;
|
||||
LibResolv();
|
||||
~LibResolv() { lib.unload(); }
|
||||
};
|
||||
}
|
||||
#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)
|
||||
|
||||
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()
|
||||
{
|
||||
#ifdef LIBRESOLV_SO
|
||||
lib.setFileName(QStringLiteral(LIBRESOLV_SO));
|
||||
if (!lib.load())
|
||||
#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"));
|
||||
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) {
|
||||
#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(r->dnsrch[0]);
|
||||
return domainName;
|
||||
};
|
||||
std::remove_pointer_t<res_state> state = {};
|
||||
if (res_ninit(&state) == 0) {
|
||||
// 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 (domainName.isEmpty())
|
||||
domainName = QUrl::fromAce(state->dnsrch[0]);
|
||||
local_res_nclose(state);
|
||||
free(state);
|
||||
|
||||
return domainName;
|
||||
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
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
if(NOT INTEGRITY)
|
||||
if(WIN32 OR TEST_res_ninit)
|
||||
add_subdirectory(qdnslookup)
|
||||
add_subdirectory(qdnslookup_appless)
|
||||
endif()
|
||||
|
@ -202,11 +202,6 @@ void tst_QDnsLookup::lookup()
|
||||
lookup.lookup();
|
||||
QTRY_VERIFY_WITH_TIMEOUT(lookup.isFinished(), Timeout);
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
if (lookup.errorString() == QStringLiteral("Not yet supported on Android"))
|
||||
QEXPECT_FAIL("", "Not yet supported on Android", Abort);
|
||||
#endif
|
||||
|
||||
QVERIFY2(int(lookup.error()) == error,
|
||||
msgDnsLookup(lookup.error(), error, domain, cname, host, srv, mx, ns, ptr, lookup.errorString()));
|
||||
if (error == QDnsLookup::NoError)
|
||||
@ -301,11 +296,6 @@ void tst_QDnsLookup::lookupReuse()
|
||||
lookup.lookup();
|
||||
QTRY_VERIFY_WITH_TIMEOUT(lookup.isFinished(), Timeout);
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
if (lookup.errorString() == QStringLiteral("Not yet supported on Android"))
|
||||
QEXPECT_FAIL("", "Not yet supported on Android", Abort);
|
||||
#endif
|
||||
|
||||
QCOMPARE(int(lookup.error()), int(QDnsLookup::NoError));
|
||||
QVERIFY(!lookup.hostAddressRecords().isEmpty());
|
||||
QCOMPARE(lookup.hostAddressRecords().first().name(), domainName("a-single"));
|
||||
@ -342,11 +332,6 @@ void tst_QDnsLookup::lookupAbortRetry()
|
||||
lookup.lookup();
|
||||
QTRY_VERIFY_WITH_TIMEOUT(lookup.isFinished(), Timeout);
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
if (lookup.errorString() == QStringLiteral("Not yet supported on Android"))
|
||||
QEXPECT_FAIL("", "Not yet supported on Android", Abort);
|
||||
#endif
|
||||
|
||||
QCOMPARE(int(lookup.error()), int(QDnsLookup::NoError));
|
||||
QVERIFY(!lookup.hostAddressRecords().isEmpty());
|
||||
QCOMPARE(lookup.hostAddressRecords().first().name(), domainName("aaaa-single"));
|
||||
|
@ -506,6 +506,7 @@ protected:
|
||||
inline void run() override
|
||||
{
|
||||
QHostInfo info = QHostInfo::fromName("a-single" TEST_DOMAIN);
|
||||
QCOMPARE(info.errorString(), "Unknown error"); // no error
|
||||
QCOMPARE(info.error(), QHostInfo::NoError);
|
||||
QVERIFY(info.addresses().size() > 0);
|
||||
QCOMPARE(info.addresses().at(0).toString(), QString("192.0.2.1"));
|
||||
|
Loading…
Reference in New Issue
Block a user