Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(239)

Side by Side Diff: chrome/browser/net/network_stats.cc

Issue 7246021: Prevent DOS attack on UDP echo servers by distinguishing between an echo request (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 9 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/net/network_stats.h" 5 #include "chrome/browser/net/network_stats.h"
6 6
7 #include "base/callback_old.h" 7 #include "base/callback_old.h"
8 #include "base/logging.h" 8 #include "base/logging.h"
9 #include "base/message_loop.h" 9 #include "base/message_loop.h"
10 #include "base/metrics/field_trial.h" 10 #include "base/metrics/field_trial.h"
11 #include "base/metrics/histogram.h" 11 #include "base/metrics/histogram.h"
12 #include "base/task.h" 12 #include "base/task.h"
13 #include "base/threading/platform_thread.h" 13 #include "base/threading/platform_thread.h"
14 #include "base/time.h" 14 #include "base/time.h"
15 #include "base/tuple.h" 15 #include "base/tuple.h"
16 #include "content/browser/browser_thread.h" 16 #include "content/browser/browser_thread.h"
17 #include "net/base/net_errors.h" 17 #include "net/base/net_errors.h"
18 #include "net/base/net_util.h" 18 #include "net/base/net_util.h"
19 #include "net/base/network_change_notifier.h" 19 #include "net/base/network_change_notifier.h"
20 #include "net/base/sys_addrinfo.h" 20 #include "net/base/sys_addrinfo.h"
21 #include "net/base/test_completion_callback.h" 21 #include "net/base/test_completion_callback.h"
22 #include "net/socket/tcp_client_socket.h" 22 #include "net/socket/tcp_client_socket.h"
23 #include "net/udp/udp_client_socket.h" 23 #include "net/udp/udp_client_socket.h"
24 #include "net/udp/udp_server_socket.h" 24 #include "net/udp/udp_server_socket.h"
25 25
26 namespace chrome_browser_net { 26 namespace chrome_browser_net {
27 27
28 // This specifies the number of bytes to be sent to the TCP/UDP servers as part 28 // This specifies the number of bytes to be sent to the TCP/UDP servers as part
29 // of small packet size test. 29 // of small packet size test.
30 static const int kSmallTestBytesToSend = 100; 30 static const uint32 kSmallTestBytesToSend = 100;
31 31
32 // This specifies the number of bytes to be sent to the TCP/UDP servers as part 32 // This specifies the number of bytes to be sent to the TCP/UDP servers as part
33 // of large packet size test. 33 // of large packet size test.
34 static const int kLargeTestBytesToSend = 1200; 34 static const uint32 kLargeTestBytesToSend = 1200;
35
36 // This specifies the maximum message (payload) size.
37 static const uint32 kMaxMessage = 2048;
38
39 // This specifies starting position of the version and length of the version in
40 // "echo request" and "echo response".
41 static const uint32 kVersionNumber = 1;
42 static const uint32 kVersionStart = 0;
43 static const uint32 kVersionLength = 2;
44 static const uint32 kVersionEnd = 2; // kVersionStart + kVersionLength.
jar (doing other things) 2011/08/05 19:37:31 nit: Suggest: kVersionEnd = kVersionStart + kVers
ramant (doing other things) 2011/08/12 01:05:08 Done.
45
46 // This specifies the starting position of the checksum and length of the
47 // checksum in "echo request" and "echo response". Maximum value for the
48 // checksum is less than (2 ** 31 - 1).
49 static const uint32 kChecksumStart = 2; // kVersionEnd.
jar (doing other things) 2011/08/05 19:37:31 nit: assign value rather than comment.. Same belo
ramant (doing other things) 2011/08/12 01:05:08 Done.
50 static const uint32 kChecksumLength = 10;
51 static const uint32 kChecksumEnd = 12; // kChecksumStart + kChecksumLength.
52
53 // This specifies the starting position of the <payload_size> and length of the
54 // <payload_size> in "echo request" and "echo response". Maximum number of bytes
55 // that can be sent in the <payload> is 9,999,999.
56 static const uint32 kPayloadSizeStart = 12; // kChecksumEnd.
57 static const uint32 kPayloadSizeLength = 7;
58 // kPayloadSizeEnd = kPayloadSizeStart + kPayloadSizeLength.
59 static const uint32 kPayloadSizeEnd = 19;
60
61 // This specifies the starting position of the |key_| and length of the |key_|
62 // in "echo response". Maximum value for the |key_| is 999,999.
63 static const uint32 kKeyStart = 19; // kPayloadSizeEnd.
64 static const uint32 kKeyLength = 6;
65 static const uint32 kKeyEnd = 25; // kKeyStart + kKeyLength.
66
67 // This specifies the starting position of the <payload> in "echo request".
68 static const uint32 kPayloadStart = 19; // kPayloadSizeEnd.
69
70 // This specifies the starting position of the <encrypted_payload> and length of
71 // the <encrypted_payload> in "echo response".
72 static const uint32 kEncryptedPayloadStart = 25; // kKeyEnd.
73
35 74
36 // NetworkStats methods and members. 75 // NetworkStats methods and members.
37 NetworkStats::NetworkStats() 76 NetworkStats::NetworkStats()
38 : bytes_to_read_(0), 77 : load_size_(0),
78 bytes_to_read_(0),
39 bytes_to_send_(0), 79 bytes_to_send_(0),
80 key_(""),
81 key_index_(0),
40 ALLOW_THIS_IN_INITIALIZER_LIST( 82 ALLOW_THIS_IN_INITIALIZER_LIST(
41 read_callback_(this, &NetworkStats::OnReadComplete)), 83 read_callback_(this, &NetworkStats::OnReadComplete)),
42 ALLOW_THIS_IN_INITIALIZER_LIST( 84 ALLOW_THIS_IN_INITIALIZER_LIST(
43 write_callback_(this, &NetworkStats::OnWriteComplete)), 85 write_callback_(this, &NetworkStats::OnWriteComplete)),
44 finished_callback_(NULL), 86 finished_callback_(NULL),
45 start_time_(base::TimeTicks::Now()) { 87 start_time_(base::TimeTicks::Now()) {
46 } 88 }
47 89
48 NetworkStats::~NetworkStats() { 90 NetworkStats::~NetworkStats() {
49 socket_.reset(); 91 socket_.reset();
50 } 92 }
51 93
52 void NetworkStats::Initialize(int bytes_to_send, 94 void NetworkStats::Initialize(uint32 bytes_to_send,
53 net::CompletionCallback* finished_callback) { 95 net::CompletionCallback* finished_callback) {
54 DCHECK(bytes_to_send); // We should have data to send. 96 DCHECK(bytes_to_send); // We should have data to send.
55 97
56 load_size_ = bytes_to_send; 98 load_size_ = bytes_to_send;
57 bytes_to_send_ = bytes_to_send; 99 bytes_to_send_ =
58 bytes_to_read_ = bytes_to_send; 100 kVersionLength + kChecksumLength + kPayloadSizeLength + load_size_;
101 bytes_to_read_ =
102 kVersionLength + kChecksumLength + kPayloadSizeLength + kKeyLength +
103 load_size_;
59 finished_callback_ = finished_callback; 104 finished_callback_ = finished_callback;
60 } 105 }
61 106
62 bool NetworkStats::DoStart(int result) { 107 bool NetworkStats::DoStart(int result) {
63 if (result < 0) { 108 if (result < 0) {
64 Finish(CONNECT_FAILED, result); 109 Finish(CONNECT_FAILED, result);
65 return false; 110 return false;
66 } 111 }
67 112
68 DCHECK(bytes_to_send_); // We should have data to send. 113 DCHECK(bytes_to_send_); // We should have data to send.
(...skipping 29 matching lines...) Expand all
98 } 143 }
99 144
100 bool NetworkStats::ReadComplete(int result) { 145 bool NetworkStats::ReadComplete(int result) {
101 DCHECK(socket_.get()); 146 DCHECK(socket_.get());
102 DCHECK_NE(net::ERR_IO_PENDING, result); 147 DCHECK_NE(net::ERR_IO_PENDING, result);
103 if (result < 0) { 148 if (result < 0) {
104 Finish(READ_FAILED, result); 149 Finish(READ_FAILED, result);
105 return true; 150 return true;
106 } 151 }
107 152
108 if (!stream_.VerifyBytes(read_buffer_->data(), result)) { 153 if (!VerifyBytes(static_cast<uint32>(result))) {
109 Finish(READ_VERIFY_FAILED, net::ERR_INVALID_RESPONSE); 154 Finish(READ_VERIFY_FAILED, net::ERR_INVALID_RESPONSE);
110 return true; 155 return true;
111 } 156 }
112 157
113 read_buffer_ = NULL; 158 read_buffer_ = NULL;
114 bytes_to_read_ -= result; 159 bytes_to_read_ -= result;
115 160
116 // No more data to read. 161 // No more data to read.
117 if (!bytes_to_read_) { 162 if (!bytes_to_read_) {
118 Finish(SUCCESS, net::OK); 163 Finish(SUCCESS, net::OK);
(...skipping 26 matching lines...) Expand all
145 if (rv != net::ERR_IO_PENDING) { 190 if (rv != net::ERR_IO_PENDING) {
146 Finish(WRITE_FAILED, rv); 191 Finish(WRITE_FAILED, rv);
147 return; 192 return;
148 } 193 }
149 } 194 }
150 } 195 }
151 } 196 }
152 197
153 void NetworkStats::ReadData() { 198 void NetworkStats::ReadData() {
154 DCHECK(!read_buffer_.get()); 199 DCHECK(!read_buffer_.get());
155 int kMaxMessage = 2048;
156 200
157 // We release the read_buffer_ in the destructor if there is an error. 201 // We release the read_buffer_ in the destructor if there is an error.
158 read_buffer_ = new net::IOBuffer(kMaxMessage); 202 read_buffer_ = new net::IOBuffer(kMaxMessage);
159 203
160 int rv; 204 int rv;
161 do { 205 do {
162 DCHECK(socket_.get()); 206 DCHECK(socket_.get());
163 rv = socket_->Read(read_buffer_, kMaxMessage, &read_callback_); 207 rv = socket_->Read(read_buffer_, kMaxMessage, &read_callback_);
164 if (rv == net::ERR_IO_PENDING) 208 if (rv == net::ERR_IO_PENDING)
165 return; 209 return;
166 if (ReadComplete(rv)) // Complete the read manually. 210 if (ReadComplete(rv)) // Complete the read manually.
167 return; 211 return;
168 } while (rv > 0); 212 } while (rv > 0);
169 } 213 }
170 214
171 int NetworkStats::SendData() { 215 int NetworkStats::SendData() {
172 DCHECK(bytes_to_send_); // We should have data to send. 216 DCHECK(bytes_to_send_); // We should have data to send.
173 do { 217 do {
174 if (!write_buffer_.get()) { 218 if (!write_buffer_.get()) {
175 scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(bytes_to_send_)); 219 scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(bytes_to_send_));
176 stream_.GetBytes(buffer->data(), bytes_to_send_); 220 GetEchoRequest(buffer);
177 write_buffer_ = new net::DrainableIOBuffer(buffer, bytes_to_send_); 221 write_buffer_ = new net::DrainableIOBuffer(buffer, bytes_to_send_);
178 } 222 }
179 223
180 DCHECK(socket_.get()); 224 DCHECK(socket_.get());
181 int rv = socket_->Write(write_buffer_, 225 int rv = socket_->Write(write_buffer_,
182 write_buffer_->BytesRemaining(), 226 write_buffer_->BytesRemaining(),
183 &write_callback_); 227 &write_callback_);
184 if (rv < 0) 228 if (rv < 0)
185 return rv; 229 return rv;
186 write_buffer_->DidConsume(rv); 230 write_buffer_->DidConsume(rv);
187 bytes_to_send_ -= rv; 231 bytes_to_send_ -= rv;
188 if (!write_buffer_->BytesRemaining()) 232 if (!write_buffer_->BytesRemaining())
189 write_buffer_ = NULL; 233 write_buffer_ = NULL;
190 } while (bytes_to_send_); 234 } while (bytes_to_send_);
191 return net::OK; 235 return net::OK;
192 } 236 }
193 237
238 void NetworkStats::GetEchoRequest(net::IOBuffer* io_buffer) {
239 // Copy the <version> into the io_buffer starting from the kVersionStart
240 // position.
241 std::string version = base::StringPrintf("%02d", kVersionNumber);
242 char* buffer = io_buffer->data() + kVersionStart;
243 DCHECK(kVersionLength == version.length());
244 memcpy(buffer, version.c_str(), kVersionLength);
245
246 // Get the <payload> from the |stream_| and copy it into io_buffer starting
247 // from the kPayloadStart position.
248 buffer = io_buffer->data() + kPayloadStart;
249 stream_.GetBytes(buffer, load_size_);
250
251 // Calculate the <checksum> of the <payload>.
252 uint32 sum = 0;
253 unsigned char* src = reinterpret_cast<unsigned char*>(buffer);
254 for (uint32 i = 0; i < load_size_; ++i)
255 sum += src[i];
256
257 // Copy the <checksum> into the io_buffer starting from the kChecksumStart
258 // position.
259 std::string checksum = base::StringPrintf("%010d", sum);
260 buffer = io_buffer->data() + kChecksumStart;
261 DCHECK(kChecksumLength == checksum.length());
262 memcpy(buffer, checksum.c_str(), kChecksumLength);
263
264 // Copy the size of the <payload> into the io_buffer starting from the
265 // kPayloadSizeStart position.
266 buffer = io_buffer->data() + kPayloadSizeStart;
267 std::string payload_size = base::StringPrintf("%07d", load_size_);
268 DCHECK(kPayloadSizeLength == payload_size.length());
269 memcpy(buffer, payload_size.c_str(), kPayloadSizeLength);
270 }
271
272 bool NetworkStats::VerifyBytes(uint32 buffer_length) {
jar (doing other things) 2011/08/05 19:37:31 Suggest copying bytes into a local buffer, and the
ramant (doing other things) 2011/08/12 01:05:08 Done.
273 // TODO(rtenneti): Handle the data if it comes out of order.
274 uint32 payload_start = 0;
275 char* buffer;
276
277 if (key_.empty()) {
278 // If the "echo response" doesn't have enough bytes, then return false.
279 if (buffer_length < kEncryptedPayloadStart)
280 return false;
281
282 // Extract the |key_| from the "echo response".
283 buffer = read_buffer_->data() + kKeyStart;
284 char key[kKeyLength + 1];
285 memcpy(key, buffer, kKeyLength);
286 key[kKeyLength] = '\0';
287 key_ = key;
jar (doing other things) 2011/08/05 19:37:31 nit: avoid memcpy, and just use a special construc
ramant (doing other things) 2011/08/12 01:05:08 Done.
288
289 // Initialize the starting position of the <payload>.
290 payload_start = kEncryptedPayloadStart;
291 }
292
293 // Read the <encrypted_payload> from the "echo response".
294 DCHECK_GE(buffer_length, payload_start);
295 uint32 message_length = buffer_length - payload_start;
296 message_length = std::min(message_length, kMaxMessage);
297 buffer = read_buffer_->data() + payload_start;
298 char encrypted_data[kMaxMessage + 1];
299 memcpy(encrypted_data, buffer, message_length);
300
301 // Decrypt the data.
302 char decrypted_data[kMaxMessage + 1];
303 uint32 kIdx = key_index_;
304 // Loop through the string and XOR each byte with the |key_| to get the
305 // decrypted byte. Append the decrypted byte to the |decrypted_data|.
306 for (uint32 idx = 0; idx < message_length; ++idx) {
307 int encrypted_byte = static_cast<int>(encrypted_data[idx]);
308 int key_byte = static_cast<int>(key_[kIdx]);
jar (doing other things) 2011/08/05 19:37:31 suggest using "char" as the types for lines 307 an
ramant (doing other things) 2011/08/12 01:05:08 Done.
309 char decrypted_byte = static_cast<char>(encrypted_byte ^ key_byte);
310 decrypted_data[idx] = decrypted_byte;
311 kIdx = (kIdx + 1) % kKeyLength;
312 }
313 // Save the kIdx in case we haven't received the whole "echo response".
314 key_index_ = kIdx;
315
316 return stream_.VerifyBytes(decrypted_data, message_length);
317 }
318
194 // UDPStatsClient methods and members. 319 // UDPStatsClient methods and members.
195 UDPStatsClient::UDPStatsClient() 320 UDPStatsClient::UDPStatsClient()
196 : NetworkStats() { 321 : NetworkStats() {
197 } 322 }
198 323
199 UDPStatsClient::~UDPStatsClient() { 324 UDPStatsClient::~UDPStatsClient() {
200 } 325 }
201 326
202 bool UDPStatsClient::Start(const std::string& ip_str, 327 bool UDPStatsClient::Start(const std::string& ip_str,
203 int port, 328 int port,
204 int bytes_to_send, 329 uint32 bytes_to_send,
205 net::CompletionCallback* finished_callback) { 330 net::CompletionCallback* finished_callback) {
206 DCHECK(port); 331 DCHECK(port);
207 DCHECK(bytes_to_send); // We should have data to send. 332 DCHECK(bytes_to_send); // We should have data to send.
208 333
209 Initialize(bytes_to_send, finished_callback); 334 Initialize(bytes_to_send, finished_callback);
210 335
211 net::IPAddressNumber ip_number; 336 net::IPAddressNumber ip_number;
212 if (!net::ParseIPLiteralToNumber(ip_str, &ip_number)) { 337 if (!net::ParseIPLiteralToNumber(ip_str, &ip_number)) {
213 Finish(IP_STRING_PARSE_FAILED, net::ERR_INVALID_ARGUMENT); 338 Finish(IP_STRING_PARSE_FAILED, net::ERR_INVALID_ARGUMENT);
214 return false; 339 return false;
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
265 resolve_callback_(this, &TCPStatsClient::OnResolveComplete)), 390 resolve_callback_(this, &TCPStatsClient::OnResolveComplete)),
266 ALLOW_THIS_IN_INITIALIZER_LIST( 391 ALLOW_THIS_IN_INITIALIZER_LIST(
267 connect_callback_(this, &TCPStatsClient::OnConnectComplete)) { 392 connect_callback_(this, &TCPStatsClient::OnConnectComplete)) {
268 } 393 }
269 394
270 TCPStatsClient::~TCPStatsClient() { 395 TCPStatsClient::~TCPStatsClient() {
271 } 396 }
272 397
273 bool TCPStatsClient::Start(net::HostResolver* host_resolver, 398 bool TCPStatsClient::Start(net::HostResolver* host_resolver,
274 const net::HostPortPair& server_host_port_pair, 399 const net::HostPortPair& server_host_port_pair,
275 int bytes_to_send, 400 uint32 bytes_to_send,
276 net::CompletionCallback* finished_callback) { 401 net::CompletionCallback* finished_callback) {
277 DCHECK(bytes_to_send); // We should have data to send. 402 DCHECK(bytes_to_send); // We should have data to send.
278 403
279 Initialize(bytes_to_send, finished_callback); 404 Initialize(bytes_to_send, finished_callback);
280 405
281 net::HostResolver::RequestInfo request(server_host_port_pair); 406 net::HostResolver::RequestInfo request(server_host_port_pair);
282 int rv = host_resolver->Resolve(request, 407 int rv = host_resolver->Resolve(request,
283 &addresses_, 408 &addresses_,
284 &resolve_callback_, 409 &resolve_callback_,
285 NULL, 410 NULL,
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after
424 TCPStatsClient* small_tcp_client = new TCPStatsClient(); 549 TCPStatsClient* small_tcp_client = new TCPStatsClient();
425 small_tcp_client->Start(host_resolver, server_address, kSmallTestBytesToSend, 550 small_tcp_client->Start(host_resolver, server_address, kSmallTestBytesToSend,
426 NULL); 551 NULL);
427 552
428 TCPStatsClient* large_tcp_client = new TCPStatsClient(); 553 TCPStatsClient* large_tcp_client = new TCPStatsClient();
429 large_tcp_client->Start(host_resolver, server_address, kLargeTestBytesToSend, 554 large_tcp_client->Start(host_resolver, server_address, kLargeTestBytesToSend,
430 NULL); 555 NULL);
431 } 556 }
432 557
433 } // namespace chrome_browser_net 558 } // namespace chrome_browser_net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698