NetUtils.h 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. #pragma once
  2. #include <QTcpServer>
  3. #include <QUrl>
  4. #include <QHostAddress>
  5. #include <QHostInfo>
  6. #include <HyperionConfig.h>
  7. #include <utils/Logger.h>
  8. #ifdef ENABLE_MDNS
  9. #include <mdns/MdnsBrowser.h>
  10. #endif
  11. namespace NetUtils {
  12. const int MAX_PORT = 65535;
  13. ///
  14. /// @brief Check if the port is available for listening
  15. /// @param[in/out] port The port to test, will be incremented if port is in use
  16. /// @param log The logger of the caller to print
  17. /// @return True on success else false
  18. ///
  19. inline bool portAvailable(quint16& port, Logger* log)
  20. {
  21. const quint16 prevPort = port;
  22. QTcpServer server;
  23. while (!server.listen(QHostAddress::Any, port))
  24. {
  25. Warning(log,"Port '%d' is already in use, will increment", port);
  26. port ++;
  27. }
  28. server.close();
  29. if(port != prevPort)
  30. {
  31. Warning(log, "The requested Port '%d' is already in use, will use Port '%d' instead", prevPort, port);
  32. return false;
  33. }
  34. return true;
  35. }
  36. ///
  37. /// @brief Check if the port is in the valid range
  38. /// @param log The logger of the caller to print///
  39. /// @param[in] port The port to be tested (port = -1 is ignored for testing)
  40. /// @param[in] host A hostname/IP-address to make reference to during logging
  41. /// @return True on success else false
  42. ///
  43. inline bool isValidPort(Logger* log, int port, const QString& host)
  44. {
  45. if ((port <= 0 || port > MAX_PORT) && port != -1)
  46. {
  47. Error(log, "Invalid port [%d] for host: (%s)! - Port must be in range [1 - %d]", port, QSTRING_CSTR(host), MAX_PORT);
  48. return false;
  49. }
  50. return true;
  51. }
  52. ///
  53. /// @brief Get host and port from an host address
  54. /// @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)
  55. /// @param[in/out] host The resolved hostname or IP-address
  56. /// @param[in/out] port The resolved port, if available.
  57. /// @return True on success else false
  58. ///
  59. inline bool resolveHostPort(const QString& address, QString& host, int& port)
  60. {
  61. if (address.isEmpty())
  62. {
  63. return false;
  64. }
  65. QString testUrl;
  66. if (address.at(0) != '[' && address.count(':') > 1)
  67. {
  68. testUrl = QString("http://[%1]").arg(address);
  69. }
  70. else
  71. {
  72. testUrl = QString("http://%1").arg(address);
  73. }
  74. QUrl url(testUrl);
  75. if (!url.isValid())
  76. {
  77. return false;
  78. }
  79. host = url.host();
  80. if (url.port() != -1)
  81. {
  82. port = url.port();
  83. }
  84. return true;
  85. }
  86. ///
  87. /// @brief Resolve a hostname (DNS/mDNS) into an IP-address. A given IP address will be passed through
  88. /// @param[in/out] log The logger of the caller to print
  89. /// @param[in] hostname The hostname to be resolved
  90. /// @param[out] hostAddress The resolved IP-Address
  91. /// @return True on success else false
  92. ///
  93. inline bool resolveMdDnsHostToAddress(Logger* log, const QString& hostname, QHostAddress& hostAddress)
  94. {
  95. bool isHostAddressOK{ false };
  96. if (!hostname.isEmpty())
  97. {
  98. #ifdef ENABLE_MDNS
  99. if (hostname.endsWith(".local") || hostname.endsWith(".local."))
  100. {
  101. QHostAddress resolvedAddress;
  102. QMetaObject::invokeMethod(&MdnsBrowser::getInstance(), "resolveAddress",
  103. Qt::BlockingQueuedConnection,
  104. Q_RETURN_ARG(bool, isHostAddressOK),
  105. Q_ARG(Logger*, log), Q_ARG(QString, hostname), Q_ARG(QHostAddress&, resolvedAddress));
  106. hostAddress = resolvedAddress;
  107. }
  108. else
  109. #endif
  110. {
  111. if (hostAddress.setAddress(hostname))
  112. {
  113. // An IP-address is not required to be resolved
  114. isHostAddressOK = true;
  115. }
  116. else
  117. {
  118. QHostInfo hostInfo = QHostInfo::fromName(hostname);
  119. if (hostInfo.error() == QHostInfo::NoError)
  120. {
  121. hostAddress = hostInfo.addresses().at(0);
  122. Debug(log, "Successfully resolved hostname (%s) to IP-address (%s)", QSTRING_CSTR(hostname), QSTRING_CSTR(hostAddress.toString()));
  123. isHostAddressOK = true;
  124. }
  125. else
  126. {
  127. QString errortext = QString("Failed resolving hostname (%1) to IP-address. Error: (%2) %3").arg(hostname).arg(hostInfo.error()).arg(hostInfo.errorString());
  128. Error(log, "%s", QSTRING_CSTR(errortext));
  129. isHostAddressOK = false;
  130. }
  131. }
  132. }
  133. }
  134. return isHostAddressOK;
  135. }
  136. ///
  137. /// @brief Resolve a hostname(DNS) or mDNS service name into an IP-address. A given IP address will be passed through
  138. /// @param[in/out] log The logger of the caller to print
  139. /// @param[in] hostname The hostname/mDNS service name to be resolved
  140. /// @param[out] hostAddress The resolved IP-Address
  141. /// @param[in/out] port The port provided by the mDNS service, if not mDNS the input port is returned
  142. /// @return True on success else false
  143. ///
  144. inline bool resolveHostToAddress(Logger* log, const QString& hostname, QHostAddress& hostAddress, int& port)
  145. {
  146. bool areHostAddressPartOK{ false };
  147. QString target {hostname};
  148. #ifdef ENABLE_MDNS
  149. if (hostname.endsWith("._tcp.local"))
  150. {
  151. //Treat hostname as service instance name that requires to be resolved into an mDNS-Hostname first
  152. QMdnsEngine::Record service = MdnsBrowser::getInstance().getServiceInstanceRecord(hostname.toUtf8());
  153. if (!service.target().isEmpty())
  154. {
  155. Info(log, "Resolved service [%s] to mDNS hostname [%s], service port [%d]", QSTRING_CSTR(hostname), service.target().constData(), service.port());
  156. target = service.target();
  157. port = service.port();
  158. }
  159. else
  160. {
  161. Error(log, "Cannot resolve mDNS hostname for given service [%s]!", QSTRING_CSTR(hostname));
  162. return areHostAddressPartOK;
  163. }
  164. }
  165. #endif
  166. QHostAddress resolvedAddress;
  167. if (NetUtils::resolveMdDnsHostToAddress(log, target, resolvedAddress))
  168. {
  169. hostAddress = resolvedAddress;
  170. if (hostname != hostAddress.toString())
  171. {
  172. Info(log, "Resolved hostname (%s) to IP-address (%s)", QSTRING_CSTR(hostname), QSTRING_CSTR(hostAddress.toString()));
  173. }
  174. if (NetUtils::isValidPort(log, port, hostAddress.toString()))
  175. {
  176. areHostAddressPartOK = true;
  177. }
  178. }
  179. return areHostAddressPartOK;
  180. }
  181. inline bool resolveHostToAddress(Logger* log, const QString& hostname, QHostAddress& hostAddress)
  182. {
  183. int ignoredPort {MAX_PORT};
  184. return resolveHostToAddress(log, hostname, hostAddress, ignoredPort);
  185. }
  186. }