Linux: Add abstract address support for QLocal{Socket,Server}

Takes advantage of Linux's and Android's support for abstract namespace
when binding sockets, which is independent of the filesystem (see man
entry for unix domain sockets).

To make QLocalServer and QLocalSocket use an abstract socket address,
one needs to set the socket options to QLocalServer::AbstractNamespaceOption.

Fixes: QTBUG-16090
Change-Id: Ia9f9c9cc1ac5c28f9d44b0a48d854a7cfbd39b11
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Piotr Mikolajczyk 2021-01-14 11:29:06 +01:00
parent aa84de1afa
commit 2e3f48637e
9 changed files with 380 additions and 56 deletions

View File

@ -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

View File

@ -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()
*/

View File

@ -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)

View File

@ -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();
}
/*!

View File

@ -65,6 +65,21 @@ QT_BEGIN_NAMESPACE
\sa QLocalServer
*/
/*!
\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
@ -438,6 +453,40 @@ QString QLocalSocket::serverName() const
return d->serverName;
}
/*!
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.

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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