summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/network/kernel/qtnetworkglobal_p.h11
-rw-r--r--src/network/socket/qlocalserver.cpp15
-rw-r--r--src/network/socket/qlocalserver.h3
-rw-r--r--src/network/socket/qlocalserver_unix.cpp80
-rw-r--r--src/network/socket/qlocalsocket.cpp49
-rw-r--r--src/network/socket/qlocalsocket.h12
-rw-r--r--src/network/socket/qlocalsocket_p.h8
-rw-r--r--src/network/socket/qlocalsocket_unix.cpp122
-rw-r--r--tests/auto/network/socket/qlocalsocket/tst_qlocalsocket.cpp136
9 files changed, 380 insertions, 56 deletions
diff --git a/src/network/kernel/qtnetworkglobal_p.h b/src/network/kernel/qtnetworkglobal_p.h
index 0bd30eaf02..960a192cde 100644
--- a/src/network/kernel/qtnetworkglobal_p.h
+++ b/src/network/kernel/qtnetworkglobal_p.h
@@ -57,4 +57,15 @@
#define Q_NETWORK_PRIVATE_EXPORT Q_NETWORK_EXPORT
+QT_BEGIN_NAMESPACE
+
+enum {
+#ifdef Q_OS_LINUX
+ PlatformSupportsAbstractNamespace = true
+#else
+ PlatformSupportsAbstractNamespace = false
+#endif
+};
+
+QT_END_NAMESPACE
#endif // QTNETWORKGLOBAL_P_H
diff --git a/src/network/socket/qlocalserver.cpp b/src/network/socket/qlocalserver.cpp
index 16dc907149..6517d6dc6a 100644
--- a/src/network/socket/qlocalserver.cpp
+++ b/src/network/socket/qlocalserver.cpp
@@ -90,6 +90,8 @@ QT_BEGIN_NAMESPACE
socket. This changes the access permissions on platforms (Linux, Windows)
that support access permissions on the socket. Both GroupAccess and OtherAccess
may vary slightly in meanings depending on the platform.
+ On Linux and Android it is possible to use sockets with abstract addresses;
+ socket permissions have no meaning for such sockets.
\value NoOptions No access restrictions have been set.
\value UserAccessOption
@@ -102,6 +104,10 @@ QT_BEGIN_NAMESPACE
Access is available to everyone on Windows.
\value WorldAccessOption
No access restrictions.
+ \value AbstractNamespaceOption
+ The listening socket will be created in the abstract namespace. This flag is specific to Linux.
+ In case of other platforms, for the sake of code portability, this flag is equivalent
+ to WorldAccessOption.
\sa socketOptions
*/
@@ -161,6 +167,11 @@ QLocalServer::~QLocalServer()
in the Windows documentation). OtherAccessOption refers to
the well known "Everyone" group.
+ On Linux platforms it is possible to create a socket in the abstract
+ namespace, which is independent of the filesystem. Using this kind
+ of socket implies ignoring permission options. On other platforms
+ AbstractNamespaceOption is equivalent to WorldAccessOption.
+
By default none of the flags are set, access permissions
are the platform default.
@@ -365,7 +376,9 @@ bool QLocalServer::listen(const QString &name)
serverName(), fullServerName() may return a string with
a name if this option is supported by the platform;
- otherwise, they return an empty QString.
+ otherwise, they return an empty QString. In particular, the addresses
+ of sockets in the abstract namespace supported by Linux will
+ not yield useful names if they contain unprintable characters.
\sa isListening(), close()
*/
diff --git a/src/network/socket/qlocalserver.h b/src/network/socket/qlocalserver.h
index 22b99d3e67..5da07653cb 100644
--- a/src/network/socket/qlocalserver.h
+++ b/src/network/socket/qlocalserver.h
@@ -67,7 +67,8 @@ public:
UserAccessOption = 0x01,
GroupAccessOption = 0x2,
OtherAccessOption = 0x4,
- WorldAccessOption = 0x7
+ WorldAccessOption = 0x7,
+ AbstractNamespaceOption = 0x8
};
Q_FLAG(SocketOption)
Q_DECLARE_FLAGS(SocketOptions, SocketOption)
diff --git a/src/network/socket/qlocalserver_unix.cpp b/src/network/socket/qlocalserver_unix.cpp
index 45fdc6dd74..1695332d55 100644
--- a/src/network/socket/qlocalserver_unix.cpp
+++ b/src/network/socket/qlocalserver_unix.cpp
@@ -44,6 +44,7 @@
#include "qnet_unix_p.h"
#include "qtemporarydir.h"
+#include <stddef.h>
#include <sys/socket.h>
#include <sys/un.h>
@@ -57,6 +58,23 @@
QT_BEGIN_NAMESPACE
+namespace {
+QLocalServer::SocketOptions optionsForPlatform(QLocalServer::SocketOptions srcOptions)
+{
+ // For OS that does not support abstract namespace the AbstractNamespaceOption
+ // means that we go for WorldAccessOption - as it is the closest option in
+ // regards of access rights. In Linux/Android case we clean-up the access rights.
+
+ if (srcOptions.testFlag(QLocalServer::AbstractNamespaceOption)) {
+ if (PlatformSupportsAbstractNamespace)
+ return QLocalServer::AbstractNamespaceOption;
+ else
+ return QLocalServer::WorldAccessOption;
+ }
+ return srcOptions;
+}
+}
+
void QLocalServerPrivate::init()
{
}
@@ -80,8 +98,12 @@ bool QLocalServerPrivate::listen(const QString &requestedServerName)
{
Q_Q(QLocalServer);
+ // socket options adjusted for current platform
+ auto options = optionsForPlatform(socketOptions.value());
+
// determine the full server path
- if (requestedServerName.startsWith(QLatin1Char('/'))) {
+ if (options.testFlag(QLocalServer::AbstractNamespaceOption)
+ || requestedServerName.startsWith(QLatin1Char('/'))) {
fullServerName = requestedServerName;
} else {
fullServerName = QDir::cleanPath(QDir::tempPath());
@@ -93,8 +115,6 @@ bool QLocalServerPrivate::listen(const QString &requestedServerName)
const QByteArray encodedFullServerName = QFile::encodeName(fullServerName);
QScopedPointer<QTemporaryDir> tempDir;
- // Check any of the flags
- const auto options = socketOptions.value();
if (options & QLocalServer::WorldAccessOption) {
QFileInfo serverNameFileInfo(fullServerName);
tempDir.reset(new QTemporaryDir(serverNameFileInfo.absolutePath() + QLatin1Char('/')));
@@ -115,15 +135,28 @@ bool QLocalServerPrivate::listen(const QString &requestedServerName)
// Construct the unix address
struct ::sockaddr_un addr;
+
addr.sun_family = PF_UNIX;
- if (sizeof(addr.sun_path) < (uint)encodedFullServerName.size() + 1) {
+ ::memset(addr.sun_path, 0, sizeof(addr.sun_path));
+
+ // for abstract namespace add 2 to length, to take into account trailing AND leading null
+ constexpr unsigned int extraCharacters = PlatformSupportsAbstractNamespace ? 2 : 1;
+
+ if (sizeof(addr.sun_path) < static_cast<size_t>(encodedFullServerName.size() + extraCharacters)) {
setError(QLatin1String("QLocalServer::listen"));
closeServer();
return false;
}
- if (options & QLocalServer::WorldAccessOption) {
- if (sizeof(addr.sun_path) < (uint)encodedTempPath.size() + 1) {
+ QT_SOCKLEN_T addrSize = sizeof(::sockaddr_un);
+ if (options.testFlag(QLocalServer::AbstractNamespaceOption)) {
+ // Abstract socket address is distinguished by the fact
+ // that sun_path[0] is a null byte ('\0')
+ ::memcpy(addr.sun_path + 1, encodedFullServerName.constData(),
+ encodedFullServerName.size() + 1);
+ addrSize = offsetof(::sockaddr_un, sun_path) + encodedFullServerName.size() + 1;
+ } else if (options & QLocalServer::WorldAccessOption) {
+ if (sizeof(addr.sun_path) < static_cast<size_t>(encodedTempPath.size() + 1)) {
setError(QLatin1String("QLocalServer::listen"));
closeServer();
return false;
@@ -136,7 +169,7 @@ bool QLocalServerPrivate::listen(const QString &requestedServerName)
}
// bind
- if (-1 == QT_SOCKET_BIND(listenSocket, (sockaddr *)&addr, sizeof(sockaddr_un))) {
+ if (-1 == QT_SOCKET_BIND(listenSocket, (sockaddr *)&addr, addrSize)) {
setError(QLatin1String("QLocalServer::listen"));
// if address is in use already, just close the socket, but do not delete the file
if (errno == EADDRINUSE)
@@ -152,9 +185,6 @@ bool QLocalServerPrivate::listen(const QString &requestedServerName)
if (-1 == qt_safe_listen(listenSocket, 50)) {
setError(QLatin1String("QLocalServer::listen"));
closeServer();
- listenSocket = -1;
- if (error != QAbstractSocket::AddressInUseError)
- QFile::remove(fullServerName);
return false;
}
@@ -202,28 +232,17 @@ bool QLocalServerPrivate::listen(qintptr socketDescriptor)
::fcntl(listenSocket, F_SETFD, FD_CLOEXEC);
::fcntl(listenSocket, F_SETFL, ::fcntl(listenSocket, F_GETFL) | O_NONBLOCK);
-#ifdef Q_OS_LINUX
+ bool abstractAddress = false;
struct ::sockaddr_un addr;
QT_SOCKLEN_T len = sizeof(addr);
memset(&addr, 0, sizeof(addr));
- if (0 == ::getsockname(listenSocket, (sockaddr *)&addr, &len)) {
- // check for absract sockets
- if (addr.sun_family == PF_UNIX && addr.sun_path[0] == 0) {
- addr.sun_path[0] = '@';
- }
- QString name = QString::fromLatin1(addr.sun_path);
- if (!name.isEmpty()) {
- fullServerName = name;
- serverName = fullServerName.mid(fullServerName.lastIndexOf(QLatin1Char('/')) + 1);
- if (serverName.isEmpty()) {
- serverName = fullServerName;
- }
+ if (::getsockname(socketDescriptor, (sockaddr *)&addr, &len) == 0) {
+ if (QLocalSocketPrivate::parseSockaddr(addr, len, fullServerName, serverName,
+ abstractAddress)) {
+ QLocalServer::SocketOptions options = socketOptions.value();
+ socketOptions = options.setFlag(QLocalServer::AbstractNamespaceOption, abstractAddress);
}
}
-#else
- serverName.clear();
- fullServerName.clear();
-#endif
Q_ASSERT(!socketNotifier);
socketNotifier = new QSocketNotifier(listenSocket,
@@ -251,8 +270,13 @@ void QLocalServerPrivate::closeServer()
QT_CLOSE(listenSocket);
listenSocket = -1;
- if (!fullServerName.isEmpty())
+ if (!fullServerName.isEmpty()
+ && !optionsForPlatform(socketOptions).testFlag(QLocalServer::AbstractNamespaceOption)) {
QFile::remove(fullServerName);
+ }
+
+ serverName.clear();
+ fullServerName.clear();
}
/*!
diff --git a/src/network/socket/qlocalsocket.cpp b/src/network/socket/qlocalsocket.cpp
index 3ee98b85c8..39c981c8d4 100644
--- a/src/network/socket/qlocalsocket.cpp
+++ b/src/network/socket/qlocalsocket.cpp
@@ -66,6 +66,21 @@ QT_BEGIN_NAMESPACE
*/
/*!
+ \enum QLocalSocket::SocketOption
+ \since 6.2
+ This enum describes the possible options that can be used to connect to
+ a server. Currently, on Linux and Android it is used for specifying
+ connection to a server listening to a socket bound to an abstract address.
+
+ \value NoOptions No options have been set.
+ \value AbstractNamespaceOption
+ The socket will try to connect to an abstract address. This flag is specific
+ to Linux and Android. On other platforms is ignored.
+
+ \sa socketOptions
+*/
+
+/*!
\fn void QLocalSocket::connectToServer(OpenMode openMode)
\since 5.1
@@ -439,6 +454,40 @@ QString QLocalSocket::serverName() const
}
/*!
+ Returns the socket options as specified by setSocketOptions(),
+
+ \sa connectToServer()
+
+ */
+QLocalSocket::SocketOptions QLocalSocket::socketOptions() const
+{
+ Q_D(const QLocalSocket);
+ return d->socketOptions;
+}
+
+/*!
+ \since 6.2
+
+ Set the socket \a options of the connection.
+
+*/
+void QLocalSocket::setSocketOptions(QLocalSocket::SocketOptions option)
+{
+ Q_D(QLocalSocket);
+ if (d->state != UnconnectedState) {
+ qWarning("QLocalSocket::setSocketOptions() called while not in unconnected state");
+ return;
+ }
+ d->socketOptions = option;
+}
+
+QBindable<QLocalSocket::SocketOptions> QLocalSocket::bindableSocketOptions()
+{
+ Q_D(QLocalSocket);
+ return &d->socketOptions;
+}
+
+/*!
Returns the server path that the socket is connected to.
\note The return value of this function is platform specific.
diff --git a/src/network/socket/qlocalsocket.h b/src/network/socket/qlocalsocket.h
index 22763cb339..817990a45c 100644
--- a/src/network/socket/qlocalsocket.h
+++ b/src/network/socket/qlocalsocket.h
@@ -54,6 +54,7 @@ class Q_NETWORK_EXPORT QLocalSocket : public QIODevice
{
Q_OBJECT
Q_DECLARE_PRIVATE(QLocalSocket)
+ Q_PROPERTY(SocketOptions socketOptions READ socketOptions WRITE setSocketOptions BINDABLE bindableSocketOptions)
public:
enum LocalSocketError
@@ -79,6 +80,13 @@ public:
ClosingState = QAbstractSocket::ClosingState
};
+ enum SocketOption {
+ NoOptions = 0x00,
+ AbstractNamespaceOption = 0x01
+ };
+ Q_DECLARE_FLAGS(SocketOptions, SocketOption)
+ Q_FLAG(SocketOptions)
+
QLocalSocket(QObject *parent = nullptr);
~QLocalSocket();
@@ -108,6 +116,10 @@ public:
OpenMode openMode = ReadWrite);
qintptr socketDescriptor() const;
+ void setSocketOptions(SocketOptions option);
+ SocketOptions socketOptions() const;
+ QBindable<SocketOptions> bindableSocketOptions();
+
LocalSocketState state() const;
bool waitForBytesWritten(int msecs = 30000) override;
bool waitForConnected(int msecs = 30000);
diff --git a/src/network/socket/qlocalsocket_p.h b/src/network/socket/qlocalsocket_p.h
index e68cc72f9d..a80c09b517 100644
--- a/src/network/socket/qlocalsocket_p.h
+++ b/src/network/socket/qlocalsocket_p.h
@@ -73,9 +73,12 @@ QT_REQUIRE_CONFIG(localserver);
# include <errno.h>
#endif
+struct sockaddr_un;
+
QT_BEGIN_NAMESPACE
#if !defined(Q_OS_WIN) || defined(QT_LOCALSOCKET_TCP)
+
class QLocalUnixSocket : public QTcpSocket
{
@@ -146,6 +149,9 @@ public:
void _q_connectToSocket();
void _q_abortConnectionAttempt();
void cancelDelayedConnect();
+ void describeSocket(qintptr socketDescriptor);
+ static bool parseSockaddr(const sockaddr_un &addr, uint len,
+ QString &fullServerName, QString &serverName, bool &abstractNamespace);
QSocketNotifier *delayConnect;
QTimer *connectTimer;
QString connectingName;
@@ -155,6 +161,8 @@ public:
QLocalSocket::LocalSocketState state;
QString serverName;
QString fullServerName;
+
+ Q_OBJECT_BINDABLE_PROPERTY(QLocalSocketPrivate, QLocalSocket::SocketOptions, socketOptions)
};
QT_END_NAMESPACE
diff --git a/src/network/socket/qlocalsocket_unix.cpp b/src/network/socket/qlocalsocket_unix.cpp
index 6fd17a6213..5e050ad323 100644
--- a/src/network/socket/qlocalsocket_unix.cpp
+++ b/src/network/socket/qlocalsocket_unix.cpp
@@ -58,13 +58,38 @@
#define QT_CONNECT_TIMEOUT 30000
+
QT_BEGIN_NAMESPACE
+namespace {
+// determine the full server path
+static QString pathNameForConnection(const QString &connectingName,
+ QLocalSocket::SocketOptions options)
+{
+ if (options.testFlag(QLocalSocket::AbstractNamespaceOption)
+ || connectingName.startsWith(QLatin1Char('/'))) {
+ return connectingName;
+ }
+
+ return QDir::tempPath() + QLatin1Char('/') + connectingName;
+}
+
+static QLocalSocket::SocketOptions optionsForPlatform(QLocalSocket::SocketOptions srcOptions)
+{
+ // For OS that does not support abstract namespace the AbstractNamespaceOption
+ // option is cleared.
+ if (!PlatformSupportsAbstractNamespace)
+ return QLocalSocket::NoOptions;
+ return srcOptions;
+}
+}
+
QLocalSocketPrivate::QLocalSocketPrivate() : QIODevicePrivate(),
delayConnect(nullptr),
connectTimer(nullptr),
connectingSocket(-1),
- state(QLocalSocket::UnconnectedState)
+ state(QLocalSocket::UnconnectedState),
+ socketOptions(QLocalSocket::NoOptions)
{
}
@@ -261,27 +286,33 @@ void QLocalSocket::connectToServer(OpenMode openMode)
void QLocalSocketPrivate::_q_connectToSocket()
{
Q_Q(QLocalSocket);
- QString connectingPathName;
-
- // determine the full server path
- if (connectingName.startsWith(QLatin1Char('/'))) {
- connectingPathName = connectingName;
- } else {
- connectingPathName = QDir::tempPath();
- connectingPathName += QLatin1Char('/') + connectingName;
- }
+ QLocalSocket::SocketOptions options = optionsForPlatform(socketOptions);
+ const QString connectingPathName = pathNameForConnection(connectingName, options);
const QByteArray encodedConnectingPathName = QFile::encodeName(connectingPathName);
- struct sockaddr_un name;
- name.sun_family = PF_UNIX;
- if (sizeof(name.sun_path) < (uint)encodedConnectingPathName.size() + 1) {
+ struct ::sockaddr_un addr;
+ addr.sun_family = PF_UNIX;
+ memset(addr.sun_path, 0, sizeof(addr.sun_path));
+
+ // for abstract socket add 2 to length, to take into account trailing AND leading null
+ constexpr unsigned int extraCharacters = PlatformSupportsAbstractNamespace ? 2 : 1;
+
+ if (sizeof(addr.sun_path) < static_cast<size_t>(encodedConnectingPathName.size() + extraCharacters)) {
QString function = QLatin1String("QLocalSocket::connectToServer");
setErrorAndEmit(QLocalSocket::ServerNotFoundError, function);
return;
}
- ::memcpy(name.sun_path, encodedConnectingPathName.constData(),
- encodedConnectingPathName.size() + 1);
- if (-1 == qt_safe_connect(connectingSocket, (struct sockaddr *)&name, sizeof(name))) {
+
+ QT_SOCKLEN_T addrSize = sizeof(::sockaddr_un);
+ if (options.testFlag(QLocalSocket::AbstractNamespaceOption)) {
+ ::memcpy(addr.sun_path + 1, encodedConnectingPathName.constData(),
+ encodedConnectingPathName.size() + 1);
+ addrSize = offsetof(::sockaddr_un, sun_path) + encodedConnectingPathName.size() + 1;
+ } else {
+ ::memcpy(addr.sun_path, encodedConnectingPathName.constData(),
+ encodedConnectingPathName.size() + 1);
+ }
+ if (-1 == qt_safe_connect(connectingSocket, (struct sockaddr *)&addr, addrSize)) {
QString function = QLatin1String("QLocalSocket::connectToServer");
switch (errno)
{
@@ -359,10 +390,69 @@ bool QLocalSocket::setSocketDescriptor(qintptr socketDescriptor,
}
QIODevice::open(openMode);
d->state = socketState;
+ d->describeSocket(socketDescriptor);
return d->unixSocket.setSocketDescriptor(socketDescriptor,
newSocketState, openMode);
}
+void QLocalSocketPrivate::describeSocket(qintptr socketDescriptor)
+{
+ bool abstractAddress = false;
+
+ struct ::sockaddr_un addr;
+ QT_SOCKLEN_T len = sizeof(addr);
+ memset(&addr, 0, sizeof(addr));
+ const int getpeernameStatus = ::getpeername(socketDescriptor, (sockaddr *)&addr, &len);
+ if (getpeernameStatus != 0 || len == offsetof(sockaddr_un, sun_path)) {
+ // this is the case when we call it from QLocalServer, then there is no peername
+ len = sizeof(addr);
+ if (::getsockname(socketDescriptor, (sockaddr *)&addr, &len) != 0)
+ return;
+ }
+ if (parseSockaddr(addr, static_cast<uint>(len), fullServerName, serverName, abstractAddress)) {
+ QLocalSocket::SocketOptions options = socketOptions.value();
+ socketOptions = options.setFlag(QLocalSocket::AbstractNamespaceOption, abstractAddress);
+ }
+}
+
+bool QLocalSocketPrivate::parseSockaddr(const struct ::sockaddr_un &addr,
+ uint len,
+ QString &fullServerName,
+ QString &serverName,
+ bool &abstractNamespace)
+{
+ if (len <= offsetof(::sockaddr_un, sun_path))
+ return false;
+ len -= offsetof(::sockaddr_un, sun_path);
+ // check for abstract socket address
+ abstractNamespace = PlatformSupportsAbstractNamespace
+ && (addr.sun_family == PF_UNIX && addr.sun_path[0] == 0);
+ QStringDecoder toUtf16(QStringDecoder::System, QStringDecoder::Flag::Stateless);
+ // An abstract socket address can be arbitrary binary. To properly handle such a case,
+ // we'd have to add new access functions for this very specific case. Instead, we just
+ // attempt to decode it according to OS text encoding. If it fails we ignore the result.
+ QByteArrayView textData(addr.sun_path + (abstractNamespace ? 1 : 0),
+ len - (abstractNamespace ? 1 : 0));
+ QString name = toUtf16(textData);
+ if (!name.isEmpty() && !toUtf16.hasError()) {
+ //conversion encodes the trailing zeros. So, in case of non-abstract namespace we
+ //chop them off as \0 character is not allowed in filenames
+ if (!abstractNamespace && (name.at(name.length() - 1) == QChar::fromLatin1('\0'))) {
+ int truncPos = name.length() - 1;
+ while (truncPos > 0 && name.at(truncPos - 1) == QChar::fromLatin1('\0'))
+ truncPos--;
+ name.truncate(truncPos);
+ }
+ fullServerName = name;
+ serverName = abstractNamespace
+ ? name
+ : fullServerName.mid(fullServerName.lastIndexOf(QLatin1Char('/')) + 1);
+ if (serverName.isEmpty())
+ serverName = fullServerName;
+ }
+ return true;
+}
+
void QLocalSocketPrivate::_q_abortConnectionAttempt()
{
Q_Q(QLocalSocket);
diff --git a/tests/auto/network/socket/qlocalsocket/tst_qlocalsocket.cpp b/tests/auto/network/socket/qlocalsocket/tst_qlocalsocket.cpp
index 8b2b4ea4da..f20f82ff88 100644
--- a/tests/auto/network/socket/qlocalsocket/tst_qlocalsocket.cpp
+++ b/tests/auto/network/socket/qlocalsocket/tst_qlocalsocket.cpp
@@ -56,6 +56,7 @@
Q_DECLARE_METATYPE(QLocalSocket::LocalSocketError)
Q_DECLARE_METATYPE(QLocalSocket::LocalSocketState)
Q_DECLARE_METATYPE(QLocalServer::SocketOption)
+Q_DECLARE_METATYPE(QLocalSocket::SocketOption)
Q_DECLARE_METATYPE(QFile::Permissions)
class tst_QLocalSocket : public QObject
@@ -77,6 +78,12 @@ private slots:
void listenAndConnect_data();
void listenAndConnect();
+ void listenAndConnectAbstractNamespace_data();
+ void listenAndConnectAbstractNamespace();
+
+ void listenAndConnectAbstractNamespaceTrailingZeros_data();
+ void listenAndConnectAbstractNamespaceTrailingZeros();
+
void connectWithOpen();
void connectWithOldOpen();
@@ -135,6 +142,7 @@ tst_QLocalSocket::tst_QLocalSocket()
qRegisterMetaType<QLocalSocket::LocalSocketState>("QLocalSocket::LocalSocketState");
qRegisterMetaType<QLocalSocket::LocalSocketError>("QLocalSocket::LocalSocketError");
qRegisterMetaType<QLocalServer::SocketOption>("QLocalServer::SocketOption");
+ qRegisterMetaType<QLocalServer::SocketOption>("QLocalSocket::SocketOption");
qRegisterMetaType<QFile::Permissions>("QFile::Permissions");
}
@@ -483,6 +491,105 @@ void tst_QLocalSocket::connectWithOpen()
server.close();
}
+void tst_QLocalSocket::listenAndConnectAbstractNamespaceTrailingZeros_data()
+{
+#ifdef Q_OS_LINUX
+ QTest::addColumn<bool>("server_0");
+ QTest::addColumn<bool>("client_0");
+ QTest::addColumn<bool>("success");
+ QTest::newRow("srv0_cli0") << true << true << true;
+ QTest::newRow("srv_cli0") << false << true << false;
+ QTest::newRow("srv0_cli") << true << false << false;
+ QTest::newRow("srv_cli") << false << false << true;
+#else
+ return;
+#endif
+}
+
+void tst_QLocalSocket::listenAndConnectAbstractNamespaceTrailingZeros()
+{
+#ifdef Q_OS_LINUX
+ QFETCH(bool, server_0);
+ QFETCH(bool, client_0);
+ QFETCH(bool, success);
+ bool expectedTimeOut = !success;
+ QString server_path("tst_qlocalsocket");
+ QString client_path("tst_qlocalsocket");
+
+ if (server_0)
+ server_path.append(QChar('\0'));
+ if (client_0)
+ client_path.append(QChar('\0'));
+ LocalServer server;
+ server.setSocketOptions(QLocalServer::AbstractNamespaceOption);
+ QVERIFY(server.listen(server_path));
+ QCOMPARE(server.fullServerName(), server_path);
+
+ LocalSocket socket;
+ socket.setSocketOptions(QLocalSocket::AbstractNamespaceOption);
+ socket.setServerName(client_path);
+ QCOMPARE(socket.open(), success);
+ if (success)
+ QCOMPARE(socket.fullServerName(), client_path);
+ else
+ QVERIFY(socket.fullServerName().isEmpty());
+
+ bool timedOut = true;
+ QCOMPARE(server.waitForNewConnection(3000, &timedOut), success);
+
+#if defined(QT_LOCALSOCKET_TCP)
+ QTest::qWait(250);
+#endif
+ QCOMPARE(timedOut, expectedTimeOut);
+
+ socket.close();
+ server.close();
+#else
+ return;
+#endif
+}
+
+void tst_QLocalSocket::listenAndConnectAbstractNamespace_data()
+{
+ QTest::addColumn<QLocalServer::SocketOption>("serverOption");
+ QTest::addColumn<QLocalSocket::SocketOption>("socketOption");
+ QTest::addColumn<bool>("success");
+ QTest::newRow("abs_abs") << QLocalServer::AbstractNamespaceOption << QLocalSocket::AbstractNamespaceOption << true;
+ QTest::newRow("reg_reg") << QLocalServer::NoOptions << QLocalSocket::NoOptions << true;
+#ifdef Q_OS_LINUX
+ QTest::newRow("reg_abs") << QLocalServer::UserAccessOption << QLocalSocket::AbstractNamespaceOption << false;
+ QTest::newRow("abs_reg") << QLocalServer::AbstractNamespaceOption << QLocalSocket::NoOptions << false;
+#endif
+}
+
+void tst_QLocalSocket::listenAndConnectAbstractNamespace()
+{
+ QFETCH(QLocalServer::SocketOption, serverOption);
+ QFETCH(QLocalSocket::SocketOption, socketOption);
+ QFETCH(bool, success);
+ bool expectedTimeOut = !success;
+
+ LocalServer server;
+ server.setSocketOptions(serverOption);
+ QVERIFY(server.listen("tst_qlocalsocket"));
+
+ LocalSocket socket;
+ socket.setSocketOptions(socketOption);
+ socket.setServerName("tst_qlocalsocket");
+ QCOMPARE(socket.open(), success);
+
+ bool timedOut = true;
+ QCOMPARE(server.waitForNewConnection(3000, &timedOut), success);
+
+#if defined(QT_LOCALSOCKET_TCP)
+ QTest::qWait(250);
+#endif
+ QCOMPARE(timedOut, expectedTimeOut);
+
+ socket.close();
+ server.close();
+}
+
void tst_QLocalSocket::connectWithOldOpen()
{
class OverriddenOpen : public LocalSocket
@@ -1331,15 +1438,15 @@ void tst_QLocalSocket::verifyListenWithDescriptor()
int listenSocket;
+ // Construct the unix address
+ struct ::sockaddr_un addr;
+ addr.sun_family = PF_UNIX;
+
if (bound) {
// create the unix socket
listenSocket = ::socket(PF_UNIX, SOCK_STREAM, 0);
QVERIFY2(listenSocket != -1, "failed to create test socket");
- // Construct the unix address
- struct ::sockaddr_un addr;
- addr.sun_family = PF_UNIX;
-
QVERIFY2(sizeof(addr.sun_path) > ((uint)path.size() + 1), "path to large to create socket");
::memset(addr.sun_path, 0, sizeof(addr.sun_path));
@@ -1368,12 +1475,12 @@ void tst_QLocalSocket::verifyListenWithDescriptor()
QVERIFY2(server.listen(listenSocket), "failed to start create QLocalServer with local socket");
#ifdef Q_OS_LINUX
- const QChar at(QLatin1Char('@'));
if (!bound) {
- QCOMPARE(server.serverName().at(0), at);
- QCOMPARE(server.fullServerName().at(0), at);
+ QCOMPARE(server.serverName().isEmpty(), true);
+ QCOMPARE(server.fullServerName().isEmpty(), true);
} else if (abstract) {
- QVERIFY2(server.fullServerName().at(0) == at, "abstract sockets should start with a '@'");
+ QVERIFY2(server.fullServerName().at(0) == addr.sun_path[1],
+ "abstract sockets should match server path without leading null");
} else {
QCOMPARE(server.fullServerName(), path);
if (path.contains(QLatin1Char('/'))) {
@@ -1383,8 +1490,17 @@ void tst_QLocalSocket::verifyListenWithDescriptor()
}
}
#else
- QVERIFY(server.serverName().isEmpty());
- QVERIFY(server.fullServerName().isEmpty());
+ if (bound) {
+ QCOMPARE(server.fullServerName(), path);
+ if (path.contains(QLatin1Char('/'))) {
+ QVERIFY2(server.serverName() == path.mid(path.lastIndexOf(QLatin1Char('/'))+1), "server name invalid short name");
+ } else {
+ QVERIFY2(server.serverName() == path, "server name doesn't match the path provided");
+ }
+ } else {
+ QVERIFY(server.serverName().isEmpty());
+ QVERIFY(server.fullServerName().isEmpty());
+ }
#endif