MythTV  master
portchecker.cpp
Go to the documentation of this file.
1 // Copyright (c) 2017 MythTV Developers <mythtv-dev@mythtv.org>
3 //
4 // This is part of MythTV (https://www.mythtv.org)
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 2 of the License, or
9 // (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with this program. If not, see <http://www.gnu.org/licenses/>.
22 //
24 
25 #include <QCoreApplication>
26 #include <QHostAddress>
27 #include <QTcpSocket>
28 #include <QEventLoop>
29 #include <QNetworkInterface>
30 #include <QNetworkAddressEntry>
31 
32 #include <thread>
33 
34 #include "mythcorecontext.h"
35 #include "mythtimer.h"
36 #include "portchecker.h"
37 
38 #define LOC QString("PortChecker::%1(): ").arg(__func__)
39 
73 bool PortChecker::checkPort(QString &host, int port, int timeLimit, bool linkLocalOnly)
74 {
75  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("host %1 port %2 timeLimit %3 linkLocalOnly %4")
76  .arg(host).arg(port).arg(timeLimit).arg(linkLocalOnly));
77  m_cancelCheck = false;
78  QHostAddress addr;
79  bool isIPAddress = addr.setAddress(host);
80  bool islinkLocal = false;
81 // Windows does not need the scope on the ip address so we can skip
82 // some processing
83 #ifndef _WIN32
84  if (isIPAddress
85  && addr.protocol() == QAbstractSocket::IPv6Protocol
86  && addr.isInSubnet(QHostAddress::parseSubnet("fe80::/10")))
87  islinkLocal = true;
88 #endif
89  if (linkLocalOnly)
90  {
91  // cppcheck-suppress knownConditionTrueFalse
92  if (islinkLocal)
93  {
94  // If we already know the scope, set it here and return
96  {
97  host = addr.toString();
98  return true;
99  }
100  }
101  else
102  return false;
103  }
104  // cppcheck-suppress unreadVariable
105  QList<QNetworkInterface> cards = QNetworkInterface::allInterfaces();
106 #ifndef _WIN32
107  QListIterator<QNetworkInterface> iCard = cards;
108 #endif
110  QTcpSocket socket(this);
111  QAbstractSocket::SocketState state = QAbstractSocket::UnconnectedState;
112  int retryCount = 0;
113  QString scope;
114  bool testedAll = false;
115  while (state != QAbstractSocket::ConnectedState
116  && (timer.elapsed() < timeLimit))
117  {
118  if (state == QAbstractSocket::UnconnectedState)
119  {
120 // Windows does not need the scope on the ip address so we can skip
121 // some processing
122 #ifndef _WIN32
123  int iCardsEnd = 0;
124  if (islinkLocal && !gCoreContext->GetScopeForAddress(addr))
125  {
126  addr.setScopeId(QString());
127  while (addr.scopeId().isEmpty() && iCardsEnd<2)
128  {
129  // search for the next available IPV6 interface.
130  if (iCard.hasNext())
131  {
132  QNetworkInterface card = iCard.next();
133  LOG(VB_GENERAL, LOG_DEBUG, QString("Trying interface %1").arg(card.name()));
134  unsigned int flags = card.flags();
135  if ((flags & QNetworkInterface::IsLoopBack)
136  || !(flags & QNetworkInterface::IsRunning))
137  continue;
138  // check that IPv6 is enabled on that interface
139  QList<QNetworkAddressEntry> addresses = card.addressEntries();
140  bool foundv6 = false;
141  for (const auto& ae : qAsConst(addresses))
142  {
143  if (ae.ip().protocol() == QAbstractSocket::IPv6Protocol)
144  {
145  foundv6 = true;
146  break;
147  }
148  }
149  if (foundv6)
150  {
151  scope = card.name();
152  addr.setScopeId(scope);
153  break;
154  }
155  }
156  else
157  {
158  // Get a new list in case a new interface
159  // has been added.
160  cards = QNetworkInterface::allInterfaces();
161  iCard = cards;
162  iCard.toFront();
163  testedAll=true;
164  iCardsEnd++;
165  }
166  }
167  }
168  if (iCardsEnd > 1)
169  {
170  LOG(VB_GENERAL, LOG_ERR, LOC + QString("There is no IPV6 compatible interface for %1")
171  .arg(host));
172  break;
173  }
174 #endif
175  QString dest;
176  if (isIPAddress)
177  dest=addr.toString();
178  else
179  dest=host;
180  socket.connectToHost(dest, port);
181  retryCount=0;
182  }
183  else
184  retryCount++;
185  // This retry count of 6 means 3 seconds of waiting for
186  // connection before aborting and starting a new connection attempt.
187  if (retryCount > 6)
188  socket.abort();
189  processEvents();
190  // Check if user got impatient and canceled
191  if (m_cancelCheck)
192  break;
193  std::this_thread::sleep_for(std::chrono::milliseconds(500));
194  state = socket.state();
195  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("socket state %1")
196  .arg(state));
197  if (linkLocalOnly
198  && state == QAbstractSocket::UnconnectedState
199  && testedAll)
200  break;
201  }
202  if (state == QAbstractSocket::ConnectedState
203  && islinkLocal && !scope.isEmpty())
204  {
206  host = addr.toString();
207  }
208  socket.abort();
209  processEvents();
210  return (state == QAbstractSocket::ConnectedState);
211 }
212 
227 // static method
228 bool PortChecker::resolveLinkLocal(QString &host, int port, int timeLimit)
229 {
230  PortChecker checker;
231  return checker.checkPort(host,port,timeLimit,true);
232 }
233 
235 {
236  qApp->processEvents(QEventLoop::AllEvents, 250);
237  qApp->processEvents(QEventLoop::AllEvents, 250);
238 }
239 
248 {
249  m_cancelCheck = true;
250 }
251 
252 
253 /* vim: set expandtab tabstop=4 shiftwidth=4: */
build_compdb.dest
dest
Definition: build_compdb.py:9
PortChecker
Small class to handle TCP port checking and finding link-local context.
Definition: portchecker.h:44
MythTimer
A QElapsedTimer based timer to replace use of QTime as a timer.
Definition: mythtimer.h:14
arg
arg(title).arg(filename).arg(doDelete))
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
PortChecker::m_cancelCheck
bool m_cancelCheck
Definition: portchecker.h:60
MythCoreContext::GetScopeForAddress
bool GetScopeForAddress(QHostAddress &addr) const
Return the cached scope Id for the given address.
Definition: mythcorecontext.cpp:1118
PortChecker::checkPort
bool checkPort(QString &host, int port, int timeLimit=30000, bool linkLocalOnly=false)
Check if a port is open and sort out the link-local scope.
Definition: portchecker.cpp:73
PortChecker::resolveLinkLocal
static bool resolveLinkLocal(QString &host, int port, int timeLimit=30000)
Convenience method to resolve link-local address.
Definition: portchecker.cpp:228
portchecker.h
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:60
MythTimer::elapsed
int elapsed(void)
Returns milliseconds elapsed since last start() or restart()
Definition: mythtimer.cpp:90
MythTimer::kStartRunning
@ kStartRunning
Definition: mythtimer.h:17
mythcorecontext.h
mythtimer.h
MythCoreContext::SetScopeForAddress
void SetScopeForAddress(const QHostAddress &addr)
Record the scope Id of the given IP address.
Definition: mythcorecontext.cpp:1138
LOC
#define LOC
Definition: portchecker.cpp:38
PortChecker::cancelPortCheck
void cancelPortCheck(void)
Cancel the checkPort operation currently in progress.
Definition: portchecker.cpp:247
PortChecker::processEvents
static void processEvents(void)
Definition: portchecker.cpp:234