LCOV - code coverage report
Current view: directory - toolkit/components/url-classifier - ProtocolParser.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 377 231 61.3 %
Date: 2012-04-21 Functions: 24 19 79.2 %

       1                 : //* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is Url Classifier code
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * the Mozilla Foundation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2011
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   Dave Camp <dcamp@mozilla.com>
      24                 :  *   Gian-Carlo Pascutto <gpascutto@mozilla.com>
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      28                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : 
      40                 : //* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      41                 : #include "ProtocolParser.h"
      42                 : #include "LookupCache.h"
      43                 : #include "nsIKeyModule.h"
      44                 : #include "nsNetCID.h"
      45                 : #include "prlog.h"
      46                 : #include "prnetdb.h"
      47                 : #include "prprf.h"
      48                 : 
      49                 : #include "nsUrlClassifierUtils.h"
      50                 : 
      51                 : // NSPR_LOG_MODULES=UrlClassifierDbService:5
      52                 : extern PRLogModuleInfo *gUrlClassifierDbServiceLog;
      53                 : #if defined(PR_LOGGING)
      54                 : #define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args)
      55                 : #define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4)
      56                 : #else
      57                 : #define LOG(args)
      58                 : #define LOG_ENABLED() (PR_FALSE)
      59                 : #endif
      60                 : 
      61                 : namespace mozilla {
      62                 : namespace safebrowsing {
      63                 : 
      64                 : // Updates will fail if fed chunks larger than this
      65                 : const uint32 MAX_CHUNK_SIZE = (1024 * 1024);
      66                 : 
      67                 : const uint32 DOMAIN_SIZE = 4;
      68                 : 
      69                 : // Parse one stringified range of chunks of the form "n" or "n-m" from a
      70                 : // comma-separated list of chunks.  Upon return, 'begin' will point to the
      71                 : // next range of chunks in the list of chunks.
      72                 : static bool
      73               9 : ParseChunkRange(nsACString::const_iterator& aBegin,
      74                 :                 const nsACString::const_iterator& aEnd,
      75                 :                 PRUint32* aFirst, PRUint32* aLast)
      76                 : {
      77               9 :   nsACString::const_iterator iter = aBegin;
      78               9 :   FindCharInReadable(',', iter, aEnd);
      79                 : 
      80              18 :   nsCAutoString element(Substring(aBegin, iter));
      81               9 :   aBegin = iter;
      82               9 :   if (aBegin != aEnd)
      83               2 :     aBegin++;
      84                 : 
      85               9 :   PRUint32 numRead = PR_sscanf(element.get(), "%u-%u", aFirst, aLast);
      86               9 :   if (numRead == 2) {
      87               3 :     if (*aFirst > *aLast) {
      88               0 :       PRUint32 tmp = *aFirst;
      89               0 :       *aFirst = *aLast;
      90               0 :       *aLast = tmp;
      91                 :     }
      92               3 :     return true;
      93                 :   }
      94                 : 
      95               6 :   if (numRead == 1) {
      96               6 :     *aLast = *aFirst;
      97               6 :     return true;
      98                 :   }
      99                 : 
     100               0 :   return false;
     101                 : }
     102                 : 
     103             118 : ProtocolParser::ProtocolParser(PRUint32 aHashKey)
     104                 :     : mState(PROTOCOL_STATE_CONTROL)
     105                 :   , mHashKey(aHashKey)
     106                 :   , mUpdateStatus(NS_OK)
     107                 :   , mUpdateWait(0)
     108                 :   , mResetRequested(false)
     109             118 :   , mRekeyRequested(false)
     110                 : {
     111             118 : }
     112                 : 
     113             236 : ProtocolParser::~ProtocolParser()
     114                 : {
     115             118 :   CleanupUpdates();
     116             118 : }
     117                 : 
     118                 : nsresult
     119              89 : ProtocolParser::Init(nsICryptoHash* aHasher)
     120                 : {
     121              89 :   mCryptoHash = aHasher;
     122              89 :   return NS_OK;
     123                 : }
     124                 : 
     125                 : /**
     126                 :  * Initialize HMAC for the stream.
     127                 :  *
     128                 :  * If serverMAC is empty, the update stream will need to provide a
     129                 :  * server MAC.
     130                 :  */
     131                 : nsresult
     132              11 : ProtocolParser::InitHMAC(const nsACString& aClientKey,
     133                 :                          const nsACString& aServerMAC)
     134                 : {
     135              11 :   mServerMAC = aServerMAC;
     136                 : 
     137                 :   nsresult rv;
     138                 :   nsCOMPtr<nsIKeyObjectFactory> keyObjectFactory(
     139              22 :     do_GetService("@mozilla.org/security/keyobjectfactory;1", &rv));
     140                 : 
     141              11 :   if (NS_FAILED(rv)) {
     142               0 :     NS_WARNING("Failed to get nsIKeyObjectFactory service");
     143               0 :     mUpdateStatus = rv;
     144               0 :     return mUpdateStatus;
     145                 :   }
     146                 : 
     147              22 :   nsCOMPtr<nsIKeyObject> keyObject;
     148              11 :   rv = keyObjectFactory->KeyFromString(nsIKeyObject::HMAC, aClientKey,
     149              11 :                                        getter_AddRefs(keyObject));
     150              11 :   if (NS_FAILED(rv)) {
     151               0 :     NS_WARNING("Failed to create key object, maybe not FIPS compliant?");
     152               0 :     mUpdateStatus = rv;
     153               0 :     return mUpdateStatus;
     154                 :   }
     155                 : 
     156              11 :   mHMAC = do_CreateInstance(NS_CRYPTO_HMAC_CONTRACTID, &rv);
     157              11 :   if (NS_FAILED(rv)) {
     158               0 :     NS_WARNING("Failed to create nsICryptoHMAC instance");
     159               0 :     mUpdateStatus = rv;
     160               0 :     return mUpdateStatus;
     161                 :   }
     162                 : 
     163              11 :   rv = mHMAC->Init(nsICryptoHMAC::SHA1, keyObject);
     164              11 :   if (NS_FAILED(rv)) {
     165               0 :     NS_WARNING("Failed to initialize nsICryptoHMAC instance");
     166               0 :     mUpdateStatus = rv;
     167               0 :     return mUpdateStatus;
     168                 :   }
     169              11 :   return NS_OK;
     170                 : }
     171                 : 
     172                 : nsresult
     173              87 : ProtocolParser::FinishHMAC()
     174                 : {
     175              87 :   if (NS_FAILED(mUpdateStatus)) {
     176               4 :     return mUpdateStatus;
     177                 :   }
     178                 : 
     179              83 :   if (mRekeyRequested) {
     180               1 :     mUpdateStatus = NS_ERROR_FAILURE;
     181               1 :     return mUpdateStatus;
     182                 :   }
     183                 : 
     184              82 :   if (!mHMAC) {
     185              74 :     return NS_OK;
     186                 :   }
     187                 : 
     188              16 :   nsCAutoString clientMAC;
     189               8 :   mHMAC->Finish(PR_TRUE, clientMAC);
     190                 : 
     191               8 :   if (clientMAC != mServerMAC) {
     192               2 :     NS_WARNING("Invalid update MAC!");
     193               2 :     LOG(("Invalid update MAC: expected %s, got %s",
     194                 :          clientMAC.get(), mServerMAC.get()));
     195               2 :     mUpdateStatus = NS_ERROR_FAILURE;
     196                 :   }
     197               8 :   return mUpdateStatus;
     198                 : }
     199                 : 
     200                 : void
     201              87 : ProtocolParser::SetCurrentTable(const nsACString& aTable)
     202                 : {
     203              87 :   mTableUpdate = GetTableUpdate(aTable);
     204              87 : }
     205                 : 
     206                 : nsresult
     207              87 : ProtocolParser::AppendStream(const nsACString& aData)
     208                 : {
     209              87 :   if (NS_FAILED(mUpdateStatus))
     210               0 :     return mUpdateStatus;
     211                 : 
     212                 :   nsresult rv;
     213                 : 
     214                 :   // Digest the data if we have a server MAC.
     215              87 :   if (mHMAC && !mServerMAC.IsEmpty()) {
     216               8 :     rv = mHMAC->Update(reinterpret_cast<const PRUint8*>(aData.BeginReading()),
     217               8 :                        aData.Length());
     218               4 :     if (NS_FAILED(rv)) {
     219               0 :       mUpdateStatus = rv;
     220               0 :       return rv;
     221                 :     }
     222                 :   }
     223                 : 
     224              87 :   mPending.Append(aData);
     225                 : 
     226              87 :   bool done = false;
     227             427 :   while (!done) {
     228             257 :     if (mState == PROTOCOL_STATE_CONTROL) {
     229             172 :       rv = ProcessControl(&done);
     230              85 :     } else if (mState == PROTOCOL_STATE_CHUNK) {
     231              85 :       rv = ProcessChunk(&done);
     232                 :     } else {
     233               0 :       NS_ERROR("Unexpected protocol state");
     234               0 :       rv = NS_ERROR_FAILURE;
     235                 :     }
     236             257 :     if (NS_FAILED(rv)) {
     237               4 :       mUpdateStatus = rv;
     238               4 :       return rv;
     239                 :     }
     240                 :   }
     241              83 :   return NS_OK;
     242                 : }
     243                 : 
     244                 : nsresult
     245             172 : ProtocolParser::ProcessControl(bool* aDone)
     246                 : {
     247                 :   nsresult rv;
     248                 : 
     249             344 :   nsCAutoString line;
     250             172 :   *aDone = true;
     251             619 :   while (NextLine(line)) {
     252                 :     //LOG(("Processing %s\n", line.get()));
     253                 : 
     254             365 :     if (line.EqualsLiteral("e:pleaserekey")) {
     255               1 :       mRekeyRequested = true;
     256               1 :       return NS_OK;
     257             364 :     } else if (mHMAC && mServerMAC.IsEmpty()) {
     258               6 :       rv = ProcessMAC(line);
     259               6 :       NS_ENSURE_SUCCESS(rv, rv);
     260             358 :     } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("i:"))) {
     261              75 :       SetCurrentTable(Substring(line, 2));
     262             283 :     } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("n:"))) {
     263              86 :       if (PR_sscanf(line.get(), "n:%d", &mUpdateWait) != 1) {
     264               0 :         LOG(("Error parsing n: '%s' (%d)", line.get(), mUpdateWait));
     265               0 :         mUpdateWait = 0;
     266                 :       }
     267             197 :     } else if (line.EqualsLiteral("r:pleasereset")) {
     268               1 :       mResetRequested = true;
     269             196 :     } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("u:"))) {
     270              17 :       rv = ProcessForward(line);
     271              17 :       NS_ENSURE_SUCCESS(rv, rv);
     272             578 :     } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("a:")) ||
     273             399 :                StringBeginsWith(line, NS_LITERAL_CSTRING("s:"))) {
     274              87 :       rv = ProcessChunkControl(line);
     275              87 :       NS_ENSURE_SUCCESS(rv, rv);
     276              85 :       *aDone = false;
     277              85 :       return NS_OK;
     278             360 :     } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("ad:")) ||
     279             268 :                StringBeginsWith(line, NS_LITERAL_CSTRING("sd:"))) {
     280               7 :       rv = ProcessExpirations(line);
     281               7 :       NS_ENSURE_SUCCESS(rv, rv);
     282                 :     }
     283                 :   }
     284                 : 
     285              82 :   *aDone = true;
     286              82 :   return NS_OK;
     287                 : }
     288                 : 
     289                 : 
     290                 : nsresult
     291               6 : ProtocolParser::ProcessMAC(const nsCString& aLine)
     292                 : {
     293                 :   nsresult rv;
     294                 : 
     295               6 :   LOG(("line: %s", aLine.get()));
     296                 : 
     297               6 :   if (StringBeginsWith(aLine, NS_LITERAL_CSTRING("m:"))) {
     298               5 :     mServerMAC = Substring(aLine, 2);
     299               5 :     nsUrlClassifierUtils::UnUrlsafeBase64(mServerMAC);
     300                 : 
     301                 :     // The remainder of the pending update wasn't digested, digest it now.
     302              10 :     rv = mHMAC->Update(reinterpret_cast<const PRUint8*>(mPending.BeginReading()),
     303              10 :                        mPending.Length());
     304               5 :     return rv;
     305                 :   }
     306                 : 
     307               1 :   LOG(("No MAC specified!"));
     308               1 :   return NS_ERROR_FAILURE;
     309                 : }
     310                 : 
     311                 : nsresult
     312               7 : ProtocolParser::ProcessExpirations(const nsCString& aLine)
     313                 : {
     314               7 :   if (!mTableUpdate) {
     315               0 :     NS_WARNING("Got an expiration without a table.");
     316               0 :     return NS_ERROR_FAILURE;
     317                 :   }
     318              14 :   const nsCSubstring &list = Substring(aLine, 3);
     319               7 :   nsACString::const_iterator begin, end;
     320               7 :   list.BeginReading(begin);
     321               7 :   list.EndReading(end);
     322               7 :   while (begin != end) {
     323                 :     PRUint32 first, last;
     324               9 :     if (ParseChunkRange(begin, end, &first, &last)) {
     325              24 :       for (PRUint32 num = first; num <= last; num++) {
     326              15 :         if (aLine[0] == 'a')
     327               9 :           mTableUpdate->NewAddExpiration(num);
     328                 :         else
     329               6 :           mTableUpdate->NewSubExpiration(num);
     330                 :       }
     331                 :     } else {
     332               0 :       return NS_ERROR_FAILURE;
     333                 :     }
     334                 :   }
     335               7 :   return NS_OK;
     336                 : }
     337                 : 
     338                 : nsresult
     339              87 : ProtocolParser::ProcessChunkControl(const nsCString& aLine)
     340                 : {
     341              87 :   if (!mTableUpdate) {
     342               0 :     NS_WARNING("Got a chunk before getting a table.");
     343               0 :     return NS_ERROR_FAILURE;
     344                 :   }
     345                 : 
     346              87 :   mState = PROTOCOL_STATE_CHUNK;
     347                 :   char command;
     348                 : 
     349              87 :   mChunkState.Clear();
     350                 : 
     351              87 :   if (PR_sscanf(aLine.get(),
     352                 :                 "%c:%d:%d:%d",
     353                 :                 &command,
     354              87 :                 &mChunkState.num, &mChunkState.hashSize, &mChunkState.length)
     355                 :       != 4)
     356                 :   {
     357               1 :     return NS_ERROR_FAILURE;
     358                 :   }
     359                 : 
     360              86 :   if (mChunkState.length > MAX_CHUNK_SIZE) {
     361               0 :     return NS_ERROR_FAILURE;
     362                 :   }
     363                 : 
     364              86 :   if (!(mChunkState.hashSize == PREFIX_SIZE || mChunkState.hashSize == COMPLETE_SIZE)) {
     365               1 :     NS_WARNING("Invalid hash size specified in update.");
     366               1 :     return NS_ERROR_FAILURE;
     367                 :   }
     368                 : 
     369              85 :   mChunkState.type = (command == 'a') ? CHUNK_ADD : CHUNK_SUB;
     370                 : 
     371              85 :   if (mChunkState.type == CHUNK_ADD) {
     372              67 :     mTableUpdate->NewAddChunk(mChunkState.num);
     373                 :   } else {
     374              18 :     mTableUpdate->NewSubChunk(mChunkState.num);
     375                 :   }
     376                 : 
     377              85 :   return NS_OK;
     378                 : }
     379                 : 
     380                 : nsresult
     381              17 : ProtocolParser::ProcessForward(const nsCString& aLine)
     382                 : {
     383              34 :   const nsCSubstring &forward = Substring(aLine, 2);
     384              17 :   if (mHMAC) {
     385                 :     // We're expecting MACs alongside any url forwards.
     386               7 :     nsCSubstring::const_iterator begin, end, sepBegin, sepEnd;
     387               7 :     forward.BeginReading(begin);
     388               7 :     sepBegin = begin;
     389                 : 
     390               7 :     forward.EndReading(end);
     391               7 :     sepEnd = end;
     392                 : 
     393               7 :     if (!RFindInReadable(NS_LITERAL_CSTRING(","), sepBegin, sepEnd)) {
     394               1 :       NS_WARNING("No MAC specified for a redirect in a request that expects a MAC");
     395               1 :       return NS_ERROR_FAILURE;
     396                 :     }
     397                 : 
     398              12 :     nsCString serverMAC(Substring(sepEnd, end));
     399               6 :     nsUrlClassifierUtils::UnUrlsafeBase64(serverMAC);
     400               6 :     return AddForward(Substring(begin, sepBegin), serverMAC);
     401                 :   }
     402              10 :   return AddForward(forward, mServerMAC);
     403                 : }
     404                 : 
     405                 : nsresult
     406              16 : ProtocolParser::AddForward(const nsACString& aUrl, const nsACString& aMac)
     407                 : {
     408              16 :   if (!mTableUpdate) {
     409               0 :     NS_WARNING("Forward without a table name.");
     410               0 :     return NS_ERROR_FAILURE;
     411                 :   }
     412                 : 
     413              16 :   ForwardedUpdate *forward = mForwards.AppendElement();
     414              16 :   forward->table = mTableUpdate->TableName();
     415              16 :   forward->url.Assign(aUrl);
     416              16 :   forward->mac.Assign(aMac);
     417                 : 
     418              16 :   return NS_OK;
     419                 : }
     420                 : 
     421                 : nsresult
     422              85 : ProtocolParser::ProcessChunk(bool* aDone)
     423                 : {
     424              85 :   if (!mTableUpdate) {
     425               0 :     NS_WARNING("Processing chunk without an active table.");
     426               0 :     return NS_ERROR_FAILURE;
     427                 :   }
     428                 : 
     429              85 :   NS_ASSERTION(mChunkState.num != 0, "Must have a chunk number.");
     430                 : 
     431              85 :   if (mPending.Length() < mChunkState.length) {
     432               0 :     *aDone = true;
     433               0 :     return NS_OK;
     434                 :   }
     435                 : 
     436                 :   // Pull the chunk out of the pending stream data.
     437             170 :   nsCAutoString chunk;
     438              85 :   chunk.Assign(Substring(mPending, 0, mChunkState.length));
     439              85 :   mPending = Substring(mPending, mChunkState.length);
     440                 : 
     441              85 :   *aDone = false;
     442              85 :   mState = PROTOCOL_STATE_CONTROL;
     443                 : 
     444                 :   //LOG(("Handling a %d-byte chunk", chunk.Length()));
     445              85 :   if (StringEndsWith(mTableUpdate->TableName(), NS_LITERAL_CSTRING("-shavar"))) {
     446               0 :     return ProcessShaChunk(chunk);
     447                 :   } else {
     448              85 :     return ProcessPlaintextChunk(chunk);
     449                 :   }
     450                 : }
     451                 : 
     452                 : /**
     453                 :  * Process a plaintext chunk (currently only used in unit tests).
     454                 :  */
     455                 : nsresult
     456              85 : ProtocolParser::ProcessPlaintextChunk(const nsACString& aChunk)
     457                 : {
     458              85 :   if (!mTableUpdate) {
     459               0 :     NS_WARNING("Chunk received with no table.");
     460               0 :     return NS_ERROR_FAILURE;
     461                 :   }
     462                 : 
     463                 :   nsresult rv;
     464             170 :   nsTArray<nsCString> lines;
     465              85 :   ParseString(PromiseFlatCString(aChunk), '\n', lines);
     466                 : 
     467                 :   // non-hashed tables need to be hashed
     468             250 :   for (uint32 i = 0; i < lines.Length(); i++) {
     469             165 :     nsCString& line = lines[i];
     470                 : 
     471             165 :     if (mChunkState.type == CHUNK_ADD) {
     472             140 :       if (mChunkState.hashSize == COMPLETE_SIZE) {
     473                 :         Completion hash;
     474              95 :         hash.FromPlaintext(line, mCryptoHash);
     475              95 :         mTableUpdate->NewAddComplete(mChunkState.num, hash);
     476                 :       } else {
     477              45 :         NS_ASSERTION(mChunkState.hashSize == 4, "Only 32- or 4-byte hashes can be used for add chunks.");
     478                 :         Completion hash;
     479                 :         Completion domHash;
     480                 :         Prefix newHash;
     481              45 :         rv = LookupCache::GetKey(line, &domHash, mCryptoHash);
     482              45 :         NS_ENSURE_SUCCESS(rv, rv);
     483              45 :         hash.FromPlaintext(line, mCryptoHash);
     484                 :         PRUint32 codedHash;
     485              45 :         rv = LookupCache::KeyedHash(hash.ToUint32(), domHash.ToUint32(), mHashKey, &codedHash);
     486              45 :         NS_ENSURE_SUCCESS(rv, rv);
     487              45 :         newHash.FromUint32(codedHash);
     488              45 :         mTableUpdate->NewAddPrefix(mChunkState.num, newHash);
     489                 :       }
     490                 :     } else {
     491              25 :       nsCString::const_iterator begin, iter, end;
     492              25 :       line.BeginReading(begin);
     493              25 :       line.EndReading(end);
     494              25 :       iter = begin;
     495                 :       uint32 addChunk;
     496              50 :       if (!FindCharInReadable(':', iter, end) ||
     497              25 :           PR_sscanf(lines[i].get(), "%d:", &addChunk) != 1) {
     498               0 :         NS_WARNING("Received sub chunk without associated add chunk.");
     499               0 :         return NS_ERROR_FAILURE;
     500                 :       }
     501              25 :       iter++;
     502                 : 
     503              25 :       if (mChunkState.hashSize == COMPLETE_SIZE) {
     504                 :         Completion hash;
     505              24 :         hash.FromPlaintext(Substring(iter, end), mCryptoHash);
     506              24 :         mTableUpdate->NewSubComplete(addChunk, hash, mChunkState.num);
     507                 :       } else {
     508               1 :         NS_ASSERTION(mChunkState.hashSize == 4, "Only 32- or 4-byte hashes can be used for add chunks.");
     509                 :         Prefix hash;
     510                 :         Completion domHash;
     511                 :         Prefix newHash;
     512               1 :         rv = LookupCache::GetKey(Substring(iter, end), &domHash, mCryptoHash);
     513               1 :         NS_ENSURE_SUCCESS(rv, rv);
     514               1 :         hash.FromPlaintext(Substring(iter, end), mCryptoHash);
     515                 :         PRUint32 codedHash;
     516               1 :         rv = LookupCache::KeyedHash(hash.ToUint32(), domHash.ToUint32(), mHashKey, &codedHash);
     517               1 :         NS_ENSURE_SUCCESS(rv, rv);
     518               1 :         newHash.FromUint32(codedHash);
     519               1 :         mTableUpdate->NewSubPrefix(addChunk, newHash, mChunkState.num);
     520                 :         // Needed to knock out completes
     521                 :         // Fake chunk nr, will cause it to be removed next update
     522               1 :         mTableUpdate->NewSubPrefix(addChunk, hash, 0);
     523               1 :         mTableUpdate->NewSubChunk(0);
     524                 :       }
     525                 :     }
     526                 :   }
     527                 : 
     528              85 :   return NS_OK;
     529                 : }
     530                 : 
     531                 : nsresult
     532               0 : ProtocolParser::ProcessShaChunk(const nsACString& aChunk)
     533                 : {
     534               0 :   PRUint32 start = 0;
     535               0 :   while (start < aChunk.Length()) {
     536                 :     // First four bytes are the domain key.
     537                 :     Prefix domain;
     538               0 :     domain.Assign(Substring(aChunk, start, DOMAIN_SIZE));
     539               0 :     start += DOMAIN_SIZE;
     540                 : 
     541                 :     // Then a count of entries.
     542               0 :     uint8 numEntries = static_cast<uint8>(aChunk[start]);
     543               0 :     start++;
     544                 : 
     545                 :     nsresult rv;
     546               0 :     if (mChunkState.type == CHUNK_ADD && mChunkState.hashSize == PREFIX_SIZE) {
     547               0 :       rv = ProcessHostAdd(domain, numEntries, aChunk, &start);
     548               0 :     } else if (mChunkState.type == CHUNK_ADD && mChunkState.hashSize == COMPLETE_SIZE) {
     549               0 :       rv = ProcessHostAddComplete(numEntries, aChunk, &start);
     550               0 :     } else if (mChunkState.type == CHUNK_SUB && mChunkState.hashSize == PREFIX_SIZE) {
     551               0 :       rv = ProcessHostSub(domain, numEntries, aChunk, &start);
     552               0 :     } else if (mChunkState.type == CHUNK_SUB && mChunkState.hashSize == COMPLETE_SIZE) {
     553               0 :       rv = ProcessHostSubComplete(numEntries, aChunk, &start);
     554                 :     } else {
     555               0 :       NS_WARNING("Unexpected chunk type/hash size!");
     556               0 :       LOG(("Got an unexpected chunk type/hash size: %s:%d",
     557                 :            mChunkState.type == CHUNK_ADD ? "add" : "sub",
     558                 :            mChunkState.hashSize));
     559               0 :       return NS_ERROR_FAILURE;
     560                 :     }
     561               0 :     NS_ENSURE_SUCCESS(rv, rv);
     562                 :   }
     563                 : 
     564               0 :   return NS_OK;
     565                 : }
     566                 : 
     567                 : nsresult
     568               0 : ProtocolParser::ProcessHostAdd(const Prefix& aDomain, PRUint8 aNumEntries,
     569                 :                                const nsACString& aChunk, PRUint32* aStart)
     570                 : {
     571               0 :   NS_ASSERTION(mChunkState.hashSize == PREFIX_SIZE,
     572                 :                "ProcessHostAdd should only be called for prefix hashes.");
     573                 : 
     574                 :   PRUint32 codedHash;
     575               0 :   PRUint32 domHash = aDomain.ToUint32();
     576                 : 
     577               0 :   if (aNumEntries == 0) {
     578               0 :     nsresult rv = LookupCache::KeyedHash(domHash, domHash, mHashKey, &codedHash);
     579               0 :     NS_ENSURE_SUCCESS(rv, rv);
     580                 :     Prefix newHash;
     581               0 :     newHash.FromUint32(codedHash);
     582               0 :     mTableUpdate->NewAddPrefix(mChunkState.num, newHash);
     583               0 :     return NS_OK;
     584                 :   }
     585                 : 
     586               0 :   if (*aStart + (PREFIX_SIZE * aNumEntries) > aChunk.Length()) {
     587               0 :     NS_WARNING("Chunk is not long enough to contain the expected entries.");
     588               0 :     return NS_ERROR_FAILURE;
     589                 :   }
     590                 : 
     591               0 :   for (uint8 i = 0; i < aNumEntries; i++) {
     592                 :     Prefix hash;
     593               0 :     hash.Assign(Substring(aChunk, *aStart, PREFIX_SIZE));
     594               0 :     nsresult rv = LookupCache::KeyedHash(domHash, hash.ToUint32(), mHashKey, &codedHash);
     595               0 :     NS_ENSURE_SUCCESS(rv, rv);
     596                 :     Prefix newHash;
     597               0 :     newHash.FromUint32(codedHash);
     598               0 :     mTableUpdate->NewAddPrefix(mChunkState.num, newHash);
     599               0 :     *aStart += PREFIX_SIZE;
     600                 :   }
     601                 : 
     602               0 :   return NS_OK;
     603                 : }
     604                 : 
     605                 : nsresult
     606               0 : ProtocolParser::ProcessHostSub(const Prefix& aDomain, PRUint8 aNumEntries,
     607                 :                                const nsACString& aChunk, PRUint32 *aStart)
     608                 : {
     609               0 :   NS_ASSERTION(mChunkState.hashSize == PREFIX_SIZE,
     610                 :                "ProcessHostSub should only be called for prefix hashes.");
     611                 : 
     612                 :   PRUint32 codedHash;
     613               0 :   PRUint32 domHash = aDomain.ToUint32();
     614                 : 
     615               0 :   if (aNumEntries == 0) {
     616               0 :     if ((*aStart) + 4 > aChunk.Length()) {
     617               0 :       NS_WARNING("Received a zero-entry sub chunk without an associated add.");
     618               0 :       return NS_ERROR_FAILURE;
     619                 :     }
     620                 : 
     621               0 :     const nsCSubstring& addChunkStr = Substring(aChunk, *aStart, 4);
     622               0 :     *aStart += 4;
     623                 : 
     624                 :     uint32 addChunk;
     625               0 :     memcpy(&addChunk, addChunkStr.BeginReading(), 4);
     626               0 :     addChunk = PR_ntohl(addChunk);
     627                 : 
     628               0 :     nsresult rv = LookupCache::KeyedHash(domHash, domHash, mHashKey, &codedHash);
     629               0 :     NS_ENSURE_SUCCESS(rv, rv);
     630                 :     Prefix newHash;
     631               0 :     newHash.FromUint32(codedHash);
     632                 : 
     633               0 :     mTableUpdate->NewSubPrefix(addChunk, newHash, mChunkState.num);
     634                 :     // Needed to knock out completes
     635                 :     // Fake chunk nr, will cause it to be removed next update
     636               0 :     mTableUpdate->NewSubPrefix(addChunk, aDomain, 0);
     637               0 :     mTableUpdate->NewSubChunk(0);
     638               0 :     return NS_OK;
     639                 :   }
     640                 : 
     641               0 :   if (*aStart + ((PREFIX_SIZE + 4) * aNumEntries) > aChunk.Length()) {
     642               0 :     NS_WARNING("Chunk is not long enough to contain the expected entries.");
     643               0 :     return NS_ERROR_FAILURE;
     644                 :   }
     645                 : 
     646               0 :   for (uint8 i = 0; i < aNumEntries; i++) {
     647               0 :     const nsCSubstring& addChunkStr = Substring(aChunk, *aStart, 4);
     648               0 :     *aStart += 4;
     649                 : 
     650                 :     uint32 addChunk;
     651               0 :     memcpy(&addChunk, addChunkStr.BeginReading(), 4);
     652               0 :     addChunk = PR_ntohl(addChunk);
     653                 : 
     654                 :     Prefix prefix;
     655               0 :     prefix.Assign(Substring(aChunk, *aStart, PREFIX_SIZE));
     656               0 :     *aStart += PREFIX_SIZE;
     657                 : 
     658               0 :     nsresult rv = LookupCache::KeyedHash(prefix.ToUint32(), domHash, mHashKey, &codedHash);
     659               0 :     NS_ENSURE_SUCCESS(rv, rv);
     660                 :     Prefix newHash;
     661               0 :     newHash.FromUint32(codedHash);
     662                 : 
     663               0 :     mTableUpdate->NewSubPrefix(addChunk, newHash, mChunkState.num);
     664                 :     // Needed to knock out completes
     665                 :     // Fake chunk nr, will cause it to be removed next update
     666               0 :     mTableUpdate->NewSubPrefix(addChunk, prefix, 0);
     667               0 :     mTableUpdate->NewSubChunk(0);
     668                 :   }
     669                 : 
     670               0 :   return NS_OK;
     671                 : }
     672                 : 
     673                 : nsresult
     674               0 : ProtocolParser::ProcessHostAddComplete(PRUint8 aNumEntries,
     675                 :                                        const nsACString& aChunk, PRUint32* aStart)
     676                 : {
     677               0 :   NS_ASSERTION(mChunkState.hashSize == COMPLETE_SIZE,
     678                 :                "ProcessHostAddComplete should only be called for complete hashes.");
     679                 : 
     680               0 :   if (aNumEntries == 0) {
     681                 :     // this is totally comprehensible.
     682               0 :     NS_WARNING("Expected > 0 entries for a 32-byte hash add.");
     683               0 :     return NS_OK;
     684                 :   }
     685                 : 
     686               0 :   if (*aStart + (COMPLETE_SIZE * aNumEntries) > aChunk.Length()) {
     687               0 :     NS_WARNING("Chunk is not long enough to contain the expected entries.");
     688               0 :     return NS_ERROR_FAILURE;
     689                 :   }
     690                 : 
     691               0 :   for (uint8 i = 0; i < aNumEntries; i++) {
     692                 :     Completion hash;
     693               0 :     hash.Assign(Substring(aChunk, *aStart, COMPLETE_SIZE));
     694               0 :     mTableUpdate->NewAddComplete(mChunkState.num, hash);
     695               0 :     *aStart += COMPLETE_SIZE;
     696                 :   }
     697                 : 
     698               0 :   return NS_OK;
     699                 : }
     700                 : 
     701                 : nsresult
     702               0 : ProtocolParser::ProcessHostSubComplete(PRUint8 aNumEntries,
     703                 :                                        const nsACString& aChunk, PRUint32* aStart)
     704                 : {
     705               0 :   NS_ASSERTION(mChunkState.hashSize == COMPLETE_SIZE,
     706                 :                "ProcessHostSubComplete should only be called for complete hashes.");
     707                 : 
     708               0 :   if (aNumEntries == 0) {
     709                 :     // this is totally comprehensible.
     710               0 :     NS_WARNING("Expected > 0 entries for a 32-byte hash sub.");
     711               0 :     return NS_OK;
     712                 :   }
     713                 : 
     714               0 :   if (*aStart + ((COMPLETE_SIZE + 4) * aNumEntries) > aChunk.Length()) {
     715               0 :     NS_WARNING("Chunk is not long enough to contain the expected entries.");
     716               0 :     return NS_ERROR_FAILURE;
     717                 :   }
     718                 : 
     719               0 :   for (PRUint8 i = 0; i < aNumEntries; i++) {
     720                 :     Completion hash;
     721               0 :     hash.Assign(Substring(aChunk, *aStart, COMPLETE_SIZE));
     722               0 :     *aStart += COMPLETE_SIZE;
     723                 : 
     724               0 :     const nsCSubstring& addChunkStr = Substring(aChunk, *aStart, 4);
     725               0 :     *aStart += 4;
     726                 : 
     727                 :     uint32 addChunk;
     728               0 :     memcpy(&addChunk, addChunkStr.BeginReading(), 4);
     729               0 :     addChunk = PR_ntohl(addChunk);
     730                 : 
     731               0 :     mTableUpdate->NewSubComplete(addChunk, hash, mChunkState.num);
     732                 :   }
     733                 : 
     734               0 :   return NS_OK;
     735                 : }
     736                 : 
     737                 : bool
     738             447 : ProtocolParser::NextLine(nsACString& line)
     739                 : {
     740             447 :   int32 newline = mPending.FindChar('\n');
     741             447 :   if (newline == kNotFound) {
     742              82 :     return false;
     743                 :   }
     744             365 :   line.Assign(Substring(mPending, 0, newline));
     745             365 :   mPending = Substring(mPending, newline + 1);
     746             365 :   return true;
     747                 : }
     748                 : 
     749                 : void
     750             118 : ProtocolParser::CleanupUpdates()
     751                 : {
     752             123 :   for (uint32 i = 0; i < mTableUpdates.Length(); i++) {
     753               5 :     delete mTableUpdates[i];
     754                 :   }
     755             118 :   mTableUpdates.Clear();
     756             118 : }
     757                 : 
     758                 : TableUpdate *
     759             133 : ProtocolParser::GetTableUpdate(const nsACString& aTable)
     760                 : {
     761             136 :   for (uint32 i = 0; i < mTableUpdates.Length(); i++) {
     762               3 :     if (aTable.Equals(mTableUpdates[i]->TableName())) {
     763               0 :       return mTableUpdates[i];
     764                 :     }
     765                 :   }
     766                 : 
     767                 :   // We free automatically on destruction, ownership of these
     768                 :   // updates can be transferred to DBServiceWorker, which passes
     769                 :   // them back to Classifier when doing the updates, and that
     770                 :   // will free them.
     771             133 :   TableUpdate *update = new TableUpdate(aTable);
     772             133 :   mTableUpdates.AppendElement(update);
     773             133 :   return update;
     774                 : }
     775                 : 
     776                 : }
     777                 : }

Generated by: LCOV version 1.7