diff options
Diffstat (limited to 'src/network/socket/qlocalsocket_unix.cpp')
-rw-r--r-- | src/network/socket/qlocalsocket_unix.cpp | 122 |
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); |