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

Side by Side Diff: net/tools/testserver/echo_message.py

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, 4 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
« no previous file with comments | « chrome/browser/net/network_stats.cc ('k') | net/tools/testserver/testserver.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/python2.4
2 # Copyright (c) 2011 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """Provides utility functions for TCP/UDP echo servers and clients.
7
8 This program has classes and functions to encode, decode, calculate checksum
9 and verify the "echo request" and "echo response" messages. "echo request"
10 message is an echo message sent from the client to the server. "echo response"
11 message is a response from the server to the "echo request" message from the
12 client.
13
14 The format of "echo request" message is
15 <version><checksum><payload_size><payload>. <version> is the version number
16 of the "echo request" protocol. <checksum> is the checksum of the <payload>.
17 <payload_size> is the size of the <payload>. <payload> is the echo message.
18
19 The format of "echo response" message is
20 <version><checksum><payload_size><key><encoded_payload>.<version>,
21 <checksum> and <payload_size> are same as what is in the "echo request" message.
22 <encoded_payload> is encoded version of the <payload>. <key> is a randomly
23 generated key that is used to encode/decode the <payload>.
24 """
25
26 __author__ = 'rtenneti@google.com (Raman Tenneti)'
27
28
29 from itertools import cycle
30 from itertools import izip
31 import random
32
33
34 class EchoHeader(object):
35 """Class to keep header info of the EchoRequest and EchoResponse messages.
36
37 This class knows how to parse the checksum, payload_size from the
38 "echo request" and "echo response" messages. It holds the checksum,
39 payload_size of the "echo request" and "echo response" messages.
40 """
41
42 # This specifies the version.
43 VERSION_STRING = '01'
44
45 # This specifies the starting position of the checksum and length of the
46 # checksum. Maximum value for the checksum is less than (2 ** 31 - 1).
47 CHECKSUM_START = 2
48 CHECKSUM_LENGTH = 10
49 CHECKSUM_FORMAT = '%010d'
50 CHECKSUM_END = CHECKSUM_START + CHECKSUM_LENGTH
51
52 # This specifies the starting position of the <payload_size> and length of the
53 # <payload_size>. Maximum number of bytes that can be sent in the <payload> is
54 # 9,999,999.
55 PAYLOAD_SIZE_START = CHECKSUM_END
56 PAYLOAD_SIZE_LENGTH = 7
57 PAYLOAD_SIZE_FORMAT = '%07d'
58 PAYLOAD_SIZE_END = PAYLOAD_SIZE_START + PAYLOAD_SIZE_LENGTH
59
60 def __init__(self, checksum=0, payload_size=0):
61 """Initializes the checksum and payload_size of self (EchoHeader).
62
63 Args:
64 checksum: (int)
65 The checksum of the payload.
66 payload_size: (int)
67 The size of the payload.
68 """
69 self.checksum = checksum
70 self.payload_size = payload_size
71
72 def ParseAndInitialize(self, echo_message):
73 """Parses the echo_message and initializes self with the parsed data.
74
75 This method extracts checksum, and payload_size from the echo_message
76 (echo_message could be either echo_request or echo_response messages) and
77 initializes self (EchoHeader) with checksum and payload_size.
78
79 Args:
80 echo_message: (string)
81 The string representation of EchoRequest or EchoResponse objects.
82 Raises:
83 ValueError: Invalid data
84 """
85 if not echo_message or len(echo_message) < EchoHeader.PAYLOAD_SIZE_END:
86 raise ValueError('Invalid data:%s' % echo_message)
87 self.checksum = int(echo_message[
88 EchoHeader.CHECKSUM_START:EchoHeader.CHECKSUM_END])
89 self.payload_size = int(echo_message[
90 EchoHeader.PAYLOAD_SIZE_START:EchoHeader.PAYLOAD_SIZE_END])
91
92 def InitializeFromPayload(self, payload):
93 """Initializes the EchoHeader object with the payload.
94
95 It calculates checksum for the payload and initializes self (EchoHeader)
96 with the calculated checksum and size of the payload.
97
98 This method is used by the client code during testing.
99
100 Args:
101 payload: (string)
102 The payload is the echo string (like 'hello').
103 Raises:
104 ValueError: Invalid data
105 """
106 if not payload:
107 raise ValueError('Invalid data:%s' % payload)
108 self.payload_size = len(payload)
109 self.checksum = Checksum(payload, self.payload_size)
110
111 def __str__(self):
112 """String representation of the self (EchoHeader).
113
114 Returns:
115 A string representation of self (EchoHeader).
116 """
117 checksum_string = EchoHeader.CHECKSUM_FORMAT % self.checksum
118 payload_size_string = EchoHeader.PAYLOAD_SIZE_FORMAT % self.payload_size
119 return EchoHeader.VERSION_STRING + checksum_string + payload_size_string
120
121
122 class EchoRequest(EchoHeader):
123 """Class holds data specific to the "echo request" message.
124
125 This class holds the payload extracted from the "echo request" message.
126 """
127
128 # This specifies the starting position of the <payload>.
129 PAYLOAD_START = EchoHeader.PAYLOAD_SIZE_END
130
131 def __init__(self):
132 """Initializes EchoRequest object."""
133 EchoHeader.__init__(self)
134 self.payload = ''
135
136 def ParseAndInitialize(self, echo_request_data):
137 """Parses and Initializes the EchoRequest object from the echo_request_data.
138
139 This method extracts the header information (checksum and payload_size) and
140 payload from echo_request_data.
141
142 Args:
143 echo_request_data: (string)
144 The string representation of EchoRequest object.
145 Raises:
146 ValueError: Invalid data
147 """
148 EchoHeader.ParseAndInitialize(self, echo_request_data)
149 if len(echo_request_data) <= EchoRequest.PAYLOAD_START:
150 raise ValueError('Invalid data:%s' % echo_request_data)
151 self.payload = echo_request_data[EchoRequest.PAYLOAD_START:]
152
153 def InitializeFromPayload(self, payload):
154 """Initializes the EchoRequest object with payload.
155
156 It calculates checksum for the payload and initializes self (EchoRequest)
157 object.
158
159 Args:
160 payload: (string)
161 The payload string for which "echo request" needs to be constructed.
162 """
163 EchoHeader.InitializeFromPayload(self, payload)
164 self.payload = payload
165
166 def __str__(self):
167 """String representation of the self (EchoRequest).
168
169 Returns:
170 A string representation of self (EchoRequest).
171 """
172 return EchoHeader.__str__(self) + self.payload
173
174
175 class EchoResponse(EchoHeader):
176 """Class holds data specific to the "echo response" message.
177
178 This class knows how to parse the "echo response" message. This class holds
179 key, encoded_payload and decoded_payload of the "echo response" message.
180 """
181
182 # This specifies the starting position of the |key_| and length of the |key_|.
183 # Minimum and maximum values for the |key_| are 100,000 and 999,999.
184 KEY_START = EchoHeader.PAYLOAD_SIZE_END
185 KEY_LENGTH = 6
186 KEY_FORMAT = '%06d'
187 KEY_END = KEY_START + KEY_LENGTH
188 KEY_MIN_VALUE = 0
189 KEY_MAX_VALUE = 999999
190
191 # This specifies the starting position of the <encoded_payload> and length
192 # of the <encoded_payload>.
193 ENCODED_PAYLOAD_START = KEY_END
194
195 def __init__(self, key='', encoded_payload='', decoded_payload=''):
196 """Initializes the EchoResponse object."""
197 EchoHeader.__init__(self)
198 self.key = key
199 self.encoded_payload = encoded_payload
200 self.decoded_payload = decoded_payload
201
202 def ParseAndInitialize(self, echo_response_data=None):
203 """Parses and Initializes the EchoResponse object from echo_response_data.
204
205 This method calls EchoHeader to extract header information from the
206 echo_response_data and it then extracts key and encoded_payload from the
207 echo_response_data. It holds the decoded payload of the encoded_payload.
208
209 Args:
210 echo_response_data: (string)
211 The string representation of EchoResponse object.
212 Raises:
213 ValueError: Invalid echo_request_data
214 """
215 EchoHeader.ParseAndInitialize(self, echo_response_data)
216 if len(echo_response_data) <= EchoResponse.ENCODED_PAYLOAD_START:
217 raise ValueError('Invalid echo_response_data:%s' % echo_response_data)
218 self.key = echo_response_data[EchoResponse.KEY_START:EchoResponse.KEY_END]
219 self.encoded_payload = echo_response_data[
220 EchoResponse.ENCODED_PAYLOAD_START:]
221 self.decoded_payload = Crypt(self.encoded_payload, self.key)
222
223 def InitializeFromEchoRequest(self, echo_request):
224 """Initializes EchoResponse with the data from the echo_request object.
225
226 It gets the checksum, payload_size and payload from the echo_request object
227 and then encodes the payload with a random key. It also saves the payload
228 as decoded_payload.
229
230 Args:
231 echo_request: (EchoRequest)
232 The EchoRequest object which has "echo request" message.
233 """
234 self.checksum = echo_request.checksum
235 self.payload_size = echo_request.payload_size
236 self.key = (EchoResponse.KEY_FORMAT %
237 random.randrange(EchoResponse.KEY_MIN_VALUE,
238 EchoResponse.KEY_MAX_VALUE))
239 self.encoded_payload = Crypt(echo_request.payload, self.key)
240 self.decoded_payload = echo_request.payload
241
242 def __str__(self):
243 """String representation of the self (EchoResponse).
244
245 Returns:
246 A string representation of self (EchoResponse).
247 """
248 return EchoHeader.__str__(self) + self.key + self.encoded_payload
249
250
251 def Crypt(payload, key):
252 """Encodes/decodes the payload with the key and returns encoded payload.
253
254 This method loops through the payload and XORs each byte with the key.
255
256 Args:
257 payload: (string)
258 The string to be encoded/decoded.
259 key: (string)
260 The key used to encode/decode the payload.
261
262 Returns:
263 An encoded/decoded string.
264 """
265 return ''.join(chr(ord(x) ^ ord(y)) for (x, y) in izip(payload, cycle(key)))
266
267
268 def Checksum(payload, payload_size):
269 """Calculates the checksum of the payload.
270
271 Args:
272 payload: (string)
273 The payload string for which checksum needs to be calculated.
274 payload_size: (int)
275 The number of bytes in the payload.
276
277 Returns:
278 The checksum of the payload.
279 """
280 checksum = 0
281 length = min(payload_size, len(payload))
282 for i in range (0, length):
283 checksum += ord(payload[i])
284 return checksum
285
286
287 def GetEchoRequestData(payload):
288 """Constructs an "echo request" message from the payload.
289
290 It builds an EchoRequest object from the payload and then returns a string
291 representation of the EchoRequest object.
292
293 This is used by the TCP/UDP echo clients to build the "echo request" message.
294
295 Args:
296 payload: (string)
297 The payload string for which "echo request" needs to be constructed.
298
299 Returns:
300 A string representation of the EchoRequest object.
301 Raises:
302 ValueError: Invalid payload
303 """
304 try:
305 echo_request = EchoRequest()
306 echo_request.InitializeFromPayload(payload)
307 return str(echo_request)
308 except (IndexError, ValueError):
309 raise ValueError('Invalid payload:%s' % payload)
310
311
312 def GetEchoResponseData(echo_request_data):
313 """Verifies the echo_request_data and returns "echo response" message.
314
315 It builds the EchoRequest object from the echo_request_data and then verifies
316 the checksum of the EchoRequest is same as the calculated checksum of the
317 payload. If the checksums don't match then it returns None. It checksums
318 match, it builds the echo_response object from echo_request object and returns
319 string representation of the EchoResponse object.
320
321 This is used by the TCP/UDP echo servers.
322
323 Args:
324 echo_request_data: (string)
325 The string that echo servers send to the clients.
326
327 Returns:
328 A string representation of the EchoResponse object. It returns None if the
329 echo_request_data is not valid.
330 Raises:
331 ValueError: Invalid echo_request_data
332 """
333 try:
334 if not echo_request_data:
335 raise ValueError('Invalid payload:%s' % echo_request_data)
336
337 echo_request = EchoRequest()
338 echo_request.ParseAndInitialize(echo_request_data)
339
340 if Checksum(echo_request.payload,
341 echo_request.payload_size) != echo_request.checksum:
342 return None
343
344 echo_response = EchoResponse()
345 echo_response.InitializeFromEchoRequest(echo_request)
346
347 return str(echo_response)
348 except (IndexError, ValueError):
349 raise ValueError('Invalid payload:%s' % echo_request_data)
350
351
352 def DecodeAndVerify(echo_request_data, echo_response_data):
353 """Decodes and verifies the echo_response_data.
354
355 It builds EchoRequest and EchoResponse objects from the echo_request_data and
356 echo_response_data. It returns True if the EchoResponse's payload and
357 checksum match EchoRequest's.
358
359 This is used by the TCP/UDP echo clients for testing purposes.
360
361 Args:
362 echo_request_data: (string)
363 The request clients sent to echo servers.
364 echo_response_data: (string)
365 The response clients received from the echo servers.
366
367 Returns:
368 True if echo_request_data and echo_response_data match.
369 Raises:
370 ValueError: Invalid echo_request_data or Invalid echo_response
371 """
372
373 try:
374 echo_request = EchoRequest()
375 echo_request.ParseAndInitialize(echo_request_data)
376 except (IndexError, ValueError):
377 raise ValueError('Invalid echo_request:%s' % echo_request_data)
378
379 try:
380 echo_response = EchoResponse()
381 echo_response.ParseAndInitialize(echo_response_data)
382 except (IndexError, ValueError):
383 raise ValueError('Invalid echo_response:%s' % echo_response_data)
384
385 return (echo_request.checksum == echo_response.checksum and
386 echo_request.payload == echo_response.decoded_payload)
OLDNEW
« no previous file with comments | « chrome/browser/net/network_stats.cc ('k') | net/tools/testserver/testserver.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698