123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- #pragma once
- #include <QTcpServer>
- #include <QUrl>
- #include <QHostAddress>
- #include <QHostInfo>
- #include <HyperionConfig.h>
- #include <utils/Logger.h>
- #ifdef ENABLE_MDNS
- #include <mdns/MdnsBrowser.h>
- #endif
- namespace NetUtils {
- const int MAX_PORT = 65535;
- ///
- /// @brief Check if the port is available for listening
- /// @param[in/out] port The port to test, will be incremented if port is in use
- /// @param log The logger of the caller to print
- /// @return True on success else false
- ///
- inline bool portAvailable(quint16& port, Logger* log)
- {
- const quint16 prevPort = port;
- QTcpServer server;
- while (!server.listen(QHostAddress::Any, port))
- {
- Warning(log,"Port '%d' is already in use, will increment", port);
- port ++;
- }
- server.close();
- if(port != prevPort)
- {
- Warning(log, "The requested Port '%d' is already in use, will use Port '%d' instead", prevPort, port);
- return false;
- }
- return true;
- }
- ///
- /// @brief Check if the port is in the valid range
- /// @param log The logger of the caller to print///
- /// @param[in] port The port to be tested (port = -1 is ignored for testing)
- /// @param[in] host A hostname/IP-address to make reference to during logging
- /// @return True on success else false
- ///
- inline bool isValidPort(Logger* log, int port, const QString& host)
- {
- if ((port <= 0 || port > MAX_PORT) && port != -1)
- {
- Error(log, "Invalid port [%d] for host: (%s)! - Port must be in range [1 - %d]", port, QSTRING_CSTR(host), MAX_PORT);
- return false;
- }
- return true;
- }
- ///
- /// @brief Get host and port from an host address
- /// @param[in] address Hostname or IP-address with or without port (e.g. 192.168.1.100:4711, 2003:e4:c73a:8e00:d5bb:dc3c:50cb:c76e, hyperion.fritz.box)
- /// @param[in/out] host The resolved hostname or IP-address
- /// @param[in/out] port The resolved port, if available.
- /// @return True on success else false
- ///
- inline bool resolveHostPort(const QString& address, QString& host, int& port)
- {
- if (address.isEmpty())
- {
- return false;
- }
- QString testUrl;
- if (address.at(0) != '[' && address.count(':') > 1)
- {
- testUrl = QString("http://[%1]").arg(address);
- }
- else
- {
- testUrl = QString("http://%1").arg(address);
- }
- QUrl url(testUrl);
- if (!url.isValid())
- {
- return false;
- }
- host = url.host();
- if (url.port() != -1)
- {
- port = url.port();
- }
- return true;
- }
- ///
- /// @brief Resolve a hostname (DNS/mDNS) into an IP-address. A given IP address will be passed through
- /// @param[in/out] log The logger of the caller to print
- /// @param[in] hostname The hostname to be resolved
- /// @param[out] hostAddress The resolved IP-Address
- /// @return True on success else false
- ///
- inline bool resolveMdDnsHostToAddress(Logger* log, const QString& hostname, QHostAddress& hostAddress)
- {
- bool isHostAddressOK{ false };
- if (!hostname.isEmpty())
- {
- #ifdef ENABLE_MDNS
- if (hostname.endsWith(".local") || hostname.endsWith(".local."))
- {
- QHostAddress resolvedAddress;
- QMetaObject::invokeMethod(&MdnsBrowser::getInstance(), "resolveAddress",
- Qt::BlockingQueuedConnection,
- Q_RETURN_ARG(bool, isHostAddressOK),
- Q_ARG(Logger*, log), Q_ARG(QString, hostname), Q_ARG(QHostAddress&, resolvedAddress));
- hostAddress = resolvedAddress;
- }
- else
- #endif
- {
- if (hostAddress.setAddress(hostname))
- {
- // An IP-address is not required to be resolved
- isHostAddressOK = true;
- }
- else
- {
- QHostInfo hostInfo = QHostInfo::fromName(hostname);
- if (hostInfo.error() == QHostInfo::NoError)
- {
- hostAddress = hostInfo.addresses().at(0);
- Debug(log, "Successfully resolved hostname (%s) to IP-address (%s)", QSTRING_CSTR(hostname), QSTRING_CSTR(hostAddress.toString()));
- isHostAddressOK = true;
- }
- else
- {
- QString errortext = QString("Failed resolving hostname (%1) to IP-address. Error: (%2) %3").arg(hostname).arg(hostInfo.error()).arg(hostInfo.errorString());
- Error(log, "%s", QSTRING_CSTR(errortext));
- isHostAddressOK = false;
- }
- }
- }
- }
- return isHostAddressOK;
- }
- ///
- /// @brief Resolve a hostname(DNS) or mDNS service name into an IP-address. A given IP address will be passed through
- /// @param[in/out] log The logger of the caller to print
- /// @param[in] hostname The hostname/mDNS service name to be resolved
- /// @param[out] hostAddress The resolved IP-Address
- /// @param[in/out] port The port provided by the mDNS service, if not mDNS the input port is returned
- /// @return True on success else false
- ///
- inline bool resolveHostToAddress(Logger* log, const QString& hostname, QHostAddress& hostAddress, int& port)
- {
- bool areHostAddressPartOK{ false };
- QString target {hostname};
- #ifdef ENABLE_MDNS
- if (hostname.endsWith("._tcp.local"))
- {
- //Treat hostname as service instance name that requires to be resolved into an mDNS-Hostname first
- QMdnsEngine::Record service = MdnsBrowser::getInstance().getServiceInstanceRecord(hostname.toUtf8());
- if (!service.target().isEmpty())
- {
- Info(log, "Resolved service [%s] to mDNS hostname [%s], service port [%d]", QSTRING_CSTR(hostname), service.target().constData(), service.port());
- target = service.target();
- port = service.port();
- }
- else
- {
- Error(log, "Cannot resolve mDNS hostname for given service [%s]!", QSTRING_CSTR(hostname));
- return areHostAddressPartOK;
- }
- }
- #endif
- QHostAddress resolvedAddress;
- if (NetUtils::resolveMdDnsHostToAddress(log, target, resolvedAddress))
- {
- hostAddress = resolvedAddress;
- if (hostname != hostAddress.toString())
- {
- Info(log, "Resolved hostname (%s) to IP-address (%s)", QSTRING_CSTR(hostname), QSTRING_CSTR(hostAddress.toString()));
- }
- if (NetUtils::isValidPort(log, port, hostAddress.toString()))
- {
- areHostAddressPartOK = true;
- }
- }
- return areHostAddressPartOK;
- }
- inline bool resolveHostToAddress(Logger* log, const QString& hostname, QHostAddress& hostAddress)
- {
- int ignoredPort {MAX_PORT};
- return resolveHostToAddress(log, hostname, hostAddress, ignoredPort);
- }
- }
|