summaryrefslogtreecommitdiffstats
path: root/src/network/socket/qlocalsocket_unix.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/socket/qlocalsocket_unix.cpp')
-rw-r--r--src/network/socket/qlocalsocket_unix.cpp122
1 files changed, 106 insertions, 16 deletions
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);