summaryrefslogtreecommitdiff
path: root/src/libs/utils/tcpportsgatherer.cpp
diff options
context:
space:
mode:
authorKai Koehne <kai.koehne@nokia.com>2012-02-28 12:13:04 +0100
committerhjk <qthjk@ovi.com>2012-03-02 18:37:39 +0100
commitaf205ab25d9830cc3f9c2f7dec4e2c337783c73d (patch)
tree5c217a576c8269e7f77e5e673b9112fe6b2f923e /src/libs/utils/tcpportsgatherer.cpp
parentf7e127f740bab53e542fa6d0019082e0d9aaaa64 (diff)
downloadqt-creator-af205ab25d9830cc3f9c2f7dec4e2c337783c73d.tar.gz
Add TcpPortsGatherer to utils
Can be used to check (non-intrusively) which TCP ports are still available. Change-Id: I61300f6b7215cb2c0e4a3e6135de305cfd38b5a9 Reviewed-by: Christian Kandeler <christian.kandeler@nokia.com> Reviewed-by: hjk <qthjk@ovi.com>
Diffstat (limited to 'src/libs/utils/tcpportsgatherer.cpp')
-rw-r--r--src/libs/utils/tcpportsgatherer.cpp260
1 files changed, 260 insertions, 0 deletions
diff --git a/src/libs/utils/tcpportsgatherer.cpp b/src/libs/utils/tcpportsgatherer.cpp
new file mode 100644
index 0000000000..2a89f17ef2
--- /dev/null
+++ b/src/libs/utils/tcpportsgatherer.cpp
@@ -0,0 +1,260 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "tcpportsgatherer.h"
+#include "qtcassert.h"
+#include <QFile>
+#include <QStringList>
+#include <QProcess>
+
+#ifdef Q_OS_WIN
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <iphlpapi.h>
+#endif
+
+namespace Utils {
+namespace Internal {
+
+class TcpPortsGathererPrivate
+{
+public:
+ TcpPortsGathererPrivate(TcpPortsGatherer::ProtocolFlags protocolFlags)
+ : protocolFlags(protocolFlags) {}
+
+ TcpPortsGatherer::ProtocolFlags protocolFlags;
+ PortList usedPorts;
+
+ void updateWin(TcpPortsGatherer::ProtocolFlags protocolFlags);
+ void updateLinux(TcpPortsGatherer::ProtocolFlags protocolFlags);
+ void updateNetstat(TcpPortsGatherer::ProtocolFlags protocolFlags);
+};
+
+#ifdef Q_OS_WIN
+template <typename Table, ULONG (__stdcall *Func)(Table*, PULONG, BOOL) >
+QSet<int> usedTcpPorts()
+{
+ Table *table = static_cast<Table*>(malloc(sizeof(Table)));
+ DWORD dwSize = sizeof(Table);
+
+ // get the necessary size into the dwSize variable
+ DWORD dwRetVal = Func(table, &dwSize, false);
+ if (dwRetVal == ERROR_INSUFFICIENT_BUFFER) {
+ free(table);
+ table = static_cast<Table*>(malloc(dwSize));
+ }
+
+ // get the actual data
+ QSet<int> result;
+ dwRetVal = Func(table, &dwSize, false);
+ if (dwRetVal == NO_ERROR) {
+ for (quint32 i = 0; i < table->dwNumEntries; i++) {
+ quint16 port = ntohs(table->table[i].dwLocalPort);
+ if (!result.contains(port))
+ result.insert(port);
+ }
+ } else {
+ qWarning() << "TcpPortsGatherer: GetTcpTable failed with" << dwRetVal;
+ }
+
+ free(table);
+ return result;
+}
+#endif
+
+void TcpPortsGathererPrivate::updateWin(TcpPortsGatherer::ProtocolFlags protocolFlags)
+{
+#ifdef Q_OS_WIN
+ QSet<int> ports;
+
+ if (protocolFlags & TcpPortsGatherer::IPv4Protocol)
+ ports.unite(usedTcpPorts<MIB_TCPTABLE, GetTcpTable>());
+ if (protocolFlags & TcpPortsGatherer::IPv6Protocol)
+ ports.unite(usedTcpPorts<MIB_TCP6TABLE, GetTcp6Table>());
+
+ foreach (int port, ports) {
+ if (!usedPorts.contains(port))
+ usedPorts.addPort(port);
+ }
+#endif
+ Q_UNUSED(protocolFlags);
+}
+
+void TcpPortsGathererPrivate::updateLinux(TcpPortsGatherer::ProtocolFlags protocolFlags)
+{
+ QStringList filePaths;
+ if (protocolFlags & TcpPortsGatherer::IPv4Protocol)
+ filePaths.append(QLatin1String("/proc/net/tcp"));
+ if (protocolFlags & TcpPortsGatherer::IPv6Protocol)
+ filePaths.append(QLatin1String("/proc/net/tcp6"));
+
+ foreach (const QString &filePath, filePaths) {
+ QFile file(filePath);
+ if (!file.open(QFile::ReadOnly | QFile::Text)) {
+ qWarning() << "TcpPortsGatherer: Cannot open file"
+ << filePath << ":" << file.errorString();
+ continue;
+ }
+
+ if (file.atEnd()) // read first line describing the output
+ file.readLine();
+
+ static QRegExp pattern(QLatin1String("^\\s*" // start of line, whitespace
+ "\\d+:\\s*" // integer, colon, space
+ "[0-9A-Fa-f]+:" // hexadecimal number (ip), colon
+ "([0-9A-Fa-f]+)" // hexadecimal number (port!)
+ ));
+ while (!file.atEnd()) {
+ QByteArray line = file.readLine();
+ if (pattern.indexIn(line) != -1) {
+ bool isNumber;
+ quint16 port = pattern.cap(1).toUShort(&isNumber, 16);
+ QTC_ASSERT(isNumber, continue);
+ if (!usedPorts.contains(port))
+ usedPorts.addPort(port);
+ } else {
+ qWarning() << "TcpPortsGatherer: File" << filePath << "has unexpected format.";
+ continue;
+ }
+ }
+ }
+}
+
+// Only works with FreeBSD version of netstat like we have on Mac OS X
+void TcpPortsGathererPrivate::updateNetstat(TcpPortsGatherer::ProtocolFlags protocolFlags)
+{
+ QStringList netstatArgs;
+
+ netstatArgs.append(QLatin1String("-a")); // show also sockets of server processes
+ netstatArgs.append(QLatin1String("-n")); // show network addresses as numbers
+ netstatArgs.append(QLatin1String("-p"));
+ netstatArgs.append(QLatin1String("tcp"));
+ if (protocolFlags != TcpPortsGatherer::AnyIPProcol) {
+ netstatArgs.append(QLatin1String("-f")); // limit to address family
+ if (protocolFlags == TcpPortsGatherer::IPv4Protocol)
+ netstatArgs.append(QLatin1String("inet"));
+ else
+ netstatArgs.append(QLatin1String("inet6"));
+ }
+
+ QProcess netstatProcess;
+ netstatProcess.start(QLatin1String("netstat"), netstatArgs);
+ if (!netstatProcess.waitForFinished(30000)) {
+ qWarning() << "TcpPortsGatherer: netstat did not return in time.";
+ return;
+ }
+
+ QList<QByteArray> output = netstatProcess.readAllStandardOutput().split('\n');
+ foreach (const QByteArray &line, output) {
+ static QRegExp pattern(QLatin1String("^tcp[46]+" // "tcp", followed by "4", "6", "46"
+ "\\s+\\d+" // whitespace, number (Recv-Q)
+ "\\s+\\d+" // whitespace, number (Send-Q)
+ "\\s+(\\S+)")); // whitespace, Local Address
+ if (pattern.indexIn(line) != -1) {
+ QString localAddress = pattern.cap(1);
+
+ // Examples of local addresses:
+ // '*.56501' , '*.*' 'fe80::1%lo0.123'
+ int portDelimiterPos = localAddress.lastIndexOf(".");
+ if (portDelimiterPos == -1)
+ continue;
+
+ localAddress = localAddress.mid(portDelimiterPos + 1);
+ bool isNumber;
+ quint16 port = localAddress.toUShort(&isNumber);
+ if (!isNumber)
+ continue;
+
+ if (!usedPorts.contains(port))
+ usedPorts.addPort(port);
+ }
+ }
+}
+
+} // namespace Internal
+
+
+/*!
+ \class Utils::TcpPortsGatherer
+
+ \brief Gather the list of local TCP ports already in use.
+
+ Query the system for the list of local TCP ports already in use. This information can be used
+ to select a port for use in a range.
+*/
+
+TcpPortsGatherer::TcpPortsGatherer(TcpPortsGatherer::ProtocolFlags protocolFlags)
+ : d(new Internal::TcpPortsGathererPrivate(protocolFlags))
+{
+ update();
+}
+
+TcpPortsGatherer::~TcpPortsGatherer()
+{
+ delete d;
+}
+
+void TcpPortsGatherer::update()
+{
+ d->usedPorts = PortList();
+
+#if defined(Q_OS_WIN)
+ d->updateWin(d->protocolFlags);
+#elif defined(Q_OS_LINUX)
+ d->updateLinux(d->protocolFlags);
+#else
+ d->updateNetstat(d->protocolFlags);
+#endif
+}
+
+PortList TcpPortsGatherer::usedPorts() const
+{
+ return d->usedPorts;
+}
+
+/*!
+ Select a port out of \a freePorts that is not yet used.
+
+ Returns the port, or 0 if no free port is available.
+ */
+quint16 TcpPortsGatherer::getNextFreePort(PortList *freePorts)
+{
+ QTC_ASSERT(freePorts, return 0);
+ while (freePorts->hasMore()) {
+ const int port = freePorts->getNext();
+ if (!d->usedPorts.contains(port))
+ return port;
+ }
+ return 0;
+}
+
+} // namespace Utils