LCOV - code coverage report
Current view: top level - toolkit/components/url-classifier - nsUrlClassifierUtils.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 136 361 37.7 %
Date: 2017-07-14 16:53:18 Functions: 15 31 48.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* This Source Code Form is subject to the terms of the Mozilla Public
       2             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       3             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       4             : 
       5             : #include "nsEscape.h"
       6             : #include "nsString.h"
       7             : #include "nsIURI.h"
       8             : #include "nsUrlClassifierUtils.h"
       9             : #include "nsTArray.h"
      10             : #include "nsReadableUtils.h"
      11             : #include "plbase64.h"
      12             : #include "nsPrintfCString.h"
      13             : #include "safebrowsing.pb.h"
      14             : #include "mozilla/Sprintf.h"
      15             : #include "mozilla/Mutex.h"
      16             : 
      17             : #define DEFAULT_PROTOCOL_VERSION "2.2"
      18             : 
      19           0 : static char int_to_hex_digit(int32_t i)
      20             : {
      21           0 :   NS_ASSERTION((i >= 0) && (i <= 15), "int too big in int_to_hex_digit");
      22           0 :   return static_cast<char>(((i < 10) ? (i + '0') : ((i - 10) + 'A')));
      23             : }
      24             : 
      25             : static bool
      26           0 : IsDecimal(const nsACString & num)
      27             : {
      28           0 :   for (uint32_t i = 0; i < num.Length(); i++) {
      29           0 :     if (!isdigit(num[i])) {
      30           0 :       return false;
      31             :     }
      32             :   }
      33             : 
      34           0 :   return true;
      35             : }
      36             : 
      37             : static bool
      38           0 : IsHex(const nsACString & num)
      39             : {
      40           0 :   if (num.Length() < 3) {
      41           0 :     return false;
      42             :   }
      43             : 
      44           0 :   if (num[0] != '0' || !(num[1] == 'x' || num[1] == 'X')) {
      45           0 :     return false;
      46             :   }
      47             : 
      48           0 :   for (uint32_t i = 2; i < num.Length(); i++) {
      49           0 :     if (!isxdigit(num[i])) {
      50           0 :       return false;
      51             :     }
      52             :   }
      53             : 
      54           0 :   return true;
      55             : }
      56             : 
      57             : static bool
      58           0 : IsOctal(const nsACString & num)
      59             : {
      60           0 :   if (num.Length() < 2) {
      61           0 :     return false;
      62             :   }
      63             : 
      64           0 :   if (num[0] != '0') {
      65           0 :     return false;
      66             :   }
      67             : 
      68           0 :   for (uint32_t i = 1; i < num.Length(); i++) {
      69           0 :     if (!isdigit(num[i]) || num[i] == '8' || num[i] == '9') {
      70           0 :       return false;
      71             :     }
      72             :   }
      73             : 
      74           0 :   return true;
      75             : }
      76             : 
      77             : /////////////////////////////////////////////////////////////////
      78             : // SafeBrowsing V4 related utits.
      79             : 
      80             : namespace mozilla {
      81             : namespace safebrowsing {
      82             : 
      83             : static PlatformType
      84           0 : GetPlatformType()
      85             : {
      86             : #if defined(ANDROID)
      87             :   return ANDROID_PLATFORM;
      88             : #elif defined(XP_MACOSX)
      89             :   return OSX_PLATFORM;
      90             : #elif defined(XP_LINUX)
      91           0 :   return LINUX_PLATFORM;
      92             : #elif defined(XP_WIN)
      93             :   return WINDOWS_PLATFORM;
      94             : #else
      95             :   // Default to Linux for other platforms (see bug 1362501).
      96             :   return LINUX_PLATFORM;
      97             : #endif
      98             : }
      99             : 
     100             : typedef FetchThreatListUpdatesRequest_ListUpdateRequest ListUpdateRequest;
     101             : typedef FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints Constraints;
     102             : 
     103             : static void
     104           0 : InitListUpdateRequest(ThreatType aThreatType,
     105             :                       const char* aStateBase64,
     106             :                       ListUpdateRequest* aListUpdateRequest)
     107             : {
     108           0 :   aListUpdateRequest->set_threat_type(aThreatType);
     109           0 :   aListUpdateRequest->set_platform_type(GetPlatformType());
     110           0 :   aListUpdateRequest->set_threat_entry_type(URL);
     111             : 
     112           0 :   Constraints* contraints = new Constraints();
     113           0 :   contraints->add_supported_compressions(RICE);
     114           0 :   aListUpdateRequest->set_allocated_constraints(contraints);
     115             : 
     116             :   // Only set non-empty state.
     117           0 :   if (aStateBase64[0] != '\0') {
     118           0 :     nsCString stateBinary;
     119           0 :     nsresult rv = Base64Decode(nsDependentCString(aStateBase64), stateBinary);
     120           0 :     if (NS_SUCCEEDED(rv)) {
     121           0 :       aListUpdateRequest->set_state(stateBinary.get(), stateBinary.Length());
     122             :     }
     123             :   }
     124           0 : }
     125             : 
     126             : static ClientInfo*
     127           0 : CreateClientInfo()
     128             : {
     129           0 :   ClientInfo* c = new ClientInfo();
     130             : 
     131             :   nsCOMPtr<nsIPrefBranch> prefBranch =
     132           0 :     do_GetService(NS_PREFSERVICE_CONTRACTID);
     133             : 
     134           0 :   nsXPIDLCString clientId;
     135           0 :   nsresult rv = prefBranch->GetCharPref("browser.safebrowsing.id",
     136           0 :                                         getter_Copies(clientId));
     137             : 
     138           0 :   if (NS_FAILED(rv)) {
     139           0 :     clientId = "Firefox"; // Use "Firefox" as fallback.
     140             :   }
     141             : 
     142           0 :   c->set_client_id(clientId.get());
     143             : 
     144           0 :   return c;
     145             : }
     146             : 
     147             : } // end of namespace safebrowsing.
     148             : } // end of namespace mozilla.
     149             : 
     150           1 : nsUrlClassifierUtils::nsUrlClassifierUtils()
     151           1 :   : mProviderDictLock("nsUrlClassifierUtils.mProviderDictLock")
     152             : {
     153           1 : }
     154             : 
     155             : nsresult
     156           1 : nsUrlClassifierUtils::Init()
     157             : {
     158             :   // nsIUrlClassifierUtils is a thread-safe service so it's
     159             :   // allowed to use on non-main threads. However, building
     160             :   // the provider dictionary must be on the main thread.
     161             :   // We forcefully load nsUrlClassifierUtils in
     162             :   // nsUrlClassifierDBService::Init() to ensure we must
     163             :   // now be on the main thread.
     164           1 :   nsresult rv = ReadProvidersFromPrefs(mProviderDict);
     165           1 :   NS_ENSURE_SUCCESS(rv, rv);
     166             : 
     167             :   // Add an observer for shutdown
     168             :   nsCOMPtr<nsIObserverService> observerService =
     169           2 :       mozilla::services::GetObserverService();
     170           1 :   if (!observerService)
     171           0 :     return NS_ERROR_FAILURE;
     172             : 
     173           1 :   observerService->AddObserver(this, "xpcom-shutdown-threads", false);
     174           1 :   Preferences::AddStrongObserver(this, "browser.safebrowsing");
     175             : 
     176           1 :   return NS_OK;
     177             : }
     178             : 
     179         159 : NS_IMPL_ISUPPORTS(nsUrlClassifierUtils,
     180             :                   nsIUrlClassifierUtils,
     181             :                   nsIObserver)
     182             : 
     183             : /////////////////////////////////////////////////////////////////////////////
     184             : // nsIUrlClassifierUtils
     185             : 
     186             : NS_IMETHODIMP
     187           1 : nsUrlClassifierUtils::GetKeyForURI(nsIURI * uri, nsACString & _retval)
     188             : {
     189           2 :   nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(uri);
     190           1 :   if (!innerURI)
     191           0 :     innerURI = uri;
     192             : 
     193           2 :   nsAutoCString host;
     194           1 :   innerURI->GetAsciiHost(host);
     195             : 
     196           1 :   if (host.IsEmpty()) {
     197           0 :     return NS_ERROR_MALFORMED_URI;
     198             :   }
     199             : 
     200           1 :   nsresult rv = CanonicalizeHostname(host, _retval);
     201           1 :   NS_ENSURE_SUCCESS(rv, rv);
     202             : 
     203           2 :   nsAutoCString path;
     204           1 :   rv = innerURI->GetPath(path);
     205           1 :   NS_ENSURE_SUCCESS(rv, rv);
     206             : 
     207             :   // strip out anchors
     208           1 :   int32_t ref = path.FindChar('#');
     209           1 :   if (ref != kNotFound)
     210           0 :     path.SetLength(ref);
     211             : 
     212           2 :   nsAutoCString temp;
     213           1 :   rv = CanonicalizePath(path, temp);
     214           1 :   NS_ENSURE_SUCCESS(rv, rv);
     215             : 
     216           1 :   _retval.Append(temp);
     217             : 
     218           1 :   return NS_OK;
     219             : }
     220             : 
     221             : // We use "goog-*-proto" as the list name for v4, where "proto" indicates
     222             : // it's updated (as well as hash completion) via protobuf.
     223             : //
     224             : // In the mozilla official build, we are allowed to use the
     225             : // private phishing list (goog-phish-proto). See Bug 1288840.
     226             : static const struct {
     227             :   const char* mListName;
     228             :   uint32_t mThreatType;
     229             : } THREAT_TYPE_CONV_TABLE[] = {
     230             :   { "goog-malware-proto",  MALWARE_THREAT},            // 1
     231             :   { "googpub-phish-proto", SOCIAL_ENGINEERING_PUBLIC}, // 2
     232             :   { "goog-unwanted-proto", UNWANTED_SOFTWARE},         // 3
     233             :   { "goog-phish-proto", SOCIAL_ENGINEERING},           // 5
     234             : 
     235             :   // For application reputation
     236             :   { "goog-badbinurl-proto", MALICIOUS_BINARY},         // 7
     237             :   { "goog-downloadwhite-proto", CSD_DOWNLOAD_WHITELIST},  // 9
     238             : 
     239             :   // For testing purpose.
     240             :   { "test-phish-proto",    SOCIAL_ENGINEERING_PUBLIC}, // 2
     241             :   { "test-unwanted-proto", UNWANTED_SOFTWARE}, // 3
     242             : };
     243             : 
     244             : NS_IMETHODIMP
     245           0 : nsUrlClassifierUtils::ConvertThreatTypeToListNames(uint32_t aThreatType,
     246             :                                                    nsACString& aListNames)
     247             : {
     248           0 :   for (uint32_t i = 0; i < ArrayLength(THREAT_TYPE_CONV_TABLE); i++) {
     249           0 :     if (aThreatType == THREAT_TYPE_CONV_TABLE[i].mThreatType) {
     250           0 :       if (!aListNames.IsEmpty()) {
     251           0 :         aListNames.AppendLiteral(",");
     252             :       }
     253           0 :       aListNames += THREAT_TYPE_CONV_TABLE[i].mListName;
     254             :     }
     255             :   }
     256             : 
     257           0 :   return aListNames.IsEmpty() ? NS_ERROR_FAILURE : NS_OK;
     258             : }
     259             : 
     260             : NS_IMETHODIMP
     261           0 : nsUrlClassifierUtils::ConvertListNameToThreatType(const nsACString& aListName,
     262             :                                                   uint32_t* aThreatType)
     263             : {
     264           0 :   for (uint32_t i = 0; i < ArrayLength(THREAT_TYPE_CONV_TABLE); i++) {
     265           0 :     if (aListName.EqualsASCII(THREAT_TYPE_CONV_TABLE[i].mListName)) {
     266           0 :       *aThreatType = THREAT_TYPE_CONV_TABLE[i].mThreatType;
     267           0 :       return NS_OK;
     268             :     }
     269             :   }
     270             : 
     271           0 :   return NS_ERROR_FAILURE;
     272             : }
     273             : 
     274             : NS_IMETHODIMP
     275          47 : nsUrlClassifierUtils::GetProvider(const nsACString& aTableName,
     276             :                                   nsACString& aProvider)
     277             : {
     278          94 :   MutexAutoLock lock(mProviderDictLock);
     279          47 :   nsCString* provider = nullptr;
     280          47 :   if (StringBeginsWith(aTableName, NS_LITERAL_CSTRING("test"))) {
     281          44 :     aProvider = NS_LITERAL_CSTRING(TESTING_TABLE_PROVIDER_NAME);
     282           3 :   } else if (mProviderDict.Get(aTableName, &provider)) {
     283           3 :     aProvider = provider ? *provider : EmptyCString();
     284             :   } else {
     285           0 :     aProvider = EmptyCString();
     286             :   }
     287          94 :   return NS_OK;
     288             : }
     289             : 
     290             : NS_IMETHODIMP
     291           4 : nsUrlClassifierUtils::GetTelemetryProvider(const nsACString& aTableName,
     292             :                                   nsACString& aProvider)
     293             : {
     294           4 :   GetProvider(aTableName, aProvider);
     295             :   // Whitelist known providers to avoid reporting on private ones.
     296             :   // An empty provider is treated as "other"
     297          20 :   if (!NS_LITERAL_CSTRING("mozilla").Equals(aProvider) &&
     298          12 :       !NS_LITERAL_CSTRING("google").Equals(aProvider) &&
     299          12 :       !NS_LITERAL_CSTRING("google4").Equals(aProvider) &&
     300          12 :       !NS_LITERAL_CSTRING("baidu").Equals(aProvider) &&
     301          12 :       !NS_LITERAL_CSTRING("mozcn").Equals(aProvider) &&
     302          24 :       !NS_LITERAL_CSTRING("yandex").Equals(aProvider) &&
     303           8 :       !NS_LITERAL_CSTRING(TESTING_TABLE_PROVIDER_NAME).Equals(aProvider)) {
     304           0 :     aProvider.Assign(NS_LITERAL_CSTRING("other"));
     305             :   }
     306             : 
     307           4 :   return NS_OK;
     308             : }
     309             : 
     310             : NS_IMETHODIMP
     311           0 : nsUrlClassifierUtils::GetProtocolVersion(const nsACString& aProvider,
     312             :                                          nsACString& aVersion)
     313             : {
     314           0 :   nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID);
     315           0 :   if (prefBranch) {
     316             :       nsPrintfCString prefName("browser.safebrowsing.provider.%s.pver",
     317           0 :                                nsCString(aProvider).get());
     318           0 :       nsXPIDLCString version;
     319           0 :       nsresult rv = prefBranch->GetCharPref(prefName.get(), getter_Copies(version));
     320             : 
     321           0 :       aVersion = NS_SUCCEEDED(rv) ? version : DEFAULT_PROTOCOL_VERSION;
     322             :   } else {
     323           0 :       aVersion = DEFAULT_PROTOCOL_VERSION;
     324             :   }
     325             : 
     326           0 :   return NS_OK;
     327             : }
     328             : 
     329             : NS_IMETHODIMP
     330           0 : nsUrlClassifierUtils::MakeUpdateRequestV4(const char** aListNames,
     331             :                                           const char** aStatesBase64,
     332             :                                           uint32_t aCount,
     333             :                                           nsACString &aRequest)
     334             : {
     335             :   using namespace mozilla::safebrowsing;
     336             : 
     337           0 :   FetchThreatListUpdatesRequest r;
     338           0 :   r.set_allocated_client(CreateClientInfo());
     339             : 
     340           0 :   for (uint32_t i = 0; i < aCount; i++) {
     341           0 :     nsCString listName(aListNames[i]);
     342             :     uint32_t threatType;
     343           0 :     nsresult rv = ConvertListNameToThreatType(listName, &threatType);
     344           0 :     if (NS_FAILED(rv)) {
     345           0 :       continue; // Unknown list name.
     346             :     }
     347           0 :     auto lur = r.mutable_list_update_requests()->Add();
     348           0 :     InitListUpdateRequest(static_cast<ThreatType>(threatType), aStatesBase64[i], lur);
     349             :   }
     350             : 
     351             :   // Then serialize.
     352           0 :   std::string s;
     353           0 :   r.SerializeToString(&s);
     354             : 
     355           0 :   nsCString out;
     356           0 :   nsresult rv = Base64URLEncode(s.size(),
     357           0 :                                 (const uint8_t*)s.c_str(),
     358             :                                 Base64URLEncodePaddingPolicy::Include,
     359           0 :                                 out);
     360           0 :   NS_ENSURE_SUCCESS(rv, rv);
     361             : 
     362           0 :   aRequest = out;
     363             : 
     364           0 :   return NS_OK;
     365             : }
     366             : 
     367             : NS_IMETHODIMP
     368           0 : nsUrlClassifierUtils::MakeFindFullHashRequestV4(const char** aListNames,
     369             :                                                 const char** aListStatesBase64,
     370             :                                                 const char** aPrefixesBase64,
     371             :                                                 uint32_t aListCount,
     372             :                                                 uint32_t aPrefixCount,
     373             :                                                 nsACString &aRequest)
     374             : {
     375           0 :   FindFullHashesRequest r;
     376           0 :   r.set_allocated_client(CreateClientInfo());
     377             : 
     378             :   nsresult rv;
     379             : 
     380             :   // Set up FindFullHashesRequest.client_states.
     381           0 :   for (uint32_t i = 0; i < aListCount; i++) {
     382           0 :     nsCString stateBinary;
     383           0 :     rv = Base64Decode(nsDependentCString(aListStatesBase64[i]), stateBinary);
     384           0 :     NS_ENSURE_SUCCESS(rv, rv);
     385           0 :     r.add_client_states(stateBinary.get(), stateBinary.Length());
     386             :   }
     387             : 
     388             :   //-------------------------------------------------------------------
     389             :   // Set up FindFullHashesRequest.threat_info.
     390           0 :   auto threatInfo = r.mutable_threat_info();
     391             : 
     392             :   // 1) Set threat types.
     393           0 :   for (uint32_t i = 0; i < aListCount; i++) {
     394             :     uint32_t threatType;
     395           0 :     rv = ConvertListNameToThreatType(nsDependentCString(aListNames[i]), &threatType);
     396           0 :     NS_ENSURE_SUCCESS(rv, rv);
     397           0 :     threatInfo->add_threat_types((ThreatType)threatType);
     398             :   }
     399             : 
     400             :   // 2) Set platform type.
     401           0 :   threatInfo->add_platform_types(GetPlatformType());
     402             : 
     403             :   // 3) Set threat entry type.
     404           0 :   threatInfo->add_threat_entry_types(URL);
     405             : 
     406             :   // 4) Set threat entries.
     407           0 :   for (uint32_t i = 0; i < aPrefixCount; i++) {
     408           0 :     nsCString prefixBinary;
     409           0 :     rv = Base64Decode(nsDependentCString(aPrefixesBase64[i]), prefixBinary);
     410           0 :     threatInfo->add_threat_entries()->set_hash(prefixBinary.get(),
     411           0 :                                                prefixBinary.Length());
     412             :   }
     413             :   //-------------------------------------------------------------------
     414             : 
     415             :   // Then serialize.
     416           0 :   std::string s;
     417           0 :   r.SerializeToString(&s);
     418             : 
     419           0 :   nsCString out;
     420           0 :   rv = Base64URLEncode(s.size(),
     421           0 :                        (const uint8_t*)s.c_str(),
     422             :                        Base64URLEncodePaddingPolicy::Include,
     423           0 :                        out);
     424           0 :   NS_ENSURE_SUCCESS(rv, rv);
     425             : 
     426           0 :   aRequest = out;
     427             : 
     428           0 :   return NS_OK;
     429             : }
     430             : 
     431             : static uint32_t
     432           0 : DurationToMs(const Duration& aDuration)
     433             : {
     434             :   // Seconds precision is good enough. Ignore nanoseconds like Chrome does.
     435           0 :   return aDuration.seconds() * 1000;
     436             : }
     437             : 
     438             : NS_IMETHODIMP
     439           0 : nsUrlClassifierUtils::ParseFindFullHashResponseV4(const nsACString& aResponse,
     440             :                                                   nsIUrlClassifierParseFindFullHashCallback *aCallback)
     441             : {
     442             :   enum CompletionErrorType {
     443             :     SUCCESS = 0,
     444             :     PARSING_FAILURE = 1,
     445             :     UNKNOWN_THREAT_TYPE = 2,
     446             :   };
     447             : 
     448           0 :   FindFullHashesResponse r;
     449           0 :   if (!r.ParseFromArray(aResponse.BeginReading(), aResponse.Length())) {
     450           0 :     NS_WARNING("Invalid response");
     451             :     Telemetry::Accumulate(Telemetry::URLCLASSIFIER_COMPLETION_ERROR,
     452           0 :                           PARSING_FAILURE);
     453           0 :     return NS_ERROR_FAILURE;
     454             :   }
     455             : 
     456           0 :   bool hasUnknownThreatType = false;
     457             : 
     458           0 :   for (auto& m : r.matches()) {
     459           0 :     nsCString tableNames;
     460           0 :     nsresult rv = ConvertThreatTypeToListNames(m.threat_type(), tableNames);
     461           0 :     if (NS_FAILED(rv)) {
     462           0 :       hasUnknownThreatType = true;
     463           0 :       continue; // Ignore un-convertable threat type.
     464             :     }
     465           0 :     auto& hash = m.threat().hash();
     466           0 :     auto cacheDurationSec = m.cache_duration().seconds();
     467           0 :     aCallback->OnCompleteHashFound(nsDependentCString(hash.c_str(), hash.length()),
     468           0 :                                    tableNames, cacheDurationSec);
     469             : 
     470           0 :     Telemetry::Accumulate(Telemetry::URLCLASSIFIER_POSITIVE_CACHE_DURATION,
     471           0 :                           cacheDurationSec * PR_MSEC_PER_SEC);
     472             :   }
     473             : 
     474           0 :   auto minWaitDuration = DurationToMs(r.minimum_wait_duration());
     475           0 :   auto negCacheDurationSec = r.negative_cache_duration().seconds();
     476             : 
     477           0 :   aCallback->OnResponseParsed(minWaitDuration, negCacheDurationSec);
     478             : 
     479           0 :   Telemetry::Accumulate(Telemetry::URLCLASSIFIER_COMPLETION_ERROR,
     480           0 :                         hasUnknownThreatType ? UNKNOWN_THREAT_TYPE : SUCCESS);
     481             : 
     482           0 :   Telemetry::Accumulate(Telemetry::URLCLASSIFIER_NEGATIVE_CACHE_DURATION,
     483           0 :                         negCacheDurationSec * PR_MSEC_PER_SEC);
     484             : 
     485           0 :   return NS_OK;
     486             : }
     487             : 
     488             : //////////////////////////////////////////////////////////
     489             : // nsIObserver
     490             : 
     491             : NS_IMETHODIMP
     492           0 : nsUrlClassifierUtils::Observe(nsISupports *aSubject, const char *aTopic,
     493             :                               const char16_t *aData)
     494             : {
     495           0 :   if (0 == strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
     496           0 :     MutexAutoLock lock(mProviderDictLock);
     497           0 :     return ReadProvidersFromPrefs(mProviderDict);
     498             :   }
     499             : 
     500           0 :   if (0 == strcmp(aTopic, "xpcom-shutdown-threads")) {
     501           0 :     nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
     502           0 :     NS_ENSURE_TRUE(prefs, NS_ERROR_FAILURE);
     503           0 :     return prefs->RemoveObserver("browser.safebrowsing", this);
     504             :   }
     505             : 
     506           0 :   return NS_ERROR_UNEXPECTED;
     507             : }
     508             : 
     509             : /////////////////////////////////////////////////////////////////////////////
     510             : // non-interface methods
     511             : 
     512             : nsresult
     513           1 : nsUrlClassifierUtils::ReadProvidersFromPrefs(ProviderDictType& aDict)
     514             : {
     515           1 :   MOZ_ASSERT(NS_IsMainThread(), "ReadProvidersFromPrefs must be on main thread");
     516             : 
     517           2 :   nsCOMPtr<nsIPrefService> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
     518           1 :   NS_ENSURE_TRUE(prefs, NS_ERROR_FAILURE);
     519           2 :   nsCOMPtr<nsIPrefBranch> prefBranch;
     520           2 :   nsresult rv = prefs->GetBranch("browser.safebrowsing.provider.",
     521           2 :                                   getter_AddRefs(prefBranch));
     522           1 :   NS_ENSURE_SUCCESS(rv, rv);
     523             : 
     524             :   // We've got a pref branch for "browser.safebrowsing.provider.".
     525             :   // Enumerate all children prefs and parse providers.
     526             :   uint32_t childCount;
     527             :   char** childArray;
     528           1 :   rv = prefBranch->GetChildList("", &childCount, &childArray);
     529           1 :   NS_ENSURE_SUCCESS(rv, rv);
     530             : 
     531             :   // Collect providers from childArray.
     532           2 :   nsTHashtable<nsCStringHashKey> providers;
     533          24 :   for (uint32_t i = 0; i < childCount; i++) {
     534          46 :     nsCString child(childArray[i]);
     535          23 :     auto dotPos = child.FindChar('.');
     536          23 :     if (dotPos < 0) {
     537           0 :       continue;
     538             :     }
     539             : 
     540          46 :     nsDependentCSubstring provider = Substring(child, 0, dotPos);
     541             : 
     542          23 :     providers.PutEntry(provider);
     543             :   }
     544           1 :   NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(childCount, childArray);
     545             : 
     546             :   // Now we have all providers. Check which one owns |aTableName|.
     547             :   // e.g. The owning lists of provider "google" is defined in
     548             :   // "browser.safebrowsing.provider.google.lists".
     549           4 :   for (auto itr = providers.Iter(); !itr.Done(); itr.Next()) {
     550           3 :     auto entry = itr.Get();
     551           6 :     nsCString provider(entry->GetKey());
     552           6 :     nsPrintfCString owninListsPref("%s.lists", provider.get());
     553             : 
     554           6 :     nsXPIDLCString owningLists;
     555           6 :     nsresult rv = prefBranch->GetCharPref(owninListsPref.get(),
     556           6 :                                           getter_Copies(owningLists));
     557           3 :     if (NS_FAILED(rv)) {
     558           0 :       continue;
     559             :     }
     560             : 
     561             :     // We've got the owning lists (represented as string) of |provider|.
     562             :     // Build the dictionary for the owning list and the current provider.
     563           6 :     nsTArray<nsCString> tables;
     564           3 :     Classifier::SplitTables(owningLists, tables);
     565          27 :     for (auto tableName : tables) {
     566          24 :       aDict.Put(tableName, new nsCString(provider));
     567             :     }
     568             :   }
     569             : 
     570           1 :   return NS_OK;
     571             : }
     572             : 
     573             : nsresult
     574           1 : nsUrlClassifierUtils::CanonicalizeHostname(const nsACString & hostname,
     575             :                                            nsACString & _retval)
     576             : {
     577           2 :   nsAutoCString unescaped;
     578           2 :   if (!NS_UnescapeURL(PromiseFlatCString(hostname).get(),
     579           2 :                       PromiseFlatCString(hostname).Length(),
     580             :                       0, unescaped)) {
     581           1 :     unescaped.Assign(hostname);
     582             :   }
     583             : 
     584           2 :   nsAutoCString cleaned;
     585           1 :   CleanupHostname(unescaped, cleaned);
     586             : 
     587           2 :   nsAutoCString temp;
     588           1 :   ParseIPAddress(cleaned, temp);
     589           1 :   if (!temp.IsEmpty()) {
     590           0 :     cleaned.Assign(temp);
     591             :   }
     592             : 
     593           1 :   ToLowerCase(cleaned);
     594           1 :   SpecialEncode(cleaned, false, _retval);
     595             : 
     596           2 :   return NS_OK;
     597             : }
     598             : 
     599             : 
     600             : nsresult
     601           1 : nsUrlClassifierUtils::CanonicalizePath(const nsACString & path,
     602             :                                        nsACString & _retval)
     603             : {
     604           1 :   _retval.Truncate();
     605             : 
     606           2 :   nsAutoCString decodedPath(path);
     607           2 :   nsAutoCString temp;
     608           1 :   while (NS_UnescapeURL(decodedPath.get(), decodedPath.Length(), 0, temp)) {
     609           0 :     decodedPath.Assign(temp);
     610           0 :     temp.Truncate();
     611             :   }
     612             : 
     613           1 :   SpecialEncode(decodedPath, true, _retval);
     614             :   // XXX: lowercase the path?
     615             : 
     616           2 :   return NS_OK;
     617             : }
     618             : 
     619             : void
     620           1 : nsUrlClassifierUtils::CleanupHostname(const nsACString & hostname,
     621             :                                       nsACString & _retval)
     622             : {
     623           1 :   _retval.Truncate();
     624             : 
     625           1 :   const char* curChar = hostname.BeginReading();
     626           1 :   const char* end = hostname.EndReading();
     627           1 :   char lastChar = '\0';
     628          19 :   while (curChar != end) {
     629           9 :     unsigned char c = static_cast<unsigned char>(*curChar);
     630           9 :     if (c == '.' && (lastChar == '\0' || lastChar == '.')) {
     631             :       // skip
     632             :     } else {
     633           9 :       _retval.Append(*curChar);
     634             :     }
     635           9 :     lastChar = c;
     636           9 :     ++curChar;
     637             :   }
     638             : 
     639             :   // cut off trailing dots
     640           0 :   while (_retval.Length() > 0 && _retval[_retval.Length() - 1] == '.') {
     641           0 :     _retval.SetLength(_retval.Length() - 1);
     642             :   }
     643           1 : }
     644             : 
     645             : void
     646           1 : nsUrlClassifierUtils::ParseIPAddress(const nsACString & host,
     647             :                                      nsACString & _retval)
     648             : {
     649           1 :   _retval.Truncate();
     650           1 :   nsACString::const_iterator iter, end;
     651           1 :   host.BeginReading(iter);
     652           1 :   host.EndReading(end);
     653             : 
     654           1 :   if (host.Length() <= 15) {
     655             :     // The Windows resolver allows a 4-part dotted decimal IP address to
     656             :     // have a space followed by any old rubbish, so long as the total length
     657             :     // of the string doesn't get above 15 characters. So, "10.192.95.89 xy"
     658             :     // is resolved to 10.192.95.89.
     659             :     // If the string length is greater than 15 characters, e.g.
     660             :     // "10.192.95.89 xy.wildcard.example.com", it will be resolved through
     661             :     // DNS.
     662             : 
     663           1 :     if (FindCharInReadable(' ', iter, end)) {
     664           0 :       end = iter;
     665             :     }
     666             :   }
     667             : 
     668           1 :   for (host.BeginReading(iter); iter != end; iter++) {
     669           1 :     if (!(isxdigit(*iter) || *iter == 'x' || *iter == 'X' || *iter == '.')) {
     670             :       // not an IP
     671           1 :       return;
     672             :     }
     673             :   }
     674             : 
     675           0 :   host.BeginReading(iter);
     676           0 :   nsTArray<nsCString> parts;
     677           0 :   ParseString(PromiseFlatCString(Substring(iter, end)), '.', parts);
     678           0 :   if (parts.Length() > 4) {
     679           0 :     return;
     680             :   }
     681             : 
     682             :   // If any potentially-octal numbers (start with 0 but not hex) have
     683             :   // non-octal digits, no part of the ip can be in octal
     684             :   // XXX: this came from the old javascript implementation, is it really
     685             :   // supposed to be like this?
     686           0 :   bool allowOctal = true;
     687             :   uint32_t i;
     688             : 
     689           0 :   for (i = 0; i < parts.Length(); i++) {
     690           0 :     const nsCString& part = parts[i];
     691           0 :     if (part[0] == '0') {
     692           0 :       for (uint32_t j = 1; j < part.Length(); j++) {
     693           0 :         if (part[j] == 'x') {
     694           0 :           break;
     695             :         }
     696           0 :         if (part[j] == '8' || part[j] == '9') {
     697           0 :           allowOctal = false;
     698           0 :           break;
     699             :         }
     700             :       }
     701             :     }
     702             :   }
     703             : 
     704           0 :   for (i = 0; i < parts.Length(); i++) {
     705           0 :     nsAutoCString canonical;
     706             : 
     707           0 :     if (i == parts.Length() - 1) {
     708           0 :       CanonicalNum(parts[i], 5 - parts.Length(), allowOctal, canonical);
     709             :     } else {
     710           0 :       CanonicalNum(parts[i], 1, allowOctal, canonical);
     711             :     }
     712             : 
     713           0 :     if (canonical.IsEmpty()) {
     714           0 :       _retval.Truncate();
     715           0 :       return;
     716             :     }
     717             : 
     718           0 :     if (_retval.IsEmpty()) {
     719           0 :       _retval.Assign(canonical);
     720             :     } else {
     721           0 :       _retval.Append('.');
     722           0 :       _retval.Append(canonical);
     723             :     }
     724             :   }
     725           0 :   return;
     726             : }
     727             : 
     728             : void
     729           0 : nsUrlClassifierUtils::CanonicalNum(const nsACString& num,
     730             :                                    uint32_t bytes,
     731             :                                    bool allowOctal,
     732             :                                    nsACString& _retval)
     733             : {
     734           0 :   _retval.Truncate();
     735             : 
     736           0 :   if (num.Length() < 1) {
     737           0 :     return;
     738             :   }
     739             : 
     740             :   uint32_t val;
     741           0 :   if (allowOctal && IsOctal(num)) {
     742           0 :     if (PR_sscanf(PromiseFlatCString(num).get(), "%o", &val) != 1) {
     743           0 :       return;
     744             :     }
     745           0 :   } else if (IsDecimal(num)) {
     746           0 :     if (PR_sscanf(PromiseFlatCString(num).get(), "%u", &val) != 1) {
     747           0 :       return;
     748             :     }
     749           0 :   } else if (IsHex(num)) {
     750           0 :   if (PR_sscanf(PromiseFlatCString(num).get(), num[1] == 'X' ? "0X%x" : "0x%x",
     751             :                 &val) != 1) {
     752           0 :       return;
     753             :     }
     754             :   } else {
     755           0 :     return;
     756             :   }
     757             : 
     758           0 :   while (bytes--) {
     759             :     char buf[20];
     760           0 :     SprintfLiteral(buf, "%u", val & 0xff);
     761           0 :     if (_retval.IsEmpty()) {
     762           0 :       _retval.Assign(buf);
     763             :     } else {
     764           0 :       _retval = nsDependentCString(buf) + NS_LITERAL_CSTRING(".") + _retval;
     765             :     }
     766           0 :     val >>= 8;
     767             :   }
     768             : }
     769             : 
     770             : // This function will encode all "special" characters in typical url
     771             : // encoding, that is %hh where h is a valid hex digit.  It will also fold
     772             : // any duplicated slashes.
     773             : bool
     774           2 : nsUrlClassifierUtils::SpecialEncode(const nsACString & url,
     775             :                                     bool foldSlashes,
     776             :                                     nsACString & _retval)
     777             : {
     778           2 :   bool changed = false;
     779           2 :   const char* curChar = url.BeginReading();
     780           2 :   const char* end = url.EndReading();
     781             : 
     782           2 :   unsigned char lastChar = '\0';
     783          44 :   while (curChar != end) {
     784          21 :     unsigned char c = static_cast<unsigned char>(*curChar);
     785          21 :     if (ShouldURLEscape(c)) {
     786           0 :       _retval.Append('%');
     787           0 :       _retval.Append(int_to_hex_digit(c / 16));
     788           0 :       _retval.Append(int_to_hex_digit(c % 16));
     789             : 
     790           0 :       changed = true;
     791          21 :     } else if (foldSlashes && (c == '/' && lastChar == '/')) {
     792             :       // skip
     793             :     } else {
     794          21 :       _retval.Append(*curChar);
     795             :     }
     796          21 :     lastChar = c;
     797          21 :     curChar++;
     798             :   }
     799           2 :   return changed;
     800             : }
     801             : 
     802             : bool
     803          21 : nsUrlClassifierUtils::ShouldURLEscape(const unsigned char c) const
     804             : {
     805          21 :   return c <= 32 || c == '%' || c >=127;
     806             : }

Generated by: LCOV version 1.13