toolkit/components/url-classifier/ProtocolParser.cpp

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

michael@0 1 //* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "ProtocolParser.h"
michael@0 7 #include "LookupCache.h"
michael@0 8 #include "nsNetCID.h"
michael@0 9 #include "prlog.h"
michael@0 10 #include "prnetdb.h"
michael@0 11 #include "prprf.h"
michael@0 12
michael@0 13 #include "nsUrlClassifierUtils.h"
michael@0 14
michael@0 15 // NSPR_LOG_MODULES=UrlClassifierDbService:5
michael@0 16 extern PRLogModuleInfo *gUrlClassifierDbServiceLog;
michael@0 17 #if defined(PR_LOGGING)
michael@0 18 #define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args)
michael@0 19 #define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4)
michael@0 20 #else
michael@0 21 #define LOG(args)
michael@0 22 #define LOG_ENABLED() (false)
michael@0 23 #endif
michael@0 24
michael@0 25 namespace mozilla {
michael@0 26 namespace safebrowsing {
michael@0 27
michael@0 28 // Updates will fail if fed chunks larger than this
michael@0 29 const uint32_t MAX_CHUNK_SIZE = (1024 * 1024);
michael@0 30
michael@0 31 const uint32_t DOMAIN_SIZE = 4;
michael@0 32
michael@0 33 // Parse one stringified range of chunks of the form "n" or "n-m" from a
michael@0 34 // comma-separated list of chunks. Upon return, 'begin' will point to the
michael@0 35 // next range of chunks in the list of chunks.
michael@0 36 static bool
michael@0 37 ParseChunkRange(nsACString::const_iterator& aBegin,
michael@0 38 const nsACString::const_iterator& aEnd,
michael@0 39 uint32_t* aFirst, uint32_t* aLast)
michael@0 40 {
michael@0 41 nsACString::const_iterator iter = aBegin;
michael@0 42 FindCharInReadable(',', iter, aEnd);
michael@0 43
michael@0 44 nsAutoCString element(Substring(aBegin, iter));
michael@0 45 aBegin = iter;
michael@0 46 if (aBegin != aEnd)
michael@0 47 aBegin++;
michael@0 48
michael@0 49 uint32_t numRead = PR_sscanf(element.get(), "%u-%u", aFirst, aLast);
michael@0 50 if (numRead == 2) {
michael@0 51 if (*aFirst > *aLast) {
michael@0 52 uint32_t tmp = *aFirst;
michael@0 53 *aFirst = *aLast;
michael@0 54 *aLast = tmp;
michael@0 55 }
michael@0 56 return true;
michael@0 57 }
michael@0 58
michael@0 59 if (numRead == 1) {
michael@0 60 *aLast = *aFirst;
michael@0 61 return true;
michael@0 62 }
michael@0 63
michael@0 64 return false;
michael@0 65 }
michael@0 66
michael@0 67 ProtocolParser::ProtocolParser()
michael@0 68 : mState(PROTOCOL_STATE_CONTROL)
michael@0 69 , mUpdateStatus(NS_OK)
michael@0 70 , mUpdateWait(0)
michael@0 71 , mResetRequested(false)
michael@0 72 {
michael@0 73 }
michael@0 74
michael@0 75 ProtocolParser::~ProtocolParser()
michael@0 76 {
michael@0 77 CleanupUpdates();
michael@0 78 }
michael@0 79
michael@0 80 nsresult
michael@0 81 ProtocolParser::Init(nsICryptoHash* aHasher)
michael@0 82 {
michael@0 83 mCryptoHash = aHasher;
michael@0 84 return NS_OK;
michael@0 85 }
michael@0 86
michael@0 87 void
michael@0 88 ProtocolParser::SetCurrentTable(const nsACString& aTable)
michael@0 89 {
michael@0 90 mTableUpdate = GetTableUpdate(aTable);
michael@0 91 }
michael@0 92
michael@0 93 nsresult
michael@0 94 ProtocolParser::AppendStream(const nsACString& aData)
michael@0 95 {
michael@0 96 if (NS_FAILED(mUpdateStatus))
michael@0 97 return mUpdateStatus;
michael@0 98
michael@0 99 nsresult rv;
michael@0 100 mPending.Append(aData);
michael@0 101
michael@0 102 bool done = false;
michael@0 103 while (!done) {
michael@0 104 if (mState == PROTOCOL_STATE_CONTROL) {
michael@0 105 rv = ProcessControl(&done);
michael@0 106 } else if (mState == PROTOCOL_STATE_CHUNK) {
michael@0 107 rv = ProcessChunk(&done);
michael@0 108 } else {
michael@0 109 NS_ERROR("Unexpected protocol state");
michael@0 110 rv = NS_ERROR_FAILURE;
michael@0 111 }
michael@0 112 if (NS_FAILED(rv)) {
michael@0 113 mUpdateStatus = rv;
michael@0 114 return rv;
michael@0 115 }
michael@0 116 }
michael@0 117 return NS_OK;
michael@0 118 }
michael@0 119
michael@0 120 nsresult
michael@0 121 ProtocolParser::ProcessControl(bool* aDone)
michael@0 122 {
michael@0 123 nsresult rv;
michael@0 124
michael@0 125 nsAutoCString line;
michael@0 126 *aDone = true;
michael@0 127 while (NextLine(line)) {
michael@0 128 //LOG(("Processing %s\n", line.get()));
michael@0 129
michael@0 130 if (StringBeginsWith(line, NS_LITERAL_CSTRING("i:"))) {
michael@0 131 // Set the table name from the table header line.
michael@0 132 SetCurrentTable(Substring(line, 2));
michael@0 133 } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("n:"))) {
michael@0 134 if (PR_sscanf(line.get(), "n:%d", &mUpdateWait) != 1) {
michael@0 135 LOG(("Error parsing n: '%s' (%d)", line.get(), mUpdateWait));
michael@0 136 mUpdateWait = 0;
michael@0 137 }
michael@0 138 } else if (line.EqualsLiteral("r:pleasereset")) {
michael@0 139 mResetRequested = true;
michael@0 140 } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("u:"))) {
michael@0 141 rv = ProcessForward(line);
michael@0 142 NS_ENSURE_SUCCESS(rv, rv);
michael@0 143 } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("a:")) ||
michael@0 144 StringBeginsWith(line, NS_LITERAL_CSTRING("s:"))) {
michael@0 145 rv = ProcessChunkControl(line);
michael@0 146 NS_ENSURE_SUCCESS(rv, rv);
michael@0 147 *aDone = false;
michael@0 148 return NS_OK;
michael@0 149 } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("ad:")) ||
michael@0 150 StringBeginsWith(line, NS_LITERAL_CSTRING("sd:"))) {
michael@0 151 rv = ProcessExpirations(line);
michael@0 152 NS_ENSURE_SUCCESS(rv, rv);
michael@0 153 }
michael@0 154 }
michael@0 155
michael@0 156 *aDone = true;
michael@0 157 return NS_OK;
michael@0 158 }
michael@0 159
michael@0 160 nsresult
michael@0 161 ProtocolParser::ProcessExpirations(const nsCString& aLine)
michael@0 162 {
michael@0 163 if (!mTableUpdate) {
michael@0 164 NS_WARNING("Got an expiration without a table.");
michael@0 165 return NS_ERROR_FAILURE;
michael@0 166 }
michael@0 167 const nsCSubstring &list = Substring(aLine, 3);
michael@0 168 nsACString::const_iterator begin, end;
michael@0 169 list.BeginReading(begin);
michael@0 170 list.EndReading(end);
michael@0 171 while (begin != end) {
michael@0 172 uint32_t first, last;
michael@0 173 if (ParseChunkRange(begin, end, &first, &last)) {
michael@0 174 for (uint32_t num = first; num <= last; num++) {
michael@0 175 if (aLine[0] == 'a')
michael@0 176 mTableUpdate->NewAddExpiration(num);
michael@0 177 else
michael@0 178 mTableUpdate->NewSubExpiration(num);
michael@0 179 }
michael@0 180 } else {
michael@0 181 return NS_ERROR_FAILURE;
michael@0 182 }
michael@0 183 }
michael@0 184 return NS_OK;
michael@0 185 }
michael@0 186
michael@0 187 nsresult
michael@0 188 ProtocolParser::ProcessChunkControl(const nsCString& aLine)
michael@0 189 {
michael@0 190 if (!mTableUpdate) {
michael@0 191 NS_WARNING("Got a chunk before getting a table.");
michael@0 192 return NS_ERROR_FAILURE;
michael@0 193 }
michael@0 194
michael@0 195 mState = PROTOCOL_STATE_CHUNK;
michael@0 196 char command;
michael@0 197
michael@0 198 mChunkState.Clear();
michael@0 199
michael@0 200 if (PR_sscanf(aLine.get(),
michael@0 201 "%c:%d:%d:%d",
michael@0 202 &command,
michael@0 203 &mChunkState.num, &mChunkState.hashSize, &mChunkState.length)
michael@0 204 != 4)
michael@0 205 {
michael@0 206 return NS_ERROR_FAILURE;
michael@0 207 }
michael@0 208
michael@0 209 if (mChunkState.length > MAX_CHUNK_SIZE) {
michael@0 210 return NS_ERROR_FAILURE;
michael@0 211 }
michael@0 212
michael@0 213 if (!(mChunkState.hashSize == PREFIX_SIZE || mChunkState.hashSize == COMPLETE_SIZE)) {
michael@0 214 NS_WARNING("Invalid hash size specified in update.");
michael@0 215 return NS_ERROR_FAILURE;
michael@0 216 }
michael@0 217
michael@0 218 if (StringEndsWith(mTableUpdate->TableName(),
michael@0 219 NS_LITERAL_CSTRING("-shavar")) ||
michael@0 220 StringEndsWith(mTableUpdate->TableName(),
michael@0 221 NS_LITERAL_CSTRING("-simple"))) {
michael@0 222 // Accommodate test tables ending in -simple for now.
michael@0 223 mChunkState.type = (command == 'a') ? CHUNK_ADD : CHUNK_SUB;
michael@0 224 } else if (StringEndsWith(mTableUpdate->TableName(),
michael@0 225 NS_LITERAL_CSTRING("-digest256"))) {
michael@0 226 LOG(("Processing digest256 data"));
michael@0 227 mChunkState.type = (command == 'a') ? CHUNK_ADD_DIGEST : CHUNK_SUB_DIGEST;
michael@0 228 }
michael@0 229 switch (mChunkState.type) {
michael@0 230 case CHUNK_ADD:
michael@0 231 mTableUpdate->NewAddChunk(mChunkState.num);
michael@0 232 break;
michael@0 233 case CHUNK_SUB:
michael@0 234 mTableUpdate->NewSubChunk(mChunkState.num);
michael@0 235 break;
michael@0 236 case CHUNK_ADD_DIGEST:
michael@0 237 mTableUpdate->NewAddChunk(mChunkState.num);
michael@0 238 break;
michael@0 239 case CHUNK_SUB_DIGEST:
michael@0 240 mTableUpdate->NewSubChunk(mChunkState.num);
michael@0 241 break;
michael@0 242 }
michael@0 243
michael@0 244 return NS_OK;
michael@0 245 }
michael@0 246
michael@0 247 nsresult
michael@0 248 ProtocolParser::ProcessForward(const nsCString& aLine)
michael@0 249 {
michael@0 250 const nsCSubstring &forward = Substring(aLine, 2);
michael@0 251 return AddForward(forward);
michael@0 252 }
michael@0 253
michael@0 254 nsresult
michael@0 255 ProtocolParser::AddForward(const nsACString& aUrl)
michael@0 256 {
michael@0 257 if (!mTableUpdate) {
michael@0 258 NS_WARNING("Forward without a table name.");
michael@0 259 return NS_ERROR_FAILURE;
michael@0 260 }
michael@0 261
michael@0 262 ForwardedUpdate *forward = mForwards.AppendElement();
michael@0 263 forward->table = mTableUpdate->TableName();
michael@0 264 forward->url.Assign(aUrl);
michael@0 265
michael@0 266 return NS_OK;
michael@0 267 }
michael@0 268
michael@0 269 nsresult
michael@0 270 ProtocolParser::ProcessChunk(bool* aDone)
michael@0 271 {
michael@0 272 if (!mTableUpdate) {
michael@0 273 NS_WARNING("Processing chunk without an active table.");
michael@0 274 return NS_ERROR_FAILURE;
michael@0 275 }
michael@0 276
michael@0 277 NS_ASSERTION(mChunkState.num != 0, "Must have a chunk number.");
michael@0 278
michael@0 279 if (mPending.Length() < mChunkState.length) {
michael@0 280 *aDone = true;
michael@0 281 return NS_OK;
michael@0 282 }
michael@0 283
michael@0 284 // Pull the chunk out of the pending stream data.
michael@0 285 nsAutoCString chunk;
michael@0 286 chunk.Assign(Substring(mPending, 0, mChunkState.length));
michael@0 287 mPending = Substring(mPending, mChunkState.length);
michael@0 288
michael@0 289 *aDone = false;
michael@0 290 mState = PROTOCOL_STATE_CONTROL;
michael@0 291
michael@0 292 //LOG(("Handling a %d-byte chunk", chunk.Length()));
michael@0 293 if (StringEndsWith(mTableUpdate->TableName(),
michael@0 294 NS_LITERAL_CSTRING("-shavar"))) {
michael@0 295 return ProcessShaChunk(chunk);
michael@0 296 }
michael@0 297 if (StringEndsWith(mTableUpdate->TableName(),
michael@0 298 NS_LITERAL_CSTRING("-digest256"))) {
michael@0 299 return ProcessDigestChunk(chunk);
michael@0 300 }
michael@0 301 return ProcessPlaintextChunk(chunk);
michael@0 302 }
michael@0 303
michael@0 304 /**
michael@0 305 * Process a plaintext chunk (currently only used in unit tests).
michael@0 306 */
michael@0 307 nsresult
michael@0 308 ProtocolParser::ProcessPlaintextChunk(const nsACString& aChunk)
michael@0 309 {
michael@0 310 if (!mTableUpdate) {
michael@0 311 NS_WARNING("Chunk received with no table.");
michael@0 312 return NS_ERROR_FAILURE;
michael@0 313 }
michael@0 314
michael@0 315 nsTArray<nsCString> lines;
michael@0 316 ParseString(PromiseFlatCString(aChunk), '\n', lines);
michael@0 317
michael@0 318 // non-hashed tables need to be hashed
michael@0 319 for (uint32_t i = 0; i < lines.Length(); i++) {
michael@0 320 nsCString& line = lines[i];
michael@0 321
michael@0 322 if (mChunkState.type == CHUNK_ADD) {
michael@0 323 if (mChunkState.hashSize == COMPLETE_SIZE) {
michael@0 324 Completion hash;
michael@0 325 hash.FromPlaintext(line, mCryptoHash);
michael@0 326 mTableUpdate->NewAddComplete(mChunkState.num, hash);
michael@0 327 } else {
michael@0 328 NS_ASSERTION(mChunkState.hashSize == 4, "Only 32- or 4-byte hashes can be used for add chunks.");
michael@0 329 Prefix hash;
michael@0 330 hash.FromPlaintext(line, mCryptoHash);
michael@0 331 mTableUpdate->NewAddPrefix(mChunkState.num, hash);
michael@0 332 }
michael@0 333 } else {
michael@0 334 nsCString::const_iterator begin, iter, end;
michael@0 335 line.BeginReading(begin);
michael@0 336 line.EndReading(end);
michael@0 337 iter = begin;
michael@0 338 uint32_t addChunk;
michael@0 339 if (!FindCharInReadable(':', iter, end) ||
michael@0 340 PR_sscanf(lines[i].get(), "%d:", &addChunk) != 1) {
michael@0 341 NS_WARNING("Received sub chunk without associated add chunk.");
michael@0 342 return NS_ERROR_FAILURE;
michael@0 343 }
michael@0 344 iter++;
michael@0 345
michael@0 346 if (mChunkState.hashSize == COMPLETE_SIZE) {
michael@0 347 Completion hash;
michael@0 348 hash.FromPlaintext(Substring(iter, end), mCryptoHash);
michael@0 349 mTableUpdate->NewSubComplete(addChunk, hash, mChunkState.num);
michael@0 350 } else {
michael@0 351 NS_ASSERTION(mChunkState.hashSize == 4, "Only 32- or 4-byte hashes can be used for add chunks.");
michael@0 352 Prefix hash;
michael@0 353 hash.FromPlaintext(Substring(iter, end), mCryptoHash);
michael@0 354 mTableUpdate->NewSubPrefix(addChunk, hash, mChunkState.num);
michael@0 355 }
michael@0 356 }
michael@0 357 }
michael@0 358
michael@0 359 return NS_OK;
michael@0 360 }
michael@0 361
michael@0 362 nsresult
michael@0 363 ProtocolParser::ProcessShaChunk(const nsACString& aChunk)
michael@0 364 {
michael@0 365 uint32_t start = 0;
michael@0 366 while (start < aChunk.Length()) {
michael@0 367 // First four bytes are the domain key.
michael@0 368 Prefix domain;
michael@0 369 domain.Assign(Substring(aChunk, start, DOMAIN_SIZE));
michael@0 370 start += DOMAIN_SIZE;
michael@0 371
michael@0 372 // Then a count of entries.
michael@0 373 uint8_t numEntries = static_cast<uint8_t>(aChunk[start]);
michael@0 374 start++;
michael@0 375
michael@0 376 nsresult rv;
michael@0 377 if (mChunkState.type == CHUNK_ADD && mChunkState.hashSize == PREFIX_SIZE) {
michael@0 378 rv = ProcessHostAdd(domain, numEntries, aChunk, &start);
michael@0 379 } else if (mChunkState.type == CHUNK_ADD && mChunkState.hashSize == COMPLETE_SIZE) {
michael@0 380 rv = ProcessHostAddComplete(numEntries, aChunk, &start);
michael@0 381 } else if (mChunkState.type == CHUNK_SUB && mChunkState.hashSize == PREFIX_SIZE) {
michael@0 382 rv = ProcessHostSub(domain, numEntries, aChunk, &start);
michael@0 383 } else if (mChunkState.type == CHUNK_SUB && mChunkState.hashSize == COMPLETE_SIZE) {
michael@0 384 rv = ProcessHostSubComplete(numEntries, aChunk, &start);
michael@0 385 } else {
michael@0 386 NS_WARNING("Unexpected chunk type/hash size!");
michael@0 387 LOG(("Got an unexpected chunk type/hash size: %s:%d",
michael@0 388 mChunkState.type == CHUNK_ADD ? "add" : "sub",
michael@0 389 mChunkState.hashSize));
michael@0 390 return NS_ERROR_FAILURE;
michael@0 391 }
michael@0 392 NS_ENSURE_SUCCESS(rv, rv);
michael@0 393 }
michael@0 394
michael@0 395 return NS_OK;
michael@0 396 }
michael@0 397
michael@0 398 nsresult
michael@0 399 ProtocolParser::ProcessDigestChunk(const nsACString& aChunk)
michael@0 400 {
michael@0 401 if (mChunkState.type == CHUNK_ADD_DIGEST) {
michael@0 402 return ProcessDigestAdd(aChunk);
michael@0 403 }
michael@0 404 if (mChunkState.type == CHUNK_SUB_DIGEST) {
michael@0 405 return ProcessDigestSub(aChunk);
michael@0 406 }
michael@0 407 return NS_ERROR_UNEXPECTED;
michael@0 408 }
michael@0 409
michael@0 410 nsresult
michael@0 411 ProtocolParser::ProcessDigestAdd(const nsACString& aChunk)
michael@0 412 {
michael@0 413 // The ABNF format for add chunks is (HASH)+, where HASH is 32 bytes.
michael@0 414 MOZ_ASSERT(aChunk.Length() % 32 == 0,
michael@0 415 "Chunk length in bytes must be divisible by 4");
michael@0 416 uint32_t start = 0;
michael@0 417 while (start < aChunk.Length()) {
michael@0 418 Completion hash;
michael@0 419 hash.Assign(Substring(aChunk, start, COMPLETE_SIZE));
michael@0 420 start += COMPLETE_SIZE;
michael@0 421 mTableUpdate->NewAddComplete(mChunkState.num, hash);
michael@0 422 }
michael@0 423 return NS_OK;
michael@0 424 }
michael@0 425
michael@0 426 nsresult
michael@0 427 ProtocolParser::ProcessDigestSub(const nsACString& aChunk)
michael@0 428 {
michael@0 429 // The ABNF format for sub chunks is (ADDCHUNKNUM HASH)+, where ADDCHUNKNUM
michael@0 430 // is a 4 byte chunk number, and HASH is 32 bytes.
michael@0 431 MOZ_ASSERT(aChunk.Length() % 36 == 0,
michael@0 432 "Chunk length in bytes must be divisible by 36");
michael@0 433 uint32_t start = 0;
michael@0 434 while (start < aChunk.Length()) {
michael@0 435 // Read ADDCHUNKNUM
michael@0 436 const nsCSubstring& addChunkStr = Substring(aChunk, start, 4);
michael@0 437 start += 4;
michael@0 438
michael@0 439 uint32_t addChunk;
michael@0 440 memcpy(&addChunk, addChunkStr.BeginReading(), 4);
michael@0 441 addChunk = PR_ntohl(addChunk);
michael@0 442
michael@0 443 // Read the hash
michael@0 444 Completion hash;
michael@0 445 hash.Assign(Substring(aChunk, start, COMPLETE_SIZE));
michael@0 446 start += COMPLETE_SIZE;
michael@0 447
michael@0 448 mTableUpdate->NewSubComplete(addChunk, hash, mChunkState.num);
michael@0 449 }
michael@0 450 return NS_OK;
michael@0 451 }
michael@0 452
michael@0 453 nsresult
michael@0 454 ProtocolParser::ProcessHostAdd(const Prefix& aDomain, uint8_t aNumEntries,
michael@0 455 const nsACString& aChunk, uint32_t* aStart)
michael@0 456 {
michael@0 457 NS_ASSERTION(mChunkState.hashSize == PREFIX_SIZE,
michael@0 458 "ProcessHostAdd should only be called for prefix hashes.");
michael@0 459
michael@0 460 if (aNumEntries == 0) {
michael@0 461 mTableUpdate->NewAddPrefix(mChunkState.num, aDomain);
michael@0 462 return NS_OK;
michael@0 463 }
michael@0 464
michael@0 465 if (*aStart + (PREFIX_SIZE * aNumEntries) > aChunk.Length()) {
michael@0 466 NS_WARNING("Chunk is not long enough to contain the expected entries.");
michael@0 467 return NS_ERROR_FAILURE;
michael@0 468 }
michael@0 469
michael@0 470 for (uint8_t i = 0; i < aNumEntries; i++) {
michael@0 471 Prefix hash;
michael@0 472 hash.Assign(Substring(aChunk, *aStart, PREFIX_SIZE));
michael@0 473 mTableUpdate->NewAddPrefix(mChunkState.num, hash);
michael@0 474 *aStart += PREFIX_SIZE;
michael@0 475 }
michael@0 476
michael@0 477 return NS_OK;
michael@0 478 }
michael@0 479
michael@0 480 nsresult
michael@0 481 ProtocolParser::ProcessHostSub(const Prefix& aDomain, uint8_t aNumEntries,
michael@0 482 const nsACString& aChunk, uint32_t *aStart)
michael@0 483 {
michael@0 484 NS_ASSERTION(mChunkState.hashSize == PREFIX_SIZE,
michael@0 485 "ProcessHostSub should only be called for prefix hashes.");
michael@0 486
michael@0 487 if (aNumEntries == 0) {
michael@0 488 if ((*aStart) + 4 > aChunk.Length()) {
michael@0 489 NS_WARNING("Received a zero-entry sub chunk without an associated add.");
michael@0 490 return NS_ERROR_FAILURE;
michael@0 491 }
michael@0 492
michael@0 493 const nsCSubstring& addChunkStr = Substring(aChunk, *aStart, 4);
michael@0 494 *aStart += 4;
michael@0 495
michael@0 496 uint32_t addChunk;
michael@0 497 memcpy(&addChunk, addChunkStr.BeginReading(), 4);
michael@0 498 addChunk = PR_ntohl(addChunk);
michael@0 499
michael@0 500 mTableUpdate->NewSubPrefix(addChunk, aDomain, mChunkState.num);
michael@0 501 return NS_OK;
michael@0 502 }
michael@0 503
michael@0 504 if (*aStart + ((PREFIX_SIZE + 4) * aNumEntries) > aChunk.Length()) {
michael@0 505 NS_WARNING("Chunk is not long enough to contain the expected entries.");
michael@0 506 return NS_ERROR_FAILURE;
michael@0 507 }
michael@0 508
michael@0 509 for (uint8_t i = 0; i < aNumEntries; i++) {
michael@0 510 const nsCSubstring& addChunkStr = Substring(aChunk, *aStart, 4);
michael@0 511 *aStart += 4;
michael@0 512
michael@0 513 uint32_t addChunk;
michael@0 514 memcpy(&addChunk, addChunkStr.BeginReading(), 4);
michael@0 515 addChunk = PR_ntohl(addChunk);
michael@0 516
michael@0 517 Prefix prefix;
michael@0 518 prefix.Assign(Substring(aChunk, *aStart, PREFIX_SIZE));
michael@0 519 *aStart += PREFIX_SIZE;
michael@0 520
michael@0 521 mTableUpdate->NewSubPrefix(addChunk, prefix, mChunkState.num);
michael@0 522 }
michael@0 523
michael@0 524 return NS_OK;
michael@0 525 }
michael@0 526
michael@0 527 nsresult
michael@0 528 ProtocolParser::ProcessHostAddComplete(uint8_t aNumEntries,
michael@0 529 const nsACString& aChunk, uint32_t* aStart)
michael@0 530 {
michael@0 531 NS_ASSERTION(mChunkState.hashSize == COMPLETE_SIZE,
michael@0 532 "ProcessHostAddComplete should only be called for complete hashes.");
michael@0 533
michael@0 534 if (aNumEntries == 0) {
michael@0 535 // this is totally comprehensible.
michael@0 536 // My sarcasm detector is going off!
michael@0 537 NS_WARNING("Expected > 0 entries for a 32-byte hash add.");
michael@0 538 return NS_OK;
michael@0 539 }
michael@0 540
michael@0 541 if (*aStart + (COMPLETE_SIZE * aNumEntries) > aChunk.Length()) {
michael@0 542 NS_WARNING("Chunk is not long enough to contain the expected entries.");
michael@0 543 return NS_ERROR_FAILURE;
michael@0 544 }
michael@0 545
michael@0 546 for (uint8_t i = 0; i < aNumEntries; i++) {
michael@0 547 Completion hash;
michael@0 548 hash.Assign(Substring(aChunk, *aStart, COMPLETE_SIZE));
michael@0 549 mTableUpdate->NewAddComplete(mChunkState.num, hash);
michael@0 550 *aStart += COMPLETE_SIZE;
michael@0 551 }
michael@0 552
michael@0 553 return NS_OK;
michael@0 554 }
michael@0 555
michael@0 556 nsresult
michael@0 557 ProtocolParser::ProcessHostSubComplete(uint8_t aNumEntries,
michael@0 558 const nsACString& aChunk, uint32_t* aStart)
michael@0 559 {
michael@0 560 NS_ASSERTION(mChunkState.hashSize == COMPLETE_SIZE,
michael@0 561 "ProcessHostSubComplete should only be called for complete hashes.");
michael@0 562
michael@0 563 if (aNumEntries == 0) {
michael@0 564 // this is totally comprehensible.
michael@0 565 NS_WARNING("Expected > 0 entries for a 32-byte hash sub.");
michael@0 566 return NS_OK;
michael@0 567 }
michael@0 568
michael@0 569 if (*aStart + ((COMPLETE_SIZE + 4) * aNumEntries) > aChunk.Length()) {
michael@0 570 NS_WARNING("Chunk is not long enough to contain the expected entries.");
michael@0 571 return NS_ERROR_FAILURE;
michael@0 572 }
michael@0 573
michael@0 574 for (uint8_t i = 0; i < aNumEntries; i++) {
michael@0 575 Completion hash;
michael@0 576 hash.Assign(Substring(aChunk, *aStart, COMPLETE_SIZE));
michael@0 577 *aStart += COMPLETE_SIZE;
michael@0 578
michael@0 579 const nsCSubstring& addChunkStr = Substring(aChunk, *aStart, 4);
michael@0 580 *aStart += 4;
michael@0 581
michael@0 582 uint32_t addChunk;
michael@0 583 memcpy(&addChunk, addChunkStr.BeginReading(), 4);
michael@0 584 addChunk = PR_ntohl(addChunk);
michael@0 585
michael@0 586 mTableUpdate->NewSubComplete(addChunk, hash, mChunkState.num);
michael@0 587 }
michael@0 588
michael@0 589 return NS_OK;
michael@0 590 }
michael@0 591
michael@0 592 bool
michael@0 593 ProtocolParser::NextLine(nsACString& line)
michael@0 594 {
michael@0 595 int32_t newline = mPending.FindChar('\n');
michael@0 596 if (newline == kNotFound) {
michael@0 597 return false;
michael@0 598 }
michael@0 599 line.Assign(Substring(mPending, 0, newline));
michael@0 600 mPending = Substring(mPending, newline + 1);
michael@0 601 return true;
michael@0 602 }
michael@0 603
michael@0 604 void
michael@0 605 ProtocolParser::CleanupUpdates()
michael@0 606 {
michael@0 607 for (uint32_t i = 0; i < mTableUpdates.Length(); i++) {
michael@0 608 delete mTableUpdates[i];
michael@0 609 }
michael@0 610 mTableUpdates.Clear();
michael@0 611 }
michael@0 612
michael@0 613 TableUpdate *
michael@0 614 ProtocolParser::GetTableUpdate(const nsACString& aTable)
michael@0 615 {
michael@0 616 for (uint32_t i = 0; i < mTableUpdates.Length(); i++) {
michael@0 617 if (aTable.Equals(mTableUpdates[i]->TableName())) {
michael@0 618 return mTableUpdates[i];
michael@0 619 }
michael@0 620 }
michael@0 621
michael@0 622 // We free automatically on destruction, ownership of these
michael@0 623 // updates can be transferred to DBServiceWorker, which passes
michael@0 624 // them back to Classifier when doing the updates, and that
michael@0 625 // will free them.
michael@0 626 TableUpdate *update = new TableUpdate(aTable);
michael@0 627 mTableUpdates.AppendElement(update);
michael@0 628 return update;
michael@0 629 }
michael@0 630
michael@0 631 } // namespace safebrowsing
michael@0 632 } // namespace mozilla

mercurial