1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/url-classifier/ProtocolParser.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,632 @@ 1.4 +//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "ProtocolParser.h" 1.10 +#include "LookupCache.h" 1.11 +#include "nsNetCID.h" 1.12 +#include "prlog.h" 1.13 +#include "prnetdb.h" 1.14 +#include "prprf.h" 1.15 + 1.16 +#include "nsUrlClassifierUtils.h" 1.17 + 1.18 +// NSPR_LOG_MODULES=UrlClassifierDbService:5 1.19 +extern PRLogModuleInfo *gUrlClassifierDbServiceLog; 1.20 +#if defined(PR_LOGGING) 1.21 +#define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args) 1.22 +#define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4) 1.23 +#else 1.24 +#define LOG(args) 1.25 +#define LOG_ENABLED() (false) 1.26 +#endif 1.27 + 1.28 +namespace mozilla { 1.29 +namespace safebrowsing { 1.30 + 1.31 +// Updates will fail if fed chunks larger than this 1.32 +const uint32_t MAX_CHUNK_SIZE = (1024 * 1024); 1.33 + 1.34 +const uint32_t DOMAIN_SIZE = 4; 1.35 + 1.36 +// Parse one stringified range of chunks of the form "n" or "n-m" from a 1.37 +// comma-separated list of chunks. Upon return, 'begin' will point to the 1.38 +// next range of chunks in the list of chunks. 1.39 +static bool 1.40 +ParseChunkRange(nsACString::const_iterator& aBegin, 1.41 + const nsACString::const_iterator& aEnd, 1.42 + uint32_t* aFirst, uint32_t* aLast) 1.43 +{ 1.44 + nsACString::const_iterator iter = aBegin; 1.45 + FindCharInReadable(',', iter, aEnd); 1.46 + 1.47 + nsAutoCString element(Substring(aBegin, iter)); 1.48 + aBegin = iter; 1.49 + if (aBegin != aEnd) 1.50 + aBegin++; 1.51 + 1.52 + uint32_t numRead = PR_sscanf(element.get(), "%u-%u", aFirst, aLast); 1.53 + if (numRead == 2) { 1.54 + if (*aFirst > *aLast) { 1.55 + uint32_t tmp = *aFirst; 1.56 + *aFirst = *aLast; 1.57 + *aLast = tmp; 1.58 + } 1.59 + return true; 1.60 + } 1.61 + 1.62 + if (numRead == 1) { 1.63 + *aLast = *aFirst; 1.64 + return true; 1.65 + } 1.66 + 1.67 + return false; 1.68 +} 1.69 + 1.70 +ProtocolParser::ProtocolParser() 1.71 + : mState(PROTOCOL_STATE_CONTROL) 1.72 + , mUpdateStatus(NS_OK) 1.73 + , mUpdateWait(0) 1.74 + , mResetRequested(false) 1.75 +{ 1.76 +} 1.77 + 1.78 +ProtocolParser::~ProtocolParser() 1.79 +{ 1.80 + CleanupUpdates(); 1.81 +} 1.82 + 1.83 +nsresult 1.84 +ProtocolParser::Init(nsICryptoHash* aHasher) 1.85 +{ 1.86 + mCryptoHash = aHasher; 1.87 + return NS_OK; 1.88 +} 1.89 + 1.90 +void 1.91 +ProtocolParser::SetCurrentTable(const nsACString& aTable) 1.92 +{ 1.93 + mTableUpdate = GetTableUpdate(aTable); 1.94 +} 1.95 + 1.96 +nsresult 1.97 +ProtocolParser::AppendStream(const nsACString& aData) 1.98 +{ 1.99 + if (NS_FAILED(mUpdateStatus)) 1.100 + return mUpdateStatus; 1.101 + 1.102 + nsresult rv; 1.103 + mPending.Append(aData); 1.104 + 1.105 + bool done = false; 1.106 + while (!done) { 1.107 + if (mState == PROTOCOL_STATE_CONTROL) { 1.108 + rv = ProcessControl(&done); 1.109 + } else if (mState == PROTOCOL_STATE_CHUNK) { 1.110 + rv = ProcessChunk(&done); 1.111 + } else { 1.112 + NS_ERROR("Unexpected protocol state"); 1.113 + rv = NS_ERROR_FAILURE; 1.114 + } 1.115 + if (NS_FAILED(rv)) { 1.116 + mUpdateStatus = rv; 1.117 + return rv; 1.118 + } 1.119 + } 1.120 + return NS_OK; 1.121 +} 1.122 + 1.123 +nsresult 1.124 +ProtocolParser::ProcessControl(bool* aDone) 1.125 +{ 1.126 + nsresult rv; 1.127 + 1.128 + nsAutoCString line; 1.129 + *aDone = true; 1.130 + while (NextLine(line)) { 1.131 + //LOG(("Processing %s\n", line.get())); 1.132 + 1.133 + if (StringBeginsWith(line, NS_LITERAL_CSTRING("i:"))) { 1.134 + // Set the table name from the table header line. 1.135 + SetCurrentTable(Substring(line, 2)); 1.136 + } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("n:"))) { 1.137 + if (PR_sscanf(line.get(), "n:%d", &mUpdateWait) != 1) { 1.138 + LOG(("Error parsing n: '%s' (%d)", line.get(), mUpdateWait)); 1.139 + mUpdateWait = 0; 1.140 + } 1.141 + } else if (line.EqualsLiteral("r:pleasereset")) { 1.142 + mResetRequested = true; 1.143 + } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("u:"))) { 1.144 + rv = ProcessForward(line); 1.145 + NS_ENSURE_SUCCESS(rv, rv); 1.146 + } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("a:")) || 1.147 + StringBeginsWith(line, NS_LITERAL_CSTRING("s:"))) { 1.148 + rv = ProcessChunkControl(line); 1.149 + NS_ENSURE_SUCCESS(rv, rv); 1.150 + *aDone = false; 1.151 + return NS_OK; 1.152 + } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("ad:")) || 1.153 + StringBeginsWith(line, NS_LITERAL_CSTRING("sd:"))) { 1.154 + rv = ProcessExpirations(line); 1.155 + NS_ENSURE_SUCCESS(rv, rv); 1.156 + } 1.157 + } 1.158 + 1.159 + *aDone = true; 1.160 + return NS_OK; 1.161 +} 1.162 + 1.163 +nsresult 1.164 +ProtocolParser::ProcessExpirations(const nsCString& aLine) 1.165 +{ 1.166 + if (!mTableUpdate) { 1.167 + NS_WARNING("Got an expiration without a table."); 1.168 + return NS_ERROR_FAILURE; 1.169 + } 1.170 + const nsCSubstring &list = Substring(aLine, 3); 1.171 + nsACString::const_iterator begin, end; 1.172 + list.BeginReading(begin); 1.173 + list.EndReading(end); 1.174 + while (begin != end) { 1.175 + uint32_t first, last; 1.176 + if (ParseChunkRange(begin, end, &first, &last)) { 1.177 + for (uint32_t num = first; num <= last; num++) { 1.178 + if (aLine[0] == 'a') 1.179 + mTableUpdate->NewAddExpiration(num); 1.180 + else 1.181 + mTableUpdate->NewSubExpiration(num); 1.182 + } 1.183 + } else { 1.184 + return NS_ERROR_FAILURE; 1.185 + } 1.186 + } 1.187 + return NS_OK; 1.188 +} 1.189 + 1.190 +nsresult 1.191 +ProtocolParser::ProcessChunkControl(const nsCString& aLine) 1.192 +{ 1.193 + if (!mTableUpdate) { 1.194 + NS_WARNING("Got a chunk before getting a table."); 1.195 + return NS_ERROR_FAILURE; 1.196 + } 1.197 + 1.198 + mState = PROTOCOL_STATE_CHUNK; 1.199 + char command; 1.200 + 1.201 + mChunkState.Clear(); 1.202 + 1.203 + if (PR_sscanf(aLine.get(), 1.204 + "%c:%d:%d:%d", 1.205 + &command, 1.206 + &mChunkState.num, &mChunkState.hashSize, &mChunkState.length) 1.207 + != 4) 1.208 + { 1.209 + return NS_ERROR_FAILURE; 1.210 + } 1.211 + 1.212 + if (mChunkState.length > MAX_CHUNK_SIZE) { 1.213 + return NS_ERROR_FAILURE; 1.214 + } 1.215 + 1.216 + if (!(mChunkState.hashSize == PREFIX_SIZE || mChunkState.hashSize == COMPLETE_SIZE)) { 1.217 + NS_WARNING("Invalid hash size specified in update."); 1.218 + return NS_ERROR_FAILURE; 1.219 + } 1.220 + 1.221 + if (StringEndsWith(mTableUpdate->TableName(), 1.222 + NS_LITERAL_CSTRING("-shavar")) || 1.223 + StringEndsWith(mTableUpdate->TableName(), 1.224 + NS_LITERAL_CSTRING("-simple"))) { 1.225 + // Accommodate test tables ending in -simple for now. 1.226 + mChunkState.type = (command == 'a') ? CHUNK_ADD : CHUNK_SUB; 1.227 + } else if (StringEndsWith(mTableUpdate->TableName(), 1.228 + NS_LITERAL_CSTRING("-digest256"))) { 1.229 + LOG(("Processing digest256 data")); 1.230 + mChunkState.type = (command == 'a') ? CHUNK_ADD_DIGEST : CHUNK_SUB_DIGEST; 1.231 + } 1.232 + switch (mChunkState.type) { 1.233 + case CHUNK_ADD: 1.234 + mTableUpdate->NewAddChunk(mChunkState.num); 1.235 + break; 1.236 + case CHUNK_SUB: 1.237 + mTableUpdate->NewSubChunk(mChunkState.num); 1.238 + break; 1.239 + case CHUNK_ADD_DIGEST: 1.240 + mTableUpdate->NewAddChunk(mChunkState.num); 1.241 + break; 1.242 + case CHUNK_SUB_DIGEST: 1.243 + mTableUpdate->NewSubChunk(mChunkState.num); 1.244 + break; 1.245 + } 1.246 + 1.247 + return NS_OK; 1.248 +} 1.249 + 1.250 +nsresult 1.251 +ProtocolParser::ProcessForward(const nsCString& aLine) 1.252 +{ 1.253 + const nsCSubstring &forward = Substring(aLine, 2); 1.254 + return AddForward(forward); 1.255 +} 1.256 + 1.257 +nsresult 1.258 +ProtocolParser::AddForward(const nsACString& aUrl) 1.259 +{ 1.260 + if (!mTableUpdate) { 1.261 + NS_WARNING("Forward without a table name."); 1.262 + return NS_ERROR_FAILURE; 1.263 + } 1.264 + 1.265 + ForwardedUpdate *forward = mForwards.AppendElement(); 1.266 + forward->table = mTableUpdate->TableName(); 1.267 + forward->url.Assign(aUrl); 1.268 + 1.269 + return NS_OK; 1.270 +} 1.271 + 1.272 +nsresult 1.273 +ProtocolParser::ProcessChunk(bool* aDone) 1.274 +{ 1.275 + if (!mTableUpdate) { 1.276 + NS_WARNING("Processing chunk without an active table."); 1.277 + return NS_ERROR_FAILURE; 1.278 + } 1.279 + 1.280 + NS_ASSERTION(mChunkState.num != 0, "Must have a chunk number."); 1.281 + 1.282 + if (mPending.Length() < mChunkState.length) { 1.283 + *aDone = true; 1.284 + return NS_OK; 1.285 + } 1.286 + 1.287 + // Pull the chunk out of the pending stream data. 1.288 + nsAutoCString chunk; 1.289 + chunk.Assign(Substring(mPending, 0, mChunkState.length)); 1.290 + mPending = Substring(mPending, mChunkState.length); 1.291 + 1.292 + *aDone = false; 1.293 + mState = PROTOCOL_STATE_CONTROL; 1.294 + 1.295 + //LOG(("Handling a %d-byte chunk", chunk.Length())); 1.296 + if (StringEndsWith(mTableUpdate->TableName(), 1.297 + NS_LITERAL_CSTRING("-shavar"))) { 1.298 + return ProcessShaChunk(chunk); 1.299 + } 1.300 + if (StringEndsWith(mTableUpdate->TableName(), 1.301 + NS_LITERAL_CSTRING("-digest256"))) { 1.302 + return ProcessDigestChunk(chunk); 1.303 + } 1.304 + return ProcessPlaintextChunk(chunk); 1.305 +} 1.306 + 1.307 +/** 1.308 + * Process a plaintext chunk (currently only used in unit tests). 1.309 + */ 1.310 +nsresult 1.311 +ProtocolParser::ProcessPlaintextChunk(const nsACString& aChunk) 1.312 +{ 1.313 + if (!mTableUpdate) { 1.314 + NS_WARNING("Chunk received with no table."); 1.315 + return NS_ERROR_FAILURE; 1.316 + } 1.317 + 1.318 + nsTArray<nsCString> lines; 1.319 + ParseString(PromiseFlatCString(aChunk), '\n', lines); 1.320 + 1.321 + // non-hashed tables need to be hashed 1.322 + for (uint32_t i = 0; i < lines.Length(); i++) { 1.323 + nsCString& line = lines[i]; 1.324 + 1.325 + if (mChunkState.type == CHUNK_ADD) { 1.326 + if (mChunkState.hashSize == COMPLETE_SIZE) { 1.327 + Completion hash; 1.328 + hash.FromPlaintext(line, mCryptoHash); 1.329 + mTableUpdate->NewAddComplete(mChunkState.num, hash); 1.330 + } else { 1.331 + NS_ASSERTION(mChunkState.hashSize == 4, "Only 32- or 4-byte hashes can be used for add chunks."); 1.332 + Prefix hash; 1.333 + hash.FromPlaintext(line, mCryptoHash); 1.334 + mTableUpdate->NewAddPrefix(mChunkState.num, hash); 1.335 + } 1.336 + } else { 1.337 + nsCString::const_iterator begin, iter, end; 1.338 + line.BeginReading(begin); 1.339 + line.EndReading(end); 1.340 + iter = begin; 1.341 + uint32_t addChunk; 1.342 + if (!FindCharInReadable(':', iter, end) || 1.343 + PR_sscanf(lines[i].get(), "%d:", &addChunk) != 1) { 1.344 + NS_WARNING("Received sub chunk without associated add chunk."); 1.345 + return NS_ERROR_FAILURE; 1.346 + } 1.347 + iter++; 1.348 + 1.349 + if (mChunkState.hashSize == COMPLETE_SIZE) { 1.350 + Completion hash; 1.351 + hash.FromPlaintext(Substring(iter, end), mCryptoHash); 1.352 + mTableUpdate->NewSubComplete(addChunk, hash, mChunkState.num); 1.353 + } else { 1.354 + NS_ASSERTION(mChunkState.hashSize == 4, "Only 32- or 4-byte hashes can be used for add chunks."); 1.355 + Prefix hash; 1.356 + hash.FromPlaintext(Substring(iter, end), mCryptoHash); 1.357 + mTableUpdate->NewSubPrefix(addChunk, hash, mChunkState.num); 1.358 + } 1.359 + } 1.360 + } 1.361 + 1.362 + return NS_OK; 1.363 +} 1.364 + 1.365 +nsresult 1.366 +ProtocolParser::ProcessShaChunk(const nsACString& aChunk) 1.367 +{ 1.368 + uint32_t start = 0; 1.369 + while (start < aChunk.Length()) { 1.370 + // First four bytes are the domain key. 1.371 + Prefix domain; 1.372 + domain.Assign(Substring(aChunk, start, DOMAIN_SIZE)); 1.373 + start += DOMAIN_SIZE; 1.374 + 1.375 + // Then a count of entries. 1.376 + uint8_t numEntries = static_cast<uint8_t>(aChunk[start]); 1.377 + start++; 1.378 + 1.379 + nsresult rv; 1.380 + if (mChunkState.type == CHUNK_ADD && mChunkState.hashSize == PREFIX_SIZE) { 1.381 + rv = ProcessHostAdd(domain, numEntries, aChunk, &start); 1.382 + } else if (mChunkState.type == CHUNK_ADD && mChunkState.hashSize == COMPLETE_SIZE) { 1.383 + rv = ProcessHostAddComplete(numEntries, aChunk, &start); 1.384 + } else if (mChunkState.type == CHUNK_SUB && mChunkState.hashSize == PREFIX_SIZE) { 1.385 + rv = ProcessHostSub(domain, numEntries, aChunk, &start); 1.386 + } else if (mChunkState.type == CHUNK_SUB && mChunkState.hashSize == COMPLETE_SIZE) { 1.387 + rv = ProcessHostSubComplete(numEntries, aChunk, &start); 1.388 + } else { 1.389 + NS_WARNING("Unexpected chunk type/hash size!"); 1.390 + LOG(("Got an unexpected chunk type/hash size: %s:%d", 1.391 + mChunkState.type == CHUNK_ADD ? "add" : "sub", 1.392 + mChunkState.hashSize)); 1.393 + return NS_ERROR_FAILURE; 1.394 + } 1.395 + NS_ENSURE_SUCCESS(rv, rv); 1.396 + } 1.397 + 1.398 + return NS_OK; 1.399 +} 1.400 + 1.401 +nsresult 1.402 +ProtocolParser::ProcessDigestChunk(const nsACString& aChunk) 1.403 +{ 1.404 + if (mChunkState.type == CHUNK_ADD_DIGEST) { 1.405 + return ProcessDigestAdd(aChunk); 1.406 + } 1.407 + if (mChunkState.type == CHUNK_SUB_DIGEST) { 1.408 + return ProcessDigestSub(aChunk); 1.409 + } 1.410 + return NS_ERROR_UNEXPECTED; 1.411 +} 1.412 + 1.413 +nsresult 1.414 +ProtocolParser::ProcessDigestAdd(const nsACString& aChunk) 1.415 +{ 1.416 + // The ABNF format for add chunks is (HASH)+, where HASH is 32 bytes. 1.417 + MOZ_ASSERT(aChunk.Length() % 32 == 0, 1.418 + "Chunk length in bytes must be divisible by 4"); 1.419 + uint32_t start = 0; 1.420 + while (start < aChunk.Length()) { 1.421 + Completion hash; 1.422 + hash.Assign(Substring(aChunk, start, COMPLETE_SIZE)); 1.423 + start += COMPLETE_SIZE; 1.424 + mTableUpdate->NewAddComplete(mChunkState.num, hash); 1.425 + } 1.426 + return NS_OK; 1.427 +} 1.428 + 1.429 +nsresult 1.430 +ProtocolParser::ProcessDigestSub(const nsACString& aChunk) 1.431 +{ 1.432 + // The ABNF format for sub chunks is (ADDCHUNKNUM HASH)+, where ADDCHUNKNUM 1.433 + // is a 4 byte chunk number, and HASH is 32 bytes. 1.434 + MOZ_ASSERT(aChunk.Length() % 36 == 0, 1.435 + "Chunk length in bytes must be divisible by 36"); 1.436 + uint32_t start = 0; 1.437 + while (start < aChunk.Length()) { 1.438 + // Read ADDCHUNKNUM 1.439 + const nsCSubstring& addChunkStr = Substring(aChunk, start, 4); 1.440 + start += 4; 1.441 + 1.442 + uint32_t addChunk; 1.443 + memcpy(&addChunk, addChunkStr.BeginReading(), 4); 1.444 + addChunk = PR_ntohl(addChunk); 1.445 + 1.446 + // Read the hash 1.447 + Completion hash; 1.448 + hash.Assign(Substring(aChunk, start, COMPLETE_SIZE)); 1.449 + start += COMPLETE_SIZE; 1.450 + 1.451 + mTableUpdate->NewSubComplete(addChunk, hash, mChunkState.num); 1.452 + } 1.453 + return NS_OK; 1.454 +} 1.455 + 1.456 +nsresult 1.457 +ProtocolParser::ProcessHostAdd(const Prefix& aDomain, uint8_t aNumEntries, 1.458 + const nsACString& aChunk, uint32_t* aStart) 1.459 +{ 1.460 + NS_ASSERTION(mChunkState.hashSize == PREFIX_SIZE, 1.461 + "ProcessHostAdd should only be called for prefix hashes."); 1.462 + 1.463 + if (aNumEntries == 0) { 1.464 + mTableUpdate->NewAddPrefix(mChunkState.num, aDomain); 1.465 + return NS_OK; 1.466 + } 1.467 + 1.468 + if (*aStart + (PREFIX_SIZE * aNumEntries) > aChunk.Length()) { 1.469 + NS_WARNING("Chunk is not long enough to contain the expected entries."); 1.470 + return NS_ERROR_FAILURE; 1.471 + } 1.472 + 1.473 + for (uint8_t i = 0; i < aNumEntries; i++) { 1.474 + Prefix hash; 1.475 + hash.Assign(Substring(aChunk, *aStart, PREFIX_SIZE)); 1.476 + mTableUpdate->NewAddPrefix(mChunkState.num, hash); 1.477 + *aStart += PREFIX_SIZE; 1.478 + } 1.479 + 1.480 + return NS_OK; 1.481 +} 1.482 + 1.483 +nsresult 1.484 +ProtocolParser::ProcessHostSub(const Prefix& aDomain, uint8_t aNumEntries, 1.485 + const nsACString& aChunk, uint32_t *aStart) 1.486 +{ 1.487 + NS_ASSERTION(mChunkState.hashSize == PREFIX_SIZE, 1.488 + "ProcessHostSub should only be called for prefix hashes."); 1.489 + 1.490 + if (aNumEntries == 0) { 1.491 + if ((*aStart) + 4 > aChunk.Length()) { 1.492 + NS_WARNING("Received a zero-entry sub chunk without an associated add."); 1.493 + return NS_ERROR_FAILURE; 1.494 + } 1.495 + 1.496 + const nsCSubstring& addChunkStr = Substring(aChunk, *aStart, 4); 1.497 + *aStart += 4; 1.498 + 1.499 + uint32_t addChunk; 1.500 + memcpy(&addChunk, addChunkStr.BeginReading(), 4); 1.501 + addChunk = PR_ntohl(addChunk); 1.502 + 1.503 + mTableUpdate->NewSubPrefix(addChunk, aDomain, mChunkState.num); 1.504 + return NS_OK; 1.505 + } 1.506 + 1.507 + if (*aStart + ((PREFIX_SIZE + 4) * aNumEntries) > aChunk.Length()) { 1.508 + NS_WARNING("Chunk is not long enough to contain the expected entries."); 1.509 + return NS_ERROR_FAILURE; 1.510 + } 1.511 + 1.512 + for (uint8_t i = 0; i < aNumEntries; i++) { 1.513 + const nsCSubstring& addChunkStr = Substring(aChunk, *aStart, 4); 1.514 + *aStart += 4; 1.515 + 1.516 + uint32_t addChunk; 1.517 + memcpy(&addChunk, addChunkStr.BeginReading(), 4); 1.518 + addChunk = PR_ntohl(addChunk); 1.519 + 1.520 + Prefix prefix; 1.521 + prefix.Assign(Substring(aChunk, *aStart, PREFIX_SIZE)); 1.522 + *aStart += PREFIX_SIZE; 1.523 + 1.524 + mTableUpdate->NewSubPrefix(addChunk, prefix, mChunkState.num); 1.525 + } 1.526 + 1.527 + return NS_OK; 1.528 +} 1.529 + 1.530 +nsresult 1.531 +ProtocolParser::ProcessHostAddComplete(uint8_t aNumEntries, 1.532 + const nsACString& aChunk, uint32_t* aStart) 1.533 +{ 1.534 + NS_ASSERTION(mChunkState.hashSize == COMPLETE_SIZE, 1.535 + "ProcessHostAddComplete should only be called for complete hashes."); 1.536 + 1.537 + if (aNumEntries == 0) { 1.538 + // this is totally comprehensible. 1.539 + // My sarcasm detector is going off! 1.540 + NS_WARNING("Expected > 0 entries for a 32-byte hash add."); 1.541 + return NS_OK; 1.542 + } 1.543 + 1.544 + if (*aStart + (COMPLETE_SIZE * aNumEntries) > aChunk.Length()) { 1.545 + NS_WARNING("Chunk is not long enough to contain the expected entries."); 1.546 + return NS_ERROR_FAILURE; 1.547 + } 1.548 + 1.549 + for (uint8_t i = 0; i < aNumEntries; i++) { 1.550 + Completion hash; 1.551 + hash.Assign(Substring(aChunk, *aStart, COMPLETE_SIZE)); 1.552 + mTableUpdate->NewAddComplete(mChunkState.num, hash); 1.553 + *aStart += COMPLETE_SIZE; 1.554 + } 1.555 + 1.556 + return NS_OK; 1.557 +} 1.558 + 1.559 +nsresult 1.560 +ProtocolParser::ProcessHostSubComplete(uint8_t aNumEntries, 1.561 + const nsACString& aChunk, uint32_t* aStart) 1.562 +{ 1.563 + NS_ASSERTION(mChunkState.hashSize == COMPLETE_SIZE, 1.564 + "ProcessHostSubComplete should only be called for complete hashes."); 1.565 + 1.566 + if (aNumEntries == 0) { 1.567 + // this is totally comprehensible. 1.568 + NS_WARNING("Expected > 0 entries for a 32-byte hash sub."); 1.569 + return NS_OK; 1.570 + } 1.571 + 1.572 + if (*aStart + ((COMPLETE_SIZE + 4) * aNumEntries) > aChunk.Length()) { 1.573 + NS_WARNING("Chunk is not long enough to contain the expected entries."); 1.574 + return NS_ERROR_FAILURE; 1.575 + } 1.576 + 1.577 + for (uint8_t i = 0; i < aNumEntries; i++) { 1.578 + Completion hash; 1.579 + hash.Assign(Substring(aChunk, *aStart, COMPLETE_SIZE)); 1.580 + *aStart += COMPLETE_SIZE; 1.581 + 1.582 + const nsCSubstring& addChunkStr = Substring(aChunk, *aStart, 4); 1.583 + *aStart += 4; 1.584 + 1.585 + uint32_t addChunk; 1.586 + memcpy(&addChunk, addChunkStr.BeginReading(), 4); 1.587 + addChunk = PR_ntohl(addChunk); 1.588 + 1.589 + mTableUpdate->NewSubComplete(addChunk, hash, mChunkState.num); 1.590 + } 1.591 + 1.592 + return NS_OK; 1.593 +} 1.594 + 1.595 +bool 1.596 +ProtocolParser::NextLine(nsACString& line) 1.597 +{ 1.598 + int32_t newline = mPending.FindChar('\n'); 1.599 + if (newline == kNotFound) { 1.600 + return false; 1.601 + } 1.602 + line.Assign(Substring(mPending, 0, newline)); 1.603 + mPending = Substring(mPending, newline + 1); 1.604 + return true; 1.605 +} 1.606 + 1.607 +void 1.608 +ProtocolParser::CleanupUpdates() 1.609 +{ 1.610 + for (uint32_t i = 0; i < mTableUpdates.Length(); i++) { 1.611 + delete mTableUpdates[i]; 1.612 + } 1.613 + mTableUpdates.Clear(); 1.614 +} 1.615 + 1.616 +TableUpdate * 1.617 +ProtocolParser::GetTableUpdate(const nsACString& aTable) 1.618 +{ 1.619 + for (uint32_t i = 0; i < mTableUpdates.Length(); i++) { 1.620 + if (aTable.Equals(mTableUpdates[i]->TableName())) { 1.621 + return mTableUpdates[i]; 1.622 + } 1.623 + } 1.624 + 1.625 + // We free automatically on destruction, ownership of these 1.626 + // updates can be transferred to DBServiceWorker, which passes 1.627 + // them back to Classifier when doing the updates, and that 1.628 + // will free them. 1.629 + TableUpdate *update = new TableUpdate(aTable); 1.630 + mTableUpdates.AppendElement(update); 1.631 + return update; 1.632 +} 1.633 + 1.634 +} // namespace safebrowsing 1.635 +} // namespace mozilla