| 1 | /**************************************************************************** |
| 2 | ** |
| 3 | ** Copyright (C) 2016 The Qt Company Ltd. |
| 4 | ** Copyright (C) 2016 Intel Corporation. |
| 5 | ** Contact: https://www.qt.io/licensing/ |
| 6 | ** |
| 7 | ** This file is part of the test suite of the Qt Toolkit. |
| 8 | ** |
| 9 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
| 10 | ** Commercial License Usage |
| 11 | ** Licensees holding valid commercial Qt licenses may use this file in |
| 12 | ** accordance with the commercial license agreement provided with the |
| 13 | ** Software or, alternatively, in accordance with the terms contained in |
| 14 | ** a written agreement between you and The Qt Company. For licensing terms |
| 15 | ** and conditions see https://www.qt.io/terms-conditions. For further |
| 16 | ** information use the contact form at https://www.qt.io/contact-us. |
| 17 | ** |
| 18 | ** GNU General Public License Usage |
| 19 | ** Alternatively, this file may be used under the terms of the GNU |
| 20 | ** General Public License version 3 as published by the Free Software |
| 21 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
| 22 | ** included in the packaging of this file. Please review the following |
| 23 | ** information to ensure the GNU General Public License requirements will |
| 24 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
| 25 | ** |
| 26 | ** $QT_END_LICENSE$ |
| 27 | ** |
| 28 | ****************************************************************************/ |
| 29 | |
| 30 | |
| 31 | // When using WinSock2 on Windows, it's the first thing that can be included |
| 32 | // (except qglobal.h), or else you'll get tons of compile errors |
| 33 | #include <qglobal.h> |
| 34 | |
| 35 | // To prevent windows system header files from re-defining min/max |
| 36 | #define NOMINMAX 1 |
| 37 | |
| 38 | #if defined(Q_OS_WIN) |
| 39 | # include <winsock2.h> |
| 40 | # include <ws2tcpip.h> |
| 41 | #endif |
| 42 | |
| 43 | #include <QtTest/QtTest> |
| 44 | #include <qcoreapplication.h> |
| 45 | #include <QDebug> |
| 46 | #include <QTcpSocket> |
| 47 | #include <private/qthread_p.h> |
| 48 | #include <QTcpServer> |
| 49 | |
| 50 | #ifndef QT_NO_BEARERMANAGEMENT |
| 51 | #include <QtNetwork/qnetworkconfigmanager.h> |
| 52 | #include <QtNetwork/qnetworkconfiguration.h> |
| 53 | #include <QtNetwork/qnetworksession.h> |
| 54 | #endif |
| 55 | |
| 56 | #include <time.h> |
| 57 | #if defined(Q_OS_WIN) |
| 58 | #include <windows.h> |
| 59 | #else |
| 60 | #include <unistd.h> |
| 61 | #include <signal.h> |
| 62 | #endif |
| 63 | |
| 64 | #include <qhostinfo.h> |
| 65 | #include "private/qhostinfo_p.h" |
| 66 | |
| 67 | #include <sys/types.h> |
| 68 | #if defined(Q_OS_UNIX) |
| 69 | # include <sys/socket.h> |
| 70 | # include <netdb.h> |
| 71 | #endif |
| 72 | |
| 73 | #include "../../../network-settings.h" |
| 74 | |
| 75 | #define TEST_DOMAIN ".test.qt-project.org" |
| 76 | |
| 77 | |
| 78 | class tst_QHostInfo : public QObject |
| 79 | { |
| 80 | Q_OBJECT |
| 81 | |
| 82 | private slots: |
| 83 | void init(); |
| 84 | void initTestCase(); |
| 85 | void swapFunction(); |
| 86 | void moveOperator(); |
| 87 | void getSetCheck(); |
| 88 | void staticInformation(); |
| 89 | void lookupIPv4_data(); |
| 90 | void lookupIPv4(); |
| 91 | void lookupIPv6_data(); |
| 92 | void lookupIPv6(); |
| 93 | void lookupConnectToFunctionPointer_data(); |
| 94 | void lookupConnectToFunctionPointer(); |
| 95 | void lookupConnectToFunctionPointerDeleted(); |
| 96 | void lookupConnectToLambda_data(); |
| 97 | void lookupConnectToLambda(); |
| 98 | void reverseLookup_data(); |
| 99 | void reverseLookup(); |
| 100 | |
| 101 | void blockingLookup_data(); |
| 102 | void blockingLookup(); |
| 103 | |
| 104 | void raceCondition(); |
| 105 | void threadSafety(); |
| 106 | void threadSafetyAsynchronousAPI(); |
| 107 | |
| 108 | void multipleSameLookups(); |
| 109 | void multipleDifferentLookups_data(); |
| 110 | void multipleDifferentLookups(); |
| 111 | |
| 112 | void cache(); |
| 113 | |
| 114 | void abortHostLookup(); |
| 115 | protected slots: |
| 116 | void resultsReady(const QHostInfo &); |
| 117 | |
| 118 | private: |
| 119 | bool ipv6LookupsAvailable; |
| 120 | bool ipv6Available; |
| 121 | bool lookupDone; |
| 122 | int lookupsDoneCounter; |
| 123 | QHostInfo lookupResults; |
| 124 | #ifndef QT_NO_BEARERMANAGEMENT |
| 125 | QNetworkConfigurationManager *netConfMan; |
| 126 | QNetworkConfiguration networkConfiguration; |
| 127 | QScopedPointer<QNetworkSession> networkSession; |
| 128 | #endif |
| 129 | }; |
| 130 | |
| 131 | void tst_QHostInfo::swapFunction() |
| 132 | { |
| 133 | QHostInfo obj1, obj2; |
| 134 | obj1.setError(QHostInfo::HostInfoError(0)); |
| 135 | obj2.setError(QHostInfo::HostInfoError(1)); |
| 136 | obj1.swap(other&: obj2); |
| 137 | QCOMPARE(QHostInfo::HostInfoError(0), obj2.error()); |
| 138 | QCOMPARE(QHostInfo::HostInfoError(1), obj1.error()); |
| 139 | } |
| 140 | |
| 141 | void tst_QHostInfo::moveOperator() |
| 142 | { |
| 143 | QHostInfo obj1, obj2, obj3(1); |
| 144 | obj1.setError(QHostInfo::HostInfoError(0)); |
| 145 | obj2.setError(QHostInfo::HostInfoError(1)); |
| 146 | obj1 = std::move(obj2); |
| 147 | obj2 = obj3; |
| 148 | QCOMPARE(QHostInfo::HostInfoError(1), obj1.error()); |
| 149 | QCOMPARE(obj3.lookupId(), obj2.lookupId()); |
| 150 | } |
| 151 | |
| 152 | |
| 153 | |
| 154 | // Testing get/set functions |
| 155 | void tst_QHostInfo::getSetCheck() |
| 156 | { |
| 157 | QHostInfo obj1; |
| 158 | // HostInfoError QHostInfo::error() |
| 159 | // void QHostInfo::setError(HostInfoError) |
| 160 | obj1.setError(QHostInfo::HostInfoError(0)); |
| 161 | QCOMPARE(QHostInfo::HostInfoError(0), obj1.error()); |
| 162 | obj1.setError(QHostInfo::HostInfoError(1)); |
| 163 | QCOMPARE(QHostInfo::HostInfoError(1), obj1.error()); |
| 164 | |
| 165 | // int QHostInfo::lookupId() |
| 166 | // void QHostInfo::setLookupId(int) |
| 167 | obj1.setLookupId(0); |
| 168 | QCOMPARE(0, obj1.lookupId()); |
| 169 | obj1.setLookupId(INT_MIN); |
| 170 | QCOMPARE(INT_MIN, obj1.lookupId()); |
| 171 | obj1.setLookupId(INT_MAX); |
| 172 | QCOMPARE(INT_MAX, obj1.lookupId()); |
| 173 | } |
| 174 | |
| 175 | void tst_QHostInfo::staticInformation() |
| 176 | { |
| 177 | qDebug() << "Hostname:" << QHostInfo::localHostName(); |
| 178 | qDebug() << "Domain name:" << QHostInfo::localDomainName(); |
| 179 | } |
| 180 | |
| 181 | void tst_QHostInfo::initTestCase() |
| 182 | { |
| 183 | #ifndef QT_NO_BEARERMANAGEMENT |
| 184 | //start the default network |
| 185 | netConfMan = new QNetworkConfigurationManager(this); |
| 186 | networkConfiguration = netConfMan->defaultConfiguration(); |
| 187 | networkSession.reset(other: new QNetworkSession(networkConfiguration)); |
| 188 | if (!networkSession->isOpen()) { |
| 189 | networkSession->open(); |
| 190 | networkSession->waitForOpened(msecs: 30000); |
| 191 | } |
| 192 | #endif |
| 193 | |
| 194 | ipv6Available = false; |
| 195 | ipv6LookupsAvailable = false; |
| 196 | |
| 197 | QTcpServer server; |
| 198 | if (server.listen(address: QHostAddress("::1" ))) { |
| 199 | // We have IPv6 support |
| 200 | ipv6Available = true; |
| 201 | } |
| 202 | |
| 203 | // check if the system getaddrinfo can do IPv6 lookups |
| 204 | struct addrinfo hint, *result = 0; |
| 205 | memset(s: &hint, c: 0, n: sizeof hint); |
| 206 | hint.ai_family = AF_UNSPEC; |
| 207 | #ifdef AI_ADDRCONFIG |
| 208 | hint.ai_flags = AI_ADDRCONFIG; |
| 209 | #endif |
| 210 | |
| 211 | int res = getaddrinfo(name: "::1" , service: "80" , req: &hint, pai: &result); |
| 212 | if (res == 0) { |
| 213 | // this test worked |
| 214 | freeaddrinfo(ai: result); |
| 215 | res = getaddrinfo(name: "aaaa-single" TEST_DOMAIN, service: "80" , req: &hint, pai: &result); |
| 216 | if (res == 0 && result != 0 && result->ai_family != AF_INET) { |
| 217 | freeaddrinfo(ai: result); |
| 218 | ipv6LookupsAvailable = true; |
| 219 | } |
| 220 | } |
| 221 | |
| 222 | // run each testcase with and without test enabled |
| 223 | QTest::addColumn<bool>(name: "cache" ); |
| 224 | QTest::newRow(dataTag: "WithCache" ) << true; |
| 225 | QTest::newRow(dataTag: "WithoutCache" ) << false; |
| 226 | } |
| 227 | |
| 228 | void tst_QHostInfo::init() |
| 229 | { |
| 230 | // delete the cache so inidividual testcase results are independent from each other |
| 231 | qt_qhostinfo_clear_cache(); |
| 232 | |
| 233 | QFETCH_GLOBAL(bool, cache); |
| 234 | qt_qhostinfo_enable_cache(e: cache); |
| 235 | } |
| 236 | |
| 237 | void tst_QHostInfo::lookupIPv4_data() |
| 238 | { |
| 239 | QTest::addColumn<QString>(name: "hostname" ); |
| 240 | QTest::addColumn<QString>(name: "addresses" ); |
| 241 | QTest::addColumn<int>(name: "err" ); |
| 242 | |
| 243 | QTest::newRow(dataTag: "empty" ) << "" << "" << int(QHostInfo::HostNotFound); |
| 244 | |
| 245 | QTest::newRow(dataTag: "single_ip4" ) << "a-single" TEST_DOMAIN << "192.0.2.1" << int(QHostInfo::NoError); |
| 246 | QTest::newRow(dataTag: "multiple_ip4" ) << "a-multi" TEST_DOMAIN << "192.0.2.1 192.0.2.2 192.0.2.3" << int(QHostInfo::NoError); |
| 247 | QTest::newRow(dataTag: "literal_ip4" ) << "192.0.2.1" << "192.0.2.1" << int(QHostInfo::NoError); |
| 248 | |
| 249 | QTest::newRow(dataTag: "notfound" ) << "invalid" TEST_DOMAIN << "" << int(QHostInfo::HostNotFound); |
| 250 | |
| 251 | QTest::newRow(dataTag: "idn-ace" ) << "a-single.xn--alqualond-34a" TEST_DOMAIN << "192.0.2.1" << int(QHostInfo::NoError); |
| 252 | QTest::newRow(dataTag: "idn-unicode" ) << QString::fromLatin1(str: "a-single.alqualond\353" TEST_DOMAIN) << "192.0.2.1" << int(QHostInfo::NoError); |
| 253 | } |
| 254 | |
| 255 | void tst_QHostInfo::lookupIPv4() |
| 256 | { |
| 257 | QFETCH(QString, hostname); |
| 258 | QFETCH(int, err); |
| 259 | QFETCH(QString, addresses); |
| 260 | |
| 261 | lookupDone = false; |
| 262 | QHostInfo::lookupHost(name: hostname, receiver: this, SLOT(resultsReady(QHostInfo))); |
| 263 | |
| 264 | QTestEventLoop::instance().enterLoop(secs: 10); |
| 265 | QVERIFY(!QTestEventLoop::instance().timeout()); |
| 266 | QVERIFY(lookupDone); |
| 267 | |
| 268 | if ((int)lookupResults.error() != (int)err) { |
| 269 | qWarning() << hostname << "=>" << lookupResults.errorString(); |
| 270 | } |
| 271 | QCOMPARE((int)lookupResults.error(), (int)err); |
| 272 | |
| 273 | QStringList tmp; |
| 274 | for (int i = 0; i < lookupResults.addresses().count(); ++i) |
| 275 | tmp.append(t: lookupResults.addresses().at(i).toString()); |
| 276 | tmp.sort(); |
| 277 | |
| 278 | QStringList expected = addresses.split(sep: ' '); |
| 279 | expected.sort(); |
| 280 | |
| 281 | QCOMPARE(tmp.join(' '), expected.join(' ')); |
| 282 | } |
| 283 | |
| 284 | void tst_QHostInfo::lookupIPv6_data() |
| 285 | { |
| 286 | QTest::addColumn<QString>(name: "hostname" ); |
| 287 | QTest::addColumn<QString>(name: "addresses" ); |
| 288 | QTest::addColumn<int>(name: "err" ); |
| 289 | |
| 290 | QTest::newRow(dataTag: "aaaa-single" ) << "aaaa-single" TEST_DOMAIN << "2001:db8::1" << int(QHostInfo::NoError); |
| 291 | QTest::newRow(dataTag: "aaaa-multi" ) << "aaaa-multi" TEST_DOMAIN << "2001:db8::1 2001:db8::2 2001:db8::3" << int(QHostInfo::NoError); |
| 292 | QTest::newRow(dataTag: "a-plus-aaaa" ) << "a-plus-aaaa" TEST_DOMAIN << "198.51.100.1 2001:db8::1:1" << int(QHostInfo::NoError); |
| 293 | |
| 294 | // avoid using real IPv6 addresses here because this will do a DNS query |
| 295 | // real addresses are between 2000:: and 3fff:ffff:ffff:ffff:ffff:ffff:ffff |
| 296 | QTest::newRow(dataTag: "literal_ip6" ) << "f001:6b0:1:ea:202:a5ff:fecd:13a6" << "f001:6b0:1:ea:202:a5ff:fecd:13a6" << int(QHostInfo::NoError); |
| 297 | QTest::newRow(dataTag: "literal_shortip6" ) << "f001:618:1401::4" << "f001:618:1401::4" << int(QHostInfo::NoError); |
| 298 | } |
| 299 | |
| 300 | void tst_QHostInfo::lookupIPv6() |
| 301 | { |
| 302 | QFETCH(QString, hostname); |
| 303 | QFETCH(int, err); |
| 304 | QFETCH(QString, addresses); |
| 305 | |
| 306 | if (!ipv6LookupsAvailable) |
| 307 | QSKIP("This platform does not support IPv6 lookups" ); |
| 308 | |
| 309 | lookupDone = false; |
| 310 | QHostInfo::lookupHost(name: hostname, receiver: this, SLOT(resultsReady(QHostInfo))); |
| 311 | |
| 312 | QTestEventLoop::instance().enterLoop(secs: 10); |
| 313 | QVERIFY(!QTestEventLoop::instance().timeout()); |
| 314 | QVERIFY(lookupDone); |
| 315 | |
| 316 | QCOMPARE((int)lookupResults.error(), (int)err); |
| 317 | |
| 318 | QStringList tmp; |
| 319 | for (int i = 0; i < lookupResults.addresses().count(); ++i) |
| 320 | tmp.append(t: lookupResults.addresses().at(i).toString()); |
| 321 | tmp.sort(); |
| 322 | |
| 323 | QStringList expected = addresses.split(sep: ' '); |
| 324 | expected.sort(); |
| 325 | |
| 326 | QCOMPARE(tmp.join(' ').toLower(), expected.join(' ').toLower()); |
| 327 | } |
| 328 | |
| 329 | void tst_QHostInfo::lookupConnectToFunctionPointer_data() |
| 330 | { |
| 331 | lookupIPv4_data(); |
| 332 | } |
| 333 | |
| 334 | void tst_QHostInfo::lookupConnectToFunctionPointer() |
| 335 | { |
| 336 | QFETCH(QString, hostname); |
| 337 | QFETCH(int, err); |
| 338 | QFETCH(QString, addresses); |
| 339 | |
| 340 | lookupDone = false; |
| 341 | QHostInfo::lookupHost(name: hostname, receiver: this, slot: &tst_QHostInfo::resultsReady); |
| 342 | |
| 343 | QTestEventLoop::instance().enterLoop(secs: 10); |
| 344 | QVERIFY(!QTestEventLoop::instance().timeout()); |
| 345 | QVERIFY(lookupDone); |
| 346 | |
| 347 | if (int(lookupResults.error()) != int(err)) |
| 348 | qWarning() << hostname << "=>" << lookupResults.errorString(); |
| 349 | QCOMPARE(int(lookupResults.error()), int(err)); |
| 350 | |
| 351 | QStringList tmp; |
| 352 | for (const auto &result : lookupResults.addresses()) |
| 353 | tmp.append(t: result.toString()); |
| 354 | tmp.sort(); |
| 355 | |
| 356 | QStringList expected = addresses.split(sep: ' '); |
| 357 | expected.sort(); |
| 358 | |
| 359 | QCOMPARE(tmp.join(' '), expected.join(' ')); |
| 360 | } |
| 361 | |
| 362 | void tst_QHostInfo::lookupConnectToFunctionPointerDeleted() |
| 363 | { |
| 364 | { |
| 365 | QObject contextObject; |
| 366 | QHostInfo::lookupHost(name: "localhost" , context: &contextObject, slot: [](const QHostInfo){ |
| 367 | QFAIL("This should never be called!" ); |
| 368 | }); |
| 369 | } |
| 370 | QTestEventLoop::instance().enterLoop(secs: 3); |
| 371 | } |
| 372 | |
| 373 | void tst_QHostInfo::lookupConnectToLambda_data() |
| 374 | { |
| 375 | lookupIPv4_data(); |
| 376 | } |
| 377 | |
| 378 | void tst_QHostInfo::lookupConnectToLambda() |
| 379 | { |
| 380 | QFETCH(QString, hostname); |
| 381 | QFETCH(int, err); |
| 382 | QFETCH(QString, addresses); |
| 383 | |
| 384 | lookupDone = false; |
| 385 | QHostInfo::lookupHost(name: hostname, slot: [=](const QHostInfo &hostInfo) { |
| 386 | resultsReady(hostInfo); |
| 387 | }); |
| 388 | |
| 389 | QTestEventLoop::instance().enterLoop(secs: 10); |
| 390 | QVERIFY(!QTestEventLoop::instance().timeout()); |
| 391 | QVERIFY(lookupDone); |
| 392 | |
| 393 | if (int(lookupResults.error()) != int(err)) |
| 394 | qWarning() << hostname << "=>" << lookupResults.errorString(); |
| 395 | QCOMPARE(int(lookupResults.error()), int(err)); |
| 396 | |
| 397 | QStringList tmp; |
| 398 | for (int i = 0; i < lookupResults.addresses().count(); ++i) |
| 399 | tmp.append(t: lookupResults.addresses().at(i).toString()); |
| 400 | tmp.sort(); |
| 401 | |
| 402 | QStringList expected = addresses.split(sep: ' '); |
| 403 | expected.sort(); |
| 404 | |
| 405 | QCOMPARE(tmp.join(' '), expected.join(' ')); |
| 406 | } |
| 407 | |
| 408 | static QStringList reverseLookupHelper(const QString &ip) |
| 409 | { |
| 410 | QStringList results; |
| 411 | |
| 412 | const QString pythonCode = |
| 413 | "import socket;" |
| 414 | "import sys;" |
| 415 | "print (socket.getnameinfo((sys.argv[1], 0), 0)[0]);" ; |
| 416 | |
| 417 | QList<QByteArray> lines; |
| 418 | QProcess python; |
| 419 | python.setProcessChannelMode(QProcess::ForwardedErrorChannel); |
| 420 | python.start(program: "python" , arguments: QStringList() << QString("-c" ) << pythonCode << ip); |
| 421 | if (python.waitForFinished()) { |
| 422 | if (python.exitStatus() == QProcess::NormalExit && python.exitCode() == 0) |
| 423 | lines = python.readAllStandardOutput().split(sep: '\n'); |
| 424 | for (QByteArray line : lines) { |
| 425 | if (!line.isEmpty()) |
| 426 | results << line.trimmed(); |
| 427 | } |
| 428 | if (!results.isEmpty()) |
| 429 | return results; |
| 430 | } |
| 431 | |
| 432 | qDebug() << "Python failed, falling back to nslookup" ; |
| 433 | QProcess lookup; |
| 434 | lookup.setProcessChannelMode(QProcess::ForwardedErrorChannel); |
| 435 | lookup.start(program: "nslookup" , arguments: QStringList(ip)); |
| 436 | if (!lookup.waitForFinished()) { |
| 437 | results << "nslookup failure" ; |
| 438 | qDebug() << "nslookup failure" ; |
| 439 | return results; |
| 440 | } |
| 441 | lines = lookup.readAllStandardOutput().split(sep: '\n'); |
| 442 | |
| 443 | QByteArray name; |
| 444 | |
| 445 | const QByteArray nameMarkerNix("name =" ); |
| 446 | const QByteArray nameMarkerWin("Name:" ); |
| 447 | const QByteArray addressMarkerWin("Address:" ); |
| 448 | |
| 449 | for (QByteArray line : lines) { |
| 450 | int index = -1; |
| 451 | if ((index = line.indexOf(a: nameMarkerNix)) != -1) { // Linux and macOS |
| 452 | name = line.mid(index: index + nameMarkerNix.length()).chopped(len: 1).trimmed(); |
| 453 | results << name; |
| 454 | } else if (line.startsWith(a: nameMarkerWin)) { // Windows formatting |
| 455 | name = line.mid(index: line.lastIndexOf(c: " " )).trimmed(); |
| 456 | } else if (line.startsWith(a: addressMarkerWin)) { |
| 457 | QByteArray address = line.mid(index: addressMarkerWin.length()).trimmed(); |
| 458 | if (address == ip.toUtf8()) { |
| 459 | results << name; |
| 460 | } |
| 461 | } |
| 462 | } |
| 463 | |
| 464 | if (results.isEmpty()) { |
| 465 | qDebug() << "Failure to parse nslookup output: " << lines; |
| 466 | } |
| 467 | return results; |
| 468 | } |
| 469 | |
| 470 | void tst_QHostInfo::reverseLookup_data() |
| 471 | { |
| 472 | QTest::addColumn<QString>(name: "address" ); |
| 473 | QTest::addColumn<QStringList>(name: "hostNames" ); |
| 474 | QTest::addColumn<int>(name: "err" ); |
| 475 | QTest::addColumn<bool>(name: "ipv6" ); |
| 476 | |
| 477 | QTest::newRow(dataTag: "dns.google" ) << QString("8.8.8.8" ) << reverseLookupHelper(ip: "8.8.8.8" ) << 0 << false; |
| 478 | QTest::newRow(dataTag: "one.one.one.one" ) << QString("1.1.1.1" ) << reverseLookupHelper(ip: "1.1.1.1" ) << 0 << false; |
| 479 | QTest::newRow(dataTag: "dns.google IPv6" ) << QString("2001:4860:4860::8888" ) << reverseLookupHelper(ip: "2001:4860:4860::8888" ) << 0 << true; |
| 480 | QTest::newRow(dataTag: "cloudflare IPv6" ) << QString("2606:4700:4700::1111" ) << reverseLookupHelper(ip: "2606:4700:4700::1111" ) << 0 << true; |
| 481 | QTest::newRow(dataTag: "bogus-name IPv6" ) << QString("1::2::3::4" ) << QStringList() << 1 << true; |
| 482 | } |
| 483 | |
| 484 | void tst_QHostInfo::reverseLookup() |
| 485 | { |
| 486 | QFETCH(QString, address); |
| 487 | QFETCH(QStringList, hostNames); |
| 488 | QFETCH(int, err); |
| 489 | QFETCH(bool, ipv6); |
| 490 | |
| 491 | if (ipv6 && !ipv6LookupsAvailable) { |
| 492 | QSKIP("IPv6 reverse lookups are not supported on this platform" ); |
| 493 | } |
| 494 | |
| 495 | QHostInfo info = QHostInfo::fromName(name: address); |
| 496 | |
| 497 | if (err == 0) { |
| 498 | if (!hostNames.contains(str: info.hostName())) |
| 499 | qDebug() << "Failure: expecting" << hostNames << ",got " << info.hostName(); |
| 500 | QVERIFY(hostNames.contains(info.hostName())); |
| 501 | QCOMPARE(info.addresses().first(), QHostAddress(address)); |
| 502 | } else { |
| 503 | QCOMPARE(info.hostName(), address); |
| 504 | QCOMPARE(info.error(), QHostInfo::HostNotFound); |
| 505 | } |
| 506 | |
| 507 | } |
| 508 | |
| 509 | void tst_QHostInfo::blockingLookup_data() |
| 510 | { |
| 511 | lookupIPv4_data(); |
| 512 | if (ipv6LookupsAvailable) |
| 513 | lookupIPv6_data(); |
| 514 | } |
| 515 | |
| 516 | void tst_QHostInfo::blockingLookup() |
| 517 | { |
| 518 | QFETCH(QString, hostname); |
| 519 | QFETCH(int, err); |
| 520 | QFETCH(QString, addresses); |
| 521 | |
| 522 | QHostInfo hostInfo = QHostInfo::fromName(name: hostname); |
| 523 | QStringList tmp; |
| 524 | for (int i = 0; i < hostInfo.addresses().count(); ++i) |
| 525 | tmp.append(t: hostInfo.addresses().at(i).toString()); |
| 526 | tmp.sort(); |
| 527 | |
| 528 | if ((int)hostInfo.error() != (int)err) { |
| 529 | qWarning() << hostname << "=>" << lookupResults.errorString(); |
| 530 | } |
| 531 | QCOMPARE((int)hostInfo.error(), (int)err); |
| 532 | |
| 533 | QStringList expected = addresses.split(sep: ' '); |
| 534 | expected.sort(); |
| 535 | |
| 536 | QCOMPARE(tmp.join(' ').toUpper(), expected.join(' ').toUpper()); |
| 537 | } |
| 538 | |
| 539 | void tst_QHostInfo::raceCondition() |
| 540 | { |
| 541 | for (int i = 0; i < 1000; ++i) { |
| 542 | QTcpSocket socket; |
| 543 | socket.connectToHost(hostName: "invalid" TEST_DOMAIN, port: 80); |
| 544 | } |
| 545 | } |
| 546 | |
| 547 | class LookupThread : public QThread |
| 548 | { |
| 549 | protected: |
| 550 | inline void run() |
| 551 | { |
| 552 | QHostInfo info = QHostInfo::fromName(name: "a-single" TEST_DOMAIN); |
| 553 | QCOMPARE(info.error(), QHostInfo::NoError); |
| 554 | QVERIFY(info.addresses().count() > 0); |
| 555 | QCOMPARE(info.addresses().at(0).toString(), QString("192.0.2.1" )); |
| 556 | } |
| 557 | }; |
| 558 | |
| 559 | void tst_QHostInfo::threadSafety() |
| 560 | { |
| 561 | const int nattempts = 5; |
| 562 | const int runs = 100; |
| 563 | LookupThread thr[nattempts]; |
| 564 | for (int j = 0; j < runs; ++j) { |
| 565 | for (int i = 0; i < nattempts; ++i) |
| 566 | thr[i].start(); |
| 567 | for (int k = nattempts - 1; k >= 0; --k) |
| 568 | thr[k].wait(); |
| 569 | } |
| 570 | } |
| 571 | |
| 572 | class LookupReceiver : public QObject |
| 573 | { |
| 574 | Q_OBJECT |
| 575 | public slots: |
| 576 | void start(); |
| 577 | void resultsReady(const QHostInfo&); |
| 578 | public: |
| 579 | QHostInfo result; |
| 580 | int numrequests; |
| 581 | }; |
| 582 | |
| 583 | void LookupReceiver::start() |
| 584 | { |
| 585 | for (int i=0;i<numrequests;i++) |
| 586 | QHostInfo::lookupHost(name: QString("a-single" TEST_DOMAIN), receiver: this, SLOT(resultsReady(QHostInfo))); |
| 587 | } |
| 588 | |
| 589 | void LookupReceiver::resultsReady(const QHostInfo &info) |
| 590 | { |
| 591 | result = info; |
| 592 | numrequests--; |
| 593 | if (numrequests == 0 || info.error() != QHostInfo::NoError) |
| 594 | QThread::currentThread()->quit(); |
| 595 | } |
| 596 | |
| 597 | void tst_QHostInfo::threadSafetyAsynchronousAPI() |
| 598 | { |
| 599 | const int nattempts = 10; |
| 600 | const int lookupsperthread = 10; |
| 601 | QList<QThread*> threads; |
| 602 | QList<LookupReceiver*> receivers; |
| 603 | for (int i = 0; i < nattempts; ++i) { |
| 604 | QThread* thread = new QThread; |
| 605 | LookupReceiver* receiver = new LookupReceiver; |
| 606 | receiver->numrequests = lookupsperthread; |
| 607 | receivers.append(t: receiver); |
| 608 | receiver->moveToThread(thread); |
| 609 | connect(sender: thread, SIGNAL(started()), receiver, SLOT(start())); |
| 610 | thread->start(); |
| 611 | threads.append(t: thread); |
| 612 | } |
| 613 | for (int k = threads.count() - 1; k >= 0; --k) |
| 614 | QVERIFY(threads.at(k)->wait(60000)); |
| 615 | foreach (LookupReceiver* receiver, receivers) { |
| 616 | QCOMPARE(receiver->result.error(), QHostInfo::NoError); |
| 617 | QCOMPARE(receiver->result.addresses().at(0).toString(), QString("192.0.2.1" )); |
| 618 | QCOMPARE(receiver->numrequests, 0); |
| 619 | } |
| 620 | } |
| 621 | |
| 622 | // this test is for the multi-threaded QHostInfo rewrite. It is about getting results at all, |
| 623 | // not about getting correct IPs |
| 624 | void tst_QHostInfo::multipleSameLookups() |
| 625 | { |
| 626 | const int COUNT = 10; |
| 627 | lookupsDoneCounter = 0; |
| 628 | |
| 629 | for (int i = 0; i < COUNT; i++) |
| 630 | QHostInfo::lookupHost(name: "localhost" , receiver: this, SLOT(resultsReady(QHostInfo))); |
| 631 | |
| 632 | QElapsedTimer timer; |
| 633 | timer.start(); |
| 634 | while (timer.elapsed() < 10000 && lookupsDoneCounter < COUNT) { |
| 635 | QTestEventLoop::instance().enterLoop(secs: 2); |
| 636 | } |
| 637 | QCOMPARE(lookupsDoneCounter, COUNT); |
| 638 | } |
| 639 | |
| 640 | // this test is for the multi-threaded QHostInfo rewrite. It is about getting results at all, |
| 641 | // not about getting correct IPs |
| 642 | void tst_QHostInfo::multipleDifferentLookups_data() |
| 643 | { |
| 644 | QTest::addColumn<int>(name: "repeats" ); |
| 645 | QTest::newRow(dataTag: "1" ) << 1; |
| 646 | QTest::newRow(dataTag: "2" ) << 2; |
| 647 | QTest::newRow(dataTag: "5" ) << 5; |
| 648 | QTest::newRow(dataTag: "10" ) << 10; |
| 649 | } |
| 650 | |
| 651 | void tst_QHostInfo::multipleDifferentLookups() |
| 652 | { |
| 653 | QStringList hostnameList; |
| 654 | hostnameList << "a-single" TEST_DOMAIN |
| 655 | << "a-multi" TEST_DOMAIN |
| 656 | << "aaaa-single" TEST_DOMAIN |
| 657 | << "aaaa-multi" TEST_DOMAIN |
| 658 | << "a-plus-aaaa" TEST_DOMAIN |
| 659 | << "multi" TEST_DOMAIN |
| 660 | << "localhost" TEST_DOMAIN |
| 661 | << "cname" TEST_DOMAIN |
| 662 | << "127.0.0.1" << "----" ; |
| 663 | |
| 664 | QFETCH(int, repeats); |
| 665 | const int COUNT = hostnameList.size(); |
| 666 | lookupsDoneCounter = 0; |
| 667 | |
| 668 | for (int i = 0; i < hostnameList.size(); i++) |
| 669 | for (int j = 0; j < repeats; ++j) |
| 670 | QHostInfo::lookupHost(name: hostnameList.at(i), receiver: this, SLOT(resultsReady(QHostInfo))); |
| 671 | |
| 672 | QElapsedTimer timer; |
| 673 | timer.start(); |
| 674 | while (timer.elapsed() < 60000 && lookupsDoneCounter < repeats*COUNT) { |
| 675 | QTestEventLoop::instance().enterLoop(secs: 2); |
| 676 | //qDebug() << "t:" << timer.elapsed(); |
| 677 | } |
| 678 | QCOMPARE(lookupsDoneCounter, repeats*COUNT); |
| 679 | } |
| 680 | |
| 681 | void tst_QHostInfo::cache() |
| 682 | { |
| 683 | QFETCH_GLOBAL(bool, cache); |
| 684 | if (!cache) |
| 685 | return; // test makes only sense when cache enabled |
| 686 | |
| 687 | // reset slot counter |
| 688 | lookupsDoneCounter = 0; |
| 689 | |
| 690 | // lookup once, wait in event loop, result should not come directly. |
| 691 | bool valid = true; |
| 692 | int id = -1; |
| 693 | QHostInfo result = qt_qhostinfo_lookup(name: "localhost" , receiver: this, SLOT(resultsReady(QHostInfo)), valid: &valid, id: &id); |
| 694 | QTestEventLoop::instance().enterLoop(secs: 5); |
| 695 | QVERIFY(!QTestEventLoop::instance().timeout()); |
| 696 | QVERIFY(!valid); |
| 697 | QVERIFY(result.addresses().isEmpty()); |
| 698 | |
| 699 | // loopkup second time, result should come directly |
| 700 | valid = false; |
| 701 | result = qt_qhostinfo_lookup(name: "localhost" , receiver: this, SLOT(resultsReady(QHostInfo)), valid: &valid, id: &id); |
| 702 | QVERIFY(valid); |
| 703 | QVERIFY(!result.addresses().isEmpty()); |
| 704 | |
| 705 | // clear the cache |
| 706 | qt_qhostinfo_clear_cache(); |
| 707 | |
| 708 | // lookup third time, result should not come directly. |
| 709 | valid = true; |
| 710 | result = qt_qhostinfo_lookup(name: "localhost" , receiver: this, SLOT(resultsReady(QHostInfo)), valid: &valid, id: &id); |
| 711 | QTestEventLoop::instance().enterLoop(secs: 5); |
| 712 | QVERIFY(!QTestEventLoop::instance().timeout()); |
| 713 | QVERIFY(!valid); |
| 714 | QVERIFY(result.addresses().isEmpty()); |
| 715 | |
| 716 | // the slot should have been called 2 times. |
| 717 | QCOMPARE(lookupsDoneCounter, 2); |
| 718 | } |
| 719 | |
| 720 | void tst_QHostInfo::resultsReady(const QHostInfo &hi) |
| 721 | { |
| 722 | QVERIFY(QThread::currentThread() == thread()); |
| 723 | lookupDone = true; |
| 724 | lookupResults = hi; |
| 725 | lookupsDoneCounter++; |
| 726 | QTestEventLoop::instance().exitLoop(); |
| 727 | } |
| 728 | |
| 729 | void tst_QHostInfo::abortHostLookup() |
| 730 | { |
| 731 | //reset counter |
| 732 | lookupsDoneCounter = 0; |
| 733 | bool valid = false; |
| 734 | int id = -1; |
| 735 | QHostInfo result = qt_qhostinfo_lookup(name: "a-single" TEST_DOMAIN, receiver: this, SLOT(resultsReady(QHostInfo)), valid: &valid, id: &id); |
| 736 | QVERIFY(!valid); |
| 737 | //it is assumed that the DNS request/response in the backend is slower than it takes to call abort |
| 738 | QHostInfo::abortHostLookup(lookupId: id); |
| 739 | QTestEventLoop::instance().enterLoop(secs: 5); |
| 740 | QCOMPARE(lookupsDoneCounter, 0); |
| 741 | } |
| 742 | |
| 743 | class LookupAborter : public QObject |
| 744 | { |
| 745 | Q_OBJECT |
| 746 | public slots: |
| 747 | void abort() |
| 748 | { |
| 749 | QHostInfo::abortHostLookup(lookupId: id); |
| 750 | QThread::currentThread()->quit(); |
| 751 | } |
| 752 | public: |
| 753 | int id; |
| 754 | }; |
| 755 | |
| 756 | QTEST_MAIN(tst_QHostInfo) |
| 757 | #include "tst_qhostinfo.moc" |
| 758 | |