2011-04-27 10:05:43 +00:00
|
|
|
/****************************************************************************
|
|
|
|
**
|
2016-01-15 12:36:27 +00:00
|
|
|
** Copyright (C) 2016 The Qt Company Ltd.
|
2016-01-21 02:33:50 +00:00
|
|
|
** Copyright (C) 2016 Intel Corporation.
|
2016-01-15 12:36:27 +00:00
|
|
|
** Contact: https://www.qt.io/licensing/
|
2011-04-27 10:05:43 +00:00
|
|
|
**
|
|
|
|
** This file is part of the test suite of the Qt Toolkit.
|
|
|
|
**
|
2016-01-15 12:36:27 +00:00
|
|
|
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
|
2012-09-19 12:28:29 +00:00
|
|
|
** 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
|
2015-01-28 08:44:43 +00:00
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
2016-01-15 12:36:27 +00:00
|
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
2012-09-19 12:28:29 +00:00
|
|
|
**
|
2016-01-15 12:36:27 +00:00
|
|
|
** GNU General Public License Usage
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
|
|
** General Public License version 3 as published by the Free Software
|
|
|
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
|
|
|
** 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-3.0.html.
|
2011-04-27 10:05:43 +00:00
|
|
|
**
|
|
|
|
** $QT_END_LICENSE$
|
|
|
|
**
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
// When using WinSock2 on Windows, it's the first thing that can be included
|
|
|
|
// (except qglobal.h), or else you'll get tons of compile errors
|
|
|
|
#include <qglobal.h>
|
|
|
|
|
2012-06-12 07:57:10 +00:00
|
|
|
// To prevent windows system header files from re-defining min/max
|
2012-09-17 10:32:09 +00:00
|
|
|
#define NOMINMAX 1
|
2012-06-12 07:57:10 +00:00
|
|
|
|
2012-11-21 10:37:03 +00:00
|
|
|
#if defined(Q_OS_WIN)
|
2011-04-27 10:05:43 +00:00
|
|
|
# include <winsock2.h>
|
|
|
|
# include <ws2tcpip.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <QtTest/QtTest>
|
|
|
|
#include <qcoreapplication.h>
|
|
|
|
#include <QDebug>
|
|
|
|
#include <QTcpSocket>
|
|
|
|
#include <private/qthread_p.h>
|
|
|
|
#include <QTcpServer>
|
|
|
|
|
|
|
|
#include <time.h>
|
2013-09-01 16:05:46 +00:00
|
|
|
#if defined(Q_OS_WIN)
|
2011-04-27 10:05:43 +00:00
|
|
|
#include <windows.h>
|
|
|
|
#else
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <qhostinfo.h>
|
|
|
|
#include "private/qhostinfo_p.h"
|
|
|
|
|
2017-08-08 03:52:11 +00:00
|
|
|
#include <sys/types.h>
|
|
|
|
#if defined(Q_OS_UNIX)
|
2011-04-27 10:05:43 +00:00
|
|
|
# include <sys/socket.h>
|
|
|
|
# include <netdb.h>
|
|
|
|
#endif
|
|
|
|
|
2011-09-06 11:46:40 +00:00
|
|
|
#include "../../../network-settings.h"
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2017-11-27 07:01:57 +00:00
|
|
|
#define TEST_DOMAIN ".test.qt-project.org"
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
class tst_QHostInfo : public QObject
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
|
2015-12-03 15:59:30 +00:00
|
|
|
private slots:
|
2011-04-27 10:05:43 +00:00
|
|
|
void init();
|
|
|
|
void initTestCase();
|
2017-04-27 17:08:56 +00:00
|
|
|
void swapFunction();
|
|
|
|
void moveOperator();
|
2011-04-27 10:05:43 +00:00
|
|
|
void getSetCheck();
|
|
|
|
void staticInformation();
|
|
|
|
void lookupIPv4_data();
|
|
|
|
void lookupIPv4();
|
|
|
|
void lookupIPv6_data();
|
|
|
|
void lookupIPv6();
|
2016-10-24 19:01:09 +00:00
|
|
|
void lookupConnectToFunctionPointer_data();
|
|
|
|
void lookupConnectToFunctionPointer();
|
QHostInfo: Always post results through the event loop to the receiver
Lookups performed via QHostInfoRunnable must not synchronously call
the user-code's receiver objects, as that would execute user-code in
the wrong thread. Instead, post a metacall event through the event
loop of the receiver object, or the thread that initiated the lookup.
This was done correctly for the trivial cases of empty host name or
cached results, so the code generally existed. By moving it from a
global function into a member function of QHostInfoResult, we can
simply access the required data to construct and post the event.
As we process that posted event, we need to check that the context
object (which is already guarded via QPointer) is still alive, if
we had one in the first place. If we had one, and it's deleted, then
abort.
[ChangeLog][QtNetwork][QHostInfo] Functors used in the lookupHost
overloads are now called correctly in the thread of the context object.
When used without context object, the thread that initiates the lookup
will run the functor, and is required to run an event loop.
Change-Id: I9b38d4f9a23cfc4d9e07bc72de2d2cefe5d0d033
Fixes: QTBUG-76276
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2019-07-24 13:05:23 +00:00
|
|
|
void lookupConnectToFunctionPointerDeleted();
|
2016-10-24 19:01:09 +00:00
|
|
|
void lookupConnectToLambda_data();
|
|
|
|
void lookupConnectToLambda();
|
2011-04-27 10:05:43 +00:00
|
|
|
void reverseLookup_data();
|
|
|
|
void reverseLookup();
|
|
|
|
|
|
|
|
void blockingLookup_data();
|
|
|
|
void blockingLookup();
|
|
|
|
|
|
|
|
void raceCondition();
|
|
|
|
void threadSafety();
|
|
|
|
void threadSafetyAsynchronousAPI();
|
|
|
|
|
|
|
|
void multipleSameLookups();
|
|
|
|
void multipleDifferentLookups_data();
|
|
|
|
void multipleDifferentLookups();
|
|
|
|
|
|
|
|
void cache();
|
|
|
|
|
|
|
|
void abortHostLookup();
|
|
|
|
protected slots:
|
|
|
|
void resultsReady(const QHostInfo &);
|
|
|
|
|
|
|
|
private:
|
|
|
|
bool ipv6LookupsAvailable;
|
|
|
|
bool ipv6Available;
|
|
|
|
bool lookupDone;
|
|
|
|
int lookupsDoneCounter;
|
|
|
|
QHostInfo lookupResults;
|
|
|
|
};
|
|
|
|
|
2017-04-27 17:08:56 +00:00
|
|
|
void tst_QHostInfo::swapFunction()
|
|
|
|
{
|
|
|
|
QHostInfo obj1, obj2;
|
|
|
|
obj1.setError(QHostInfo::HostInfoError(0));
|
|
|
|
obj2.setError(QHostInfo::HostInfoError(1));
|
|
|
|
obj1.swap(obj2);
|
|
|
|
QCOMPARE(QHostInfo::HostInfoError(0), obj2.error());
|
|
|
|
QCOMPARE(QHostInfo::HostInfoError(1), obj1.error());
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QHostInfo::moveOperator()
|
|
|
|
{
|
|
|
|
QHostInfo obj1, obj2, obj3(1);
|
|
|
|
obj1.setError(QHostInfo::HostInfoError(0));
|
|
|
|
obj2.setError(QHostInfo::HostInfoError(1));
|
|
|
|
obj1 = std::move(obj2);
|
|
|
|
obj2 = obj3;
|
|
|
|
QCOMPARE(QHostInfo::HostInfoError(1), obj1.error());
|
|
|
|
QCOMPARE(obj3.lookupId(), obj2.lookupId());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2011-04-27 10:05:43 +00:00
|
|
|
// Testing get/set functions
|
|
|
|
void tst_QHostInfo::getSetCheck()
|
|
|
|
{
|
|
|
|
QHostInfo obj1;
|
|
|
|
// HostInfoError QHostInfo::error()
|
|
|
|
// void QHostInfo::setError(HostInfoError)
|
|
|
|
obj1.setError(QHostInfo::HostInfoError(0));
|
|
|
|
QCOMPARE(QHostInfo::HostInfoError(0), obj1.error());
|
|
|
|
obj1.setError(QHostInfo::HostInfoError(1));
|
|
|
|
QCOMPARE(QHostInfo::HostInfoError(1), obj1.error());
|
|
|
|
|
|
|
|
// int QHostInfo::lookupId()
|
|
|
|
// void QHostInfo::setLookupId(int)
|
|
|
|
obj1.setLookupId(0);
|
|
|
|
QCOMPARE(0, obj1.lookupId());
|
|
|
|
obj1.setLookupId(INT_MIN);
|
|
|
|
QCOMPARE(INT_MIN, obj1.lookupId());
|
|
|
|
obj1.setLookupId(INT_MAX);
|
|
|
|
QCOMPARE(INT_MAX, obj1.lookupId());
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QHostInfo::staticInformation()
|
|
|
|
{
|
|
|
|
qDebug() << "Hostname:" << QHostInfo::localHostName();
|
|
|
|
qDebug() << "Domain name:" << QHostInfo::localDomainName();
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QHostInfo::initTestCase()
|
|
|
|
{
|
|
|
|
ipv6Available = false;
|
|
|
|
ipv6LookupsAvailable = false;
|
2011-05-20 08:04:18 +00:00
|
|
|
|
|
|
|
QTcpServer server;
|
|
|
|
if (server.listen(QHostAddress("::1"))) {
|
|
|
|
// We have IPv6 support
|
|
|
|
ipv6Available = true;
|
|
|
|
}
|
|
|
|
|
2011-04-27 10:05:43 +00:00
|
|
|
// check if the system getaddrinfo can do IPv6 lookups
|
|
|
|
struct addrinfo hint, *result = 0;
|
|
|
|
memset(&hint, 0, sizeof hint);
|
|
|
|
hint.ai_family = AF_UNSPEC;
|
2017-08-08 03:52:11 +00:00
|
|
|
#ifdef AI_ADDRCONFIG
|
2011-04-27 10:05:43 +00:00
|
|
|
hint.ai_flags = AI_ADDRCONFIG;
|
2017-08-08 03:52:11 +00:00
|
|
|
#endif
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
int res = getaddrinfo("::1", "80", &hint, &result);
|
|
|
|
if (res == 0) {
|
|
|
|
// this test worked
|
|
|
|
freeaddrinfo(result);
|
2013-05-03 19:43:53 +00:00
|
|
|
res = getaddrinfo("aaaa-single" TEST_DOMAIN, "80", &hint, &result);
|
2011-04-27 10:05:43 +00:00
|
|
|
if (res == 0 && result != 0 && result->ai_family != AF_INET) {
|
|
|
|
freeaddrinfo(result);
|
|
|
|
ipv6LookupsAvailable = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// run each testcase with and without test enabled
|
|
|
|
QTest::addColumn<bool>("cache");
|
|
|
|
QTest::newRow("WithCache") << true;
|
|
|
|
QTest::newRow("WithoutCache") << false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QHostInfo::init()
|
|
|
|
{
|
2011-07-04 08:37:56 +00:00
|
|
|
// delete the cache so inidividual testcase results are independent from each other
|
2011-04-27 10:05:43 +00:00
|
|
|
qt_qhostinfo_clear_cache();
|
|
|
|
|
|
|
|
QFETCH_GLOBAL(bool, cache);
|
|
|
|
qt_qhostinfo_enable_cache(cache);
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QHostInfo::lookupIPv4_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<QString>("hostname");
|
|
|
|
QTest::addColumn<QString>("addresses");
|
|
|
|
QTest::addColumn<int>("err");
|
|
|
|
|
|
|
|
QTest::newRow("empty") << "" << "" << int(QHostInfo::HostNotFound);
|
|
|
|
|
2013-05-03 19:43:53 +00:00
|
|
|
QTest::newRow("single_ip4") << "a-single" TEST_DOMAIN << "192.0.2.1" << int(QHostInfo::NoError);
|
|
|
|
QTest::newRow("multiple_ip4") << "a-multi" TEST_DOMAIN << "192.0.2.1 192.0.2.2 192.0.2.3" << int(QHostInfo::NoError);
|
|
|
|
QTest::newRow("literal_ip4") << "192.0.2.1" << "192.0.2.1" << int(QHostInfo::NoError);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2013-05-03 19:43:53 +00:00
|
|
|
QTest::newRow("notfound") << "invalid" TEST_DOMAIN << "" << int(QHostInfo::HostNotFound);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2013-05-03 19:43:53 +00:00
|
|
|
QTest::newRow("idn-ace") << "a-single.xn--alqualond-34a" TEST_DOMAIN << "192.0.2.1" << int(QHostInfo::NoError);
|
|
|
|
QTest::newRow("idn-unicode") << QString::fromLatin1("a-single.alqualond\353" TEST_DOMAIN) << "192.0.2.1" << int(QHostInfo::NoError);
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QHostInfo::lookupIPv4()
|
|
|
|
{
|
|
|
|
QFETCH(QString, hostname);
|
|
|
|
QFETCH(int, err);
|
|
|
|
QFETCH(QString, addresses);
|
|
|
|
|
|
|
|
lookupDone = false;
|
2012-10-16 09:10:22 +00:00
|
|
|
QHostInfo::lookupHost(hostname, this, SLOT(resultsReady(QHostInfo)));
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
QTestEventLoop::instance().enterLoop(10);
|
|
|
|
QVERIFY(!QTestEventLoop::instance().timeout());
|
|
|
|
QVERIFY(lookupDone);
|
|
|
|
|
|
|
|
if ((int)lookupResults.error() != (int)err) {
|
|
|
|
qWarning() << hostname << "=>" << lookupResults.errorString();
|
|
|
|
}
|
|
|
|
QCOMPARE((int)lookupResults.error(), (int)err);
|
|
|
|
|
|
|
|
QStringList tmp;
|
|
|
|
for (int i = 0; i < lookupResults.addresses().count(); ++i)
|
|
|
|
tmp.append(lookupResults.addresses().at(i).toString());
|
|
|
|
tmp.sort();
|
|
|
|
|
|
|
|
QStringList expected = addresses.split(' ');
|
|
|
|
expected.sort();
|
|
|
|
|
2012-05-18 18:00:23 +00:00
|
|
|
QCOMPARE(tmp.join(' '), expected.join(' '));
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QHostInfo::lookupIPv6_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<QString>("hostname");
|
|
|
|
QTest::addColumn<QString>("addresses");
|
|
|
|
QTest::addColumn<int>("err");
|
|
|
|
|
2013-05-03 19:43:53 +00:00
|
|
|
QTest::newRow("aaaa-single") << "aaaa-single" TEST_DOMAIN << "2001:db8::1" << int(QHostInfo::NoError);
|
|
|
|
QTest::newRow("aaaa-multi") << "aaaa-multi" TEST_DOMAIN << "2001:db8::1 2001:db8::2 2001:db8::3" << int(QHostInfo::NoError);
|
|
|
|
QTest::newRow("a-plus-aaaa") << "a-plus-aaaa" TEST_DOMAIN << "198.51.100.1 2001:db8::1:1" << int(QHostInfo::NoError);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
// avoid using real IPv6 addresses here because this will do a DNS query
|
|
|
|
// real addresses are between 2000:: and 3fff:ffff:ffff:ffff:ffff:ffff:ffff
|
|
|
|
QTest::newRow("literal_ip6") << "f001:6b0:1:ea:202:a5ff:fecd:13a6" << "f001:6b0:1:ea:202:a5ff:fecd:13a6" << int(QHostInfo::NoError);
|
2011-05-18 14:08:53 +00:00
|
|
|
QTest::newRow("literal_shortip6") << "f001:618:1401::4" << "f001:618:1401::4" << int(QHostInfo::NoError);
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QHostInfo::lookupIPv6()
|
|
|
|
{
|
|
|
|
QFETCH(QString, hostname);
|
|
|
|
QFETCH(int, err);
|
|
|
|
QFETCH(QString, addresses);
|
|
|
|
|
|
|
|
if (!ipv6LookupsAvailable)
|
2011-10-19 02:53:13 +00:00
|
|
|
QSKIP("This platform does not support IPv6 lookups");
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
lookupDone = false;
|
2012-10-16 09:10:22 +00:00
|
|
|
QHostInfo::lookupHost(hostname, this, SLOT(resultsReady(QHostInfo)));
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
QTestEventLoop::instance().enterLoop(10);
|
|
|
|
QVERIFY(!QTestEventLoop::instance().timeout());
|
|
|
|
QVERIFY(lookupDone);
|
|
|
|
|
|
|
|
QCOMPARE((int)lookupResults.error(), (int)err);
|
|
|
|
|
|
|
|
QStringList tmp;
|
|
|
|
for (int i = 0; i < lookupResults.addresses().count(); ++i)
|
|
|
|
tmp.append(lookupResults.addresses().at(i).toString());
|
|
|
|
tmp.sort();
|
|
|
|
|
|
|
|
QStringList expected = addresses.split(' ');
|
|
|
|
expected.sort();
|
|
|
|
|
2012-05-18 18:00:23 +00:00
|
|
|
QCOMPARE(tmp.join(' ').toLower(), expected.join(' ').toLower());
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
2016-10-24 19:01:09 +00:00
|
|
|
void tst_QHostInfo::lookupConnectToFunctionPointer_data()
|
|
|
|
{
|
|
|
|
lookupIPv4_data();
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QHostInfo::lookupConnectToFunctionPointer()
|
|
|
|
{
|
|
|
|
QFETCH(QString, hostname);
|
|
|
|
QFETCH(int, err);
|
|
|
|
QFETCH(QString, addresses);
|
|
|
|
|
|
|
|
lookupDone = false;
|
|
|
|
QHostInfo::lookupHost(hostname, this, &tst_QHostInfo::resultsReady);
|
|
|
|
|
|
|
|
QTestEventLoop::instance().enterLoop(10);
|
|
|
|
QVERIFY(!QTestEventLoop::instance().timeout());
|
|
|
|
QVERIFY(lookupDone);
|
|
|
|
|
|
|
|
if (int(lookupResults.error()) != int(err))
|
|
|
|
qWarning() << hostname << "=>" << lookupResults.errorString();
|
|
|
|
QCOMPARE(int(lookupResults.error()), int(err));
|
|
|
|
|
|
|
|
QStringList tmp;
|
|
|
|
for (const auto &result : lookupResults.addresses())
|
|
|
|
tmp.append(result.toString());
|
|
|
|
tmp.sort();
|
|
|
|
|
|
|
|
QStringList expected = addresses.split(' ');
|
|
|
|
expected.sort();
|
|
|
|
|
|
|
|
QCOMPARE(tmp.join(' '), expected.join(' '));
|
|
|
|
}
|
|
|
|
|
QHostInfo: Always post results through the event loop to the receiver
Lookups performed via QHostInfoRunnable must not synchronously call
the user-code's receiver objects, as that would execute user-code in
the wrong thread. Instead, post a metacall event through the event
loop of the receiver object, or the thread that initiated the lookup.
This was done correctly for the trivial cases of empty host name or
cached results, so the code generally existed. By moving it from a
global function into a member function of QHostInfoResult, we can
simply access the required data to construct and post the event.
As we process that posted event, we need to check that the context
object (which is already guarded via QPointer) is still alive, if
we had one in the first place. If we had one, and it's deleted, then
abort.
[ChangeLog][QtNetwork][QHostInfo] Functors used in the lookupHost
overloads are now called correctly in the thread of the context object.
When used without context object, the thread that initiates the lookup
will run the functor, and is required to run an event loop.
Change-Id: I9b38d4f9a23cfc4d9e07bc72de2d2cefe5d0d033
Fixes: QTBUG-76276
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2019-07-24 13:05:23 +00:00
|
|
|
void tst_QHostInfo::lookupConnectToFunctionPointerDeleted()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
QObject contextObject;
|
|
|
|
QHostInfo::lookupHost("localhost", &contextObject, [](const QHostInfo){
|
|
|
|
QFAIL("This should never be called!");
|
|
|
|
});
|
|
|
|
}
|
|
|
|
QTestEventLoop::instance().enterLoop(3);
|
|
|
|
}
|
|
|
|
|
2016-10-24 19:01:09 +00:00
|
|
|
void tst_QHostInfo::lookupConnectToLambda_data()
|
|
|
|
{
|
|
|
|
lookupIPv4_data();
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QHostInfo::lookupConnectToLambda()
|
|
|
|
{
|
|
|
|
QFETCH(QString, hostname);
|
|
|
|
QFETCH(int, err);
|
|
|
|
QFETCH(QString, addresses);
|
|
|
|
|
|
|
|
lookupDone = false;
|
|
|
|
QHostInfo::lookupHost(hostname, [=](const QHostInfo &hostInfo) {
|
|
|
|
resultsReady(hostInfo);
|
|
|
|
});
|
|
|
|
|
|
|
|
QTestEventLoop::instance().enterLoop(10);
|
|
|
|
QVERIFY(!QTestEventLoop::instance().timeout());
|
|
|
|
QVERIFY(lookupDone);
|
|
|
|
|
|
|
|
if (int(lookupResults.error()) != int(err))
|
|
|
|
qWarning() << hostname << "=>" << lookupResults.errorString();
|
|
|
|
QCOMPARE(int(lookupResults.error()), int(err));
|
|
|
|
|
|
|
|
QStringList tmp;
|
|
|
|
for (int i = 0; i < lookupResults.addresses().count(); ++i)
|
|
|
|
tmp.append(lookupResults.addresses().at(i).toString());
|
|
|
|
tmp.sort();
|
|
|
|
|
|
|
|
QStringList expected = addresses.split(' ');
|
|
|
|
expected.sort();
|
|
|
|
|
|
|
|
QCOMPARE(tmp.join(' '), expected.join(' '));
|
|
|
|
}
|
|
|
|
|
2019-03-28 12:43:50 +00:00
|
|
|
static QStringList reverseLookupHelper(const QString &ip)
|
|
|
|
{
|
|
|
|
QStringList results;
|
|
|
|
|
|
|
|
const QString pythonCode =
|
|
|
|
"import socket;"
|
|
|
|
"import sys;"
|
|
|
|
"print (socket.getnameinfo((sys.argv[1], 0), 0)[0]);";
|
|
|
|
|
|
|
|
QList<QByteArray> lines;
|
|
|
|
QProcess python;
|
|
|
|
python.setProcessChannelMode(QProcess::ForwardedErrorChannel);
|
|
|
|
python.start("python", QStringList() << QString("-c") << pythonCode << ip);
|
|
|
|
if (python.waitForFinished()) {
|
|
|
|
if (python.exitStatus() == QProcess::NormalExit && python.exitCode() == 0)
|
|
|
|
lines = python.readAllStandardOutput().split('\n');
|
|
|
|
for (QByteArray line : lines) {
|
|
|
|
if (!line.isEmpty())
|
|
|
|
results << line.trimmed();
|
|
|
|
}
|
|
|
|
if (!results.isEmpty())
|
|
|
|
return results;
|
|
|
|
}
|
|
|
|
|
|
|
|
qDebug() << "Python failed, falling back to nslookup";
|
|
|
|
QProcess lookup;
|
|
|
|
lookup.setProcessChannelMode(QProcess::ForwardedErrorChannel);
|
|
|
|
lookup.start("nslookup", QStringList(ip));
|
|
|
|
if (!lookup.waitForFinished()) {
|
|
|
|
results << "nslookup failure";
|
|
|
|
qDebug() << "nslookup failure";
|
|
|
|
return results;
|
|
|
|
}
|
|
|
|
lines = lookup.readAllStandardOutput().split('\n');
|
|
|
|
|
|
|
|
QByteArray name;
|
|
|
|
|
|
|
|
const QByteArray nameMarkerNix("name =");
|
|
|
|
const QByteArray nameMarkerWin("Name:");
|
|
|
|
const QByteArray addressMarkerWin("Address:");
|
|
|
|
|
|
|
|
for (QByteArray line : lines) {
|
|
|
|
int index = -1;
|
|
|
|
if ((index = line.indexOf(nameMarkerNix)) != -1) { // Linux and macOS
|
|
|
|
name = line.mid(index + nameMarkerNix.length()).chopped(1).trimmed();
|
|
|
|
results << name;
|
|
|
|
} else if (line.startsWith(nameMarkerWin)) { // Windows formatting
|
|
|
|
name = line.mid(line.lastIndexOf(" ")).trimmed();
|
|
|
|
} else if (line.startsWith(addressMarkerWin)) {
|
|
|
|
QByteArray address = line.mid(addressMarkerWin.length()).trimmed();
|
|
|
|
if (address == ip) {
|
|
|
|
results << name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (results.isEmpty()) {
|
|
|
|
qDebug() << "Failure to parse nslookup output: " << lines;
|
|
|
|
}
|
|
|
|
return results;
|
|
|
|
}
|
|
|
|
|
2011-04-27 10:05:43 +00:00
|
|
|
void tst_QHostInfo::reverseLookup_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<QString>("address");
|
|
|
|
QTest::addColumn<QStringList>("hostNames");
|
|
|
|
QTest::addColumn<int>("err");
|
2011-10-24 06:26:50 +00:00
|
|
|
QTest::addColumn<bool>("ipv6");
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2019-03-28 12:43:50 +00:00
|
|
|
QTest::newRow("dns.google") << QString("8.8.8.8") << reverseLookupHelper("8.8.8.8") << 0 << false;
|
|
|
|
QTest::newRow("one.one.one.one") << QString("1.1.1.1") << reverseLookupHelper("1.1.1.1") << 0 << false;
|
2019-07-27 13:31:03 +00:00
|
|
|
QTest::newRow("dns.google IPv6") << QString("2001:4860:4860::8888") << reverseLookupHelper("2001:4860:4860::8888") << 0 << true;
|
|
|
|
QTest::newRow("cloudflare IPv6") << QString("2606:4700:4700::1111") << reverseLookupHelper("2606:4700:4700::1111") << 0 << true;
|
|
|
|
QTest::newRow("bogus-name IPv6") << QString("1::2::3::4") << QStringList() << 1 << true;
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QHostInfo::reverseLookup()
|
|
|
|
{
|
|
|
|
QFETCH(QString, address);
|
|
|
|
QFETCH(QStringList, hostNames);
|
|
|
|
QFETCH(int, err);
|
2011-10-24 06:26:50 +00:00
|
|
|
QFETCH(bool, ipv6);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2011-10-24 06:26:50 +00:00
|
|
|
if (ipv6 && !ipv6LookupsAvailable) {
|
|
|
|
QSKIP("IPv6 reverse lookups are not supported on this platform");
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QHostInfo info = QHostInfo::fromName(address);
|
|
|
|
|
|
|
|
if (err == 0) {
|
2019-03-28 12:43:50 +00:00
|
|
|
if (!hostNames.contains(info.hostName()))
|
|
|
|
qDebug() << "Failure: expecting" << hostNames << ",got " << info.hostName();
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(hostNames.contains(info.hostName()));
|
|
|
|
QCOMPARE(info.addresses().first(), QHostAddress(address));
|
|
|
|
} else {
|
|
|
|
QCOMPARE(info.hostName(), address);
|
|
|
|
QCOMPARE(info.error(), QHostInfo::HostNotFound);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QHostInfo::blockingLookup_data()
|
|
|
|
{
|
|
|
|
lookupIPv4_data();
|
|
|
|
if (ipv6LookupsAvailable)
|
|
|
|
lookupIPv6_data();
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QHostInfo::blockingLookup()
|
|
|
|
{
|
|
|
|
QFETCH(QString, hostname);
|
|
|
|
QFETCH(int, err);
|
|
|
|
QFETCH(QString, addresses);
|
|
|
|
|
|
|
|
QHostInfo hostInfo = QHostInfo::fromName(hostname);
|
|
|
|
QStringList tmp;
|
|
|
|
for (int i = 0; i < hostInfo.addresses().count(); ++i)
|
|
|
|
tmp.append(hostInfo.addresses().at(i).toString());
|
|
|
|
tmp.sort();
|
|
|
|
|
|
|
|
if ((int)hostInfo.error() != (int)err) {
|
|
|
|
qWarning() << hostname << "=>" << lookupResults.errorString();
|
|
|
|
}
|
|
|
|
QCOMPARE((int)hostInfo.error(), (int)err);
|
|
|
|
|
|
|
|
QStringList expected = addresses.split(' ');
|
|
|
|
expected.sort();
|
|
|
|
|
2012-05-18 18:00:23 +00:00
|
|
|
QCOMPARE(tmp.join(' ').toUpper(), expected.join(' ').toUpper());
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QHostInfo::raceCondition()
|
|
|
|
{
|
|
|
|
for (int i = 0; i < 1000; ++i) {
|
|
|
|
QTcpSocket socket;
|
2013-05-03 19:43:53 +00:00
|
|
|
socket.connectToHost("invalid" TEST_DOMAIN, 80);
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class LookupThread : public QThread
|
|
|
|
{
|
|
|
|
protected:
|
|
|
|
inline void run()
|
|
|
|
{
|
2013-05-03 19:43:53 +00:00
|
|
|
QHostInfo info = QHostInfo::fromName("a-single" TEST_DOMAIN);
|
2011-04-27 10:05:43 +00:00
|
|
|
QCOMPARE(info.error(), QHostInfo::NoError);
|
|
|
|
QVERIFY(info.addresses().count() > 0);
|
2013-05-03 19:43:53 +00:00
|
|
|
QCOMPARE(info.addresses().at(0).toString(), QString("192.0.2.1"));
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void tst_QHostInfo::threadSafety()
|
|
|
|
{
|
|
|
|
const int nattempts = 5;
|
|
|
|
const int runs = 100;
|
|
|
|
LookupThread thr[nattempts];
|
|
|
|
for (int j = 0; j < runs; ++j) {
|
|
|
|
for (int i = 0; i < nattempts; ++i)
|
|
|
|
thr[i].start();
|
|
|
|
for (int k = nattempts - 1; k >= 0; --k)
|
|
|
|
thr[k].wait();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class LookupReceiver : public QObject
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
public slots:
|
|
|
|
void start();
|
|
|
|
void resultsReady(const QHostInfo&);
|
|
|
|
public:
|
|
|
|
QHostInfo result;
|
|
|
|
int numrequests;
|
|
|
|
};
|
|
|
|
|
|
|
|
void LookupReceiver::start()
|
|
|
|
{
|
|
|
|
for (int i=0;i<numrequests;i++)
|
2013-05-03 19:43:53 +00:00
|
|
|
QHostInfo::lookupHost(QString("a-single" TEST_DOMAIN), this, SLOT(resultsReady(QHostInfo)));
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void LookupReceiver::resultsReady(const QHostInfo &info)
|
|
|
|
{
|
|
|
|
result = info;
|
|
|
|
numrequests--;
|
|
|
|
if (numrequests == 0 || info.error() != QHostInfo::NoError)
|
|
|
|
QThread::currentThread()->quit();
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QHostInfo::threadSafetyAsynchronousAPI()
|
|
|
|
{
|
|
|
|
const int nattempts = 10;
|
|
|
|
const int lookupsperthread = 10;
|
|
|
|
QList<QThread*> threads;
|
|
|
|
QList<LookupReceiver*> receivers;
|
|
|
|
for (int i = 0; i < nattempts; ++i) {
|
|
|
|
QThread* thread = new QThread;
|
|
|
|
LookupReceiver* receiver = new LookupReceiver;
|
|
|
|
receiver->numrequests = lookupsperthread;
|
|
|
|
receivers.append(receiver);
|
|
|
|
receiver->moveToThread(thread);
|
|
|
|
connect(thread, SIGNAL(started()), receiver, SLOT(start()));
|
|
|
|
thread->start();
|
|
|
|
threads.append(thread);
|
|
|
|
}
|
|
|
|
for (int k = threads.count() - 1; k >= 0; --k)
|
|
|
|
QVERIFY(threads.at(k)->wait(60000));
|
|
|
|
foreach (LookupReceiver* receiver, receivers) {
|
|
|
|
QCOMPARE(receiver->result.error(), QHostInfo::NoError);
|
2013-05-03 19:43:53 +00:00
|
|
|
QCOMPARE(receiver->result.addresses().at(0).toString(), QString("192.0.2.1"));
|
2011-04-27 10:05:43 +00:00
|
|
|
QCOMPARE(receiver->numrequests, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// this test is for the multi-threaded QHostInfo rewrite. It is about getting results at all,
|
|
|
|
// not about getting correct IPs
|
|
|
|
void tst_QHostInfo::multipleSameLookups()
|
|
|
|
{
|
|
|
|
const int COUNT = 10;
|
|
|
|
lookupsDoneCounter = 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < COUNT; i++)
|
2012-10-16 09:10:22 +00:00
|
|
|
QHostInfo::lookupHost("localhost", this, SLOT(resultsReady(QHostInfo)));
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
QElapsedTimer timer;
|
|
|
|
timer.start();
|
|
|
|
while (timer.elapsed() < 10000 && lookupsDoneCounter < COUNT) {
|
|
|
|
QTestEventLoop::instance().enterLoop(2);
|
|
|
|
}
|
|
|
|
QCOMPARE(lookupsDoneCounter, COUNT);
|
|
|
|
}
|
|
|
|
|
|
|
|
// this test is for the multi-threaded QHostInfo rewrite. It is about getting results at all,
|
|
|
|
// not about getting correct IPs
|
|
|
|
void tst_QHostInfo::multipleDifferentLookups_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<int>("repeats");
|
|
|
|
QTest::newRow("1") << 1;
|
|
|
|
QTest::newRow("2") << 2;
|
|
|
|
QTest::newRow("5") << 5;
|
|
|
|
QTest::newRow("10") << 10;
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QHostInfo::multipleDifferentLookups()
|
|
|
|
{
|
|
|
|
QStringList hostnameList;
|
2013-05-03 19:43:53 +00:00
|
|
|
hostnameList << "a-single" TEST_DOMAIN
|
|
|
|
<< "a-multi" TEST_DOMAIN
|
|
|
|
<< "aaaa-single" TEST_DOMAIN
|
|
|
|
<< "aaaa-multi" TEST_DOMAIN
|
|
|
|
<< "a-plus-aaaa" TEST_DOMAIN
|
|
|
|
<< "multi" TEST_DOMAIN
|
|
|
|
<< "localhost" TEST_DOMAIN
|
|
|
|
<< "cname" TEST_DOMAIN
|
|
|
|
<< "127.0.0.1" << "----";
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
QFETCH(int, repeats);
|
|
|
|
const int COUNT = hostnameList.size();
|
|
|
|
lookupsDoneCounter = 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < hostnameList.size(); i++)
|
|
|
|
for (int j = 0; j < repeats; ++j)
|
2012-10-16 09:10:22 +00:00
|
|
|
QHostInfo::lookupHost(hostnameList.at(i), this, SLOT(resultsReady(QHostInfo)));
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
QElapsedTimer timer;
|
|
|
|
timer.start();
|
|
|
|
while (timer.elapsed() < 60000 && lookupsDoneCounter < repeats*COUNT) {
|
|
|
|
QTestEventLoop::instance().enterLoop(2);
|
|
|
|
//qDebug() << "t:" << timer.elapsed();
|
|
|
|
}
|
|
|
|
QCOMPARE(lookupsDoneCounter, repeats*COUNT);
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QHostInfo::cache()
|
|
|
|
{
|
|
|
|
QFETCH_GLOBAL(bool, cache);
|
|
|
|
if (!cache)
|
|
|
|
return; // test makes only sense when cache enabled
|
|
|
|
|
|
|
|
// reset slot counter
|
|
|
|
lookupsDoneCounter = 0;
|
|
|
|
|
|
|
|
// lookup once, wait in event loop, result should not come directly.
|
|
|
|
bool valid = true;
|
|
|
|
int id = -1;
|
|
|
|
QHostInfo result = qt_qhostinfo_lookup("localhost", this, SLOT(resultsReady(QHostInfo)), &valid, &id);
|
|
|
|
QTestEventLoop::instance().enterLoop(5);
|
|
|
|
QVERIFY(!QTestEventLoop::instance().timeout());
|
2015-07-23 11:26:28 +00:00
|
|
|
QVERIFY(!valid);
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(result.addresses().isEmpty());
|
|
|
|
|
|
|
|
// loopkup second time, result should come directly
|
|
|
|
valid = false;
|
|
|
|
result = qt_qhostinfo_lookup("localhost", this, SLOT(resultsReady(QHostInfo)), &valid, &id);
|
2015-07-23 11:26:28 +00:00
|
|
|
QVERIFY(valid);
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(!result.addresses().isEmpty());
|
|
|
|
|
|
|
|
// clear the cache
|
|
|
|
qt_qhostinfo_clear_cache();
|
|
|
|
|
|
|
|
// lookup third time, result should not come directly.
|
|
|
|
valid = true;
|
|
|
|
result = qt_qhostinfo_lookup("localhost", this, SLOT(resultsReady(QHostInfo)), &valid, &id);
|
|
|
|
QTestEventLoop::instance().enterLoop(5);
|
|
|
|
QVERIFY(!QTestEventLoop::instance().timeout());
|
2015-07-23 11:26:28 +00:00
|
|
|
QVERIFY(!valid);
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(result.addresses().isEmpty());
|
|
|
|
|
|
|
|
// the slot should have been called 2 times.
|
|
|
|
QCOMPARE(lookupsDoneCounter, 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QHostInfo::resultsReady(const QHostInfo &hi)
|
|
|
|
{
|
QHostInfo: Always post results through the event loop to the receiver
Lookups performed via QHostInfoRunnable must not synchronously call
the user-code's receiver objects, as that would execute user-code in
the wrong thread. Instead, post a metacall event through the event
loop of the receiver object, or the thread that initiated the lookup.
This was done correctly for the trivial cases of empty host name or
cached results, so the code generally existed. By moving it from a
global function into a member function of QHostInfoResult, we can
simply access the required data to construct and post the event.
As we process that posted event, we need to check that the context
object (which is already guarded via QPointer) is still alive, if
we had one in the first place. If we had one, and it's deleted, then
abort.
[ChangeLog][QtNetwork][QHostInfo] Functors used in the lookupHost
overloads are now called correctly in the thread of the context object.
When used without context object, the thread that initiates the lookup
will run the functor, and is required to run an event loop.
Change-Id: I9b38d4f9a23cfc4d9e07bc72de2d2cefe5d0d033
Fixes: QTBUG-76276
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2019-07-24 13:05:23 +00:00
|
|
|
QVERIFY(QThread::currentThread() == thread());
|
2011-04-27 10:05:43 +00:00
|
|
|
lookupDone = true;
|
|
|
|
lookupResults = hi;
|
|
|
|
lookupsDoneCounter++;
|
2011-08-18 02:53:08 +00:00
|
|
|
QTestEventLoop::instance().exitLoop();
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QHostInfo::abortHostLookup()
|
|
|
|
{
|
|
|
|
//reset counter
|
|
|
|
lookupsDoneCounter = 0;
|
|
|
|
bool valid = false;
|
|
|
|
int id = -1;
|
2013-05-03 19:43:53 +00:00
|
|
|
QHostInfo result = qt_qhostinfo_lookup("a-single" TEST_DOMAIN, this, SLOT(resultsReady(QHostInfo)), &valid, &id);
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(!valid);
|
|
|
|
//it is assumed that the DNS request/response in the backend is slower than it takes to call abort
|
|
|
|
QHostInfo::abortHostLookup(id);
|
|
|
|
QTestEventLoop::instance().enterLoop(5);
|
|
|
|
QCOMPARE(lookupsDoneCounter, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
class LookupAborter : public QObject
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
public slots:
|
|
|
|
void abort()
|
|
|
|
{
|
|
|
|
QHostInfo::abortHostLookup(id);
|
|
|
|
QThread::currentThread()->quit();
|
|
|
|
}
|
|
|
|
public:
|
|
|
|
int id;
|
|
|
|
};
|
|
|
|
|
|
|
|
QTEST_MAIN(tst_QHostInfo)
|
|
|
|
#include "tst_qhostinfo.moc"
|