[email protected] | 4e5ae20f | 2010-09-24 04:52:11 | [diff] [blame] | 1 | // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
license.bot | bf09a50 | 2008-08-24 00:55:55 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 4 | // |
| 5 | // Parse the data returned from the SafeBrowsing v2.1 protocol response. |
| 6 | |
[email protected] | 39a749c | 2011-01-28 02:40:46 | [diff] [blame] | 7 | #include <stdlib.h> |
| 8 | |
[email protected] | da2f68f | 2011-05-11 21:40:55 | [diff] [blame] | 9 | #include "base/format_macros.h" |
| 10 | #include "base/logging.h" |
| 11 | #include "base/stringprintf.h" |
| 12 | #include "base/string_split.h" |
| 13 | #include "build/build_config.h" |
[email protected] | 34b2b00 | 2009-11-20 06:53:28 | [diff] [blame] | 14 | #include "chrome/browser/safe_browsing/protocol_parser.h" |
[email protected] | f5ce36a | 2011-01-12 19:24:21 | [diff] [blame] | 15 | #include "chrome/browser/safe_browsing/safe_browsing_util.h" |
[email protected] | 34b2b00 | 2009-11-20 06:53:28 | [diff] [blame] | 16 | |
[email protected] | d3ad8b7 | 2008-09-17 17:45:02 | [diff] [blame] | 17 | #if defined(OS_WIN) |
| 18 | #include <Winsock2.h> |
| 19 | #elif defined(OS_POSIX) |
| 20 | #include <arpa/inet.h> |
| 21 | #endif |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 22 | |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 23 | namespace { |
| 24 | // Helper function for quick scans of a line oriented protocol. Note that we use |
| 25 | // std::string::assign(const charT* s, size_type n) |
| 26 | // to copy data into 'line'. This form of 'assign' does not call strlen on |
| 27 | // 'input', which is binary data and is not NULL terminated. 'input' may also |
| 28 | // contain valid NULL bytes in the payload, which a strlen based copy would |
| 29 | // truncate. |
| 30 | bool GetLine(const char* input, int input_len, std::string* line) { |
| 31 | const char* pos = input; |
| 32 | while (pos && (pos - input < input_len)) { |
| 33 | if (*pos == '\n') { |
| 34 | line->assign(input, pos - input); |
| 35 | return true; |
| 36 | } |
| 37 | ++pos; |
| 38 | } |
| 39 | return false; |
| 40 | } |
| 41 | } |
| 42 | |
| 43 | //------------------------------------------------------------------------------ |
| 44 | // SafeBrowsingParser implementation |
| 45 | |
| 46 | SafeBrowsingProtocolParser::SafeBrowsingProtocolParser() { |
| 47 | } |
| 48 | |
| 49 | bool SafeBrowsingProtocolParser::ParseGetHash( |
| 50 | const char* chunk_data, |
| 51 | int chunk_len, |
| 52 | const std::string& key, |
| 53 | bool* re_key, |
| 54 | std::vector<SBFullHashResult>* full_hashes) { |
| 55 | full_hashes->clear(); |
| 56 | int length = chunk_len; |
| 57 | const char* data = chunk_data; |
| 58 | |
| 59 | int offset; |
| 60 | std::string line; |
| 61 | if (!key.empty()) { |
| 62 | if (!GetLine(data, length, &line)) |
| 63 | return false; // Error! Bad GetHash result. |
| 64 | |
| 65 | if (line == "e:pleaserekey") { |
| 66 | *re_key = true; |
| 67 | return true; |
| 68 | } |
| 69 | |
| 70 | offset = static_cast<int>(line.size()) + 1; |
| 71 | data += offset; |
| 72 | length -= offset; |
| 73 | |
| 74 | if (!safe_browsing_util::VerifyMAC(key, line, data, length)) |
| 75 | return false; |
| 76 | } |
| 77 | |
| 78 | while (length > 0) { |
| 79 | if (!GetLine(data, length, &line)) |
| 80 | return false; |
| 81 | |
| 82 | offset = static_cast<int>(line.size()) + 1; |
| 83 | data += offset; |
| 84 | length -= offset; |
| 85 | |
| 86 | std::vector<std::string> cmd_parts; |
[email protected] | 76eb024 | 2010-10-14 00:35:36 | [diff] [blame] | 87 | base::SplitString(line, ':', &cmd_parts); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 88 | if (cmd_parts.size() != 3) |
| 89 | return false; |
| 90 | |
| 91 | SBFullHashResult full_hash; |
| 92 | full_hash.list_name = cmd_parts[0]; |
| 93 | full_hash.add_chunk_id = atoi(cmd_parts[1].c_str()); |
| 94 | int full_hash_len = atoi(cmd_parts[2].c_str()); |
| 95 | |
[email protected] | 3b0f5f6 | 2009-01-08 01:22:30 | [diff] [blame] | 96 | // Ignore hash results from lists we don't recognize. |
| 97 | if (safe_browsing_util::GetListId(full_hash.list_name) < 0) { |
| 98 | data += full_hash_len; |
| 99 | length -= full_hash_len; |
| 100 | continue; |
| 101 | } |
| 102 | |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 103 | while (full_hash_len > 0) { |
[email protected] | d3ad8b7 | 2008-09-17 17:45:02 | [diff] [blame] | 104 | DCHECK(static_cast<size_t>(full_hash_len) >= sizeof(SBFullHash)); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 105 | memcpy(&full_hash.hash, data, sizeof(SBFullHash)); |
| 106 | full_hashes->push_back(full_hash); |
| 107 | data += sizeof(SBFullHash); |
| 108 | length -= sizeof(SBFullHash); |
| 109 | full_hash_len -= sizeof(SBFullHash); |
| 110 | } |
| 111 | } |
| 112 | |
| 113 | return length == 0; |
| 114 | } |
| 115 | |
| 116 | void SafeBrowsingProtocolParser::FormatGetHash( |
| 117 | const std::vector<SBPrefix>& prefixes, std::string* request) { |
| 118 | DCHECK(request); |
| 119 | |
| 120 | // Format the request for GetHash. |
[email protected] | da2f68f | 2011-05-11 21:40:55 | [diff] [blame] | 121 | request->append(base::StringPrintf("%" PRIuS ":%" PRIuS "\n", |
| 122 | sizeof(SBPrefix), |
| 123 | sizeof(SBPrefix) * prefixes.size())); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 124 | for (size_t i = 0; i < prefixes.size(); ++i) { |
| 125 | request->append(reinterpret_cast<const char*>(&prefixes[i]), |
| 126 | sizeof(SBPrefix)); |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | bool SafeBrowsingProtocolParser::ParseUpdate( |
| 131 | const char* chunk_data, |
| 132 | int chunk_len, |
| 133 | const std::string& key, |
| 134 | int* next_update_sec, |
| 135 | bool* re_key, |
| 136 | bool* reset, |
| 137 | std::vector<SBChunkDelete>* deletes, |
| 138 | std::vector<ChunkUrl>* chunk_urls) { |
| 139 | DCHECK(next_update_sec); |
| 140 | DCHECK(deletes); |
| 141 | DCHECK(chunk_urls); |
| 142 | |
| 143 | int length = chunk_len; |
| 144 | const char* data = chunk_data; |
| 145 | |
| 146 | // Populated below. |
| 147 | std::string list_name; |
| 148 | |
| 149 | while (length > 0) { |
| 150 | std::string cmd_line; |
| 151 | if (!GetLine(data, length, &cmd_line)) |
| 152 | return false; // Error: bad list format! |
| 153 | |
| 154 | std::vector<std::string> cmd_parts; |
[email protected] | 76eb024 | 2010-10-14 00:35:36 | [diff] [blame] | 155 | base::SplitString(cmd_line, ':', &cmd_parts); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 156 | if (cmd_parts.empty()) |
| 157 | return false; |
| 158 | const std::string& command = cmd_parts[0]; |
[email protected] | 22717d1e | 2008-10-15 21:55:32 | [diff] [blame] | 159 | if (cmd_parts.size() != 2 && command[0] != 'u') |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 160 | return false; |
| 161 | |
| 162 | const int consumed = static_cast<int>(cmd_line.size()) + 1; |
| 163 | data += consumed; |
| 164 | length -= consumed; |
| 165 | if (length < 0) |
| 166 | return false; // Parsing error. |
| 167 | |
| 168 | // Differentiate on the first character of the command (which is usually |
| 169 | // only one character, with the exception of the 'ad' and 'sd' commands). |
| 170 | switch (command[0]) { |
| 171 | case 'a': |
| 172 | case 's': { |
| 173 | // Must be either an 'ad' (add-del) or 'sd' (sub-del) chunk. We must |
| 174 | // have also parsed the list name before getting here, or the add-del |
| 175 | // or sub-del will have no context. |
| 176 | if (command.size() != 2 || command[1] != 'd' || list_name.empty()) |
| 177 | return false; |
| 178 | SBChunkDelete chunk_delete; |
| 179 | chunk_delete.is_sub_del = command[0] == 's'; |
| 180 | StringToRanges(cmd_parts[1], &chunk_delete.chunk_del); |
| 181 | chunk_delete.list_name = list_name; |
| 182 | deletes->push_back(chunk_delete); |
| 183 | break; |
| 184 | } |
| 185 | |
| 186 | case 'e': |
| 187 | if (cmd_parts[1] != "pleaserekey") |
| 188 | return false; |
| 189 | *re_key = true; |
| 190 | break; |
| 191 | |
| 192 | case 'i': |
| 193 | // The line providing the name of the list (i.e. 'goog-phish-shavar'). |
| 194 | list_name = cmd_parts[1]; |
| 195 | break; |
| 196 | |
| 197 | case 'm': |
| 198 | // Verify that the MAC of the remainer of this chunk is what we expect. |
| 199 | if (!key.empty() && |
| 200 | !safe_browsing_util::VerifyMAC(key, cmd_parts[1], data, length)) |
| 201 | return false; |
| 202 | break; |
| 203 | |
| 204 | case 'n': |
| 205 | // The line providing the next earliest time (in seconds) to re-query. |
| 206 | *next_update_sec = atoi(cmd_parts[1].c_str()); |
| 207 | break; |
| 208 | |
| 209 | case 'u': { |
[email protected] | 22717d1e | 2008-10-15 21:55:32 | [diff] [blame] | 210 | // The redirect command is of the form: u:<url>,<mac> where <url> can |
| 211 | // contain multiple colons, commas or any valid URL characters. We scan |
| 212 | // backwards in the string looking for the first ',' we encounter and |
| 213 | // assume that everything before that is the URL and everything after |
| 214 | // is the MAC (if the MAC was requested). |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 215 | std::string mac; |
[email protected] | 22717d1e | 2008-10-15 21:55:32 | [diff] [blame] | 216 | std::string redirect_url(cmd_line, 2); // Skip the initial "u:". |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 217 | if (!key.empty()) { |
| 218 | std::string::size_type mac_pos = redirect_url.rfind(','); |
| 219 | if (mac_pos == std::string::npos) |
| 220 | return false; |
| 221 | mac = redirect_url.substr(mac_pos + 1); |
| 222 | redirect_url = redirect_url.substr(0, mac_pos); |
| 223 | } |
[email protected] | 22717d1e | 2008-10-15 21:55:32 | [diff] [blame] | 224 | |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 225 | ChunkUrl chunk_url; |
| 226 | chunk_url.url = redirect_url; |
[email protected] | 8b02bb8a | 2008-10-22 02:05:09 | [diff] [blame] | 227 | chunk_url.list_name = list_name; |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 228 | if (!key.empty()) |
| 229 | chunk_url.mac = mac; |
| 230 | chunk_urls->push_back(chunk_url); |
| 231 | break; |
| 232 | } |
| 233 | |
| 234 | case 'r': |
| 235 | if (cmd_parts[1] != "pleasereset") |
| 236 | return false; |
| 237 | *reset = true; |
| 238 | break; |
| 239 | |
| 240 | default: |
[email protected] | 22717d1e | 2008-10-15 21:55:32 | [diff] [blame] | 241 | // According to the spec, we ignore commands we don't understand. |
| 242 | break; |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 243 | } |
| 244 | } |
| 245 | |
| 246 | return true; |
| 247 | } |
| 248 | |
[email protected] | f5ce36a | 2011-01-12 19:24:21 | [diff] [blame] | 249 | bool SafeBrowsingProtocolParser::ParseChunk(const std::string& list_name, |
| 250 | const char* data, |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 251 | int length, |
| 252 | const std::string& key, |
| 253 | const std::string& mac, |
| 254 | bool* re_key, |
[email protected] | 7b1e3710 | 2010-03-08 21:43:16 | [diff] [blame] | 255 | SBChunkList* chunks) { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 256 | int remaining = length; |
| 257 | const char* chunk_data = data; |
| 258 | |
| 259 | if (!key.empty() && |
| 260 | !safe_browsing_util::VerifyMAC(key, mac, data, length)) { |
| 261 | return false; |
| 262 | } |
| 263 | |
| 264 | while (remaining > 0) { |
| 265 | std::string cmd_line; |
| 266 | if (!GetLine(chunk_data, length, &cmd_line)) |
| 267 | return false; // Error: bad chunk format! |
| 268 | |
| 269 | const int line_len = static_cast<int>(cmd_line.length()) + 1; |
[email protected] | a781b07 | 2011-01-10 23:07:23 | [diff] [blame] | 270 | chunk_data += line_len; |
| 271 | remaining -= line_len; |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 272 | std::vector<std::string> cmd_parts; |
[email protected] | 76eb024 | 2010-10-14 00:35:36 | [diff] [blame] | 273 | base::SplitString(cmd_line, ':', &cmd_parts); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 274 | |
| 275 | // Handle a possible re-key command. |
| 276 | if (cmd_parts.size() != 4) { |
| 277 | if (cmd_parts.size() == 2 && |
| 278 | cmd_parts[0] == "e" && |
| 279 | cmd_parts[1] == "pleaserekey") { |
| 280 | *re_key = true; |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 281 | continue; |
| 282 | } |
| 283 | return false; |
| 284 | } |
| 285 | |
| 286 | // Process the chunk data. |
| 287 | const int chunk_number = atoi(cmd_parts[1].c_str()); |
| 288 | const int hash_len = atoi(cmd_parts[2].c_str()); |
| 289 | if (hash_len != sizeof(SBPrefix) && hash_len != sizeof(SBFullHash)) { |
[email protected] | 53b7bf91 | 2010-10-25 18:33:19 | [diff] [blame] | 290 | VLOG(1) << "ParseChunk got unknown hashlen " << hash_len; |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 291 | return false; |
| 292 | } |
| 293 | |
| 294 | const int chunk_len = atoi(cmd_parts[3].c_str()); |
[email protected] | a781b07 | 2011-01-10 23:07:23 | [diff] [blame] | 295 | |
| 296 | if (remaining < chunk_len) |
| 297 | return false; // parse error. |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 298 | |
| 299 | chunks->push_back(SBChunk()); |
| 300 | chunks->back().chunk_number = chunk_number; |
| 301 | |
| 302 | if (cmd_parts[0] == "a") { |
[email protected] | 445d4cc | 2008-10-09 21:31:57 | [diff] [blame] | 303 | chunks->back().is_add = true; |
[email protected] | f5ce36a | 2011-01-12 19:24:21 | [diff] [blame] | 304 | if (!ParseAddChunk(list_name, chunk_data, chunk_len, hash_len, |
[email protected] | d321644 | 2009-03-05 21:07:27 | [diff] [blame] | 305 | &chunks->back().hosts)) |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 306 | return false; // Parse error. |
| 307 | } else if (cmd_parts[0] == "s") { |
[email protected] | 445d4cc | 2008-10-09 21:31:57 | [diff] [blame] | 308 | chunks->back().is_add = false; |
[email protected] | f5ce36a | 2011-01-12 19:24:21 | [diff] [blame] | 309 | if (!ParseSubChunk(list_name, chunk_data, chunk_len, hash_len, |
[email protected] | d321644 | 2009-03-05 21:07:27 | [diff] [blame] | 310 | &chunks->back().hosts)) |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 311 | return false; // Parse error. |
| 312 | } else { |
| 313 | NOTREACHED(); |
| 314 | return false; |
| 315 | } |
| 316 | |
| 317 | chunk_data += chunk_len; |
| 318 | remaining -= chunk_len; |
[email protected] | a781b07 | 2011-01-10 23:07:23 | [diff] [blame] | 319 | DCHECK_LE(0, remaining); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 320 | } |
| 321 | |
| 322 | DCHECK(remaining == 0); |
| 323 | |
| 324 | return true; |
| 325 | } |
| 326 | |
[email protected] | f5ce36a | 2011-01-12 19:24:21 | [diff] [blame] | 327 | bool SafeBrowsingProtocolParser::ParseAddChunk(const std::string& list_name, |
| 328 | const char* data, |
| 329 | int data_len, |
| 330 | int hash_len, |
| 331 | std::deque<SBChunkHost>* hosts) { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 332 | const char* chunk_data = data; |
[email protected] | f5ce36a | 2011-01-12 19:24:21 | [diff] [blame] | 333 | int remaining = data_len; |
| 334 | int prefix_count; |
| 335 | SBEntry::Type type = hash_len == sizeof(SBPrefix) ? |
| 336 | SBEntry::ADD_PREFIX : SBEntry::ADD_FULL_HASH; |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 337 | |
[email protected] | f5ce36a | 2011-01-12 19:24:21 | [diff] [blame] | 338 | if (list_name == safe_browsing_util::kBinHashList) { |
| 339 | // kBinHashList only contains prefixes, no HOSTKEY and COUNT. |
| 340 | DCHECK_EQ(0, remaining % hash_len); |
| 341 | prefix_count = remaining / hash_len; |
| 342 | SBChunkHost chunk_host; |
| 343 | chunk_host.host = 0; |
| 344 | chunk_host.entry = SBEntry::Create(type, prefix_count); |
| 345 | hosts->push_back(chunk_host); |
| 346 | if (!ReadPrefixes(&chunk_data, &remaining, chunk_host.entry, prefix_count)) |
| 347 | return false; |
| 348 | } else { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 349 | SBPrefix host; |
[email protected] | f5ce36a | 2011-01-12 19:24:21 | [diff] [blame] | 350 | const int min_size = sizeof(SBPrefix) + 1; |
| 351 | while (remaining >= min_size) { |
| 352 | ReadHostAndPrefixCount(&chunk_data, &remaining, &host, &prefix_count); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 353 | SBChunkHost chunk_host; |
| 354 | chunk_host.host = host; |
[email protected] | f5ce36a | 2011-01-12 19:24:21 | [diff] [blame] | 355 | chunk_host.entry = SBEntry::Create(type, prefix_count); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 356 | hosts->push_back(chunk_host); |
[email protected] | f5ce36a | 2011-01-12 19:24:21 | [diff] [blame] | 357 | if (!ReadPrefixes(&chunk_data, &remaining, chunk_host.entry, |
| 358 | prefix_count)) |
| 359 | return false; |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 360 | } |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 361 | } |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 362 | return remaining == 0; |
| 363 | } |
| 364 | |
[email protected] | f5ce36a | 2011-01-12 19:24:21 | [diff] [blame] | 365 | bool SafeBrowsingProtocolParser::ParseSubChunk(const std::string& list_name, |
| 366 | const char* data, |
| 367 | int data_len, |
| 368 | int hash_len, |
| 369 | std::deque<SBChunkHost>* hosts) { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 370 | int remaining = data_len; |
| 371 | const char* chunk_data = data; |
[email protected] | f5ce36a | 2011-01-12 19:24:21 | [diff] [blame] | 372 | int prefix_count; |
| 373 | SBEntry::Type type = hash_len == sizeof(SBPrefix) ? |
| 374 | SBEntry::SUB_PREFIX : SBEntry::SUB_FULL_HASH; |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 375 | |
[email protected] | f5ce36a | 2011-01-12 19:24:21 | [diff] [blame] | 376 | if (list_name == safe_browsing_util::kBinHashList) { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 377 | SBChunkHost chunk_host; |
[email protected] | f5ce36a | 2011-01-12 19:24:21 | [diff] [blame] | 378 | // Set host to 0 and it won't be used for kBinHashList. |
| 379 | chunk_host.host = 0; |
| 380 | // kBinHashList only contains (add_chunk_number, prefix) pairs, no HOSTKEY |
| 381 | // and COUNT. |add_chunk_number| is int32. |
| 382 | prefix_count = remaining / (sizeof(int32) + hash_len); |
| 383 | chunk_host.entry = SBEntry::Create(type, prefix_count); |
| 384 | if (!ReadPrefixes(&chunk_data, &remaining, chunk_host.entry, prefix_count)) |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 385 | return false; |
[email protected] | f5ce36a | 2011-01-12 19:24:21 | [diff] [blame] | 386 | hosts->push_back(chunk_host); |
| 387 | } else { |
| 388 | SBPrefix host; |
| 389 | const int min_size = 2 * sizeof(SBPrefix) + 1; |
| 390 | while (remaining >= min_size) { |
| 391 | ReadHostAndPrefixCount(&chunk_data, &remaining, &host, &prefix_count); |
| 392 | SBChunkHost chunk_host; |
| 393 | chunk_host.host = host; |
| 394 | chunk_host.entry = SBEntry::Create(type, prefix_count); |
| 395 | hosts->push_back(chunk_host); |
| 396 | if (prefix_count == 0) { |
| 397 | // There is only an add chunk number (no prefixes). |
| 398 | chunk_host.entry->set_chunk_id(ReadChunkId(&chunk_data, &remaining)); |
| 399 | continue; |
| 400 | } |
| 401 | if (!ReadPrefixes(&chunk_data, &remaining, chunk_host.entry, |
| 402 | prefix_count)) |
| 403 | return false; |
| 404 | } |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 405 | } |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 406 | return remaining == 0; |
| 407 | } |
| 408 | |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 409 | void SafeBrowsingProtocolParser::ReadHostAndPrefixCount( |
| 410 | const char** data, int* remaining, SBPrefix* host, int* count) { |
| 411 | // Next 4 bytes are the host prefix. |
| 412 | memcpy(host, *data, sizeof(SBPrefix)); |
| 413 | *data += sizeof(SBPrefix); |
| 414 | *remaining -= sizeof(SBPrefix); |
| 415 | |
| 416 | // Next 1 byte is the prefix count (could be zero, but never negative). |
| 417 | *count = static_cast<unsigned char>(**data); |
| 418 | *data += 1; |
| 419 | *remaining -= 1; |
| 420 | } |
| 421 | |
| 422 | int SafeBrowsingProtocolParser::ReadChunkId( |
| 423 | const char** data, int* remaining) { |
| 424 | int chunk_number; |
| 425 | memcpy(&chunk_number, *data, sizeof(chunk_number)); |
| 426 | *data += sizeof(chunk_number); |
| 427 | *remaining -= sizeof(chunk_number); |
| 428 | return htonl(chunk_number); |
| 429 | } |
| 430 | |
| 431 | bool SafeBrowsingProtocolParser::ReadPrefixes( |
[email protected] | f5ce36a | 2011-01-12 19:24:21 | [diff] [blame] | 432 | const char** data, int* remaining, SBEntry* entry, int count) { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 433 | int hash_len = entry->HashLen(); |
| 434 | for (int i = 0; i < count; ++i) { |
| 435 | if (entry->IsSub()) { |
[email protected] | f5ce36a | 2011-01-12 19:24:21 | [diff] [blame] | 436 | entry->SetChunkIdAtPrefix(i, ReadChunkId(data, remaining)); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 437 | if (*remaining <= 0) |
| 438 | return false; |
| 439 | } |
| 440 | |
[email protected] | 080438b8 | 2009-12-03 18:17:12 | [diff] [blame] | 441 | if (entry->IsPrefix()) { |
[email protected] | f5ce36a | 2011-01-12 19:24:21 | [diff] [blame] | 442 | entry->SetPrefixAt(i, *reinterpret_cast<const SBPrefix*>(*data)); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 443 | } else { |
[email protected] | f5ce36a | 2011-01-12 19:24:21 | [diff] [blame] | 444 | entry->SetFullHashAt(i, *reinterpret_cast<const SBFullHash*>(*data)); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 445 | } |
| 446 | *data += hash_len; |
| 447 | *remaining -= hash_len; |
| 448 | if (*remaining < 0) |
| 449 | return false; |
| 450 | } |
| 451 | |
| 452 | return true; |
| 453 | } |
| 454 | |
| 455 | bool SafeBrowsingProtocolParser::ParseNewKey(const char* chunk_data, |
| 456 | int chunk_length, |
| 457 | std::string* client_key, |
| 458 | std::string* wrapped_key) { |
| 459 | DCHECK(client_key && wrapped_key); |
| 460 | client_key->clear(); |
| 461 | wrapped_key->clear(); |
| 462 | |
| 463 | const char* data = chunk_data; |
| 464 | int remaining = chunk_length; |
| 465 | |
| 466 | while (remaining > 0) { |
| 467 | std::string line; |
| 468 | if (!GetLine(data, remaining, &line)) |
| 469 | return false; |
| 470 | |
| 471 | std::vector<std::string> cmd_parts; |
[email protected] | 76eb024 | 2010-10-14 00:35:36 | [diff] [blame] | 472 | base::SplitString(line, ':', &cmd_parts); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 473 | if (cmd_parts.size() != 3) |
| 474 | return false; |
| 475 | |
[email protected] | d3ad8b7 | 2008-09-17 17:45:02 | [diff] [blame] | 476 | if (static_cast<int>(cmd_parts[2].size()) != atoi(cmd_parts[1].c_str())) |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 477 | return false; |
| 478 | |
| 479 | if (cmd_parts[0] == "clientkey") { |
| 480 | client_key->assign(cmd_parts[2]); |
| 481 | } else if (cmd_parts[0] == "wrappedkey") { |
| 482 | wrapped_key->assign(cmd_parts[2]); |
| 483 | } else { |
| 484 | return false; |
| 485 | } |
| 486 | |
| 487 | data += line.size() + 1; |
| 488 | remaining -= static_cast<int>(line.size()) + 1; |
| 489 | } |
| 490 | |
| 491 | if (client_key->empty() || wrapped_key->empty()) |
| 492 | return false; |
| 493 | |
| 494 | return true; |
license.bot | bf09a50 | 2008-08-24 00:55:55 | [diff] [blame] | 495 | } |