1 : //* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Url Classifier code
16 : *
17 : * The Initial Developer of the Original Code is
18 : * the Mozilla Foundation.
19 : * Portions created by the Initial Developer are Copyright (C) 2011
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Dave Camp <dcamp@mozilla.com>
24 : * Gian-Carlo Pascutto <gpascutto@mozilla.com>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either the GNU General Public License Version 2 or later (the "GPL"), or
28 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : //* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
41 : #include "ProtocolParser.h"
42 : #include "LookupCache.h"
43 : #include "nsIKeyModule.h"
44 : #include "nsNetCID.h"
45 : #include "prlog.h"
46 : #include "prnetdb.h"
47 : #include "prprf.h"
48 :
49 : #include "nsUrlClassifierUtils.h"
50 :
51 : // NSPR_LOG_MODULES=UrlClassifierDbService:5
52 : extern PRLogModuleInfo *gUrlClassifierDbServiceLog;
53 : #if defined(PR_LOGGING)
54 : #define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args)
55 : #define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4)
56 : #else
57 : #define LOG(args)
58 : #define LOG_ENABLED() (PR_FALSE)
59 : #endif
60 :
61 : namespace mozilla {
62 : namespace safebrowsing {
63 :
64 : // Updates will fail if fed chunks larger than this
65 : const uint32 MAX_CHUNK_SIZE = (1024 * 1024);
66 :
67 : const uint32 DOMAIN_SIZE = 4;
68 :
69 : // Parse one stringified range of chunks of the form "n" or "n-m" from a
70 : // comma-separated list of chunks. Upon return, 'begin' will point to the
71 : // next range of chunks in the list of chunks.
72 : static bool
73 9 : ParseChunkRange(nsACString::const_iterator& aBegin,
74 : const nsACString::const_iterator& aEnd,
75 : PRUint32* aFirst, PRUint32* aLast)
76 : {
77 9 : nsACString::const_iterator iter = aBegin;
78 9 : FindCharInReadable(',', iter, aEnd);
79 :
80 18 : nsCAutoString element(Substring(aBegin, iter));
81 9 : aBegin = iter;
82 9 : if (aBegin != aEnd)
83 2 : aBegin++;
84 :
85 9 : PRUint32 numRead = PR_sscanf(element.get(), "%u-%u", aFirst, aLast);
86 9 : if (numRead == 2) {
87 3 : if (*aFirst > *aLast) {
88 0 : PRUint32 tmp = *aFirst;
89 0 : *aFirst = *aLast;
90 0 : *aLast = tmp;
91 : }
92 3 : return true;
93 : }
94 :
95 6 : if (numRead == 1) {
96 6 : *aLast = *aFirst;
97 6 : return true;
98 : }
99 :
100 0 : return false;
101 : }
102 :
103 118 : ProtocolParser::ProtocolParser(PRUint32 aHashKey)
104 : : mState(PROTOCOL_STATE_CONTROL)
105 : , mHashKey(aHashKey)
106 : , mUpdateStatus(NS_OK)
107 : , mUpdateWait(0)
108 : , mResetRequested(false)
109 118 : , mRekeyRequested(false)
110 : {
111 118 : }
112 :
113 236 : ProtocolParser::~ProtocolParser()
114 : {
115 118 : CleanupUpdates();
116 118 : }
117 :
118 : nsresult
119 89 : ProtocolParser::Init(nsICryptoHash* aHasher)
120 : {
121 89 : mCryptoHash = aHasher;
122 89 : return NS_OK;
123 : }
124 :
125 : /**
126 : * Initialize HMAC for the stream.
127 : *
128 : * If serverMAC is empty, the update stream will need to provide a
129 : * server MAC.
130 : */
131 : nsresult
132 11 : ProtocolParser::InitHMAC(const nsACString& aClientKey,
133 : const nsACString& aServerMAC)
134 : {
135 11 : mServerMAC = aServerMAC;
136 :
137 : nsresult rv;
138 : nsCOMPtr<nsIKeyObjectFactory> keyObjectFactory(
139 22 : do_GetService("@mozilla.org/security/keyobjectfactory;1", &rv));
140 :
141 11 : if (NS_FAILED(rv)) {
142 0 : NS_WARNING("Failed to get nsIKeyObjectFactory service");
143 0 : mUpdateStatus = rv;
144 0 : return mUpdateStatus;
145 : }
146 :
147 22 : nsCOMPtr<nsIKeyObject> keyObject;
148 11 : rv = keyObjectFactory->KeyFromString(nsIKeyObject::HMAC, aClientKey,
149 11 : getter_AddRefs(keyObject));
150 11 : if (NS_FAILED(rv)) {
151 0 : NS_WARNING("Failed to create key object, maybe not FIPS compliant?");
152 0 : mUpdateStatus = rv;
153 0 : return mUpdateStatus;
154 : }
155 :
156 11 : mHMAC = do_CreateInstance(NS_CRYPTO_HMAC_CONTRACTID, &rv);
157 11 : if (NS_FAILED(rv)) {
158 0 : NS_WARNING("Failed to create nsICryptoHMAC instance");
159 0 : mUpdateStatus = rv;
160 0 : return mUpdateStatus;
161 : }
162 :
163 11 : rv = mHMAC->Init(nsICryptoHMAC::SHA1, keyObject);
164 11 : if (NS_FAILED(rv)) {
165 0 : NS_WARNING("Failed to initialize nsICryptoHMAC instance");
166 0 : mUpdateStatus = rv;
167 0 : return mUpdateStatus;
168 : }
169 11 : return NS_OK;
170 : }
171 :
172 : nsresult
173 87 : ProtocolParser::FinishHMAC()
174 : {
175 87 : if (NS_FAILED(mUpdateStatus)) {
176 4 : return mUpdateStatus;
177 : }
178 :
179 83 : if (mRekeyRequested) {
180 1 : mUpdateStatus = NS_ERROR_FAILURE;
181 1 : return mUpdateStatus;
182 : }
183 :
184 82 : if (!mHMAC) {
185 74 : return NS_OK;
186 : }
187 :
188 16 : nsCAutoString clientMAC;
189 8 : mHMAC->Finish(PR_TRUE, clientMAC);
190 :
191 8 : if (clientMAC != mServerMAC) {
192 2 : NS_WARNING("Invalid update MAC!");
193 2 : LOG(("Invalid update MAC: expected %s, got %s",
194 : clientMAC.get(), mServerMAC.get()));
195 2 : mUpdateStatus = NS_ERROR_FAILURE;
196 : }
197 8 : return mUpdateStatus;
198 : }
199 :
200 : void
201 87 : ProtocolParser::SetCurrentTable(const nsACString& aTable)
202 : {
203 87 : mTableUpdate = GetTableUpdate(aTable);
204 87 : }
205 :
206 : nsresult
207 87 : ProtocolParser::AppendStream(const nsACString& aData)
208 : {
209 87 : if (NS_FAILED(mUpdateStatus))
210 0 : return mUpdateStatus;
211 :
212 : nsresult rv;
213 :
214 : // Digest the data if we have a server MAC.
215 87 : if (mHMAC && !mServerMAC.IsEmpty()) {
216 8 : rv = mHMAC->Update(reinterpret_cast<const PRUint8*>(aData.BeginReading()),
217 8 : aData.Length());
218 4 : if (NS_FAILED(rv)) {
219 0 : mUpdateStatus = rv;
220 0 : return rv;
221 : }
222 : }
223 :
224 87 : mPending.Append(aData);
225 :
226 87 : bool done = false;
227 427 : while (!done) {
228 257 : if (mState == PROTOCOL_STATE_CONTROL) {
229 172 : rv = ProcessControl(&done);
230 85 : } else if (mState == PROTOCOL_STATE_CHUNK) {
231 85 : rv = ProcessChunk(&done);
232 : } else {
233 0 : NS_ERROR("Unexpected protocol state");
234 0 : rv = NS_ERROR_FAILURE;
235 : }
236 257 : if (NS_FAILED(rv)) {
237 4 : mUpdateStatus = rv;
238 4 : return rv;
239 : }
240 : }
241 83 : return NS_OK;
242 : }
243 :
244 : nsresult
245 172 : ProtocolParser::ProcessControl(bool* aDone)
246 : {
247 : nsresult rv;
248 :
249 344 : nsCAutoString line;
250 172 : *aDone = true;
251 619 : while (NextLine(line)) {
252 : //LOG(("Processing %s\n", line.get()));
253 :
254 365 : if (line.EqualsLiteral("e:pleaserekey")) {
255 1 : mRekeyRequested = true;
256 1 : return NS_OK;
257 364 : } else if (mHMAC && mServerMAC.IsEmpty()) {
258 6 : rv = ProcessMAC(line);
259 6 : NS_ENSURE_SUCCESS(rv, rv);
260 358 : } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("i:"))) {
261 75 : SetCurrentTable(Substring(line, 2));
262 283 : } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("n:"))) {
263 86 : if (PR_sscanf(line.get(), "n:%d", &mUpdateWait) != 1) {
264 0 : LOG(("Error parsing n: '%s' (%d)", line.get(), mUpdateWait));
265 0 : mUpdateWait = 0;
266 : }
267 197 : } else if (line.EqualsLiteral("r:pleasereset")) {
268 1 : mResetRequested = true;
269 196 : } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("u:"))) {
270 17 : rv = ProcessForward(line);
271 17 : NS_ENSURE_SUCCESS(rv, rv);
272 578 : } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("a:")) ||
273 399 : StringBeginsWith(line, NS_LITERAL_CSTRING("s:"))) {
274 87 : rv = ProcessChunkControl(line);
275 87 : NS_ENSURE_SUCCESS(rv, rv);
276 85 : *aDone = false;
277 85 : return NS_OK;
278 360 : } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("ad:")) ||
279 268 : StringBeginsWith(line, NS_LITERAL_CSTRING("sd:"))) {
280 7 : rv = ProcessExpirations(line);
281 7 : NS_ENSURE_SUCCESS(rv, rv);
282 : }
283 : }
284 :
285 82 : *aDone = true;
286 82 : return NS_OK;
287 : }
288 :
289 :
290 : nsresult
291 6 : ProtocolParser::ProcessMAC(const nsCString& aLine)
292 : {
293 : nsresult rv;
294 :
295 6 : LOG(("line: %s", aLine.get()));
296 :
297 6 : if (StringBeginsWith(aLine, NS_LITERAL_CSTRING("m:"))) {
298 5 : mServerMAC = Substring(aLine, 2);
299 5 : nsUrlClassifierUtils::UnUrlsafeBase64(mServerMAC);
300 :
301 : // The remainder of the pending update wasn't digested, digest it now.
302 10 : rv = mHMAC->Update(reinterpret_cast<const PRUint8*>(mPending.BeginReading()),
303 10 : mPending.Length());
304 5 : return rv;
305 : }
306 :
307 1 : LOG(("No MAC specified!"));
308 1 : return NS_ERROR_FAILURE;
309 : }
310 :
311 : nsresult
312 7 : ProtocolParser::ProcessExpirations(const nsCString& aLine)
313 : {
314 7 : if (!mTableUpdate) {
315 0 : NS_WARNING("Got an expiration without a table.");
316 0 : return NS_ERROR_FAILURE;
317 : }
318 14 : const nsCSubstring &list = Substring(aLine, 3);
319 7 : nsACString::const_iterator begin, end;
320 7 : list.BeginReading(begin);
321 7 : list.EndReading(end);
322 7 : while (begin != end) {
323 : PRUint32 first, last;
324 9 : if (ParseChunkRange(begin, end, &first, &last)) {
325 24 : for (PRUint32 num = first; num <= last; num++) {
326 15 : if (aLine[0] == 'a')
327 9 : mTableUpdate->NewAddExpiration(num);
328 : else
329 6 : mTableUpdate->NewSubExpiration(num);
330 : }
331 : } else {
332 0 : return NS_ERROR_FAILURE;
333 : }
334 : }
335 7 : return NS_OK;
336 : }
337 :
338 : nsresult
339 87 : ProtocolParser::ProcessChunkControl(const nsCString& aLine)
340 : {
341 87 : if (!mTableUpdate) {
342 0 : NS_WARNING("Got a chunk before getting a table.");
343 0 : return NS_ERROR_FAILURE;
344 : }
345 :
346 87 : mState = PROTOCOL_STATE_CHUNK;
347 : char command;
348 :
349 87 : mChunkState.Clear();
350 :
351 87 : if (PR_sscanf(aLine.get(),
352 : "%c:%d:%d:%d",
353 : &command,
354 87 : &mChunkState.num, &mChunkState.hashSize, &mChunkState.length)
355 : != 4)
356 : {
357 1 : return NS_ERROR_FAILURE;
358 : }
359 :
360 86 : if (mChunkState.length > MAX_CHUNK_SIZE) {
361 0 : return NS_ERROR_FAILURE;
362 : }
363 :
364 86 : if (!(mChunkState.hashSize == PREFIX_SIZE || mChunkState.hashSize == COMPLETE_SIZE)) {
365 1 : NS_WARNING("Invalid hash size specified in update.");
366 1 : return NS_ERROR_FAILURE;
367 : }
368 :
369 85 : mChunkState.type = (command == 'a') ? CHUNK_ADD : CHUNK_SUB;
370 :
371 85 : if (mChunkState.type == CHUNK_ADD) {
372 67 : mTableUpdate->NewAddChunk(mChunkState.num);
373 : } else {
374 18 : mTableUpdate->NewSubChunk(mChunkState.num);
375 : }
376 :
377 85 : return NS_OK;
378 : }
379 :
380 : nsresult
381 17 : ProtocolParser::ProcessForward(const nsCString& aLine)
382 : {
383 34 : const nsCSubstring &forward = Substring(aLine, 2);
384 17 : if (mHMAC) {
385 : // We're expecting MACs alongside any url forwards.
386 7 : nsCSubstring::const_iterator begin, end, sepBegin, sepEnd;
387 7 : forward.BeginReading(begin);
388 7 : sepBegin = begin;
389 :
390 7 : forward.EndReading(end);
391 7 : sepEnd = end;
392 :
393 7 : if (!RFindInReadable(NS_LITERAL_CSTRING(","), sepBegin, sepEnd)) {
394 1 : NS_WARNING("No MAC specified for a redirect in a request that expects a MAC");
395 1 : return NS_ERROR_FAILURE;
396 : }
397 :
398 12 : nsCString serverMAC(Substring(sepEnd, end));
399 6 : nsUrlClassifierUtils::UnUrlsafeBase64(serverMAC);
400 6 : return AddForward(Substring(begin, sepBegin), serverMAC);
401 : }
402 10 : return AddForward(forward, mServerMAC);
403 : }
404 :
405 : nsresult
406 16 : ProtocolParser::AddForward(const nsACString& aUrl, const nsACString& aMac)
407 : {
408 16 : if (!mTableUpdate) {
409 0 : NS_WARNING("Forward without a table name.");
410 0 : return NS_ERROR_FAILURE;
411 : }
412 :
413 16 : ForwardedUpdate *forward = mForwards.AppendElement();
414 16 : forward->table = mTableUpdate->TableName();
415 16 : forward->url.Assign(aUrl);
416 16 : forward->mac.Assign(aMac);
417 :
418 16 : return NS_OK;
419 : }
420 :
421 : nsresult
422 85 : ProtocolParser::ProcessChunk(bool* aDone)
423 : {
424 85 : if (!mTableUpdate) {
425 0 : NS_WARNING("Processing chunk without an active table.");
426 0 : return NS_ERROR_FAILURE;
427 : }
428 :
429 85 : NS_ASSERTION(mChunkState.num != 0, "Must have a chunk number.");
430 :
431 85 : if (mPending.Length() < mChunkState.length) {
432 0 : *aDone = true;
433 0 : return NS_OK;
434 : }
435 :
436 : // Pull the chunk out of the pending stream data.
437 170 : nsCAutoString chunk;
438 85 : chunk.Assign(Substring(mPending, 0, mChunkState.length));
439 85 : mPending = Substring(mPending, mChunkState.length);
440 :
441 85 : *aDone = false;
442 85 : mState = PROTOCOL_STATE_CONTROL;
443 :
444 : //LOG(("Handling a %d-byte chunk", chunk.Length()));
445 85 : if (StringEndsWith(mTableUpdate->TableName(), NS_LITERAL_CSTRING("-shavar"))) {
446 0 : return ProcessShaChunk(chunk);
447 : } else {
448 85 : return ProcessPlaintextChunk(chunk);
449 : }
450 : }
451 :
452 : /**
453 : * Process a plaintext chunk (currently only used in unit tests).
454 : */
455 : nsresult
456 85 : ProtocolParser::ProcessPlaintextChunk(const nsACString& aChunk)
457 : {
458 85 : if (!mTableUpdate) {
459 0 : NS_WARNING("Chunk received with no table.");
460 0 : return NS_ERROR_FAILURE;
461 : }
462 :
463 : nsresult rv;
464 170 : nsTArray<nsCString> lines;
465 85 : ParseString(PromiseFlatCString(aChunk), '\n', lines);
466 :
467 : // non-hashed tables need to be hashed
468 250 : for (uint32 i = 0; i < lines.Length(); i++) {
469 165 : nsCString& line = lines[i];
470 :
471 165 : if (mChunkState.type == CHUNK_ADD) {
472 140 : if (mChunkState.hashSize == COMPLETE_SIZE) {
473 : Completion hash;
474 95 : hash.FromPlaintext(line, mCryptoHash);
475 95 : mTableUpdate->NewAddComplete(mChunkState.num, hash);
476 : } else {
477 45 : NS_ASSERTION(mChunkState.hashSize == 4, "Only 32- or 4-byte hashes can be used for add chunks.");
478 : Completion hash;
479 : Completion domHash;
480 : Prefix newHash;
481 45 : rv = LookupCache::GetKey(line, &domHash, mCryptoHash);
482 45 : NS_ENSURE_SUCCESS(rv, rv);
483 45 : hash.FromPlaintext(line, mCryptoHash);
484 : PRUint32 codedHash;
485 45 : rv = LookupCache::KeyedHash(hash.ToUint32(), domHash.ToUint32(), mHashKey, &codedHash);
486 45 : NS_ENSURE_SUCCESS(rv, rv);
487 45 : newHash.FromUint32(codedHash);
488 45 : mTableUpdate->NewAddPrefix(mChunkState.num, newHash);
489 : }
490 : } else {
491 25 : nsCString::const_iterator begin, iter, end;
492 25 : line.BeginReading(begin);
493 25 : line.EndReading(end);
494 25 : iter = begin;
495 : uint32 addChunk;
496 50 : if (!FindCharInReadable(':', iter, end) ||
497 25 : PR_sscanf(lines[i].get(), "%d:", &addChunk) != 1) {
498 0 : NS_WARNING("Received sub chunk without associated add chunk.");
499 0 : return NS_ERROR_FAILURE;
500 : }
501 25 : iter++;
502 :
503 25 : if (mChunkState.hashSize == COMPLETE_SIZE) {
504 : Completion hash;
505 24 : hash.FromPlaintext(Substring(iter, end), mCryptoHash);
506 24 : mTableUpdate->NewSubComplete(addChunk, hash, mChunkState.num);
507 : } else {
508 1 : NS_ASSERTION(mChunkState.hashSize == 4, "Only 32- or 4-byte hashes can be used for add chunks.");
509 : Prefix hash;
510 : Completion domHash;
511 : Prefix newHash;
512 1 : rv = LookupCache::GetKey(Substring(iter, end), &domHash, mCryptoHash);
513 1 : NS_ENSURE_SUCCESS(rv, rv);
514 1 : hash.FromPlaintext(Substring(iter, end), mCryptoHash);
515 : PRUint32 codedHash;
516 1 : rv = LookupCache::KeyedHash(hash.ToUint32(), domHash.ToUint32(), mHashKey, &codedHash);
517 1 : NS_ENSURE_SUCCESS(rv, rv);
518 1 : newHash.FromUint32(codedHash);
519 1 : mTableUpdate->NewSubPrefix(addChunk, newHash, mChunkState.num);
520 : // Needed to knock out completes
521 : // Fake chunk nr, will cause it to be removed next update
522 1 : mTableUpdate->NewSubPrefix(addChunk, hash, 0);
523 1 : mTableUpdate->NewSubChunk(0);
524 : }
525 : }
526 : }
527 :
528 85 : return NS_OK;
529 : }
530 :
531 : nsresult
532 0 : ProtocolParser::ProcessShaChunk(const nsACString& aChunk)
533 : {
534 0 : PRUint32 start = 0;
535 0 : while (start < aChunk.Length()) {
536 : // First four bytes are the domain key.
537 : Prefix domain;
538 0 : domain.Assign(Substring(aChunk, start, DOMAIN_SIZE));
539 0 : start += DOMAIN_SIZE;
540 :
541 : // Then a count of entries.
542 0 : uint8 numEntries = static_cast<uint8>(aChunk[start]);
543 0 : start++;
544 :
545 : nsresult rv;
546 0 : if (mChunkState.type == CHUNK_ADD && mChunkState.hashSize == PREFIX_SIZE) {
547 0 : rv = ProcessHostAdd(domain, numEntries, aChunk, &start);
548 0 : } else if (mChunkState.type == CHUNK_ADD && mChunkState.hashSize == COMPLETE_SIZE) {
549 0 : rv = ProcessHostAddComplete(numEntries, aChunk, &start);
550 0 : } else if (mChunkState.type == CHUNK_SUB && mChunkState.hashSize == PREFIX_SIZE) {
551 0 : rv = ProcessHostSub(domain, numEntries, aChunk, &start);
552 0 : } else if (mChunkState.type == CHUNK_SUB && mChunkState.hashSize == COMPLETE_SIZE) {
553 0 : rv = ProcessHostSubComplete(numEntries, aChunk, &start);
554 : } else {
555 0 : NS_WARNING("Unexpected chunk type/hash size!");
556 0 : LOG(("Got an unexpected chunk type/hash size: %s:%d",
557 : mChunkState.type == CHUNK_ADD ? "add" : "sub",
558 : mChunkState.hashSize));
559 0 : return NS_ERROR_FAILURE;
560 : }
561 0 : NS_ENSURE_SUCCESS(rv, rv);
562 : }
563 :
564 0 : return NS_OK;
565 : }
566 :
567 : nsresult
568 0 : ProtocolParser::ProcessHostAdd(const Prefix& aDomain, PRUint8 aNumEntries,
569 : const nsACString& aChunk, PRUint32* aStart)
570 : {
571 0 : NS_ASSERTION(mChunkState.hashSize == PREFIX_SIZE,
572 : "ProcessHostAdd should only be called for prefix hashes.");
573 :
574 : PRUint32 codedHash;
575 0 : PRUint32 domHash = aDomain.ToUint32();
576 :
577 0 : if (aNumEntries == 0) {
578 0 : nsresult rv = LookupCache::KeyedHash(domHash, domHash, mHashKey, &codedHash);
579 0 : NS_ENSURE_SUCCESS(rv, rv);
580 : Prefix newHash;
581 0 : newHash.FromUint32(codedHash);
582 0 : mTableUpdate->NewAddPrefix(mChunkState.num, newHash);
583 0 : return NS_OK;
584 : }
585 :
586 0 : if (*aStart + (PREFIX_SIZE * aNumEntries) > aChunk.Length()) {
587 0 : NS_WARNING("Chunk is not long enough to contain the expected entries.");
588 0 : return NS_ERROR_FAILURE;
589 : }
590 :
591 0 : for (uint8 i = 0; i < aNumEntries; i++) {
592 : Prefix hash;
593 0 : hash.Assign(Substring(aChunk, *aStart, PREFIX_SIZE));
594 0 : nsresult rv = LookupCache::KeyedHash(domHash, hash.ToUint32(), mHashKey, &codedHash);
595 0 : NS_ENSURE_SUCCESS(rv, rv);
596 : Prefix newHash;
597 0 : newHash.FromUint32(codedHash);
598 0 : mTableUpdate->NewAddPrefix(mChunkState.num, newHash);
599 0 : *aStart += PREFIX_SIZE;
600 : }
601 :
602 0 : return NS_OK;
603 : }
604 :
605 : nsresult
606 0 : ProtocolParser::ProcessHostSub(const Prefix& aDomain, PRUint8 aNumEntries,
607 : const nsACString& aChunk, PRUint32 *aStart)
608 : {
609 0 : NS_ASSERTION(mChunkState.hashSize == PREFIX_SIZE,
610 : "ProcessHostSub should only be called for prefix hashes.");
611 :
612 : PRUint32 codedHash;
613 0 : PRUint32 domHash = aDomain.ToUint32();
614 :
615 0 : if (aNumEntries == 0) {
616 0 : if ((*aStart) + 4 > aChunk.Length()) {
617 0 : NS_WARNING("Received a zero-entry sub chunk without an associated add.");
618 0 : return NS_ERROR_FAILURE;
619 : }
620 :
621 0 : const nsCSubstring& addChunkStr = Substring(aChunk, *aStart, 4);
622 0 : *aStart += 4;
623 :
624 : uint32 addChunk;
625 0 : memcpy(&addChunk, addChunkStr.BeginReading(), 4);
626 0 : addChunk = PR_ntohl(addChunk);
627 :
628 0 : nsresult rv = LookupCache::KeyedHash(domHash, domHash, mHashKey, &codedHash);
629 0 : NS_ENSURE_SUCCESS(rv, rv);
630 : Prefix newHash;
631 0 : newHash.FromUint32(codedHash);
632 :
633 0 : mTableUpdate->NewSubPrefix(addChunk, newHash, mChunkState.num);
634 : // Needed to knock out completes
635 : // Fake chunk nr, will cause it to be removed next update
636 0 : mTableUpdate->NewSubPrefix(addChunk, aDomain, 0);
637 0 : mTableUpdate->NewSubChunk(0);
638 0 : return NS_OK;
639 : }
640 :
641 0 : if (*aStart + ((PREFIX_SIZE + 4) * aNumEntries) > aChunk.Length()) {
642 0 : NS_WARNING("Chunk is not long enough to contain the expected entries.");
643 0 : return NS_ERROR_FAILURE;
644 : }
645 :
646 0 : for (uint8 i = 0; i < aNumEntries; i++) {
647 0 : const nsCSubstring& addChunkStr = Substring(aChunk, *aStart, 4);
648 0 : *aStart += 4;
649 :
650 : uint32 addChunk;
651 0 : memcpy(&addChunk, addChunkStr.BeginReading(), 4);
652 0 : addChunk = PR_ntohl(addChunk);
653 :
654 : Prefix prefix;
655 0 : prefix.Assign(Substring(aChunk, *aStart, PREFIX_SIZE));
656 0 : *aStart += PREFIX_SIZE;
657 :
658 0 : nsresult rv = LookupCache::KeyedHash(prefix.ToUint32(), domHash, mHashKey, &codedHash);
659 0 : NS_ENSURE_SUCCESS(rv, rv);
660 : Prefix newHash;
661 0 : newHash.FromUint32(codedHash);
662 :
663 0 : mTableUpdate->NewSubPrefix(addChunk, newHash, mChunkState.num);
664 : // Needed to knock out completes
665 : // Fake chunk nr, will cause it to be removed next update
666 0 : mTableUpdate->NewSubPrefix(addChunk, prefix, 0);
667 0 : mTableUpdate->NewSubChunk(0);
668 : }
669 :
670 0 : return NS_OK;
671 : }
672 :
673 : nsresult
674 0 : ProtocolParser::ProcessHostAddComplete(PRUint8 aNumEntries,
675 : const nsACString& aChunk, PRUint32* aStart)
676 : {
677 0 : NS_ASSERTION(mChunkState.hashSize == COMPLETE_SIZE,
678 : "ProcessHostAddComplete should only be called for complete hashes.");
679 :
680 0 : if (aNumEntries == 0) {
681 : // this is totally comprehensible.
682 0 : NS_WARNING("Expected > 0 entries for a 32-byte hash add.");
683 0 : return NS_OK;
684 : }
685 :
686 0 : if (*aStart + (COMPLETE_SIZE * aNumEntries) > aChunk.Length()) {
687 0 : NS_WARNING("Chunk is not long enough to contain the expected entries.");
688 0 : return NS_ERROR_FAILURE;
689 : }
690 :
691 0 : for (uint8 i = 0; i < aNumEntries; i++) {
692 : Completion hash;
693 0 : hash.Assign(Substring(aChunk, *aStart, COMPLETE_SIZE));
694 0 : mTableUpdate->NewAddComplete(mChunkState.num, hash);
695 0 : *aStart += COMPLETE_SIZE;
696 : }
697 :
698 0 : return NS_OK;
699 : }
700 :
701 : nsresult
702 0 : ProtocolParser::ProcessHostSubComplete(PRUint8 aNumEntries,
703 : const nsACString& aChunk, PRUint32* aStart)
704 : {
705 0 : NS_ASSERTION(mChunkState.hashSize == COMPLETE_SIZE,
706 : "ProcessHostSubComplete should only be called for complete hashes.");
707 :
708 0 : if (aNumEntries == 0) {
709 : // this is totally comprehensible.
710 0 : NS_WARNING("Expected > 0 entries for a 32-byte hash sub.");
711 0 : return NS_OK;
712 : }
713 :
714 0 : if (*aStart + ((COMPLETE_SIZE + 4) * aNumEntries) > aChunk.Length()) {
715 0 : NS_WARNING("Chunk is not long enough to contain the expected entries.");
716 0 : return NS_ERROR_FAILURE;
717 : }
718 :
719 0 : for (PRUint8 i = 0; i < aNumEntries; i++) {
720 : Completion hash;
721 0 : hash.Assign(Substring(aChunk, *aStart, COMPLETE_SIZE));
722 0 : *aStart += COMPLETE_SIZE;
723 :
724 0 : const nsCSubstring& addChunkStr = Substring(aChunk, *aStart, 4);
725 0 : *aStart += 4;
726 :
727 : uint32 addChunk;
728 0 : memcpy(&addChunk, addChunkStr.BeginReading(), 4);
729 0 : addChunk = PR_ntohl(addChunk);
730 :
731 0 : mTableUpdate->NewSubComplete(addChunk, hash, mChunkState.num);
732 : }
733 :
734 0 : return NS_OK;
735 : }
736 :
737 : bool
738 447 : ProtocolParser::NextLine(nsACString& line)
739 : {
740 447 : int32 newline = mPending.FindChar('\n');
741 447 : if (newline == kNotFound) {
742 82 : return false;
743 : }
744 365 : line.Assign(Substring(mPending, 0, newline));
745 365 : mPending = Substring(mPending, newline + 1);
746 365 : return true;
747 : }
748 :
749 : void
750 118 : ProtocolParser::CleanupUpdates()
751 : {
752 123 : for (uint32 i = 0; i < mTableUpdates.Length(); i++) {
753 5 : delete mTableUpdates[i];
754 : }
755 118 : mTableUpdates.Clear();
756 118 : }
757 :
758 : TableUpdate *
759 133 : ProtocolParser::GetTableUpdate(const nsACString& aTable)
760 : {
761 136 : for (uint32 i = 0; i < mTableUpdates.Length(); i++) {
762 3 : if (aTable.Equals(mTableUpdates[i]->TableName())) {
763 0 : return mTableUpdates[i];
764 : }
765 : }
766 :
767 : // We free automatically on destruction, ownership of these
768 : // updates can be transferred to DBServiceWorker, which passes
769 : // them back to Classifier when doing the updates, and that
770 : // will free them.
771 133 : TableUpdate *update = new TableUpdate(aTable);
772 133 : mTableUpdates.AppendElement(update);
773 133 : return update;
774 : }
775 :
776 : }
777 : }
|