// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include #if defined(_WIN32) #include #else #include #include #define SOCKET int #define INVALID_SOCKET -1 #endif #include #include #include #ifndef Q_OS_WIN #include #include #endif #include #include #include #include #if QT_CONFIG(process) # include #endif #include #include #include #include #include #include #include #include "../../../network-settings.h" #if defined(Q_OS_LINUX) #define SHOULD_CHECK_SYSCALL_SUPPORT #include #include #include #endif class tst_QTcpServer : public QObject { Q_OBJECT private slots: void initTestCase_data(); void initTestCase(); void init(); void cleanup(); void getSetCheck(); void constructing(); void clientServerLoop(); void ipv6Server(); void dualStack_data(); void dualStack(); void ipv6ServerMapped(); void crashTests(); void maxPendingConnections(); void listenError(); void waitForConnectionTest(); void setSocketDescriptor(); void listenWhileListening(); void addressReusable(); void setNewSocketDescriptorBlocking(); #ifndef QT_NO_NETWORKPROXY void invalidProxy_data(); void invalidProxy(); void proxyFactory_data(); void proxyFactory(); #endif // !QT_NO_NETWORKPROXY void qtbug14268_peek(); void serverAddress_data(); void serverAddress(); void qtbug6305_data() { serverAddress_data(); } void qtbug6305(); void linkLocal(); void eagainBlockingAccept(); void canAccessPendingConnectionsWhileNotListening(); void pauseAccepting(); void pendingConnectionAvailable_data(); void pendingConnectionAvailable(); private: bool shouldSkipIpv6TestsForBrokenGetsockopt(); #ifdef SHOULD_CHECK_SYSCALL_SUPPORT bool ipv6GetsockoptionMissing(int level, int optname); #endif QString crashingServerDir; }; // Testing get/set functions void tst_QTcpServer::getSetCheck() { QTcpServer obj1; // int QTcpServer::maxPendingConnections() // void QTcpServer::setMaxPendingConnections(int) obj1.setMaxPendingConnections(0); QCOMPARE(0, obj1.maxPendingConnections()); obj1.setMaxPendingConnections(INT_MIN); QCOMPARE(INT_MIN, obj1.maxPendingConnections()); obj1.setMaxPendingConnections(INT_MAX); QCOMPARE(INT_MAX, obj1.maxPendingConnections()); } void tst_QTcpServer::initTestCase_data() { QTest::addColumn("setProxy"); QTest::addColumn("proxyType"); QTest::newRow("WithoutProxy") << false << 0; #if QT_CONFIG(socks5) QTest::newRow("WithSocks5Proxy") << true << int(QNetworkProxy::Socks5Proxy); #endif crashingServerDir = QFINDTESTDATA("crashingServer"); QVERIFY2(!crashingServerDir.isEmpty(), qPrintable( QString::fromLatin1("Couldn't find crashingServer dir starting from %1.").arg(QDir::currentPath()))); } void tst_QTcpServer::initTestCase() { #ifdef QT_TEST_SERVER QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::socksProxyServerName(), 1080)); QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::httpProxyServerName(), 3128)); // FTP currently not supported: // QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::ftpProxyServerName(), 2121)); QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::imapServerName(), 143)); #else if (!QtNetworkSettings::verifyTestNetworkSettings()) QSKIP("No network test server available"); #endif } void tst_QTcpServer::init() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) { #ifndef QT_NO_NETWORKPROXY QFETCH_GLOBAL(int, proxyType); if (proxyType == QNetworkProxy::Socks5Proxy) { QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::socksProxyServerName(), 1080)); } #else // !QT_NO_NETWORKPROXY QSKIP("No proxy support"); #endif // QT_NO_NETWORKPROXY } } void tst_QTcpServer::cleanup() { #ifndef QT_NO_NETWORKPROXY QNetworkProxy::setApplicationProxy(QNetworkProxy::DefaultProxy); #endif } #ifdef SHOULD_CHECK_SYSCALL_SUPPORT bool tst_QTcpServer::ipv6GetsockoptionMissing(int level, int optname) { int testSocket; testSocket = socket(PF_INET6, SOCK_STREAM, 0); // If we can't test here, assume it's not missing if (testSocket == -1) return false; bool result = false; if (getsockopt(testSocket, level, optname, nullptr, 0) == -1) { if (errno == EOPNOTSUPP) { result = true; } } close(testSocket); return result; } #endif //SHOULD_CHECK_SYSCALL_SUPPORT bool tst_QTcpServer::shouldSkipIpv6TestsForBrokenGetsockopt() { #ifdef SHOULD_CHECK_SYSCALL_SUPPORT // Following parameters for setsockopt are not supported by all QEMU versions: if (ipv6GetsockoptionMissing(SOL_IPV6, IPV6_V6ONLY)) { return true; } #endif //SHOULD_CHECK_SYSCALL_SUPPORT return false; } //---------------------------------------------------------------------------------- void tst_QTcpServer::constructing() { QTcpServer socket; // Check the initial state of the QTcpSocket. QCOMPARE(socket.isListening(), false); QCOMPARE((int)socket.serverPort(), 0); QCOMPARE(socket.serverAddress(), QHostAddress()); QCOMPARE(socket.maxPendingConnections(), 30); QCOMPARE(socket.hasPendingConnections(), false); QCOMPARE(socket.socketDescriptor(), (qintptr)-1); QCOMPARE(socket.serverError(), QAbstractSocket::UnknownSocketError); // Check the state of the socket layer? } //---------------------------------------------------------------------------------- void tst_QTcpServer::clientServerLoop() { QTcpServer server; QSignalSpy spy(&server, SIGNAL(newConnection())); QVERIFY(!server.isListening()); QVERIFY(!server.hasPendingConnections()); QVERIFY(server.listen(QHostAddress::Any, 11423)); QVERIFY(server.isListening()); QTcpSocket client; QHostAddress serverAddress = QHostAddress::LocalHost; if (!(server.serverAddress() == QHostAddress::Any) && !(server.serverAddress() == QHostAddress::AnyIPv6) && !(server.serverAddress() == QHostAddress::AnyIPv4)) serverAddress = server.serverAddress(); client.connectToHost(serverAddress, server.serverPort()); QVERIFY(client.waitForConnected(5000)); QVERIFY(server.waitForNewConnection(5000)); QVERIFY(server.hasPendingConnections()); QCOMPARE(spy.size(), 1); QTcpSocket *serverSocket = server.nextPendingConnection(); QVERIFY(serverSocket != 0); QVERIFY(serverSocket->write("Greetings, client!\n", 19) == 19); serverSocket->flush(); QVERIFY(client.waitForReadyRead(5000)); QByteArray arr = client.readAll(); QCOMPARE(arr.constData(), "Greetings, client!\n"); QVERIFY(client.write("Well, hello to you!\n", 20) == 20); client.flush(); QVERIFY(serverSocket->waitForReadyRead(5000)); arr = serverSocket->readAll(); QCOMPARE(arr.constData(), "Well, hello to you!\n"); } //---------------------------------------------------------------------------------- void tst_QTcpServer::ipv6Server() { if (!QtNetworkSettings::hasIPv6()) QSKIP("system doesn't support ipv6!"); //### need to enter the event loop for the server to get the connection ?? ( windows) QTcpServer server; if (!server.listen(QHostAddress::LocalHostIPv6, 8944)) { QCOMPARE(server.serverError(), QAbstractSocket::UnsupportedSocketOperationError); return; } QCOMPARE(server.serverPort(), quint16(8944)); QVERIFY(server.serverAddress() == QHostAddress::LocalHostIPv6); QTcpSocket client; client.connectToHost("::1", 8944); QVERIFY(client.waitForConnected(5000)); QVERIFY(server.waitForNewConnection()); QVERIFY(server.hasPendingConnections()); QTcpSocket *serverSocket = 0; QVERIFY((serverSocket = server.nextPendingConnection())); serverSocket->close(); delete serverSocket; } Q_DECLARE_METATYPE(QHostAddress); void tst_QTcpServer::dualStack_data() { QTest::addColumn("bindAddress"); QTest::addColumn("v4ok"); QTest::addColumn("v6ok"); QTest::newRow("any") << QHostAddress(QHostAddress::Any) << true << true; QTest::newRow("anyIPv4") << QHostAddress(QHostAddress::AnyIPv4) << true << false; QTest::newRow("anyIPv6") << QHostAddress(QHostAddress::AnyIPv6) << false << true; } void tst_QTcpServer::dualStack() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) QSKIP("test server proxy doesn't support ipv6"); if (!QtNetworkSettings::hasIPv6()) QSKIP("system doesn't support ipv6!"); QFETCH(QHostAddress, bindAddress); QFETCH(bool, v4ok); QFETCH(bool, v6ok); QTcpServer server; QVERIFY(server.listen(bindAddress)); QTcpSocket v4client; v4client.connectToHost(QHostAddress::LocalHost, server.serverPort()); QTcpSocket v6client; v6client.connectToHost(QHostAddress::LocalHostIPv6, server.serverPort()); QCOMPARE(v4client.waitForConnected(5000), v4ok); QCOMPARE(v6client.waitForConnected(5000), v6ok); } //---------------------------------------------------------------------------------- void tst_QTcpServer::ipv6ServerMapped() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; QTcpServer server; QVERIFY(server.listen(QHostAddress::LocalHost)); // let's try the normal case QTcpSocket client1; client1.connectToHost("127.0.0.1", server.serverPort()); QVERIFY(server.waitForNewConnection(5000)); delete server.nextPendingConnection(); if (!QtNetworkSettings::hasIPv6()) QSKIP("system doesn't support ipv6!"); // let's try the mapped one in the nice format QTcpSocket client2; client2.connectToHost("::ffff:127.0.0.1", server.serverPort()); QVERIFY(server.waitForNewConnection(5000)); delete server.nextPendingConnection(); // let's try the mapped in hex format QTcpSocket client3; client3.connectToHost("::ffff:7F00:0001", server.serverPort()); QVERIFY(server.waitForNewConnection(5000)); delete server.nextPendingConnection(); // However connecting to the v6 localhost should not work QTcpSocket client4; client4.connectToHost("::1", server.serverPort()); QVERIFY(!server.waitForNewConnection(5000)); } //---------------------------------------------------------------------------------- void tst_QTcpServer::crashTests() { QTcpServer server; server.close(); QVERIFY(server.listen()); } //---------------------------------------------------------------------------------- void tst_QTcpServer::maxPendingConnections() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) { #ifndef QT_NO_NETWORKPROXY QFETCH_GLOBAL(int, proxyType); if (proxyType == QNetworkProxy::Socks5Proxy) QSKIP("With socks5 only 1 connection is allowed ever"); #else // !QT_NO_NETWORKPROXY QSKIP("No proxy support"); #endif // QT_NO_NETWORKPROXY } //### sees to fail sometimes ... a timing issue with the test on windows QTcpServer server; server.setMaxPendingConnections(2); QTcpSocket socket1; QTcpSocket socket2; QTcpSocket socket3; QSignalSpy spy(&server, SIGNAL(newConnection())); QVERIFY(server.listen()); socket1.connectToHost(QHostAddress::LocalHost, server.serverPort()); socket2.connectToHost(QHostAddress::LocalHost, server.serverPort()); socket3.connectToHost(QHostAddress::LocalHost, server.serverPort()); // We must have two and only two connections. First compare waits until // two connections have been made. The second compare makes sure no // more are accepted. Creating connections happens multithreaded so // qWait must be used for that. QTRY_COMPARE(spy.size(), 2); QTest::qWait(100); QCOMPARE(spy.size(), 2); QVERIFY(server.hasPendingConnections()); QVERIFY(server.nextPendingConnection()); QVERIFY(server.hasPendingConnections()); QVERIFY(server.nextPendingConnection()); QVERIFY(!server.hasPendingConnections()); QCOMPARE(server.nextPendingConnection(), (QTcpSocket*)0); QVERIFY(server.waitForNewConnection(5000)); QVERIFY(server.hasPendingConnections()); QVERIFY(server.nextPendingConnection()); } //---------------------------------------------------------------------------------- void tst_QTcpServer::listenError() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) { #ifndef QT_NO_NETWORKPROXY QFETCH_GLOBAL(int, proxyType); if (proxyType == QNetworkProxy::Socks5Proxy) QSKIP("With socks5 we can not make hard requirements on the address or port"); #else // !QT_NO_NETWORKPROXY QSKIP("No proxy support"); #endif //QT_NO_NETWORKPROXY } QTcpServer server; QVERIFY(!server.listen(QHostAddress("1.2.3.4"), 0)); QCOMPARE(server.serverError(), QAbstractSocket::SocketAddressNotAvailableError); QCOMPARE(server.errorString().toLatin1().constData(), "The address is not available"); } class ThreadConnector : public QThread { public: ThreadConnector(const QHostAddress &host, quint16 port) : host(host), port(port) { } ~ThreadConnector() { wait(); } protected: void run() override { sleep(2); QTcpSocket socket; socket.connectToHost(host, port); QEventLoop loop; QTimer::singleShot(5000, &loop, SLOT(quit())); loop.exec(); } private: QHostAddress host; quint16 port; }; //---------------------------------------------------------------------------------- void tst_QTcpServer::waitForConnectionTest() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) { #ifndef QT_NO_NETWORKPROXY QFETCH_GLOBAL(int, proxyType); if (proxyType == QNetworkProxy::Socks5Proxy) QSKIP("Localhost servers don't work well with SOCKS5"); #else // !QT_NO_NETWORKPROXY QSKIP("No proxy support"); #endif // QT_NO_NETWORKPROXY } QTcpSocket findLocalIpSocket; findLocalIpSocket.connectToHost(QtNetworkSettings::imapServerName(), 143); QVERIFY(findLocalIpSocket.waitForConnected(5000)); QTcpServer server; bool timeout = false; QVERIFY(server.listen(findLocalIpSocket.localAddress())); QVERIFY(!server.waitForNewConnection(1000, &timeout)); QCOMPARE(server.serverError(), QAbstractSocket::SocketTimeoutError); QVERIFY(timeout); ThreadConnector connector(findLocalIpSocket.localAddress(), server.serverPort()); connector.start(); QVERIFY(server.waitForNewConnection(3000, &timeout)); QVERIFY(!timeout); } //---------------------------------------------------------------------------------- void tst_QTcpServer::setSocketDescriptor() { QTcpServer server; QVERIFY(!server.setSocketDescriptor(42)); QCOMPARE(server.serverError(), QAbstractSocket::UnsupportedSocketOperationError); //adopting Open C sockets is not supported, neither is adopting externally created RSocket #ifdef Q_OS_WIN // ensure winsock is started WSADATA wsaData; QVERIFY(WSAStartup(MAKEWORD(2,0), &wsaData) == NO_ERROR); #endif SOCKET sock = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); QVERIFY(sock != INVALID_SOCKET); sockaddr_in sin; memset(&sin, 0, sizeof(sockaddr_in)); sin.sin_family = AF_INET; sin.sin_port = 0; sin.sin_addr.s_addr = 0x00000000; QVERIFY(::bind(sock, (sockaddr*)&sin, sizeof(sockaddr_in)) == 0); QVERIFY(::listen(sock, 10) == 0); QVERIFY(server.setSocketDescriptor(sock)); #ifdef Q_OS_WIN WSACleanup(); #endif } //---------------------------------------------------------------------------------- void tst_QTcpServer::listenWhileListening() { QTcpServer server; QVERIFY(server.listen()); QTest::ignoreMessage(QtWarningMsg, "QTcpServer::listen() called when already listening"); QVERIFY(!server.listen()); } //---------------------------------------------------------------------------------- class SeverWithBlockingSockets : public QTcpServer { public: SeverWithBlockingSockets() : ok(false) { } bool ok; protected: void incomingConnection(qintptr socketDescriptor) override { // how a user woulddo it (qabstractsocketengine is not public) unsigned long arg = 0; #if defined(Q_OS_WIN) ok = ::ioctlsocket(socketDescriptor, FIONBIO, &arg) == 0; ::closesocket(socketDescriptor); #else ok = ::ioctl(socketDescriptor, FIONBIO, &arg) == 0; ::close(socketDescriptor); #endif } }; void tst_QTcpServer::addressReusable() { #if !QT_CONFIG(process) QSKIP("No qprocess support", SkipAll); #else QFETCH_GLOBAL(bool, setProxy); if (setProxy) { #ifndef QT_NO_NETWORKPROXY QFETCH_GLOBAL(int, proxyType); if (proxyType == QNetworkProxy::Socks5Proxy) QSKIP("With socks5 this test does not make senans at the momment"); #else // !QT_NO_NETWORKPROXY QSKIP("No proxy support"); #endif // QT_NO_NETWORKPROXY } QTcpServer server; QVERIFY(server.listen(QHostAddress::LocalHost, 0)); quint16 serverPort = server.serverPort(); qDebug() << "Got port" << serverPort; server.close(); // cleanly close QTest::qSleep(10); // The crashingServer process will crash once it gets a connection. QProcess process; QString processExe = crashingServerDir + "/crashingServer"; process.start(processExe, { QString::number(serverPort) }); QVERIFY2(process.waitForStarted(), qPrintable( QString::fromLatin1("Could not start %1: %2").arg(processExe, process.errorString()))); QVERIFY2(process.waitForReadyRead(5000), qPrintable(process.readAllStandardError())); QTcpSocket socket; socket.connectToHost(QHostAddress::LocalHost, serverPort); QVERIFY(socket.waitForConnected(5000)); QVERIFY(process.waitForFinished(30000)); // Give the system some time. QTest::qSleep(10); // listen again QVERIFY2(server.listen(QHostAddress::LocalHost, serverPort), qPrintable(server.errorString())); #endif } void tst_QTcpServer::setNewSocketDescriptorBlocking() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) { #ifndef QT_NO_NETWORKPROXY QFETCH_GLOBAL(int, proxyType); if (proxyType == QNetworkProxy::Socks5Proxy) QSKIP("With socks5 we can not make the socket descripter blocking"); #else // !QT_NO_NETWORKPROXY QSKIP("No proxy support"); #endif // QT_NO_NETWORKPROXY } SeverWithBlockingSockets server; QVERIFY(server.listen()); QTcpSocket socket; socket.connectToHost(QHostAddress::LocalHost, server.serverPort()); QVERIFY(server.waitForNewConnection(5000)); QVERIFY(server.ok); } #ifndef QT_NO_NETWORKPROXY void tst_QTcpServer::invalidProxy_data() { QTest::addColumn("type"); QTest::addColumn("host"); QTest::addColumn("port"); QTest::addColumn("expectedError"); const QString imapIp = QtNetworkSettings::imapServerIp().toString(); const QString httpProxyIp = QtNetworkSettings::httpProxyServerIp().toString(); const QString socksIp = QtNetworkSettings::socksProxyServerIp().toString(); QTest::newRow("ftp-proxy") << int(QNetworkProxy::FtpCachingProxy) << imapIp << 143 << int(QAbstractSocket::UnsupportedSocketOperationError); QTest::newRow("http-proxy") << int(QNetworkProxy::HttpProxy) << httpProxyIp << 3128 << int(QAbstractSocket::UnsupportedSocketOperationError); QTest::newRow("no-such-host") << int(QNetworkProxy::Socks5Proxy) << "invalid.test.qt-project.org" << 1080 << int(QAbstractSocket::ProxyNotFoundError); QTest::newRow("socks5-on-http") << int(QNetworkProxy::Socks5Proxy) << httpProxyIp << 3128 << int(QAbstractSocket::SocketTimeoutError); } void tst_QTcpServer::invalidProxy() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; QFETCH(int, type); QFETCH(QString, host); QFETCH(int, port); QNetworkProxy::ProxyType proxyType = QNetworkProxy::ProxyType(type); QNetworkProxy proxy(proxyType, host, port); QTcpServer server; server.setProxy(proxy); bool listenResult = server.listen(); QVERIFY(!listenResult); QVERIFY(!server.errorString().isEmpty()); // note: the following test is not a hard failure. // Sometimes, error codes change for the better QTEST(int(server.serverError()), "expectedError"); } // copied from tst_qnetworkreply.cpp class MyProxyFactory: public QNetworkProxyFactory { public: int callCount; QList toReturn; QNetworkProxyQuery lastQuery; inline MyProxyFactory() { clear(); } inline void clear() { callCount = 0; toReturn = QList() << QNetworkProxy::DefaultProxy; lastQuery = QNetworkProxyQuery(); } virtual QList queryProxy(const QNetworkProxyQuery &query) override { lastQuery = query; ++callCount; return toReturn; } }; void tst_QTcpServer::proxyFactory_data() { QTest::addColumn >("proxyList"); QTest::addColumn("proxyUsed"); QTest::addColumn("fails"); QTest::addColumn("expectedError"); QList proxyList; // tests that do get to listen proxyList << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::socksProxyServerName(), 1080); QTest::newRow("socks5") << proxyList << proxyList.at(0) << false << int(QAbstractSocket::UnknownSocketError); proxyList.clear(); proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::httpProxyServerName(), 3128) << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::socksProxyServerName(), 1080); QTest::newRow("cachinghttp+socks5") << proxyList << proxyList.at(1) << false << int(QAbstractSocket::UnknownSocketError); #if 0 // ftp not currently supported proxyList.clear(); proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::ftpProxyServerName(), 2121) << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::httpProxyServerName(), 3128) << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::socksProxyServerName(), 1080); QTest::newRow("ftp+cachinghttp+socks5") << proxyList << proxyList.at(2) << false << int(QAbstractSocket::UnknownSocketError); #endif // tests that fail to listen proxyList.clear(); proxyList << QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::httpProxyServerName(), 3128); QTest::newRow("http") << proxyList << proxyList.at(0) << true << int(QAbstractSocket::UnsupportedSocketOperationError); proxyList.clear(); proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::httpProxyServerName(), 3128); QTest::newRow("cachinghttp") << proxyList << QNetworkProxy() << true << int(QAbstractSocket::UnsupportedSocketOperationError); #if 0 // ftp not currently supported proxyList.clear(); proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::ftpProxyServerName(), 2121); QTest::newRow("ftp") << proxyList << QNetworkProxy() << true << int(QAbstractSocket::UnsupportedSocketOperationError); proxyList.clear(); proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::ftpProxyServerName(), 2121) << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::httpProxyServerName(), 3128); QTest::newRow("ftp+cachinghttp") << proxyList << QNetworkProxy() << true << int(QAbstractSocket::UnsupportedSocketOperationError); #endif } void tst_QTcpServer::proxyFactory() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; QFETCH(QList, proxyList); QFETCH(QNetworkProxy, proxyUsed); QFETCH(bool, fails); MyProxyFactory *factory = new MyProxyFactory; factory->toReturn = proxyList; QNetworkProxyFactory::setApplicationProxyFactory(factory); QTcpServer server; bool listenResult = server.listen(); // Verify that the factory was called properly QCOMPARE(factory->callCount, 1); QCOMPARE(factory->lastQuery, QNetworkProxyQuery(0, QString(), QNetworkProxyQuery::TcpServer)); QCOMPARE(listenResult, !fails); QCOMPARE(server.errorString().isEmpty(), !fails); // note: the following test is not a hard failure. // Sometimes, error codes change for the better QTEST(int(server.serverError()), "expectedError"); } #endif // !QT_NO_NETWORKPROXY class Qtbug14268Helper : public QObject { Q_OBJECT public: QByteArray lastDataPeeked; public slots: void newConnection() { QTcpServer* server=static_cast(sender()); QTcpSocket* s=server->nextPendingConnection(); connect(s,SIGNAL(readyRead()),this,SLOT(onServerReadyRead())); } void onServerReadyRead() { QTcpSocket* clientSocket=static_cast(sender()); lastDataPeeked = clientSocket->peek(128*1024).toHex(); QTestEventLoop::instance().exitLoop(); } }; // there is a similar test inside tst_qtcpsocket that uses the waitFor* functions instead void tst_QTcpServer::qtbug14268_peek() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; QTcpServer server; server.listen(); Qtbug14268Helper helper; QObject::connect(&server, SIGNAL(newConnection()), &helper, SLOT(newConnection())); QTcpSocket client; client.connectToHost(QHostAddress::LocalHost, server.serverPort()); QVERIFY(client.waitForConnected(2000)); client.write("abc\n"); QTestEventLoop::instance().enterLoop(5); QVERIFY(!QTestEventLoop::instance().timeout()); QCOMPARE(helper.lastDataPeeked, QByteArray("6162630a")); client.write("def\n"); QTestEventLoop::instance().enterLoop(5); QVERIFY(!QTestEventLoop::instance().timeout()); QCOMPARE(helper.lastDataPeeked, QByteArray("6162630a6465660a")); client.write("ghi\n"); QTestEventLoop::instance().enterLoop(5); QVERIFY(!QTestEventLoop::instance().timeout()); QCOMPARE(helper.lastDataPeeked, QByteArray("6162630a6465660a6768690a")); } void tst_QTcpServer::serverAddress_data() { QTest::addColumn("listenAddress"); QTest::addColumn("serverAddress"); if (QtNetworkSettings::hasIPv6()) QTest::newRow("Any") << QHostAddress(QHostAddress::Any) << QHostAddress(QHostAddress::Any); else QTest::newRow("Any") << QHostAddress(QHostAddress::Any) << QHostAddress(QHostAddress::AnyIPv4); QTest::newRow("AnyIPv4") << QHostAddress(QHostAddress::AnyIPv4) << QHostAddress(QHostAddress::AnyIPv4); if (QtNetworkSettings::hasIPv6()) QTest::newRow("AnyIPv6") << QHostAddress(QHostAddress::AnyIPv6) << QHostAddress(QHostAddress::AnyIPv6); foreach (const QNetworkInterface &iface, QNetworkInterface::allInterfaces()) { if ((iface.flags() & QNetworkInterface::IsUp) == 0) continue; foreach (const QNetworkAddressEntry &entry, iface.addressEntries()) { QTest::newRow(qPrintable(entry.ip().toString())) << entry.ip() << entry.ip(); } } } void tst_QTcpServer::serverAddress() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; QFETCH(QHostAddress, listenAddress); QFETCH(QHostAddress, serverAddress); QTcpServer server; if (shouldSkipIpv6TestsForBrokenGetsockopt() && listenAddress == QHostAddress(QHostAddress::Any)) { QSKIP("Syscalls needed for ipv6 sockoptions missing functionality"); } // TODO: why does this QSKIP? if (!server.listen(listenAddress)) QSKIP(qPrintable(server.errorString())); QCOMPARE(server.serverAddress(), serverAddress); } // on OS X, calling listen() multiple times would succeed each time, which is // most definitely not wanted. void tst_QTcpServer::qtbug6305() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; QFETCH(QHostAddress, listenAddress); QTcpServer server; QVERIFY2(server.listen(listenAddress), qPrintable(server.errorString())); QTcpServer server2; QVERIFY(!server2.listen(listenAddress, server.serverPort())); // second listen should fail } void tst_QTcpServer::linkLocal() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; QList addresses; QSet scopes; QHostAddress localMaskv4("169.254.0.0"); QHostAddress localMaskv6("fe80::"); foreach (const QNetworkInterface& iface, QNetworkInterface::allInterfaces()) { //Windows preallocates link local addresses to interfaces that are down. //These may or may not work depending on network driver (they do not work for the Bluetooth PAN driver) if (iface.flags() & QNetworkInterface::IsUp) { #if defined(Q_OS_WIN) // Do not connect to the Teredo Tunneling interface on Windows Xp. if (iface.humanReadableName() == QString("Teredo Tunneling Pseudo-Interface")) continue; #elif defined(Q_OS_DARWIN) // Do not add "utun" interfaces on macOS: nothing ever gets received // (we don't know why) if (iface.name().startsWith("utun")) continue; // Do not use the iBridge interfae if (iface.hardwareAddress() == "AC:DE:48:00:11:22") continue; // Do no use the Apple Wireless Direct Link interfaces if (iface.name().startsWith("awdl")) continue; #endif foreach (QNetworkAddressEntry addressEntry, iface.addressEntries()) { QHostAddress addr = addressEntry.ip(); if (addr.isInSubnet(localMaskv4, 16)) { addresses << addr; qDebug() << addr; } else if (!addr.scopeId().isEmpty() && addr.isInSubnet(localMaskv6, 64)) { scopes << addr.scopeId(); addresses << addr; qDebug() << addr; } } } } if (addresses.isEmpty()) QSKIP("no link local addresses"); QList servers; quint16 port = 0; foreach (const QHostAddress& addr, addresses) { QTcpServer *server = new QTcpServer; QVERIFY(server->listen(addr, port)); port = server->serverPort(); //listen to same port on different interfaces servers << server; } QList clients; foreach (const QHostAddress& addr, addresses) { //unbound socket QTcpSocket *socket = new QTcpSocket; socket->connectToHost(addr, port); QVERIFY(socket->waitForConnected(5000)); clients << socket; //bound socket socket = new QTcpSocket; QVERIFY(socket->bind(addr)); socket->connectToHost(addr, port); QVERIFY(socket->waitForConnected(5000)); clients << socket; } //each server should have two connections foreach (QTcpServer* server, servers) { //qDebug() << "checking for connections" << server->serverAddress() << ":" << server->serverPort(); QVERIFY(server->waitForNewConnection(5000)); QTcpSocket* remote = server->nextPendingConnection(); QVERIFY(remote != nullptr); remote->close(); delete remote; if (!server->hasPendingConnections()) QVERIFY(server->waitForNewConnection(5000)); remote = server->nextPendingConnection(); QVERIFY(remote != nullptr); remote->close(); delete remote; QVERIFY(!server->hasPendingConnections()); } //Connecting to the same address with different scope should normally fail //However it will pass if there are two interfaces connected to the same physical network, //e.g. connected via wired and wireless interfaces, or two wired NICs. //which is a reasonably common case. //So this is not auto tested. qDeleteAll(clients); qDeleteAll(servers); } void tst_QTcpServer::eagainBlockingAccept() { QTcpServer server; server.listen(QHostAddress::LocalHost, 7896); // Receiving a new connection causes TemporaryError, but shouldn't pause accepting. QTcpSocket s; s.connectToHost(QHostAddress::LocalHost, 7896); QSignalSpy spy(&server, SIGNAL(newConnection())); QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 1, 500); s.close(); // To test try again, should connect just fine. s.connectToHost(QHostAddress::LocalHost, 7896); QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 2, 500); s.close(); server.close(); } class NonListeningTcpServer : public QTcpServer { public: void addSocketFromOutside(QTcpSocket* s) { addPendingConnection(s); } }; void tst_QTcpServer::canAccessPendingConnectionsWhileNotListening() { NonListeningTcpServer server; QTcpSocket socket; server.addSocketFromOutside(&socket); QCOMPARE(&socket, server.nextPendingConnection()); } void tst_QTcpServer::pauseAccepting() { QTcpServer server; QSignalSpy spy(&server, &QTcpServer::newConnection); QVERIFY(server.listen()); QFETCH_GLOBAL(bool, setProxy); const auto address = QHostAddress(setProxy ? QtNetworkSettings::socksProxyServerIp() : QHostAddress::LocalHost); const int NumSockets = 6; QTcpSocket sockets[NumSockets]; sockets[0].connectToHost(address, server.serverPort()); QVERIFY(spy.wait()); QCOMPARE(spy.size(), 1); server.pauseAccepting(); for (int i = 1; i < NumSockets; ++i) sockets[i].connectToHost(address, server.serverPort()); QVERIFY(!spy.wait(400)); QCOMPARE(spy.size(), 1); server.resumeAccepting(); if (setProxy) { QEXPECT_FAIL("", "The socks proxy does weird things after accepting the first connection", Abort); } QVERIFY(spy.wait()); QCOMPARE(spy.size(), 6); } // Only adds the socket to the pending connections list after emitNextSocket is // called. It's very artificial, but it allows us to test the behavior of // the pendingConnectionAvailable signal when a server doesn't add the socket // during the incomingConnection virtual function. class DerivedServer : public QTcpServer { public: explicit DerivedServer(QObject *parent = nullptr) : QTcpServer(parent) { } void emitNextSocket() { if (m_socketDescriptors.isEmpty()) return; auto *socket = new QTcpSocket(this); socket->setSocketDescriptor(m_socketDescriptors.back()); m_socketDescriptors.pop_back(); addPendingConnection(socket); } protected: void incomingConnection(qintptr socketDescriptor) override { m_socketDescriptors.push_back(socketDescriptor); } private: QList m_socketDescriptors; }; void tst_QTcpServer::pendingConnectionAvailable_data() { QTest::addColumn("useDerivedServer"); QTest::newRow("QTcpServer") << false; QTest::newRow("DerivedServer") << true; } void tst_QTcpServer::pendingConnectionAvailable() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) QSKIP("This feature does not differentiate with or without proxy"); QFETCH(bool, useDerivedServer); QTcpServer *server = useDerivedServer ? new DerivedServer : new QTcpServer; if (!server->listen(QHostAddress::LocalHost, 0)) { qWarning() << "Server failed to listen:" << server->errorString(); QSKIP("Server failed to listen"); } QSignalSpy newConnectionSpy(server, &QTcpServer::newConnection); QSignalSpy pendingConnectionSpy(server, &QTcpServer::pendingConnectionAvailable); QTcpSocket socket; socket.connectToHost(QHostAddress::LocalHost, server->serverPort()); QVERIFY(newConnectionSpy.wait()); QVERIFY(socket.waitForConnected()); QCOMPARE(socket.state(), QTcpSocket::ConnectedState); int expectedPendingConnections = useDerivedServer ? 0 : 1; QCOMPARE(pendingConnectionSpy.size(), expectedPendingConnections); if (useDerivedServer) static_cast(server)->emitNextSocket(); QCOMPARE(pendingConnectionSpy.size(), 1); } QTEST_MAIN(tst_QTcpServer) #include "tst_qtcpserver.moc"