File indexing completed on 2024-05-12 17:07:35
0001 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0002 // SPDX-FileCopyrightText: 2018 Alexis Lopes Zubeta <contact@azubieta.net> 0003 // SPDX-FileCopyrightText: 2020 Tomaz Canabrava <tcanabrava@kde.org> 0004 0005 #include "netstathelper.h" 0006 0007 #include <KLocalizedString> 0008 0009 #include <QDebug> 0010 #include <QStandardPaths> 0011 #include <QStringList> 0012 #include <QTimer> 0013 0014 Q_LOGGING_CATEGORY(NetstatHelperDebug, "netstat.helper") 0015 0016 NetstatHelper::NetstatHelper() 0017 { 0018 } 0019 0020 void NetstatHelper::query() 0021 { 0022 m_executableProcess = new QProcess(); 0023 m_processKillerTimer = new QTimer(); 0024 m_processKillerTimer->setSingleShot(true); 0025 0026 /* parameters passed to ss 0027 * -r, --resolve resolve host names 0028 * -a, --all display all sockets 0029 * -p, --processes show process using socket 0030 * -u, --udp display only UDP sockets 0031 * -t, --tcp display only TCP sockets 0032 */ 0033 0034 const QStringList netstatArgs(m_hasTimeoutError ? QStringList({"-tuap"}) : QStringList({"-tuapr"})); 0035 const QString executable = QStringLiteral("ss"); 0036 0037 connect(m_executableProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &NetstatHelper::stepExecuteFinished); 0038 0039 connect(m_processKillerTimer, &QTimer::timeout, this, &NetstatHelper::stopProcess); 0040 0041 m_executableProcess->start(executable, netstatArgs, QIODevice::ReadOnly); 0042 0043 // We wait 10 seconds before killing the process. 0044 m_processKillerTimer->start(10000); 0045 qDebug() << "Running process"; 0046 } 0047 0048 void NetstatHelper::stopProcess() 0049 { 0050 qDebug() << "Timing out!"; 0051 m_hasTimeoutError = true; 0052 0053 m_processKillerTimer->stop(); 0054 m_processKillerTimer->deleteLater(); 0055 m_processKillerTimer = nullptr; 0056 0057 m_executableProcess->disconnect(); 0058 m_executableProcess->kill(); 0059 m_executableProcess->deleteLater(); 0060 m_executableProcess = nullptr; 0061 } 0062 0063 void NetstatHelper::stepExecuteFinished(int exitCode) 0064 { 0065 // No need to kill anything - we had success executing the process. 0066 if (!m_processKillerTimer) { 0067 return; 0068 } 0069 0070 if (m_processKillerTimer != nullptr) { 0071 m_processKillerTimer->stop(); 0072 m_processKillerTimer->deleteLater(); 0073 m_processKillerTimer = nullptr; 0074 } 0075 0076 m_hasError = false; 0077 0078 if (0 != exitCode) { 0079 m_hasError = true; 0080 m_errorString = m_executableProcess->readAllStandardError(); 0081 } else { 0082 QVector<QStringList> result = parseSSOutput(m_executableProcess->readAllStandardOutput()); 0083 Q_EMIT queryFinished(result); 0084 ; 0085 } 0086 0087 m_executableProcess->deleteLater(); 0088 m_executableProcess = nullptr; 0089 } 0090 0091 bool NetstatHelper::hasError() const 0092 { 0093 return m_hasError; 0094 } 0095 0096 QString NetstatHelper::errorString() const 0097 { 0098 return m_errorString; 0099 } 0100 0101 QVector<QStringList> NetstatHelper::parseSSOutput(const QByteArray &netstatOutput) 0102 { 0103 QString rawOutput = netstatOutput; 0104 QStringList outputLines = rawOutput.split(QStringLiteral("\n")); 0105 0106 QVector<QStringList> connections; 0107 0108 // discard lines. 0109 while (outputLines.size()) { 0110 if (outputLines.first().indexOf(QLatin1String("Recv-Q"))) { 0111 outputLines.removeFirst(); 0112 break; 0113 } 0114 outputLines.removeFirst(); 0115 } 0116 0117 // can't easily parse because of the spaces in Local and Peer AddressPort. 0118 QStringList headerLines = { 0119 QStringLiteral("Netid"), 0120 QStringLiteral("State"), 0121 QStringLiteral("Recv-Q"), 0122 QStringLiteral("Send-Q"), 0123 QStringLiteral("Local Address:Port"), 0124 QStringLiteral("Peer Address:Port"), 0125 QStringLiteral("Process"), 0126 }; 0127 0128 // Extract Information 0129 for (const auto &line : qAsConst(outputLines)) { 0130 QStringList values = line.split(QLatin1Char(' '), Qt::SkipEmptyParts); 0131 if (values.isEmpty()) { 0132 continue; 0133 } 0134 0135 // Some lines lack one or two values. 0136 while (values.size() < headerLines.size()) { 0137 values.append(QString()); 0138 } 0139 0140 QString appName; 0141 QString pid; 0142 if (values[6].size()) { 0143 values[6].remove(0, QStringLiteral("users:((").size()); 0144 values[6].chop(QStringLiteral("))").size()); 0145 0146 QStringList substrings = values[6].split(','); 0147 appName = substrings[0].remove(QStringLiteral("\"")); 0148 pid = substrings[1].split('=')[1]; 0149 } 0150 0151 /* Insertion order needs to match the Model Columns: 0152 ProtocolRole = Qt::UserRole + 1, 0153 LocalAddressRole, 0154 ForeignAddressRole, 0155 StatusRole, 0156 PidRole, 0157 ProgramRole 0158 */ 0159 QStringList connection{ 0160 values[0], // NetId 0161 values[4], // Local Address 0162 values[5], // Peer Address, 0163 values[1], // State 0164 pid, 0165 appName, 0166 }; 0167 0168 connections.append(connection); 0169 } 0170 0171 return connections; 0172 } 0173 0174 QString NetstatHelper::extractAndStrip(const QString &src, int index, int size) 0175 { 0176 QString str = src.mid(index, size); 0177 str.remove(QLatin1String(" ")); 0178 return str; 0179 }