michael@0: //* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "ProtocolParser.h" michael@0: #include "LookupCache.h" michael@0: #include "nsNetCID.h" michael@0: #include "prlog.h" michael@0: #include "prnetdb.h" michael@0: #include "prprf.h" michael@0: michael@0: #include "nsUrlClassifierUtils.h" michael@0: michael@0: // NSPR_LOG_MODULES=UrlClassifierDbService:5 michael@0: extern PRLogModuleInfo *gUrlClassifierDbServiceLog; michael@0: #if defined(PR_LOGGING) michael@0: #define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args) michael@0: #define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4) michael@0: #else michael@0: #define LOG(args) michael@0: #define LOG_ENABLED() (false) michael@0: #endif michael@0: michael@0: namespace mozilla { michael@0: namespace safebrowsing { michael@0: michael@0: // Updates will fail if fed chunks larger than this michael@0: const uint32_t MAX_CHUNK_SIZE = (1024 * 1024); michael@0: michael@0: const uint32_t DOMAIN_SIZE = 4; michael@0: michael@0: // Parse one stringified range of chunks of the form "n" or "n-m" from a michael@0: // comma-separated list of chunks. Upon return, 'begin' will point to the michael@0: // next range of chunks in the list of chunks. michael@0: static bool michael@0: ParseChunkRange(nsACString::const_iterator& aBegin, michael@0: const nsACString::const_iterator& aEnd, michael@0: uint32_t* aFirst, uint32_t* aLast) michael@0: { michael@0: nsACString::const_iterator iter = aBegin; michael@0: FindCharInReadable(',', iter, aEnd); michael@0: michael@0: nsAutoCString element(Substring(aBegin, iter)); michael@0: aBegin = iter; michael@0: if (aBegin != aEnd) michael@0: aBegin++; michael@0: michael@0: uint32_t numRead = PR_sscanf(element.get(), "%u-%u", aFirst, aLast); michael@0: if (numRead == 2) { michael@0: if (*aFirst > *aLast) { michael@0: uint32_t tmp = *aFirst; michael@0: *aFirst = *aLast; michael@0: *aLast = tmp; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: if (numRead == 1) { michael@0: *aLast = *aFirst; michael@0: return true; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: ProtocolParser::ProtocolParser() michael@0: : mState(PROTOCOL_STATE_CONTROL) michael@0: , mUpdateStatus(NS_OK) michael@0: , mUpdateWait(0) michael@0: , mResetRequested(false) michael@0: { michael@0: } michael@0: michael@0: ProtocolParser::~ProtocolParser() michael@0: { michael@0: CleanupUpdates(); michael@0: } michael@0: michael@0: nsresult michael@0: ProtocolParser::Init(nsICryptoHash* aHasher) michael@0: { michael@0: mCryptoHash = aHasher; michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: ProtocolParser::SetCurrentTable(const nsACString& aTable) michael@0: { michael@0: mTableUpdate = GetTableUpdate(aTable); michael@0: } michael@0: michael@0: nsresult michael@0: ProtocolParser::AppendStream(const nsACString& aData) michael@0: { michael@0: if (NS_FAILED(mUpdateStatus)) michael@0: return mUpdateStatus; michael@0: michael@0: nsresult rv; michael@0: mPending.Append(aData); michael@0: michael@0: bool done = false; michael@0: while (!done) { michael@0: if (mState == PROTOCOL_STATE_CONTROL) { michael@0: rv = ProcessControl(&done); michael@0: } else if (mState == PROTOCOL_STATE_CHUNK) { michael@0: rv = ProcessChunk(&done); michael@0: } else { michael@0: NS_ERROR("Unexpected protocol state"); michael@0: rv = NS_ERROR_FAILURE; michael@0: } michael@0: if (NS_FAILED(rv)) { michael@0: mUpdateStatus = rv; michael@0: return rv; michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: ProtocolParser::ProcessControl(bool* aDone) michael@0: { michael@0: nsresult rv; michael@0: michael@0: nsAutoCString line; michael@0: *aDone = true; michael@0: while (NextLine(line)) { michael@0: //LOG(("Processing %s\n", line.get())); michael@0: michael@0: if (StringBeginsWith(line, NS_LITERAL_CSTRING("i:"))) { michael@0: // Set the table name from the table header line. michael@0: SetCurrentTable(Substring(line, 2)); michael@0: } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("n:"))) { michael@0: if (PR_sscanf(line.get(), "n:%d", &mUpdateWait) != 1) { michael@0: LOG(("Error parsing n: '%s' (%d)", line.get(), mUpdateWait)); michael@0: mUpdateWait = 0; michael@0: } michael@0: } else if (line.EqualsLiteral("r:pleasereset")) { michael@0: mResetRequested = true; michael@0: } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("u:"))) { michael@0: rv = ProcessForward(line); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("a:")) || michael@0: StringBeginsWith(line, NS_LITERAL_CSTRING("s:"))) { michael@0: rv = ProcessChunkControl(line); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: *aDone = false; michael@0: return NS_OK; michael@0: } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("ad:")) || michael@0: StringBeginsWith(line, NS_LITERAL_CSTRING("sd:"))) { michael@0: rv = ProcessExpirations(line); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: } michael@0: michael@0: *aDone = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: ProtocolParser::ProcessExpirations(const nsCString& aLine) michael@0: { michael@0: if (!mTableUpdate) { michael@0: NS_WARNING("Got an expiration without a table."); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: const nsCSubstring &list = Substring(aLine, 3); michael@0: nsACString::const_iterator begin, end; michael@0: list.BeginReading(begin); michael@0: list.EndReading(end); michael@0: while (begin != end) { michael@0: uint32_t first, last; michael@0: if (ParseChunkRange(begin, end, &first, &last)) { michael@0: for (uint32_t num = first; num <= last; num++) { michael@0: if (aLine[0] == 'a') michael@0: mTableUpdate->NewAddExpiration(num); michael@0: else michael@0: mTableUpdate->NewSubExpiration(num); michael@0: } michael@0: } else { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: ProtocolParser::ProcessChunkControl(const nsCString& aLine) michael@0: { michael@0: if (!mTableUpdate) { michael@0: NS_WARNING("Got a chunk before getting a table."); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: mState = PROTOCOL_STATE_CHUNK; michael@0: char command; michael@0: michael@0: mChunkState.Clear(); michael@0: michael@0: if (PR_sscanf(aLine.get(), michael@0: "%c:%d:%d:%d", michael@0: &command, michael@0: &mChunkState.num, &mChunkState.hashSize, &mChunkState.length) michael@0: != 4) michael@0: { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (mChunkState.length > MAX_CHUNK_SIZE) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (!(mChunkState.hashSize == PREFIX_SIZE || mChunkState.hashSize == COMPLETE_SIZE)) { michael@0: NS_WARNING("Invalid hash size specified in update."); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (StringEndsWith(mTableUpdate->TableName(), michael@0: NS_LITERAL_CSTRING("-shavar")) || michael@0: StringEndsWith(mTableUpdate->TableName(), michael@0: NS_LITERAL_CSTRING("-simple"))) { michael@0: // Accommodate test tables ending in -simple for now. michael@0: mChunkState.type = (command == 'a') ? CHUNK_ADD : CHUNK_SUB; michael@0: } else if (StringEndsWith(mTableUpdate->TableName(), michael@0: NS_LITERAL_CSTRING("-digest256"))) { michael@0: LOG(("Processing digest256 data")); michael@0: mChunkState.type = (command == 'a') ? CHUNK_ADD_DIGEST : CHUNK_SUB_DIGEST; michael@0: } michael@0: switch (mChunkState.type) { michael@0: case CHUNK_ADD: michael@0: mTableUpdate->NewAddChunk(mChunkState.num); michael@0: break; michael@0: case CHUNK_SUB: michael@0: mTableUpdate->NewSubChunk(mChunkState.num); michael@0: break; michael@0: case CHUNK_ADD_DIGEST: michael@0: mTableUpdate->NewAddChunk(mChunkState.num); michael@0: break; michael@0: case CHUNK_SUB_DIGEST: michael@0: mTableUpdate->NewSubChunk(mChunkState.num); michael@0: break; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: ProtocolParser::ProcessForward(const nsCString& aLine) michael@0: { michael@0: const nsCSubstring &forward = Substring(aLine, 2); michael@0: return AddForward(forward); michael@0: } michael@0: michael@0: nsresult michael@0: ProtocolParser::AddForward(const nsACString& aUrl) michael@0: { michael@0: if (!mTableUpdate) { michael@0: NS_WARNING("Forward without a table name."); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: ForwardedUpdate *forward = mForwards.AppendElement(); michael@0: forward->table = mTableUpdate->TableName(); michael@0: forward->url.Assign(aUrl); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: ProtocolParser::ProcessChunk(bool* aDone) michael@0: { michael@0: if (!mTableUpdate) { michael@0: NS_WARNING("Processing chunk without an active table."); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_ASSERTION(mChunkState.num != 0, "Must have a chunk number."); michael@0: michael@0: if (mPending.Length() < mChunkState.length) { michael@0: *aDone = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Pull the chunk out of the pending stream data. michael@0: nsAutoCString chunk; michael@0: chunk.Assign(Substring(mPending, 0, mChunkState.length)); michael@0: mPending = Substring(mPending, mChunkState.length); michael@0: michael@0: *aDone = false; michael@0: mState = PROTOCOL_STATE_CONTROL; michael@0: michael@0: //LOG(("Handling a %d-byte chunk", chunk.Length())); michael@0: if (StringEndsWith(mTableUpdate->TableName(), michael@0: NS_LITERAL_CSTRING("-shavar"))) { michael@0: return ProcessShaChunk(chunk); michael@0: } michael@0: if (StringEndsWith(mTableUpdate->TableName(), michael@0: NS_LITERAL_CSTRING("-digest256"))) { michael@0: return ProcessDigestChunk(chunk); michael@0: } michael@0: return ProcessPlaintextChunk(chunk); michael@0: } michael@0: michael@0: /** michael@0: * Process a plaintext chunk (currently only used in unit tests). michael@0: */ michael@0: nsresult michael@0: ProtocolParser::ProcessPlaintextChunk(const nsACString& aChunk) michael@0: { michael@0: if (!mTableUpdate) { michael@0: NS_WARNING("Chunk received with no table."); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsTArray lines; michael@0: ParseString(PromiseFlatCString(aChunk), '\n', lines); michael@0: michael@0: // non-hashed tables need to be hashed michael@0: for (uint32_t i = 0; i < lines.Length(); i++) { michael@0: nsCString& line = lines[i]; michael@0: michael@0: if (mChunkState.type == CHUNK_ADD) { michael@0: if (mChunkState.hashSize == COMPLETE_SIZE) { michael@0: Completion hash; michael@0: hash.FromPlaintext(line, mCryptoHash); michael@0: mTableUpdate->NewAddComplete(mChunkState.num, hash); michael@0: } else { michael@0: NS_ASSERTION(mChunkState.hashSize == 4, "Only 32- or 4-byte hashes can be used for add chunks."); michael@0: Prefix hash; michael@0: hash.FromPlaintext(line, mCryptoHash); michael@0: mTableUpdate->NewAddPrefix(mChunkState.num, hash); michael@0: } michael@0: } else { michael@0: nsCString::const_iterator begin, iter, end; michael@0: line.BeginReading(begin); michael@0: line.EndReading(end); michael@0: iter = begin; michael@0: uint32_t addChunk; michael@0: if (!FindCharInReadable(':', iter, end) || michael@0: PR_sscanf(lines[i].get(), "%d:", &addChunk) != 1) { michael@0: NS_WARNING("Received sub chunk without associated add chunk."); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: iter++; michael@0: michael@0: if (mChunkState.hashSize == COMPLETE_SIZE) { michael@0: Completion hash; michael@0: hash.FromPlaintext(Substring(iter, end), mCryptoHash); michael@0: mTableUpdate->NewSubComplete(addChunk, hash, mChunkState.num); michael@0: } else { michael@0: NS_ASSERTION(mChunkState.hashSize == 4, "Only 32- or 4-byte hashes can be used for add chunks."); michael@0: Prefix hash; michael@0: hash.FromPlaintext(Substring(iter, end), mCryptoHash); michael@0: mTableUpdate->NewSubPrefix(addChunk, hash, mChunkState.num); michael@0: } michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: ProtocolParser::ProcessShaChunk(const nsACString& aChunk) michael@0: { michael@0: uint32_t start = 0; michael@0: while (start < aChunk.Length()) { michael@0: // First four bytes are the domain key. michael@0: Prefix domain; michael@0: domain.Assign(Substring(aChunk, start, DOMAIN_SIZE)); michael@0: start += DOMAIN_SIZE; michael@0: michael@0: // Then a count of entries. michael@0: uint8_t numEntries = static_cast(aChunk[start]); michael@0: start++; michael@0: michael@0: nsresult rv; michael@0: if (mChunkState.type == CHUNK_ADD && mChunkState.hashSize == PREFIX_SIZE) { michael@0: rv = ProcessHostAdd(domain, numEntries, aChunk, &start); michael@0: } else if (mChunkState.type == CHUNK_ADD && mChunkState.hashSize == COMPLETE_SIZE) { michael@0: rv = ProcessHostAddComplete(numEntries, aChunk, &start); michael@0: } else if (mChunkState.type == CHUNK_SUB && mChunkState.hashSize == PREFIX_SIZE) { michael@0: rv = ProcessHostSub(domain, numEntries, aChunk, &start); michael@0: } else if (mChunkState.type == CHUNK_SUB && mChunkState.hashSize == COMPLETE_SIZE) { michael@0: rv = ProcessHostSubComplete(numEntries, aChunk, &start); michael@0: } else { michael@0: NS_WARNING("Unexpected chunk type/hash size!"); michael@0: LOG(("Got an unexpected chunk type/hash size: %s:%d", michael@0: mChunkState.type == CHUNK_ADD ? "add" : "sub", michael@0: mChunkState.hashSize)); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: ProtocolParser::ProcessDigestChunk(const nsACString& aChunk) michael@0: { michael@0: if (mChunkState.type == CHUNK_ADD_DIGEST) { michael@0: return ProcessDigestAdd(aChunk); michael@0: } michael@0: if (mChunkState.type == CHUNK_SUB_DIGEST) { michael@0: return ProcessDigestSub(aChunk); michael@0: } michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: nsresult michael@0: ProtocolParser::ProcessDigestAdd(const nsACString& aChunk) michael@0: { michael@0: // The ABNF format for add chunks is (HASH)+, where HASH is 32 bytes. michael@0: MOZ_ASSERT(aChunk.Length() % 32 == 0, michael@0: "Chunk length in bytes must be divisible by 4"); michael@0: uint32_t start = 0; michael@0: while (start < aChunk.Length()) { michael@0: Completion hash; michael@0: hash.Assign(Substring(aChunk, start, COMPLETE_SIZE)); michael@0: start += COMPLETE_SIZE; michael@0: mTableUpdate->NewAddComplete(mChunkState.num, hash); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: ProtocolParser::ProcessDigestSub(const nsACString& aChunk) michael@0: { michael@0: // The ABNF format for sub chunks is (ADDCHUNKNUM HASH)+, where ADDCHUNKNUM michael@0: // is a 4 byte chunk number, and HASH is 32 bytes. michael@0: MOZ_ASSERT(aChunk.Length() % 36 == 0, michael@0: "Chunk length in bytes must be divisible by 36"); michael@0: uint32_t start = 0; michael@0: while (start < aChunk.Length()) { michael@0: // Read ADDCHUNKNUM michael@0: const nsCSubstring& addChunkStr = Substring(aChunk, start, 4); michael@0: start += 4; michael@0: michael@0: uint32_t addChunk; michael@0: memcpy(&addChunk, addChunkStr.BeginReading(), 4); michael@0: addChunk = PR_ntohl(addChunk); michael@0: michael@0: // Read the hash michael@0: Completion hash; michael@0: hash.Assign(Substring(aChunk, start, COMPLETE_SIZE)); michael@0: start += COMPLETE_SIZE; michael@0: michael@0: mTableUpdate->NewSubComplete(addChunk, hash, mChunkState.num); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: ProtocolParser::ProcessHostAdd(const Prefix& aDomain, uint8_t aNumEntries, michael@0: const nsACString& aChunk, uint32_t* aStart) michael@0: { michael@0: NS_ASSERTION(mChunkState.hashSize == PREFIX_SIZE, michael@0: "ProcessHostAdd should only be called for prefix hashes."); michael@0: michael@0: if (aNumEntries == 0) { michael@0: mTableUpdate->NewAddPrefix(mChunkState.num, aDomain); michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (*aStart + (PREFIX_SIZE * aNumEntries) > aChunk.Length()) { michael@0: NS_WARNING("Chunk is not long enough to contain the expected entries."); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: for (uint8_t i = 0; i < aNumEntries; i++) { michael@0: Prefix hash; michael@0: hash.Assign(Substring(aChunk, *aStart, PREFIX_SIZE)); michael@0: mTableUpdate->NewAddPrefix(mChunkState.num, hash); michael@0: *aStart += PREFIX_SIZE; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: ProtocolParser::ProcessHostSub(const Prefix& aDomain, uint8_t aNumEntries, michael@0: const nsACString& aChunk, uint32_t *aStart) michael@0: { michael@0: NS_ASSERTION(mChunkState.hashSize == PREFIX_SIZE, michael@0: "ProcessHostSub should only be called for prefix hashes."); michael@0: michael@0: if (aNumEntries == 0) { michael@0: if ((*aStart) + 4 > aChunk.Length()) { michael@0: NS_WARNING("Received a zero-entry sub chunk without an associated add."); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: const nsCSubstring& addChunkStr = Substring(aChunk, *aStart, 4); michael@0: *aStart += 4; michael@0: michael@0: uint32_t addChunk; michael@0: memcpy(&addChunk, addChunkStr.BeginReading(), 4); michael@0: addChunk = PR_ntohl(addChunk); michael@0: michael@0: mTableUpdate->NewSubPrefix(addChunk, aDomain, mChunkState.num); michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (*aStart + ((PREFIX_SIZE + 4) * aNumEntries) > aChunk.Length()) { michael@0: NS_WARNING("Chunk is not long enough to contain the expected entries."); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: for (uint8_t i = 0; i < aNumEntries; i++) { michael@0: const nsCSubstring& addChunkStr = Substring(aChunk, *aStart, 4); michael@0: *aStart += 4; michael@0: michael@0: uint32_t addChunk; michael@0: memcpy(&addChunk, addChunkStr.BeginReading(), 4); michael@0: addChunk = PR_ntohl(addChunk); michael@0: michael@0: Prefix prefix; michael@0: prefix.Assign(Substring(aChunk, *aStart, PREFIX_SIZE)); michael@0: *aStart += PREFIX_SIZE; michael@0: michael@0: mTableUpdate->NewSubPrefix(addChunk, prefix, mChunkState.num); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: ProtocolParser::ProcessHostAddComplete(uint8_t aNumEntries, michael@0: const nsACString& aChunk, uint32_t* aStart) michael@0: { michael@0: NS_ASSERTION(mChunkState.hashSize == COMPLETE_SIZE, michael@0: "ProcessHostAddComplete should only be called for complete hashes."); michael@0: michael@0: if (aNumEntries == 0) { michael@0: // this is totally comprehensible. michael@0: // My sarcasm detector is going off! michael@0: NS_WARNING("Expected > 0 entries for a 32-byte hash add."); michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (*aStart + (COMPLETE_SIZE * aNumEntries) > aChunk.Length()) { michael@0: NS_WARNING("Chunk is not long enough to contain the expected entries."); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: for (uint8_t i = 0; i < aNumEntries; i++) { michael@0: Completion hash; michael@0: hash.Assign(Substring(aChunk, *aStart, COMPLETE_SIZE)); michael@0: mTableUpdate->NewAddComplete(mChunkState.num, hash); michael@0: *aStart += COMPLETE_SIZE; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: ProtocolParser::ProcessHostSubComplete(uint8_t aNumEntries, michael@0: const nsACString& aChunk, uint32_t* aStart) michael@0: { michael@0: NS_ASSERTION(mChunkState.hashSize == COMPLETE_SIZE, michael@0: "ProcessHostSubComplete should only be called for complete hashes."); michael@0: michael@0: if (aNumEntries == 0) { michael@0: // this is totally comprehensible. michael@0: NS_WARNING("Expected > 0 entries for a 32-byte hash sub."); michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (*aStart + ((COMPLETE_SIZE + 4) * aNumEntries) > aChunk.Length()) { michael@0: NS_WARNING("Chunk is not long enough to contain the expected entries."); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: for (uint8_t i = 0; i < aNumEntries; i++) { michael@0: Completion hash; michael@0: hash.Assign(Substring(aChunk, *aStart, COMPLETE_SIZE)); michael@0: *aStart += COMPLETE_SIZE; michael@0: michael@0: const nsCSubstring& addChunkStr = Substring(aChunk, *aStart, 4); michael@0: *aStart += 4; michael@0: michael@0: uint32_t addChunk; michael@0: memcpy(&addChunk, addChunkStr.BeginReading(), 4); michael@0: addChunk = PR_ntohl(addChunk); michael@0: michael@0: mTableUpdate->NewSubComplete(addChunk, hash, mChunkState.num); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: ProtocolParser::NextLine(nsACString& line) michael@0: { michael@0: int32_t newline = mPending.FindChar('\n'); michael@0: if (newline == kNotFound) { michael@0: return false; michael@0: } michael@0: line.Assign(Substring(mPending, 0, newline)); michael@0: mPending = Substring(mPending, newline + 1); michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: ProtocolParser::CleanupUpdates() michael@0: { michael@0: for (uint32_t i = 0; i < mTableUpdates.Length(); i++) { michael@0: delete mTableUpdates[i]; michael@0: } michael@0: mTableUpdates.Clear(); michael@0: } michael@0: michael@0: TableUpdate * michael@0: ProtocolParser::GetTableUpdate(const nsACString& aTable) michael@0: { michael@0: for (uint32_t i = 0; i < mTableUpdates.Length(); i++) { michael@0: if (aTable.Equals(mTableUpdates[i]->TableName())) { michael@0: return mTableUpdates[i]; michael@0: } michael@0: } michael@0: michael@0: // We free automatically on destruction, ownership of these michael@0: // updates can be transferred to DBServiceWorker, which passes michael@0: // them back to Classifier when doing the updates, and that michael@0: // will free them. michael@0: TableUpdate *update = new TableUpdate(aTable); michael@0: mTableUpdates.AppendElement(update); michael@0: return update; michael@0: } michael@0: michael@0: } // namespace safebrowsing michael@0: } // namespace mozilla