File indexing completed on 2024-04-28 05:30:12

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