blob: 78a05f8407361436cddedad8aa08a26bde11a3a3 [file] [log] [blame]
/****************************************************************************
**
** 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$
**
****************************************************************************/
//#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 <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
#if defined(Q_OS_FREEBSD) || QT_CONFIG(dlopen)
# include <dlfcn.h>
#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 = 0;
typedef int (*res_ninit_proto)(res_state_ptr);
static res_ninit_proto local_res_ninit = 0;
typedef void (*res_nclose_proto)(res_state_ptr);
static res_nclose_proto local_res_nclose = 0;
static res_state_ptr local_res = 0;
#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()
{
QLibrary lib;
#ifdef LIBRESOLV_SO
lib.setFileName(QStringLiteral(LIBRESOLV_SO));
if (!lib.load())
#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"));
}
}
}
Q_GLOBAL_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;
#if defined(QHOSTINFO_DEBUG)
qDebug("QHostInfoAgent::fromName(%s) looking up...",
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();
QHostAddress address;
if (address.setAddress(hostName))
return reverseLookup(address);
return lookup(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 (domainName.isEmpty())
domainName = QUrl::fromAce(state->dnsrch[0]);
local_res_nclose(state);
free(state);
return domainName;
}
if (local_res_init && local_res) {
// using thread-unsafe version
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
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());
// 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();
int pos = searchDomain.indexOf(' ');
if (pos != -1)
searchDomain.truncate(pos);
domainName = QUrl::fromAce(searchDomain);
}
}
// return the fallen-back-to searched domain
return domainName;
}
QT_END_NAMESPACE