File indexing completed on 2025-10-26 05:13:45
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 void NetstatHelper::query() 0017 { 0018 if (m_executableProcess) { 0019 // If we get queried again before the process finished, we'll forcefully terminate the process and start from scratch 0020 // without host name resolution to hopefully speed things up. 0021 stopProcess(); 0022 } 0023 0024 m_executableProcess = new QProcess(this); 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 m_executableProcess->start(executable, netstatArgs, QIODevice::ReadOnly); 0039 0040 qDebug() << "Running process"; 0041 } 0042 0043 void NetstatHelper::stopProcess() 0044 { 0045 qDebug() << "Timing out!"; 0046 m_hasTimeoutError = true; 0047 0048 m_executableProcess->disconnect(); 0049 m_executableProcess->kill(); 0050 m_executableProcess->deleteLater(); 0051 m_executableProcess = nullptr; 0052 } 0053 0054 void NetstatHelper::stepExecuteFinished(int exitCode) 0055 { 0056 m_hasError = false; 0057 0058 if (0 != exitCode) { 0059 m_hasError = true; 0060 m_errorString = m_executableProcess->readAllStandardError(); 0061 } else { 0062 QList<QStringList> result = parseSSOutput(m_executableProcess->readAllStandardOutput()); 0063 Q_EMIT queryFinished(result); 0064 } 0065 0066 m_executableProcess->deleteLater(); 0067 m_executableProcess = nullptr; 0068 } 0069 0070 bool NetstatHelper::hasError() const 0071 { 0072 return m_hasError; 0073 } 0074 0075 QString NetstatHelper::errorString() const 0076 { 0077 return m_errorString; 0078 } 0079 0080 QList<QStringList> NetstatHelper::parseSSOutput(const QByteArray &netstatOutput) 0081 { 0082 QString rawOutput = netstatOutput; 0083 QStringList outputLines = rawOutput.split(QStringLiteral("\n")); 0084 0085 QList<QStringList> connections; 0086 0087 // discard lines. 0088 while (outputLines.size()) { 0089 if (outputLines.first().indexOf(QLatin1String("Recv-Q"))) { 0090 outputLines.removeFirst(); 0091 break; 0092 } 0093 outputLines.removeFirst(); 0094 } 0095 0096 // can't easily parse because of the spaces in Local and Peer AddressPort. 0097 QStringList headerLines = { 0098 QStringLiteral("Netid"), 0099 QStringLiteral("State"), 0100 QStringLiteral("Recv-Q"), 0101 QStringLiteral("Send-Q"), 0102 QStringLiteral("Local Address:Port"), 0103 QStringLiteral("Peer Address:Port"), 0104 QStringLiteral("Process"), 0105 }; 0106 0107 // Extract Information 0108 for (const auto &line : std::as_const(outputLines)) { 0109 QStringList values = line.split(QLatin1Char(' '), Qt::SkipEmptyParts); 0110 if (values.isEmpty()) { 0111 continue; 0112 } 0113 0114 // Some lines lack one or two values. 0115 while (values.size() < headerLines.size()) { 0116 values.append(QString()); 0117 } 0118 0119 QString appName; 0120 QString pid; 0121 if (values[6].size()) { 0122 values[6].remove(0, QStringLiteral("users:((").size()); 0123 values[6].chop(QStringLiteral("))").size()); 0124 0125 QStringList substrings = values[6].split(','); 0126 appName = substrings[0].remove(QStringLiteral("\"")); 0127 pid = substrings[1].split('=')[1]; 0128 } 0129 0130 /* Insertion order needs to match the Model Columns: 0131 ProtocolRole = Qt::UserRole + 1, 0132 LocalAddressRole, 0133 ForeignAddressRole, 0134 StatusRole, 0135 PidRole, 0136 ProgramRole 0137 */ 0138 QStringList connection{ 0139 values[0], // NetId 0140 values[4], // Local Address 0141 values[5], // Peer Address, 0142 values[1], // State 0143 pid, 0144 appName, 0145 }; 0146 0147 connections.append(connection); 0148 } 0149 0150 return connections; 0151 } 0152 0153 QString NetstatHelper::extractAndStrip(const QString &src, int index, int size) 0154 { 0155 QString str = src.mid(index, size); 0156 str.remove(QLatin1String(" ")); 0157 return str; 0158 }