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