15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2011 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)"""Provides utility functions for TCP/UDP echo servers and clients.
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)This program has classes and functions to encode, decode, calculate checksum
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)and verify the "echo request" and "echo response" messages. "echo request"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)message is an echo message sent from the client to the server. "echo response"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)message is a response from the server to the "echo request" message from the
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)client.
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)The format of "echo request" message is
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)<version><checksum><payload_size><payload>. <version> is the version number
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)of the "echo request" protocol. <checksum> is the checksum of the <payload>.
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)<payload_size> is the size of the <payload>. <payload> is the echo message.
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)The format of "echo response" message is
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)<version><checksum><payload_size><key><encoded_payload>.<version>,
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)<checksum> and <payload_size> are same as what is in the "echo request" message.
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)<encoded_payload> is encoded version of the <payload>. <key> is a randomly
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)generated key that is used to encode/decode the <payload>.
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)__author__ = 'rtenneti@google.com (Raman Tenneti)'
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from itertools import cycle
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from itertools import izip
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import random
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class EchoHeader(object):
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Class to keep header info of the EchoRequest and EchoResponse messages.
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  This class knows how to parse the checksum, payload_size from the
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  "echo request" and "echo response" messages. It holds the checksum,
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  payload_size of the "echo request" and "echo response" messages.
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # This specifies the version.
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  VERSION_STRING = '01'
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # This specifies the starting position of the checksum and length of the
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # checksum. Maximum value for the checksum is less than (2 ** 31 - 1).
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECKSUM_START = 2
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECKSUM_LENGTH = 10
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECKSUM_FORMAT = '%010d'
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECKSUM_END = CHECKSUM_START + CHECKSUM_LENGTH
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # This specifies the starting position of the <payload_size> and length of the
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # <payload_size>. Maximum number of bytes that can be sent in the <payload> is
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # 9,999,999.
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PAYLOAD_SIZE_START = CHECKSUM_END
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PAYLOAD_SIZE_LENGTH = 7
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PAYLOAD_SIZE_FORMAT = '%07d'
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PAYLOAD_SIZE_END = PAYLOAD_SIZE_START + PAYLOAD_SIZE_LENGTH
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, checksum=0, payload_size=0):
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Initializes the checksum and payload_size of self (EchoHeader).
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      checksum: (int)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        The checksum of the payload.
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      payload_size: (int)
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        The size of the payload.
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.checksum = checksum
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.payload_size = payload_size
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def ParseAndInitialize(self, echo_message):
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Parses the echo_message and initializes self with the parsed data.
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    This method extracts checksum, and payload_size from the echo_message
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    (echo_message could be either echo_request or echo_response messages) and
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    initializes self (EchoHeader) with checksum and payload_size.
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      echo_message: (string)
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        The string representation of EchoRequest or EchoResponse objects.
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Raises:
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ValueError: Invalid data
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not echo_message or len(echo_message) < EchoHeader.PAYLOAD_SIZE_END:
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      raise ValueError('Invalid data:%s' % echo_message)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.checksum = int(echo_message[
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        EchoHeader.CHECKSUM_START:EchoHeader.CHECKSUM_END])
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.payload_size = int(echo_message[
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        EchoHeader.PAYLOAD_SIZE_START:EchoHeader.PAYLOAD_SIZE_END])
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def InitializeFromPayload(self, payload):
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Initializes the EchoHeader object with the payload.
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    It calculates checksum for the payload and initializes self (EchoHeader)
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    with the calculated checksum and size of the payload.
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    This method is used by the client code during testing.
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      payload: (string)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        The payload is the echo string (like 'hello').
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Raises:
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ValueError: Invalid data
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not payload:
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      raise ValueError('Invalid data:%s' % payload)
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.payload_size = len(payload)
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.checksum = Checksum(payload, self.payload_size)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __str__(self):
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """String representation of the self (EchoHeader).
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      A string representation of self (EchoHeader).
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    checksum_string = EchoHeader.CHECKSUM_FORMAT % self.checksum
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    payload_size_string = EchoHeader.PAYLOAD_SIZE_FORMAT % self.payload_size
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return EchoHeader.VERSION_STRING + checksum_string + payload_size_string
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class EchoRequest(EchoHeader):
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Class holds data specific to the "echo request" message.
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  This class holds the payload extracted from the "echo request" message.
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # This specifies the starting position of the <payload>.
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PAYLOAD_START = EchoHeader.PAYLOAD_SIZE_END
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self):
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Initializes EchoRequest object."""
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EchoHeader.__init__(self)
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.payload = ''
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def ParseAndInitialize(self, echo_request_data):
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Parses and Initializes the EchoRequest object from the echo_request_data.
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    This method extracts the header information (checksum and payload_size) and
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    payload from echo_request_data.
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      echo_request_data: (string)
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        The string representation of EchoRequest object.
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Raises:
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ValueError: Invalid data
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EchoHeader.ParseAndInitialize(self, echo_request_data)
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if len(echo_request_data) <= EchoRequest.PAYLOAD_START:
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      raise ValueError('Invalid data:%s' % echo_request_data)
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.payload = echo_request_data[EchoRequest.PAYLOAD_START:]
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def InitializeFromPayload(self, payload):
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Initializes the EchoRequest object with payload.
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    It calculates checksum for the payload and initializes self (EchoRequest)
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    object.
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      payload: (string)
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        The payload string for which "echo request" needs to be constructed.
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EchoHeader.InitializeFromPayload(self, payload)
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.payload = payload
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __str__(self):
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """String representation of the self (EchoRequest).
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      A string representation of self (EchoRequest).
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return EchoHeader.__str__(self) + self.payload
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class EchoResponse(EchoHeader):
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Class holds data specific to the "echo response" message.
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  This class knows how to parse the "echo response" message. This class holds
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  key, encoded_payload and decoded_payload of the "echo response" message.
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # This specifies the starting position of the |key_| and length of the |key_|.
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Minimum and maximum values for the |key_| are 100,000 and 999,999.
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  KEY_START = EchoHeader.PAYLOAD_SIZE_END
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  KEY_LENGTH = 6
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  KEY_FORMAT = '%06d'
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  KEY_END = KEY_START + KEY_LENGTH
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  KEY_MIN_VALUE = 0
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  KEY_MAX_VALUE = 999999
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # This specifies the starting position of the <encoded_payload> and length
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # of the <encoded_payload>.
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ENCODED_PAYLOAD_START = KEY_END
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, key='', encoded_payload='', decoded_payload=''):
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Initializes the EchoResponse object."""
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EchoHeader.__init__(self)
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.key = key
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.encoded_payload = encoded_payload
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.decoded_payload = decoded_payload
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def ParseAndInitialize(self, echo_response_data=None):
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Parses and Initializes the EchoResponse object from echo_response_data.
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    This method calls EchoHeader to extract header information from the
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    echo_response_data and it then extracts key and encoded_payload from the
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    echo_response_data. It holds the decoded payload of the encoded_payload.
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      echo_response_data: (string)
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        The string representation of EchoResponse object.
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Raises:
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ValueError: Invalid echo_request_data
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EchoHeader.ParseAndInitialize(self, echo_response_data)
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if len(echo_response_data) <= EchoResponse.ENCODED_PAYLOAD_START:
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      raise ValueError('Invalid echo_response_data:%s' % echo_response_data)
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.key = echo_response_data[EchoResponse.KEY_START:EchoResponse.KEY_END]
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.encoded_payload = echo_response_data[
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        EchoResponse.ENCODED_PAYLOAD_START:]
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.decoded_payload = Crypt(self.encoded_payload, self.key)
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def InitializeFromEchoRequest(self, echo_request):
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Initializes EchoResponse with the data from the echo_request object.
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    It gets the checksum, payload_size and payload from the echo_request object
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    and then encodes the payload with a random key. It also saves the payload
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    as decoded_payload.
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      echo_request: (EchoRequest)
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        The EchoRequest object which has "echo request" message.
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.checksum = echo_request.checksum
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.payload_size = echo_request.payload_size
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.key = (EchoResponse.KEY_FORMAT %
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                random.randrange(EchoResponse.KEY_MIN_VALUE,
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 EchoResponse.KEY_MAX_VALUE))
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.encoded_payload = Crypt(echo_request.payload, self.key)
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.decoded_payload = echo_request.payload
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __str__(self):
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """String representation of the self (EchoResponse).
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      A string representation of self (EchoResponse).
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return EchoHeader.__str__(self) + self.key + self.encoded_payload
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def Crypt(payload, key):
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Encodes/decodes the payload with the key and returns encoded payload.
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  This method loops through the payload and XORs each byte with the key.
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Args:
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    payload: (string)
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      The string to be encoded/decoded.
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    key: (string)
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      The key used to encode/decode the payload.
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Returns:
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    An encoded/decoded string.
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ''.join(chr(ord(x) ^ ord(y)) for (x, y) in izip(payload, cycle(key)))
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def Checksum(payload, payload_size):
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Calculates the checksum of the payload.
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Args:
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    payload: (string)
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      The payload string for which checksum needs to be calculated.
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    payload_size: (int)
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      The number of bytes in the payload.
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Returns:
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The checksum of the payload.
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  checksum = 0
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  length = min(payload_size, len(payload))
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for i in range (0, length):
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    checksum += ord(payload[i])
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return checksum
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def GetEchoRequestData(payload):
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Constructs an "echo request" message from the payload.
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  It builds an EchoRequest object from the payload and then returns a string
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  representation of the EchoRequest object.
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  This is used by the TCP/UDP echo clients to build the "echo request" message.
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Args:
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    payload: (string)
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      The payload string for which "echo request" needs to be constructed.
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Returns:
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    A string representation of the EchoRequest object.
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Raises:
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ValueError: Invalid payload
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  try:
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    echo_request = EchoRequest()
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    echo_request.InitializeFromPayload(payload)
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return str(echo_request)
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  except (IndexError, ValueError):
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise ValueError('Invalid payload:%s' % payload)
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def GetEchoResponseData(echo_request_data):
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Verifies the echo_request_data and returns "echo response" message.
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  It builds the EchoRequest object from the echo_request_data and then verifies
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  the checksum of the EchoRequest is same as the calculated checksum of the
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  payload. If the checksums don't match then it returns None. It checksums
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  match, it builds the echo_response object from echo_request object and returns
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string representation of the EchoResponse object.
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  This is used by the TCP/UDP echo servers.
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Args:
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    echo_request_data: (string)
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      The string that echo servers send to the clients.
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Returns:
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    A string representation of the EchoResponse object. It returns None if the
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    echo_request_data is not valid.
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Raises:
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ValueError: Invalid echo_request_data
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  try:
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not echo_request_data:
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      raise ValueError('Invalid payload:%s' % echo_request_data)
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    echo_request = EchoRequest()
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    echo_request.ParseAndInitialize(echo_request_data)
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if Checksum(echo_request.payload,
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                echo_request.payload_size) != echo_request.checksum:
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return None
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    echo_response = EchoResponse()
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    echo_response.InitializeFromEchoRequest(echo_request)
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return str(echo_response)
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  except (IndexError, ValueError):
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise ValueError('Invalid payload:%s' % echo_request_data)
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def DecodeAndVerify(echo_request_data, echo_response_data):
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Decodes and verifies the echo_response_data.
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  It builds EchoRequest and EchoResponse objects from the echo_request_data and
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  echo_response_data. It returns True if the EchoResponse's payload and
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  checksum match EchoRequest's.
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  This is used by the TCP/UDP echo clients for testing purposes.
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Args:
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    echo_request_data: (string)
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      The request clients sent to echo servers.
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    echo_response_data: (string)
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      The response clients received from the echo servers.
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Returns:
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    True if echo_request_data and echo_response_data match.
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Raises:
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ValueError: Invalid echo_request_data or Invalid echo_response
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  try:
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    echo_request = EchoRequest()
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    echo_request.ParseAndInitialize(echo_request_data)
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  except (IndexError, ValueError):
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise ValueError('Invalid echo_request:%s' % echo_request_data)
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  try:
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    echo_response = EchoResponse()
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    echo_response.ParseAndInitialize(echo_response_data)
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  except (IndexError, ValueError):
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise ValueError('Invalid echo_response:%s' % echo_response_data)
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return (echo_request.checksum == echo_response.checksum and
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          echo_request.payload == echo_response.decoded_payload)
386