LCOV - code coverage report
Current view: top level - netwerk/dns - GetAddrInfo.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 30 35 85.7 %
Date: 2017-07-14 16:53:18 Functions: 4 4 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13