forked from qt-creator/qt-creator
ProjectExplorer: Fix parsing ports from netstat output on Windows
Handle IPv6, and do not try to interpret the result as hex. Change-Id: I700c233d03cc706bc8712ab9fabee75d7f126df3 Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
@@ -269,4 +269,71 @@ QTCREATOR_UTILS_EXPORT bool readMultiLineString(const QJsonValue &value, QString
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QTCREATOR_UTILS_EXPORT int parseUsedPortFromNetstatOutput(const QByteArray &line)
|
||||||
|
{
|
||||||
|
const QByteArray trimmed = line.trimmed();
|
||||||
|
int base = 0;
|
||||||
|
QByteArray portString;
|
||||||
|
|
||||||
|
if (trimmed.startsWith("TCP") || trimmed.startsWith("UDP")) {
|
||||||
|
// Windows. Expected output is something like
|
||||||
|
//
|
||||||
|
// Active Connections
|
||||||
|
//
|
||||||
|
// Proto Local Address Foreign Address State
|
||||||
|
// TCP 0.0.0.0:80 0.0.0.0:0 LISTENING
|
||||||
|
// TCP 0.0.0.0:113 0.0.0.0:0 LISTENING
|
||||||
|
// [...]
|
||||||
|
// TCP 10.9.78.4:14714 0.0.0.0:0 LISTENING
|
||||||
|
// TCP 10.9.78.4:50233 12.13.135.180:993 ESTABLISHED
|
||||||
|
// [...]
|
||||||
|
// TCP [::]:445 [::]:0 LISTENING
|
||||||
|
// TCP 192.168.0.80:51905 169.55.74.50:443 ESTABLISHED
|
||||||
|
// UDP [fe80::880a:2932:8dff:a858%6]:1900 *:*
|
||||||
|
const int firstBracketPos = trimmed.indexOf('[');
|
||||||
|
int colonPos = -1;
|
||||||
|
if (firstBracketPos == -1) {
|
||||||
|
colonPos = trimmed.indexOf(':'); // IPv4
|
||||||
|
} else {
|
||||||
|
// jump over host part
|
||||||
|
const int secondBracketPos = trimmed.indexOf(']', firstBracketPos + 1);
|
||||||
|
colonPos = trimmed.indexOf(':', secondBracketPos);
|
||||||
|
}
|
||||||
|
const int firstDigitPos = colonPos + 1;
|
||||||
|
const int spacePos = trimmed.indexOf(' ', firstDigitPos);
|
||||||
|
if (spacePos < 0)
|
||||||
|
return -1;
|
||||||
|
const int len = spacePos - firstDigitPos;
|
||||||
|
base = 10;
|
||||||
|
portString = trimmed.mid(firstDigitPos, len);
|
||||||
|
} else {
|
||||||
|
// Expected output on Linux something like
|
||||||
|
//
|
||||||
|
// sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt ...
|
||||||
|
// 0: 00000000:2805 00000000:0000 0A 00000000:00000000 00:00000000 00000000 ...
|
||||||
|
//
|
||||||
|
const int firstColonPos = trimmed.indexOf(':');
|
||||||
|
if (firstColonPos < 0)
|
||||||
|
return -1;
|
||||||
|
const int secondColonPos = trimmed.indexOf(':', firstColonPos + 1);
|
||||||
|
if (secondColonPos < 0)
|
||||||
|
return -1;
|
||||||
|
const int spacePos = trimmed.indexOf(' ', secondColonPos + 1);
|
||||||
|
if (spacePos < 0)
|
||||||
|
return -1;
|
||||||
|
const int len = spacePos - secondColonPos - 1;
|
||||||
|
base = 16;
|
||||||
|
portString = trimmed.mid(secondColonPos + 1, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ok = true;
|
||||||
|
const int port = portString.toInt(&ok, base);
|
||||||
|
if (!ok) {
|
||||||
|
qWarning("%s: Unexpected string '%s' is not a port. Tried to read from '%s'",
|
||||||
|
Q_FUNC_INFO, line.data(), portString.data());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Utils
|
} // namespace Utils
|
||||||
|
|||||||
@@ -79,4 +79,6 @@ private:
|
|||||||
QTCREATOR_UTILS_EXPORT void expandMacros(QString *str, AbstractMacroExpander *mx);
|
QTCREATOR_UTILS_EXPORT void expandMacros(QString *str, AbstractMacroExpander *mx);
|
||||||
QTCREATOR_UTILS_EXPORT QString expandMacros(const QString &str, AbstractMacroExpander *mx);
|
QTCREATOR_UTILS_EXPORT QString expandMacros(const QString &str, AbstractMacroExpander *mx);
|
||||||
|
|
||||||
|
QTCREATOR_UTILS_EXPORT int parseUsedPortFromNetstatOutput(const QByteArray &line);
|
||||||
|
|
||||||
} // namespace Utils
|
} // namespace Utils
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
#include <utils/environment.h>
|
#include <utils/environment.h>
|
||||||
#include <utils/hostosinfo.h>
|
#include <utils/hostosinfo.h>
|
||||||
#include <utils/portlist.h>
|
#include <utils/portlist.h>
|
||||||
|
#include <utils/stringutils.h>
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
|
||||||
@@ -168,65 +169,10 @@ class DesktopPortsGatheringMethod : public PortsGatheringMethod
|
|||||||
{
|
{
|
||||||
QList<Utils::Port> ports;
|
QList<Utils::Port> ports;
|
||||||
const QList<QByteArray> lines = output.split('\n');
|
const QList<QByteArray> lines = output.split('\n');
|
||||||
if (HostOsInfo::isWindowsHost()) {
|
for (const QByteArray &line : lines) {
|
||||||
// Expected output is something like
|
const Port port(Utils::parseUsedPortFromNetstatOutput(line));
|
||||||
//
|
if (port.isValid() && !ports.contains(port))
|
||||||
// Active Connections
|
ports.append(port);
|
||||||
//
|
|
||||||
// Proto Local Address Foreign Address State
|
|
||||||
// TCP 0.0.0.0:80 0.0.0.0:0 LISTENING
|
|
||||||
// TCP 0.0.0.0:113 0.0.0.0:0 LISTENING
|
|
||||||
// [...]
|
|
||||||
// TCP 10.9.78.4:14714 0.0.0.0:0 LISTENING
|
|
||||||
// TCP 10.9.78.4:50233 12.13.135.180:993 ESTABLISHED
|
|
||||||
for (const QByteArray &line : lines) {
|
|
||||||
const QByteArray trimmed = line.trimmed();
|
|
||||||
if (!trimmed.startsWith("TCP"))
|
|
||||||
continue;
|
|
||||||
int colonPos = trimmed.indexOf(':');
|
|
||||||
if (colonPos < 0)
|
|
||||||
continue;
|
|
||||||
int spacePos = trimmed.indexOf(':', colonPos + 1);
|
|
||||||
if (spacePos < 0)
|
|
||||||
continue;
|
|
||||||
bool ok;
|
|
||||||
int len = spacePos - colonPos - 1;
|
|
||||||
const Utils::Port port(line.mid(colonPos + 1, len).toInt(&ok, 16));
|
|
||||||
if (ok) {
|
|
||||||
if (!ports.contains(port))
|
|
||||||
ports << port;
|
|
||||||
} else {
|
|
||||||
qWarning("%s: Unexpected string '%s' is not a port.",
|
|
||||||
Q_FUNC_INFO, line.data());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (HostOsInfo::isLinuxHost()) {
|
|
||||||
// Expected outpit is something like
|
|
||||||
//
|
|
||||||
// sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt ...
|
|
||||||
// 0: 00000000:2805 00000000:0000 0A 00000000:00000000 00:00000000 00000000 ...
|
|
||||||
//
|
|
||||||
for (const QByteArray &line : lines) {
|
|
||||||
int firstColonPos = line.indexOf(':');
|
|
||||||
if (firstColonPos < 0)
|
|
||||||
continue;
|
|
||||||
int secondColonPos = line.indexOf(':', firstColonPos + 1);
|
|
||||||
if (secondColonPos < 0)
|
|
||||||
continue;
|
|
||||||
int spacePos = line.indexOf(':', secondColonPos + 1);
|
|
||||||
if (spacePos < 0)
|
|
||||||
continue;
|
|
||||||
bool ok;
|
|
||||||
int len = spacePos - secondColonPos - 1;
|
|
||||||
const Utils::Port port(line.mid(secondColonPos + 1, len).toInt(&ok, 16));
|
|
||||||
if (ok) {
|
|
||||||
if (!ports.contains(port))
|
|
||||||
ports << port;
|
|
||||||
} else {
|
|
||||||
qWarning("%s: Unexpected string '%s' is not a port.",
|
|
||||||
Q_FUNC_INFO, line.data());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return ports;
|
return ports;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,6 +82,8 @@ private slots:
|
|||||||
void testMacroExpander();
|
void testMacroExpander();
|
||||||
void testStripAccelerator();
|
void testStripAccelerator();
|
||||||
void testStripAccelerator_data();
|
void testStripAccelerator_data();
|
||||||
|
void testParseUsedPortFromNetstatOutput();
|
||||||
|
void testParseUsedPortFromNetstatOutput_data();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TestMacroExpander mx;
|
TestMacroExpander mx;
|
||||||
@@ -202,6 +204,37 @@ void tst_StringUtils::testStripAccelerator_data()
|
|||||||
QTest::newRow("Test&") << "Test";
|
QTest::newRow("Test&") << "Test";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_StringUtils::testParseUsedPortFromNetstatOutput()
|
||||||
|
{
|
||||||
|
QFETCH(QString, line);
|
||||||
|
QFETCH(int, port);
|
||||||
|
|
||||||
|
QCOMPARE(Utils::parseUsedPortFromNetstatOutput(line.toUtf8()), port);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_StringUtils::testParseUsedPortFromNetstatOutput_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QString>("line");
|
||||||
|
QTest::addColumn<int>("port");
|
||||||
|
|
||||||
|
QTest::newRow("Empty") << "" << -1;
|
||||||
|
|
||||||
|
// Windows netstat.
|
||||||
|
QTest::newRow("Win1") << "Active Connection" << -1;
|
||||||
|
QTest::newRow("Win2") << " Proto Local Address Foreign Address State" << -1;
|
||||||
|
QTest::newRow("Win3") << " TCP 0.0.0.0:80 0.0.0.0:0 LISTENING" << 80;
|
||||||
|
QTest::newRow("Win4") << " TCP 0.0.0.0:113 0.0.0.0:0 LISTENING" << 113;
|
||||||
|
QTest::newRow("Win5") << " TCP 10.9.78.4:14714 0.0.0.0:0 LISTENING" << 14714;
|
||||||
|
QTest::newRow("Win6") << " TCP 10.9.78.4:50233 12.13.135.180:993 ESTABLISHED" << 50233;
|
||||||
|
QTest::newRow("Win7") << " TCP [::]:445 [::]:0 LISTENING" << 445;
|
||||||
|
QTest::newRow("Win8") << " TCP 192.168.0.80:51905 169.55.74.50:443 ESTABLISHED" << 51905;
|
||||||
|
QTest::newRow("Win9") << " UDP [fe80::840a:2942:8def:abcd%6]:1900 *:* " << 1900;
|
||||||
|
|
||||||
|
// Linux
|
||||||
|
QTest::newRow("Linux1") << "sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt ..." << -1;
|
||||||
|
QTest::newRow("Linux2") << "0: 00000000:2805 00000000:0000 0A 00000000:00000000 00:00000000 00000000 ..." << 10245;
|
||||||
|
}
|
||||||
|
|
||||||
QTEST_MAIN(tst_StringUtils)
|
QTEST_MAIN(tst_StringUtils)
|
||||||
|
|
||||||
#include "tst_stringutils.moc"
|
#include "tst_stringutils.moc"
|
||||||
|
|||||||
Reference in New Issue
Block a user