15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Parse the data returned from the SafeBrowsing v2.1 protocol response.
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// TODOv3(shess): Review these changes carefully.
8f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stdlib.h>
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/format_macros.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
13f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "base/strings/string_number_conversions.h"
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_split.h"
15868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/stringprintf.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/sys_byteorder.h"
17f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "base/time/time.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "build/build_config.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/safe_browsing/protocol_parser.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/safe_browsing/safe_browsing_util.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
23f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
24f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// Helper class for scanning a buffer.
25f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)class BufferReader {
26f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) public:
27f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  BufferReader(const char* data, size_t length)
28f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      : data_(data),
29f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        length_(length) {
30f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
31f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
32f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Return info about remaining buffer data.
33f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  size_t length() const {
34f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return length_;
35f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
36f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  const char* data() const {
37f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return data_;
38f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
39f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  bool empty() const {
40f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return length_ == 0;
41f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
42f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
43f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Remove |l| characters from the buffer.
44f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  void Advance(size_t l) {
45f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    DCHECK_LE(l, length());
46f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    data_ += l;
47f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    length_ -= l;
48f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
49f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
50f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Get a reference to data in the buffer.
51f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // TODO(shess): I'm not sure I like this.  Fill out a StringPiece instead?
52f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  bool RefData(const void** pptr, size_t l) {
53f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (length() < l) {
54f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      Advance(length());  // poison
55f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      return false;
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
57f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
58f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    *pptr = data();
59f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    Advance(l);
60f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return true;
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
63f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Copy data out of the buffer.
64f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  bool GetData(void* ptr, size_t l) {
65f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    const void* buf_ptr;
66f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (!RefData(&buf_ptr, l))
67f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      return false;
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
69f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    memcpy(ptr, buf_ptr, l);
70f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return true;
71f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
72f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
73f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Read a 32-bit integer in network byte order into a local uint32.
74f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  bool GetNet32(uint32* i) {
75f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (!GetData(i, sizeof(*i)))
76f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      return false;
77f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
78f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    *i = base::NetToHost32(*i);
79f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return true;
80f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
82f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Returns false if there is no data, otherwise fills |*line| with a reference
83f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // to the next line of data in the buffer.
84f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  bool GetLine(base::StringPiece* line) {
85f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (!length_)
86f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      return false;
87f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
88f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // Find the end of the line, or the end of the input.
89f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    size_t eol = 0;
90f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    while (eol < length_ && data_[eol] != '\n') {
91f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      ++eol;
92f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    }
93f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    line->set(data_, eol);
94f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    Advance(eol);
95f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
96f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // Skip the newline if present.
97f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (length_ && data_[0] == '\n')
98f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      Advance(1);
99f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
100f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return true;
101f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
102f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
103f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Read out |c| colon-separated pieces from the next line.  The resulting
104f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // pieces point into the original data buffer.
105f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  bool GetPieces(size_t c, std::vector<base::StringPiece>* pieces) {
106f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    base::StringPiece line;
107f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (!GetLine(&line))
108f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      return false;
109f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
110f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // Find the parts separated by ':'.
111f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    while (pieces->size() + 1 < c) {
112f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      size_t colon_ofs = line.find(':');
113f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      if (colon_ofs == base::StringPiece::npos) {
114f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        Advance(length_);
115f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        return false;
116f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      }
117f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
118f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      pieces->push_back(line.substr(0, colon_ofs));
119f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      line.remove_prefix(colon_ofs + 1);
120f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    }
121f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
122f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // The last piece runs to the end of the line.
123f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    pieces->push_back(line);
124f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return true;
125f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
126f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
127f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) private:
128f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  const char* data_;
129f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  size_t length_;
130f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
131f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(BufferReader);
132f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
133f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccibool ParseGetHashMetadata(size_t hash_count,
1351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                          BufferReader* reader,
1361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                          std::vector<SBFullHashResult>* full_hashes) {
1371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  for (size_t i = 0; i < hash_count; ++i) {
1381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    base::StringPiece line;
1391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (!reader->GetLine(&line))
1401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return false;
1411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    size_t meta_data_len;
1431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (!base::StringToSizeT(line, &meta_data_len))
1441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return false;
1451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    const void* meta_data;
1471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (!reader->RefData(&meta_data, meta_data_len))
1481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return false;
1491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (full_hashes) {
1511320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      (*full_hashes)[full_hashes->size() - hash_count + i].metadata.assign(
1521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci          reinterpret_cast<const char*>(meta_data), meta_data_len);
1531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    }
1541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
1551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return true;
1561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
1571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
158f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}  // namespace
159f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
160f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)namespace safe_browsing {
161f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
162f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// BODY          = CACHELIFETIME LF HASHENTRY* EOF
163f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// CACHELIFETIME = DIGIT+
164f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// HASHENTRY     = LISTNAME ":" HASHSIZE ":" NUMRESPONSES [":m"] LF
165f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)//                 HASHDATA (METADATALEN LF METADATA)*
166f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// HASHSIZE      = DIGIT+                  # Length of each full hash
167f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// NUMRESPONSES  = DIGIT+                  # Number of full hashes in HASHDATA
168f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// HASHDATA      = <HASHSIZE*NUMRESPONSES number of unsigned bytes>
169f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// METADATALEN   = DIGIT+
170f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// METADATA      = <METADATALEN number of unsigned bytes>
171f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)bool ParseGetHash(const char* chunk_data,
172f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                  size_t chunk_len,
173f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                  base::TimeDelta* cache_lifetime,
174f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                  std::vector<SBFullHashResult>* full_hashes) {
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  full_hashes->clear();
176f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  BufferReader reader(chunk_data, chunk_len);
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
178f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Parse out cache lifetime.
179f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  {
180f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    base::StringPiece line;
181f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (!reader.GetLine(&line))
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
184f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    int64_t cache_lifetime_seconds;
185f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (!base::StringToInt64(line, &cache_lifetime_seconds))
186f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      return false;
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
188f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // TODO(shess): Zero also doesn't make sense, but isn't clearly forbidden,
189f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // either.  Maybe there should be a threshold involved.
190f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (cache_lifetime_seconds < 0)
191f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      return false;
192f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
193f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    *cache_lifetime = base::TimeDelta::FromSeconds(cache_lifetime_seconds);
194f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
195f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
196f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  while (!reader.empty()) {
197f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    std::vector<base::StringPiece> cmd_parts;
198f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (!reader.GetPieces(3, &cmd_parts))
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SBFullHashResult full_hash;
2020529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    full_hash.list_id = safe_browsing_util::GetListId(cmd_parts[0]);
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
204f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    size_t hash_len;
205f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (!base::StringToSizeT(cmd_parts[1], &hash_len))
206f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      return false;
207f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
208f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // TODO(shess): Is this possible?  If not, why the length present?
209f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (hash_len != sizeof(SBFullHash))
210f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      return false;
211f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
212f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // Metadata is indicated by an optional ":m" at the end of the line.
213f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    bool has_metadata = false;
214f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    base::StringPiece hash_count_string = cmd_parts[2];
215f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    size_t optional_colon = hash_count_string.find(':', 0);
216f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (optional_colon != base::StringPiece::npos) {
217f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      if (hash_count_string.substr(optional_colon) != ":m")
218f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        return false;
219f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      has_metadata = true;
220f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      hash_count_string.remove_suffix(2);
221f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    }
222f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
223f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    size_t hash_count;
224f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (!base::StringToSizeT(hash_count_string, &hash_count))
225f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      return false;
226f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
227f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (hash_len * hash_count > reader.length())
2285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      return false;
2295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Ignore hash results from lists we don't recognize.
2310529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    if (full_hash.list_id < 0) {
232f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      reader.Advance(hash_len * hash_count);
2331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      if (has_metadata && !ParseGetHashMetadata(hash_count, &reader, NULL))
2341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        return false;
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
238f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    for (size_t i = 0; i < hash_count; ++i) {
239f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      if (!reader.GetData(&full_hash.hash, hash_len))
240f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        return false;
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      full_hashes->push_back(full_hash);
242f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    }
243f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
2441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (has_metadata && !ParseGetHashMetadata(hash_count, &reader, full_hashes))
2451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return false;
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
248f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return reader.empty();
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
251f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// BODY       = HEADER LF PREFIXES EOF
252f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// HEADER     = PREFIXSIZE ":" LENGTH
253f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// PREFIXSIZE = DIGIT+         # Size of each prefix in bytes
254f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// LENGTH     = DIGIT+         # Size of PREFIXES in bytes
255f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)std::string FormatGetHash(const std::vector<SBPrefix>& prefixes) {
256f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  std::string request;
257f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  request.append(base::Uint64ToString(sizeof(SBPrefix)));
258f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  request.append(":");
259f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  request.append(base::Uint64ToString(sizeof(SBPrefix) * prefixes.size()));
260f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  request.append("\n");
261f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
262f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // SBPrefix values are read without concern for byte order, so write back the
263f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // same way.
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < prefixes.size(); ++i) {
265f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    request.append(reinterpret_cast<const char*>(&prefixes[i]),
266f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                   sizeof(SBPrefix));
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
268f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
269f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return request;
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
272f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)bool ParseUpdate(const char* chunk_data,
273f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                 size_t chunk_len,
274f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                 size_t* next_update_sec,
275f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                 bool* reset,
276f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                 std::vector<SBChunkDelete>* deletes,
277f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                 std::vector<ChunkUrl>* chunk_urls) {
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(next_update_sec);
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(deletes);
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(chunk_urls);
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
282f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  BufferReader reader(chunk_data, chunk_len);
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Populated below.
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string list_name;
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
287f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  while (!reader.empty()) {
288f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    std::vector<base::StringPiece> pieces;
289f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (!reader.GetPieces(2, &pieces))
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
292f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    base::StringPiece& command = pieces[0];
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Differentiate on the first character of the command (which is usually
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // only one character, with the exception of the 'ad' and 'sd' commands).
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    switch (command[0]) {
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case 'a':
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case 's': {
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Must be either an 'ad' (add-del) or 'sd' (sub-del) chunk. We must
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // have also parsed the list name before getting here, or the add-del
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // or sub-del will have no context.
302f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        if (list_name.empty() || (command != "ad" && command != "sd"))
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return false;
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        SBChunkDelete chunk_delete;
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        chunk_delete.is_sub_del = command[0] == 's';
306f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        StringToRanges(pieces[1].as_string(), &chunk_delete.chunk_del);
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        chunk_delete.list_name = list_name;
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        deletes->push_back(chunk_delete);
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case 'i':
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // The line providing the name of the list (i.e. 'goog-phish-shavar').
314f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        list_name = pieces[1].as_string();
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case 'n':
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // The line providing the next earliest time (in seconds) to re-query.
319f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        if (!base::StringToSizeT(pieces[1], next_update_sec))
320f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          return false;
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case 'u': {
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ChunkUrl chunk_url;
325f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        chunk_url.url = pieces[1].as_string();  // Skip the initial "u:".
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        chunk_url.list_name = list_name;
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        chunk_urls->push_back(chunk_url);
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case 'r':
332f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        if (pieces[1] != "pleasereset")
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return false;
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        *reset = true;
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      default:
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // According to the spec, we ignore commands we don't understand.
339f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        // TODO(shess): Does this apply to r:unknown or n:not-integer?
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
347f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// BODY      = (UINT32 CHUNKDATA)+
348f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// UINT32    = Unsigned 32-bit integer in network byte order
349f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// CHUNKDATA = Encoded ChunkData protocol message
350f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)bool ParseChunk(const char* data,
351f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                size_t length,
352f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                ScopedVector<SBChunkData>* chunks) {
353f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  BufferReader reader(data, length);
354f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
355f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  while (!reader.empty()) {
356f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    uint32 l = 0;
357f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (!reader.GetNet32(&l) || l == 0 || l > reader.length())
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
360f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    const void* p = NULL;
361f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (!reader.RefData(&p, l))
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
364f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    scoped_ptr<SBChunkData> chunk(new SBChunkData());
365f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (!chunk->ParseFrom(reinterpret_cast<const unsigned char*>(p), l))
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
368f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    chunks->push_back(chunk.release());
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
371f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  DCHECK(reader.empty());
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
375f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// LIST      = LISTNAME ";" LISTINFO (":" LISTINFO)*
376f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// LISTINFO  = CHUNKTYPE ":" CHUNKLIST
377f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// CHUNKTYPE = "a" | "s"
378f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// CHUNKLIST = (RANGE | NUMBER) ["," CHUNKLIST]
379f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// NUMBER    = DIGIT+
380f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// RANGE     = NUMBER "-" NUMBER
381f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)std::string FormatList(const SBListChunkRanges& list) {
382f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  std::string formatted_results = list.name;
383f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  formatted_results.append(";");
384f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
385f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (!list.adds.empty())
386f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    formatted_results.append("a:").append(list.adds);
387f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (!list.adds.empty() && !list.subs.empty())
388f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    formatted_results.append(":");
389f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (!list.subs.empty())
390f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    formatted_results.append("s:").append(list.subs);
391f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  formatted_results.append("\n");
392f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
393f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return formatted_results;
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
396f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}  // namespace safe_browsing
397