c6b9c6e5f2
The sources were already added conditionally in the project file since
179fe5981f
.
Change-Id: I0baaec2e772f3e596d311c1973b9745aa2b80423
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@qt.io>
1057 lines
33 KiB
C++
1057 lines
33 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2016 The Qt Company Ltd.
|
|
** Contact: https://www.qt.io/licensing/
|
|
**
|
|
** This file is part of the test suite of the Qt Toolkit.
|
|
**
|
|
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
|
|
** Commercial License Usage
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
** accordance with the commercial license agreement provided with the
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
|
**
|
|
** GNU 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.
|
|
**
|
|
** $QT_END_LICENSE$
|
|
**
|
|
****************************************************************************/
|
|
|
|
#include <qglobal.h>
|
|
// To prevent windows system header files from re-defining min/max
|
|
#define NOMINMAX 1
|
|
#if defined(_WIN32)
|
|
#include <winsock2.h>
|
|
#else
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#define SOCKET int
|
|
#define INVALID_SOCKET -1
|
|
#endif
|
|
|
|
#include <QtTest/QtTest>
|
|
|
|
#ifndef Q_OS_WIN
|
|
#include <unistd.h>
|
|
#include <sys/ioctl.h>
|
|
#endif
|
|
|
|
#include <qcoreapplication.h>
|
|
#include <qtcpsocket.h>
|
|
#include <qtcpserver.h>
|
|
#include <qhostaddress.h>
|
|
#if QT_CONFIG(process)
|
|
# include <qprocess.h>
|
|
#endif
|
|
#include <qstringlist.h>
|
|
#include <qplatformdefs.h>
|
|
#include <qhostinfo.h>
|
|
|
|
#include <QNetworkProxy>
|
|
|
|
#include <QNetworkSession>
|
|
#include <QNetworkConfiguration>
|
|
#include <QNetworkConfigurationManager>
|
|
#include "../../../network-settings.h"
|
|
|
|
#if defined(Q_OS_LINUX)
|
|
#define SHOULD_CHECK_SYSCALL_SUPPORT
|
|
#include <netinet/in.h>
|
|
#include <sys/socket.h>
|
|
#include <errno.h>
|
|
#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();
|
|
#ifndef Q_OS_WINRT
|
|
void setSocketDescriptor();
|
|
#endif
|
|
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();
|
|
|
|
private:
|
|
bool shouldSkipIpv6TestsForBrokenGetsockopt();
|
|
#ifdef SHOULD_CHECK_SYSCALL_SUPPORT
|
|
bool ipv6GetsockoptionMissing(int level, int optname);
|
|
#endif
|
|
|
|
#ifndef QT_NO_BEARERMANAGEMENT
|
|
QNetworkSession *networkSession;
|
|
#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<bool>("setProxy");
|
|
QTest::addColumn<int>("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()
|
|
{
|
|
if (!QtNetworkSettings::verifyTestNetworkSettings())
|
|
QSKIP("No network test server available");
|
|
#ifndef QT_NO_BEARERMANAGEMENT
|
|
QNetworkConfigurationManager man;
|
|
networkSession = new QNetworkSession(man.defaultConfiguration(), this);
|
|
networkSession->open();
|
|
QVERIFY(networkSession->waitForOpened());
|
|
#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::serverName(), 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.count(), 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<QHostAddress>("bindAddress");
|
|
QTest::addColumn<bool>("v4ok");
|
|
QTest::addColumn<bool>("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;
|
|
|
|
QVERIFY(server.listen());
|
|
|
|
socket1.connectToHost(QHostAddress::LocalHost, server.serverPort());
|
|
socket2.connectToHost(QHostAddress::LocalHost, server.serverPort());
|
|
socket3.connectToHost(QHostAddress::LocalHost, server.serverPort());
|
|
|
|
QVERIFY(server.waitForNewConnection(5000));
|
|
|
|
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()
|
|
{
|
|
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::serverName(), 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);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------
|
|
#ifndef Q_OS_WINRT
|
|
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
|
|
}
|
|
#endif // !Q_OS_WINRT
|
|
|
|
//----------------------------------------------------------------------------------
|
|
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:
|
|
#ifndef Q_OS_WINRT
|
|
void incomingConnection(qintptr socketDescriptor)
|
|
{
|
|
// 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
|
|
}
|
|
#endif // !Q_OS_WINRT
|
|
};
|
|
|
|
void tst_QTcpServer::addressReusable()
|
|
{
|
|
#if !QT_CONFIG(process)
|
|
QSKIP("No qprocess support", SkipAll);
|
|
#else
|
|
#ifdef Q_OS_LINUX
|
|
QSKIP("The addressReusable test is unstable on Linux. See QTBUG-39985.");
|
|
#endif
|
|
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
|
|
}
|
|
// The crashingServer process will crash once it gets a connection.
|
|
QProcess process;
|
|
QString processExe = crashingServerDir + "/crashingServer";
|
|
process.start(processExe);
|
|
QVERIFY2(process.waitForStarted(), qPrintable(
|
|
QString::fromLatin1("Could not start %1: %2").arg(processExe, process.errorString())));
|
|
QVERIFY(process.waitForReadyRead(5000));
|
|
|
|
QTcpSocket socket;
|
|
socket.connectToHost(QHostAddress::LocalHost, 49199);
|
|
QVERIFY(socket.waitForConnected(5000));
|
|
|
|
QVERIFY(process.waitForFinished(30000));
|
|
|
|
// Give the system some time.
|
|
QTest::qSleep(10);
|
|
|
|
QTcpServer server;
|
|
QVERIFY(server.listen(QHostAddress::LocalHost, 49199));
|
|
#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<int>("type");
|
|
QTest::addColumn<QString>("host");
|
|
QTest::addColumn<int>("port");
|
|
QTest::addColumn<int>("expectedError");
|
|
|
|
QString fluke = QHostInfo::fromName(QtNetworkSettings::serverName()).addresses().first().toString();
|
|
QTest::newRow("ftp-proxy") << int(QNetworkProxy::FtpCachingProxy) << fluke << 143
|
|
<< int(QAbstractSocket::UnsupportedSocketOperationError);
|
|
QTest::newRow("http-proxy") << int(QNetworkProxy::HttpProxy) << fluke << 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) << fluke << 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<QNetworkProxy> toReturn;
|
|
QNetworkProxyQuery lastQuery;
|
|
inline MyProxyFactory() { clear(); }
|
|
|
|
inline void clear()
|
|
{
|
|
callCount = 0;
|
|
toReturn = QList<QNetworkProxy>() << QNetworkProxy::DefaultProxy;
|
|
lastQuery = QNetworkProxyQuery();
|
|
}
|
|
|
|
virtual QList<QNetworkProxy> queryProxy(const QNetworkProxyQuery &query)
|
|
{
|
|
lastQuery = query;
|
|
++callCount;
|
|
return toReturn;
|
|
}
|
|
};
|
|
|
|
void tst_QTcpServer::proxyFactory_data()
|
|
{
|
|
QTest::addColumn<QList<QNetworkProxy> >("proxyList");
|
|
QTest::addColumn<QNetworkProxy>("proxyUsed");
|
|
QTest::addColumn<bool>("fails");
|
|
QTest::addColumn<int>("expectedError");
|
|
|
|
QList<QNetworkProxy> proxyList;
|
|
|
|
// tests that do get to listen
|
|
|
|
proxyList << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080);
|
|
QTest::newRow("socks5")
|
|
<< proxyList << proxyList.at(0)
|
|
<< false << int(QAbstractSocket::UnknownSocketError);
|
|
|
|
proxyList.clear();
|
|
proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3128)
|
|
<< QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080);
|
|
QTest::newRow("cachinghttp+socks5")
|
|
<< proxyList << proxyList.at(1)
|
|
<< false << int(QAbstractSocket::UnknownSocketError);
|
|
|
|
proxyList.clear();
|
|
proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121)
|
|
<< QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3128)
|
|
<< QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080);
|
|
QTest::newRow("ftp+cachinghttp+socks5")
|
|
<< proxyList << proxyList.at(2)
|
|
<< false << int(QAbstractSocket::UnknownSocketError);
|
|
|
|
// tests that fail to listen
|
|
proxyList.clear();
|
|
proxyList << QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::serverName(), 3128);
|
|
QTest::newRow("http")
|
|
<< proxyList << proxyList.at(0)
|
|
<< true << int(QAbstractSocket::UnsupportedSocketOperationError);
|
|
|
|
proxyList.clear();
|
|
proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3128);
|
|
QTest::newRow("cachinghttp")
|
|
<< proxyList << QNetworkProxy()
|
|
<< true << int(QAbstractSocket::UnsupportedSocketOperationError);
|
|
|
|
proxyList.clear();
|
|
proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121);
|
|
QTest::newRow("ftp")
|
|
<< proxyList << QNetworkProxy()
|
|
<< true << int(QAbstractSocket::UnsupportedSocketOperationError);
|
|
|
|
proxyList.clear();
|
|
proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121)
|
|
<< QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3128);
|
|
QTest::newRow("ftp+cachinghttp")
|
|
<< proxyList << QNetworkProxy()
|
|
<< true << int(QAbstractSocket::UnsupportedSocketOperationError);
|
|
}
|
|
|
|
void tst_QTcpServer::proxyFactory()
|
|
{
|
|
QFETCH_GLOBAL(bool, setProxy);
|
|
if (setProxy)
|
|
return;
|
|
|
|
QFETCH(QList<QNetworkProxy>, 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<QTcpServer*>(sender());
|
|
QTcpSocket* s=server->nextPendingConnection();
|
|
connect(s,SIGNAL(readyRead()),this,SLOT(onServerReadyRead()));
|
|
}
|
|
void onServerReadyRead() {
|
|
QTcpSocket* clientSocket=static_cast<QTcpSocket*>(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<QHostAddress>("listenAddress");
|
|
QTest::addColumn<QHostAddress>("serverAddress");
|
|
#ifdef Q_OS_WIN
|
|
if (QSysInfo::windowsVersion() < QSysInfo::WV_6_0)
|
|
QTest::newRow("Any") << QHostAddress(QHostAddress::Any) << QHostAddress(QHostAddress::AnyIPv4); //windows XP doesn't support dual stack sockets
|
|
else
|
|
#endif
|
|
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 <QHostAddress> addresses;
|
|
QSet <QString> 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;
|
|
#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<QTcpServer*> 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<QTcpSocket*> 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.count(), 1, 500);
|
|
s.close();
|
|
|
|
// To test try again, should connect just fine.
|
|
s.connectToHost(QHostAddress::LocalHost, 7896);
|
|
QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 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());
|
|
}
|
|
|
|
QTEST_MAIN(tst_QTcpServer)
|
|
#include "tst_qtcpserver.moc"
|