25#include <QCoreApplication>
26#include <QHostAddress>
30#include <QNetworkInterface>
31#include <QNetworkAddressEntry>
40#define LOC QString("PortChecker::%1(): ").arg(__func__)
77 LOG(VB_GENERAL, LOG_DEBUG,
LOC + QString(
"host %1 port %2 timeLimit %3 linkLocalOnly %4")
78 .arg(host).arg(port).arg(timeLimit.count()).arg(linkLocalOnly));
81 bool isIPAddress = addr.setAddress(host);
82 bool islinkLocal =
false;
87 && addr.protocol() == QAbstractSocket::IPv6Protocol
88 && addr.isInSubnet(QHostAddress::parseSubnet(
"fe80::/10")))
98 host = addr.toString();
107 QList<QNetworkInterface> cards = QNetworkInterface::allInterfaces();
109 QListIterator<QNetworkInterface> iCard = cards;
112 QTcpSocket socket(
this);
113 QAbstractSocket::SocketState state = QAbstractSocket::UnconnectedState;
115 bool testedAll =
false;
116 while (state != QAbstractSocket::ConnectedState
117 && (timer.
elapsed() < timeLimit))
125 addr.setScopeId(QString());
126 while (addr.scopeId().isEmpty() && iCardsEnd<2)
131 QNetworkInterface card = iCard.next();
132 LOG(VB_GENERAL, LOG_DEBUG, QString(
"Trying interface %1").arg(card.name()));
133 unsigned int flags = card.flags();
134 if ((flags & QNetworkInterface::IsLoopBack)
135 || !(flags & QNetworkInterface::IsRunning))
138 QList<QNetworkAddressEntry> addresses = card.addressEntries();
139 bool foundv6 =
false;
140 for (
const auto& ae : std::as_const(addresses))
142 if (ae.ip().protocol() == QAbstractSocket::IPv6Protocol)
151 addr.setScopeId(scope);
159 cards = QNetworkInterface::allInterfaces();
169 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"There is no IPV6 compatible interface for %1")
176 dest=addr.toString();
179 socket.connectToHost(
dest, port);
182 static constexpr std::chrono::milliseconds k_poll_interval {1ms};
183 while (state != QAbstractSocket::ConnectedState
184 && (timer.
elapsed() < timeLimit)
185 && attempt_time.elapsed() < 3s
188 if (QCoreApplication::instance() !=
nullptr &&
189 QThread::currentThread() == QCoreApplication::instance()->thread()
192 QCoreApplication::processEvents(QEventLoop::AllEvents, k_poll_interval.count());
193 std::this_thread::sleep_for(1ns);
197 std::this_thread::sleep_for(k_poll_interval);
199 state = socket.state();
200 LOG(VB_GENERAL, LOG_DEBUG,
LOC + QString(
"host %1 port %2 socket state %3, attempt time: %4")
201 .arg(host, QString::number(port), QString::number(state),
202 QString::number(attempt_time.elapsed().count())
208 if (linkLocalOnly && testedAll
209 && state == QAbstractSocket::UnconnectedState
210 && attempt_time.elapsed() > 500ms
219 && state == QAbstractSocket::UnconnectedState
223 if (state == QAbstractSocket::ConnectedState
224 && islinkLocal && !scope.isEmpty())
227 host = addr.toString();
229 return (state == QAbstractSocket::ConnectedState);
250 return checker.
checkPort(host,port,timeLimit,
true);
void SetScopeForAddress(const QHostAddress &addr)
Record the scope Id of the given IP address.
bool GetScopeForAddress(QHostAddress &addr) const
Return the cached scope Id for the given address.
A QElapsedTimer based timer to replace use of QTime as a timer.
std::chrono::milliseconds elapsed(void)
Returns milliseconds elapsed since last start() or restart()
Small class to handle TCP port checking and finding link-local context.
static bool resolveLinkLocal(QString &host, int port, std::chrono::milliseconds timeLimit=30s)
Convenience method to resolve link-local address.
bool checkPort(QString &host, int port, std::chrono::milliseconds timeLimit=30s, bool linkLocalOnly=false)
Check if a port is open and sort out the link-local scope.
void cancelPortCheck(void)
Cancel the checkPort operation currently in progress.
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)