Fri, 16 Jan 2015 18:13:44 +0100
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 |