File indexing completed on 2024-02-18 16:19:52

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 // own
0010 #include "client_machine.h"
0011 #include "main.h"
0012 #include "utils/common.h"
0013 // KF5
0014 #include <NETWM>
0015 // Qt
0016 #include <QFutureWatcher>
0017 #include <QtConcurrentRun>
0018 // system
0019 #include <netdb.h>
0020 #include <sys/socket.h>
0021 #include <sys/types.h>
0022 #include <unistd.h>
0023 
0024 namespace KWin
0025 {
0026 
0027 static QString getHostName()
0028 {
0029 #ifdef HOST_NAME_MAX
0030     char hostnamebuf[HOST_NAME_MAX];
0031 #else
0032     char hostnamebuf[256];
0033 #endif
0034     if (gethostname(hostnamebuf, sizeof hostnamebuf) >= 0) {
0035         hostnamebuf[sizeof(hostnamebuf) - 1] = 0;
0036         return QString::fromLocal8Bit(hostnamebuf);
0037     }
0038     return QString();
0039 }
0040 
0041 GetAddrInfo::GetAddrInfo(const QString &hostName, QObject *parent)
0042     : QObject(parent)
0043     , m_resolving(false)
0044     , m_resolved(false)
0045     , m_ownResolved(false)
0046     , m_hostName(hostName)
0047     , m_addressHints(std::make_unique<addrinfo>())
0048     , m_address(nullptr)
0049     , m_ownAddress(nullptr)
0050     , m_watcher(std::make_unique<QFutureWatcher<int>>())
0051     , m_ownAddressWatcher(std::make_unique<QFutureWatcher<int>>())
0052 {
0053     // watcher will be deleted together with the GetAddrInfo once the future
0054     // got canceled or finished
0055     connect(m_watcher.get(), &QFutureWatcher<int>::canceled, this, &GetAddrInfo::deleteLater);
0056     connect(m_watcher.get(), &QFutureWatcher<int>::finished, this, &GetAddrInfo::slotResolved);
0057     connect(m_ownAddressWatcher.get(), &QFutureWatcher<int>::canceled, this, &GetAddrInfo::deleteLater);
0058     connect(m_ownAddressWatcher.get(), &QFutureWatcher<int>::finished, this, &GetAddrInfo::slotOwnAddressResolved);
0059 }
0060 
0061 GetAddrInfo::~GetAddrInfo()
0062 {
0063     if (m_watcher && m_watcher->isRunning()) {
0064         m_watcher->cancel();
0065         m_watcher->waitForFinished();
0066     }
0067     if (m_ownAddressWatcher && m_ownAddressWatcher->isRunning()) {
0068         m_ownAddressWatcher->cancel();
0069         m_ownAddressWatcher->waitForFinished();
0070     }
0071     if (m_address) {
0072         freeaddrinfo(m_address);
0073     }
0074     if (m_ownAddress) {
0075         freeaddrinfo(m_ownAddress);
0076     }
0077 }
0078 
0079 void GetAddrInfo::resolve()
0080 {
0081     if (m_resolving) {
0082         return;
0083     }
0084     m_resolving = true;
0085     *m_addressHints = {};
0086     m_addressHints->ai_family = PF_UNSPEC;
0087     m_addressHints->ai_socktype = SOCK_STREAM;
0088     m_addressHints->ai_flags |= AI_CANONNAME;
0089 
0090     m_watcher->setFuture(QtConcurrent::run([this]() {
0091         return getaddrinfo(m_hostName.toLocal8Bit().constData(), nullptr, m_addressHints.get(), &m_address);
0092     }));
0093     m_ownAddressWatcher->setFuture(QtConcurrent::run([this] {
0094         // needs to be performed in a lambda as getHostName() returns a temporary value which would
0095         // get destroyed in the main thread before the getaddrinfo thread is able to read it
0096         return getaddrinfo(getHostName().toLocal8Bit().constData(), nullptr, m_addressHints.get(), &m_ownAddress);
0097     }));
0098 }
0099 
0100 void GetAddrInfo::slotResolved()
0101 {
0102     if (resolved(m_watcher.get())) {
0103         m_resolved = true;
0104         compare();
0105     }
0106 }
0107 
0108 void GetAddrInfo::slotOwnAddressResolved()
0109 {
0110     if (resolved(m_ownAddressWatcher.get())) {
0111         m_ownResolved = true;
0112         compare();
0113     }
0114 }
0115 
0116 bool GetAddrInfo::resolved(QFutureWatcher<int> *watcher)
0117 {
0118     if (!watcher->isFinished()) {
0119         return false;
0120     }
0121     if (watcher->result() != 0) {
0122         qCDebug(KWIN_CORE) << "getaddrinfo failed with error:" << gai_strerror(watcher->result());
0123         // call failed;
0124         deleteLater();
0125         return false;
0126     }
0127     return true;
0128 }
0129 
0130 void GetAddrInfo::compare()
0131 {
0132     if (!m_resolved || !m_ownResolved) {
0133         return;
0134     }
0135     addrinfo *address = m_address;
0136     while (address) {
0137         if (address->ai_canonname && m_hostName == QByteArray(address->ai_canonname).toLower()) {
0138             addrinfo *ownAddress = m_ownAddress;
0139             bool localFound = false;
0140             while (ownAddress) {
0141                 if (ownAddress->ai_canonname && QByteArray(ownAddress->ai_canonname).toLower() == m_hostName) {
0142                     localFound = true;
0143                     break;
0144                 }
0145                 ownAddress = ownAddress->ai_next;
0146             }
0147             if (localFound) {
0148                 Q_EMIT local();
0149                 break;
0150             }
0151         }
0152         address = address->ai_next;
0153     }
0154     deleteLater();
0155 }
0156 
0157 ClientMachine::ClientMachine(QObject *parent)
0158     : QObject(parent)
0159     , m_localhost(false)
0160     , m_resolved(false)
0161     , m_resolving(false)
0162 {
0163 }
0164 
0165 ClientMachine::~ClientMachine()
0166 {
0167 }
0168 
0169 void ClientMachine::resolve(xcb_window_t window, xcb_window_t clientLeader)
0170 {
0171     if (m_resolved) {
0172         return;
0173     }
0174     QString name = NETWinInfo(connection(), window, rootWindow(), NET::Properties(), NET::WM2ClientMachine).clientMachine();
0175     if (name.isEmpty() && clientLeader && clientLeader != window) {
0176         name = NETWinInfo(connection(), clientLeader, rootWindow(), NET::Properties(), NET::WM2ClientMachine).clientMachine();
0177     }
0178     if (name.isEmpty()) {
0179         name = localhost();
0180     }
0181     if (name == localhost()) {
0182         setLocal();
0183     }
0184     m_hostName = name;
0185     checkForLocalhost();
0186     m_resolved = true;
0187 }
0188 
0189 void ClientMachine::checkForLocalhost()
0190 {
0191     if (isLocal()) {
0192         // nothing to do
0193         return;
0194     }
0195     QString host = getHostName();
0196 
0197     if (!host.isEmpty()) {
0198         host = host.toLower();
0199         const QString lowerHostName(m_hostName.toLower());
0200         if (host == lowerHostName) {
0201             setLocal();
0202             return;
0203         }
0204         if (int index = host.indexOf('.'); index != -1) {
0205             if (QStringView(host).left(index) == lowerHostName) {
0206                 setLocal();
0207                 return;
0208             }
0209         } else {
0210             m_resolving = true;
0211             // check using information from get addr info
0212             // GetAddrInfo gets automatically destroyed once it finished or not
0213             GetAddrInfo *info = new GetAddrInfo(lowerHostName, this);
0214             connect(info, &GetAddrInfo::local, this, &ClientMachine::setLocal);
0215             connect(info, &GetAddrInfo::destroyed, this, &ClientMachine::resolveFinished);
0216             info->resolve();
0217         }
0218     }
0219 }
0220 
0221 void ClientMachine::setLocal()
0222 {
0223     m_localhost = true;
0224     Q_EMIT localhostChanged();
0225 }
0226 
0227 void ClientMachine::resolveFinished()
0228 {
0229     m_resolving = false;
0230 }
0231 
0232 } // namespace