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 
41  cancelCheck(false)
42 {
43 }
44 
78 bool PortChecker::checkPort(QString &host, int port, int timeLimit, bool linkLocalOnly)
79 {
80  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("host %1 port %2 timeLimit %3 linkLocalOnly %4")
81  .arg(host).arg(port).arg(timeLimit).arg(linkLocalOnly));
82  cancelCheck = false;
83  QHostAddress addr;
84  bool isIPAddress = addr.setAddress(host);
85  bool islinkLocal = false;
86 // Windows does not need the scope on the ip address so we can skip
87 // some processing
88 #ifndef _WIN32
89  if (isIPAddress
90  && addr.protocol() == QAbstractSocket::IPv6Protocol
91  && addr.isInSubnet(QHostAddress::parseSubnet("fe80::/10")))
92  islinkLocal = true;
93 #endif
94  if (linkLocalOnly)
95  {
96  if (islinkLocal)
97  {
98  // If we already know the scope, set it here and return
100  {
101  host = addr.toString();
102  return true;
103  }
104  }
105  else
106  return false;
107  }
108  QList<QNetworkInterface> cards = QNetworkInterface::allInterfaces();
109  QListIterator<QNetworkInterface> iCard = cards;
111  QTcpSocket socket(this);
112  QAbstractSocket::SocketState state = QAbstractSocket::UnconnectedState;
113  int retryCount = 0;
114  QString scope;
115  bool testedAll = false;
116  while (state != QAbstractSocket::ConnectedState
117  && (timer.elapsed() < timeLimit))
118  {
119  if (state == QAbstractSocket::UnconnectedState)
120  {
121 // Windows does not need the scope on the ip address so we can skip
122 // some processing
123 #ifndef _WIN32
124  int iCardsEnd = 0;
125  if (islinkLocal && !gCoreContext->GetScopeForAddress(addr))
126  {
127  addr.setScopeId(QString());
128  while (addr.scopeId().isEmpty() && iCardsEnd<2)
129  {
130  // search for the next available IPV6 interface.
131  if (iCard.hasNext())
132  {
133  QNetworkInterface card = iCard.next();
134  LOG(VB_GENERAL, LOG_DEBUG, QString("Trying interface %1").arg(card.name()));
135  unsigned int flags = card.flags();
136  if ((flags & QNetworkInterface::IsLoopBack)
137  || !(flags & QNetworkInterface::IsRunning))
138  continue;
139  // check that IPv6 is enabled on that interface
140  QList<QNetworkAddressEntry> addresses = card.addressEntries();
141  bool foundv6 = false;
142  foreach (QNetworkAddressEntry ae, addresses)
143  {
144  if (ae.ip().protocol() == QAbstractSocket::IPv6Protocol)
145  {
146  foundv6 = true;
147  break;
148  }
149  }
150  if (foundv6)
151  {
152  scope = card.name();
153  addr.setScopeId(scope);
154  break;
155  }
156  }
157  else
158  {
159  // Get a new list in case a new interface
160  // has been added.
161  cards = QNetworkInterface::allInterfaces();
162  iCard = cards;
163  iCard.toFront();
164  testedAll=true;
165  iCardsEnd++;
166  }
167  }
168  }
169  if (iCardsEnd > 1)
170  {
171  LOG(VB_GENERAL, LOG_ERR, LOC + QString("There is no IPV6 compatible interface for %1")
172  .arg(host));
173  break;
174  }
175 #endif
176  QString dest;
177  if (isIPAddress)
178  dest=addr.toString();
179  else
180  dest=host;
181  socket.connectToHost(dest, port);
182  retryCount=0;
183  }
184  else
185  retryCount++;
186  // This retry count of 6 means 3 seconds of waiting for
187  // connection before aborting and starting a new connection attempt.
188  if (retryCount > 6)
189  socket.abort();
190  processEvents();
191  // Check if user got impatient and canceled
192  if (cancelCheck)
193  break;
194  std::this_thread::sleep_for(std::chrono::milliseconds(500));
195  state = socket.state();
196  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("socket state %1")
197  .arg(state));
198  if (linkLocalOnly
199  && state == QAbstractSocket::UnconnectedState
200  && testedAll)
201  break;
202  }
203  if (state == QAbstractSocket::ConnectedState
204  && islinkLocal && !scope.isEmpty())
205  {
207  host = addr.toString();
208  }
209  socket.abort();
210  processEvents();
211  return (state == QAbstractSocket::ConnectedState);
212 }
213 
228 // static method
229 bool PortChecker::resolveLinkLocal(QString &host, int port, int timeLimit)
230 {
231  PortChecker checker;
232  return checker.checkPort(host,port,timeLimit,true);
233 }
234 
236 {
237  qApp->processEvents(QEventLoop::AllEvents, 250);
238  qApp->processEvents(QEventLoop::AllEvents, 250);
239 }
240 
249 {
250  cancelCheck = true;
251 }
252 
253 
254 /* vim: set expandtab tabstop=4 shiftwidth=4: */
A QElapsedTimer based timer to replace use of QTime as a timer.
Definition: mythtimer.h:13
void cancelPortCheck(void)
Cancel the checkPort operation currently in progress.
#define LOC
Definition: portchecker.cpp:38
void SetScopeForAddress(const QHostAddress &addr)
Record the scope Id of the given IP address.
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
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:78
static bool resolveLinkLocal(QString &host, int port, int timeLimit=30000)
Convenience method to resolve link-local address.
bool cancelCheck
Definition: portchecker.h:60
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:85
Small class to handle TCP port checking and finding link-local context.
Definition: portchecker.h:43
int elapsed(void) const
Returns milliseconds elapsed since last start() or restart()
Definition: mythtimer.cpp:90
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
bool GetScopeForAddress(QHostAddress &addr) const
Return the cached scope Id for the given address.
void processEvents(void)