GetAddrInfo.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "GetAddrInfo.h"
  6. #include "mozilla/net/DNS.h"
  7. #include "prnetdb.h"
  8. #include "nsHostResolver.h"
  9. #include "nsError.h"
  10. #include "mozilla/Mutex.h"
  11. #include "nsAutoPtr.h"
  12. #include "mozilla/StaticPtr.h"
  13. #include "MainThreadUtils.h"
  14. #include "mozilla/DebugOnly.h"
  15. #include "mozilla/net/DNS.h"
  16. #include <algorithm>
  17. #include "prerror.h"
  18. #include "mozilla/Logging.h"
  19. #if DNSQUERY_AVAILABLE
  20. // There is a bug in windns.h where the type of parameter ppQueryResultsSet for
  21. // DnsQuery_A is dependent on UNICODE being set. It should *always* be
  22. // PDNS_RECORDA, but if UNICODE is set it is PDNS_RECORDW. To get around this
  23. // we make sure that UNICODE is unset.
  24. #undef UNICODE
  25. #include <ws2tcpip.h>
  26. #undef GetAddrInfo
  27. #include <windns.h>
  28. #endif
  29. namespace mozilla {
  30. namespace net {
  31. static LazyLogModule gGetAddrInfoLog("GetAddrInfo");
  32. #define LOG(msg, ...) \
  33. MOZ_LOG(gGetAddrInfoLog, LogLevel::Debug, ("[DNS]: " msg, ##__VA_ARGS__))
  34. #define LOG_WARNING(msg, ...) \
  35. MOZ_LOG(gGetAddrInfoLog, LogLevel::Warning, ("[DNS]: " msg, ##__VA_ARGS__))
  36. #if DNSQUERY_AVAILABLE
  37. ////////////////////////////
  38. // WINDOWS IMPLEMENTATION //
  39. ////////////////////////////
  40. // Ensure consistency of PR_* and AF_* constants to allow for legacy usage of
  41. // PR_* constants with this API.
  42. static_assert(PR_AF_INET == AF_INET && PR_AF_INET6 == AF_INET6
  43. && PR_AF_UNSPEC == AF_UNSPEC, "PR_AF_* must match AF_*");
  44. // We intentionally leak this mutex. This is because we can run into a
  45. // situation where the worker threads are still running until the process
  46. // is actually fully shut down, and at any time one of those worker
  47. // threads can access gDnsapiInfoLock.
  48. static OffTheBooksMutex* gDnsapiInfoLock = nullptr;
  49. struct DnsapiInfo
  50. {
  51. public:
  52. NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DnsapiInfo);
  53. HMODULE mLibrary;
  54. decltype(&DnsQuery_A) mDnsQueryFunc;
  55. decltype(&DnsFree) mDnsFreeFunc;
  56. private:
  57. // This will either be called during shutdown of the GetAddrInfo module, or
  58. // when a worker thread is done doing a lookup (ie: within
  59. // _GetAddrInfo_Windows). Note that the lock must be held when this is
  60. // called.
  61. ~DnsapiInfo()
  62. {
  63. if (gDnsapiInfoLock) {
  64. gDnsapiInfoLock->AssertCurrentThreadOwns();
  65. } else {
  66. MOZ_ASSERT_UNREACHABLE("No mutex available during GetAddrInfo "
  67. "shutdown.");
  68. return;
  69. }
  70. LOG("Freeing Dnsapi.dll");
  71. MOZ_ASSERT(mLibrary);
  72. DebugOnly<BOOL> rv = FreeLibrary(mLibrary);
  73. NS_WARNING_ASSERTION(rv, "Failed to free Dnsapi.dll.");
  74. }
  75. };
  76. static StaticRefPtr<DnsapiInfo> gDnsapiInfo;
  77. static MOZ_ALWAYS_INLINE nsresult
  78. _GetAddrInfoInit_Windows()
  79. {
  80. // This is necessary to ensure strict thread safety because if two threads
  81. // run this function at the same time they can potentially create two
  82. // mutexes.
  83. MOZ_ASSERT(NS_IsMainThread(),
  84. "Do not initialize GetAddrInfo off main thread!");
  85. if (!gDnsapiInfoLock) {
  86. gDnsapiInfoLock = new OffTheBooksMutex("GetAddrInfo.cpp::gDnsapiInfoLock");
  87. }
  88. OffTheBooksMutexAutoLock lock(*gDnsapiInfoLock);
  89. if (gDnsapiInfo) {
  90. MOZ_ASSERT_UNREACHABLE("GetAddrInfo is being initialized multiple times!");
  91. return NS_ERROR_ALREADY_INITIALIZED;
  92. }
  93. HMODULE library = LoadLibraryA("Dnsapi.dll");
  94. if (NS_WARN_IF(!library)) {
  95. return NS_ERROR_FAILURE;
  96. }
  97. FARPROC dnsQueryFunc = GetProcAddress(library, "DnsQuery_A");
  98. FARPROC dnsFreeFunc = GetProcAddress(library, "DnsFree");
  99. if (NS_WARN_IF(!dnsQueryFunc) || NS_WARN_IF(!dnsFreeFunc)) {
  100. DebugOnly<BOOL> rv = FreeLibrary(library);
  101. NS_WARNING_ASSERTION(rv, "Failed to free Dnsapi.dll.");
  102. return NS_ERROR_FAILURE;
  103. }
  104. DnsapiInfo* info = new DnsapiInfo;
  105. info->mLibrary = library;
  106. info->mDnsQueryFunc = (decltype(info->mDnsQueryFunc)) dnsQueryFunc;
  107. info->mDnsFreeFunc = (decltype(info->mDnsFreeFunc)) dnsFreeFunc;
  108. gDnsapiInfo = info;
  109. return NS_OK;
  110. }
  111. static MOZ_ALWAYS_INLINE nsresult
  112. _GetAddrInfoShutdown_Windows()
  113. {
  114. OffTheBooksMutexAutoLock lock(*gDnsapiInfoLock);
  115. if (NS_WARN_IF(!gDnsapiInfo) || NS_WARN_IF(!gDnsapiInfoLock)) {
  116. MOZ_ASSERT_UNREACHABLE("GetAddrInfo not initialized!");
  117. return NS_ERROR_NOT_INITIALIZED;
  118. }
  119. gDnsapiInfo = nullptr;
  120. return NS_OK;
  121. }
  122. // If successful, returns in aResult a TTL value that is smaller or
  123. // equal with the one already there. Gets the TTL value by calling
  124. // to dnsapi->mDnsQueryFunc and iterating through the returned
  125. // records to find the one with the smallest TTL value.
  126. static MOZ_ALWAYS_INLINE nsresult
  127. _GetMinTTLForRequestType_Windows(DnsapiInfo * dnsapi, const char* aHost,
  128. uint16_t aRequestType, unsigned int* aResult)
  129. {
  130. MOZ_ASSERT(dnsapi);
  131. MOZ_ASSERT(aHost);
  132. MOZ_ASSERT(aResult);
  133. PDNS_RECORDA dnsData = nullptr;
  134. DNS_STATUS status = dnsapi->mDnsQueryFunc(
  135. aHost,
  136. aRequestType,
  137. (DNS_QUERY_STANDARD | DNS_QUERY_NO_NETBT | DNS_QUERY_NO_HOSTS_FILE
  138. | DNS_QUERY_NO_MULTICAST | DNS_QUERY_ACCEPT_TRUNCATED_RESPONSE
  139. | DNS_QUERY_DONT_RESET_TTL_VALUES),
  140. nullptr,
  141. &dnsData,
  142. nullptr);
  143. if (status == DNS_INFO_NO_RECORDS || status == DNS_ERROR_RCODE_NAME_ERROR
  144. || !dnsData) {
  145. LOG("No DNS records found for %s. status=%X. aRequestType = %X\n",
  146. aHost, status, aRequestType);
  147. return NS_ERROR_FAILURE;
  148. } else if (status != NOERROR) {
  149. LOG_WARNING("DnsQuery_A failed with status %X.\n", status);
  150. return NS_ERROR_UNEXPECTED;
  151. }
  152. for (PDNS_RECORDA curRecord = dnsData; curRecord; curRecord = curRecord->pNext) {
  153. // Only records in the answer section are important
  154. if (curRecord->Flags.S.Section != DnsSectionAnswer) {
  155. continue;
  156. }
  157. if (curRecord->wType == aRequestType) {
  158. *aResult = std::min<unsigned int>(*aResult, curRecord->dwTtl);
  159. } else {
  160. LOG("Received unexpected record type %u in response for %s.\n",
  161. curRecord->wType, aHost);
  162. }
  163. }
  164. dnsapi->mDnsFreeFunc(dnsData, DNS_FREE_TYPE::DnsFreeRecordList);
  165. return NS_OK;
  166. }
  167. static MOZ_ALWAYS_INLINE nsresult
  168. _GetTTLData_Windows(const char* aHost, uint16_t* aResult, uint16_t aAddressFamily)
  169. {
  170. MOZ_ASSERT(aHost);
  171. MOZ_ASSERT(aResult);
  172. if (aAddressFamily != PR_AF_UNSPEC &&
  173. aAddressFamily != PR_AF_INET &&
  174. aAddressFamily != PR_AF_INET6) {
  175. return NS_ERROR_UNEXPECTED;
  176. }
  177. RefPtr<DnsapiInfo> dnsapi = nullptr;
  178. {
  179. OffTheBooksMutexAutoLock lock(*gDnsapiInfoLock);
  180. dnsapi = gDnsapiInfo;
  181. }
  182. if (!dnsapi) {
  183. LOG_WARNING("GetAddrInfo has been shutdown or has not been initialized.");
  184. return NS_ERROR_NOT_INITIALIZED;
  185. }
  186. // In order to avoid using ANY records which are not always implemented as a
  187. // "Gimme what you have" request in hostname resolvers, we should send A
  188. // and/or AAAA requests, based on the address family requested.
  189. unsigned int ttl = -1;
  190. if (aAddressFamily == PR_AF_UNSPEC || aAddressFamily == PR_AF_INET) {
  191. _GetMinTTLForRequestType_Windows(dnsapi, aHost, DNS_TYPE_A, &ttl);
  192. }
  193. if (aAddressFamily == PR_AF_UNSPEC || aAddressFamily == PR_AF_INET6) {
  194. _GetMinTTLForRequestType_Windows(dnsapi, aHost, DNS_TYPE_AAAA, &ttl);
  195. }
  196. {
  197. // dnsapi's destructor is not thread-safe, so we release explicitly here
  198. OffTheBooksMutexAutoLock lock(*gDnsapiInfoLock);
  199. dnsapi = nullptr;
  200. }
  201. if (ttl == -1) {
  202. LOG("No useable TTL found.");
  203. return NS_ERROR_FAILURE;
  204. }
  205. *aResult = ttl;
  206. return NS_OK;
  207. }
  208. #endif
  209. ////////////////////////////////////
  210. // PORTABLE RUNTIME IMPLEMENTATION//
  211. ////////////////////////////////////
  212. static MOZ_ALWAYS_INLINE nsresult
  213. _GetAddrInfo_Portable(const char* aCanonHost, uint16_t aAddressFamily,
  214. uint16_t aFlags, const char* aNetworkInterface,
  215. AddrInfo** aAddrInfo)
  216. {
  217. MOZ_ASSERT(aCanonHost);
  218. MOZ_ASSERT(aAddrInfo);
  219. // We accept the same aFlags that nsHostResolver::ResolveHost accepts, but we
  220. // need to translate the aFlags into a form that PR_GetAddrInfoByName
  221. // accepts.
  222. int prFlags = PR_AI_ADDRCONFIG;
  223. if (!(aFlags & nsHostResolver::RES_CANON_NAME)) {
  224. prFlags |= PR_AI_NOCANONNAME;
  225. }
  226. // We need to remove IPv4 records manually because PR_GetAddrInfoByName
  227. // doesn't support PR_AF_INET6.
  228. bool disableIPv4 = aAddressFamily == PR_AF_INET6;
  229. if (disableIPv4) {
  230. aAddressFamily = PR_AF_UNSPEC;
  231. }
  232. PRAddrInfo* prai = PR_GetAddrInfoByName(aCanonHost, aAddressFamily, prFlags);
  233. if (!prai) {
  234. return NS_ERROR_UNKNOWN_HOST;
  235. }
  236. const char* canonName = nullptr;
  237. if (aFlags & nsHostResolver::RES_CANON_NAME) {
  238. canonName = PR_GetCanonNameFromAddrInfo(prai);
  239. }
  240. bool filterNameCollision = !(aFlags & nsHostResolver::RES_ALLOW_NAME_COLLISION);
  241. nsAutoPtr<AddrInfo> ai(new AddrInfo(aCanonHost, prai, disableIPv4,
  242. filterNameCollision, canonName));
  243. PR_FreeAddrInfo(prai);
  244. if (ai->mAddresses.isEmpty()) {
  245. return NS_ERROR_UNKNOWN_HOST;
  246. }
  247. *aAddrInfo = ai.forget();
  248. return NS_OK;
  249. }
  250. //////////////////////////////////////
  251. // COMMON/PLATFORM INDEPENDENT CODE //
  252. //////////////////////////////////////
  253. nsresult
  254. GetAddrInfoInit() {
  255. LOG("Initializing GetAddrInfo.\n");
  256. #if DNSQUERY_AVAILABLE
  257. return _GetAddrInfoInit_Windows();
  258. #else
  259. return NS_OK;
  260. #endif
  261. }
  262. nsresult
  263. GetAddrInfoShutdown() {
  264. LOG("Shutting down GetAddrInfo.\n");
  265. #if DNSQUERY_AVAILABLE
  266. return _GetAddrInfoShutdown_Windows();
  267. #else
  268. return NS_OK;
  269. #endif
  270. }
  271. nsresult
  272. GetAddrInfo(const char* aHost, uint16_t aAddressFamily, uint16_t aFlags,
  273. const char* aNetworkInterface, AddrInfo** aAddrInfo, bool aGetTtl)
  274. {
  275. if (NS_WARN_IF(!aHost) || NS_WARN_IF(!aAddrInfo)) {
  276. return NS_ERROR_NULL_POINTER;
  277. }
  278. #if DNSQUERY_AVAILABLE
  279. // The GetTTLData needs the canonical name to function properly
  280. if (aGetTtl) {
  281. aFlags |= nsHostResolver::RES_CANON_NAME;
  282. }
  283. #endif
  284. *aAddrInfo = nullptr;
  285. nsresult rv = _GetAddrInfo_Portable(aHost, aAddressFamily, aFlags,
  286. aNetworkInterface, aAddrInfo);
  287. #if DNSQUERY_AVAILABLE
  288. if (aGetTtl && NS_SUCCEEDED(rv)) {
  289. // Figure out the canonical name, or if that fails, just use the host name
  290. // we have.
  291. const char *name = nullptr;
  292. if (*aAddrInfo != nullptr && (*aAddrInfo)->mCanonicalName) {
  293. name = (*aAddrInfo)->mCanonicalName;
  294. } else {
  295. name = aHost;
  296. }
  297. LOG("Getting TTL for %s (cname = %s).", aHost, name);
  298. uint16_t ttl = 0;
  299. nsresult ttlRv = _GetTTLData_Windows(name, &ttl, aAddressFamily);
  300. if (NS_SUCCEEDED(ttlRv)) {
  301. (*aAddrInfo)->ttl = ttl;
  302. LOG("Got TTL %u for %s (name = %s).", ttl, aHost, name);
  303. } else {
  304. LOG_WARNING("Could not get TTL for %s (cname = %s).", aHost, name);
  305. }
  306. }
  307. #endif
  308. return rv;
  309. }
  310. } // namespace net
  311. } // namespace mozilla