15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#!/usr/bin/env python
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2011 The Chromium Authors. All rights reserved.
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file.
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""Issue a series of GetHash requests to the SafeBrowsing servers and measure
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)the response times.
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Usage:
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $ ./gethash_timer.py --period=600 --samples=20 --output=resp.csv
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  --period (or -p):  The amount of time (in seconds) to wait between GetHash
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     requests. Using a value of more than 300 (5 minutes) to
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     include the effect of DNS.
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  --samples (or -s): The number of requests to issue. If this parameter is not
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     specified, the test will run indefinitely.
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  --output (or -o):  The path to a file where the output will be written in
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     CSV format: sample_number,response_code,elapsed_time_ms
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import getopt
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import httplib
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import sys
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import time
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)_GETHASH_HOST = 'safebrowsing.clients.google.com'
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)_GETHASH_REQUEST = (
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    '/safebrowsing/gethash?client=googleclient&appver=1.0&pver=2.1')
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Global logging file handle.
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)g_file_handle = None
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def IssueGetHash(prefix):
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  '''Issue one GetHash request to the safebrowsing servers.
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Args:
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    prefix: A 4 byte value to look up on the server.
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Returns:
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The HTTP response code for the GetHash request.
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  '''
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  body = '4:4\n' + prefix
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  h = httplib.HTTPConnection(_GETHASH_HOST)
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  h.putrequest('POST', _GETHASH_REQUEST)
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  h.putheader('content-length', str(len(body)))
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  h.endheaders()
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  h.send(body)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  response_code = h.getresponse().status
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  h.close()
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return response_code
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def TimedGetHash(prefix):
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  '''Measure the amount of time it takes to receive a GetHash response.
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Args:
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    prefix: A 4 byte value to look up on the the server.
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Returns:
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    A tuple of HTTP resonse code and the response time (in milliseconds).
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  '''
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  start = time.time()
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  response_code = IssueGetHash(prefix)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return response_code, (time.time() - start) * 1000
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def RunTimedGetHash(period, samples=None):
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  '''Runs an experiment to measure the amount of time it takes to receive
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  multiple responses from the GetHash servers.
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Args:
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    period:  A floating point value that indicates (in seconds) the delay
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             between requests.
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    samples: An integer value indicating the number of requests to make.
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             If 'None', the test continues indefinitely.
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Returns:
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    None.
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  '''
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  global g_file_handle
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  prefix = '\x50\x61\x75\x6c'
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sample_count = 1
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while True:
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    response_code, elapsed_time = TimedGetHash(prefix)
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LogResponse(sample_count, response_code, elapsed_time)
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sample_count += 1
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if samples is not None and sample_count == samples:
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    time.sleep(period)
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def LogResponse(sample_count, response_code, elapsed_time):
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  '''Output the response for one GetHash query.
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Args:
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sample_count:  The current sample number.
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    response_code: The HTTP response code for the GetHash request.
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elapsed_time:  The round-trip time (in milliseconds) for the
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   GetHash request.
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Returns:
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    None.
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  '''
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  global g_file_handle
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  output_list = (sample_count, response_code, elapsed_time)
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  print 'Request: %d, status: %d, elapsed time: %f ms' % output_list
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if g_file_handle is not None:
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    g_file_handle.write(('%d,%d,%f' % output_list) + '\n')
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    g_file_handle.flush()
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def SetupOutputFile(file_name):
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  '''Open a file for logging results.
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Args:
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    file_name: A path to a file to store the output.
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Returns:
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    None.
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  '''
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  global g_file_handle
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  g_file_handle = open(file_name, 'w')
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def main():
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  period = 10
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  samples = None
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  options, args = getopt.getopt(sys.argv[1:],
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                's:p:o:',
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                ['samples=', 'period=', 'output='])
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for option, value in options:
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if option == '-s' or option == '--samples':
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      samples = int(value)
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif option == '-p' or option == '--period':
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      period = float(value)
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif option == '-o' or option == '--output':
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      file_name = value
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print 'Bad option: %s' % option
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return 1
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  try:
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print 'Starting Timed GetHash ----------'
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetupOutputFile(file_name)
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    RunTimedGetHash(period, samples)
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  except KeyboardInterrupt:
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    pass
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  print 'Timed GetHash complete ----------'
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  g_file_handle.close()
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if __name__ == '__main__':
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sys.exit(main())
150