source: trunk/bin/bletchley-http2py @ 44

Last change on this file since 44 was 40, checked in by tmorgan, 12 years ago

Switched to python3 only, since supporting both 2.x and 3.x is such a pain

Added preliminary python-requests support to http2py

  • Property svn:executable set to *
File size: 6.7 KB
RevLine 
[40]1#!/usr/bin/env python3
[4]2
[40]3# Requires Python 3+
[4]4
5'''
[40]6This script reads a raw HTTP request and writes to stdout a Python
7script.  The generated script sends the same (or a very similar)
8request using the standard httplib/http.client library, or optionally
9using the more user friendly python-requests library.
[4]10
11Certainly if you have a raw request, you could simply send it via TCP
12sockets, but if for some reason the server behaves oddly with flow control,
13insists on using gzip/deflate encoding, insists on using chunked encoding,
14or any number of other annoying things, then using an HTTP library is a
15lot more convenient.  This script attempts to make that conversion easy.
16
17
[39]18Copyright (C) 2011-2013 Virtual Security Research, LLC
[4]19Author: Timothy D. Morgan
20
21 This program is free software: you can redistribute it and/or modify
22 it under the terms of the GNU Lesser General Public License, version 3,
23 as published by the Free Software Foundation.
24
25 This program is distributed in the hope that it will be useful,
26 but WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
28 GNU General Public License for more details.
29
30 You should have received a copy of the GNU General Public License
31 along with this program.  If not, see <http://www.gnu.org/licenses/>.
32'''
33
34
35import sys
36import argparse
37
[28]38parser = argparse.ArgumentParser(
39    description='A script which accepts an HTTP request and prints out a'
40    ' generated Python script which sends a similar request.  This is useful'
41    ' when one wants to automate sending a large number of requests to a'
42    ' particular page or application.'
43    ' For more information, see: http://code.google.com/p/bletchley/wiki/Overview')
44parser.add_argument(
[40]45    'requestfile', type=open, nargs='?', default=sys.stdin, 
[28]46    help='A file containing an HTTP request.  Defaults to stdin if omitted.')
47parser.add_argument(
[40]48    '--requests', action='store_true', help='Generate a script that uses the'
49    ' python-requests module rather than httplib/http.client (experimental).')
50
[4]51args = parser.parse_args()
[40]52input_req = args.requestfile.read()
[4]53
54
55if '\r\n\r\n' in input_req:
56    raw_headers,body = input_req.split('\r\n\r\n', 1)
57elif '\n\n' in input_req:
58    raw_headers,body = input_req.split('\n\n', 1)
59else:
60    raw_headers = input_req
61    body = ''
62
63header_lines = raw_headers.split('\n')
64method,path,version = header_lines[0].split(' ', 2)
65
66host = 'TODO'
[14]67port = 80
[4]68use_ssl = False
[40]69protocol = 'http'
[4]70
71headers = []
72for l in header_lines[1:]:
73    if len(l) < 1: 
74        break
75    # Handle header line continuations
[40]76    if l[0] in ' \t':
[4]77        if len(headers) == 0:
78            continue
79        name,values = headers[-1]
80        values.append(l.lstrip('\t'))
81        headers[-1] = (name,values)
82        continue
83
84    name,value = l.split(':',1)
85    value = value.lstrip(' ').rstrip('\r')
86
87    # Skip headers that have to do with transfer encodings and connection longevity
88    if name.lower() not in ['accept','accept-language',
89                            'accept-encoding','accept-charset',
90                            'connection', 'keep-alive', 'host', 
[39]91                            'content-length', 'proxy-connection']:
[4]92        headers.append((name,[value]))
93
94    if name.lower() == 'host':
95        if ':' in value:
96            host,port = value.split(':',1)
97            if port == 443:
98                use_ssl = True
[40]99                protocol = 'https'
[4]100        else:
101            host = value
102
[39]103
[40]104formatted_body = '\n            '.join([repr(body[i:i+40].encode()) for i in range(0,len(body),40)])
105if formatted_body == '':
106    formatted_body = "b''"
[39]107
[40]108
109if args.requests:
110    print('''#!/usr/bin/env python3
111
[39]112import sys
113try:
[40]114    import requests
[39]115except:
[40]116    sys.stderr.write('ERROR: Could not import requests module.  Ensure it is installed.\\n')
117    sys.stderr.write('       Under Debian, the package name is "python3-requests"\\n.')
118    sys.exit(1)
119
120
121# TODO: ensure the host, port, and SSL settings are correct.
122host = %s
123port = %s
124protocol = %s
125''' % (repr(host),repr(port),repr(protocol)))
126
127    headers = dict(headers)
128    # XXX: We don't currently support exactly formatted header
129    #      continuations with python requests, but this should be
130    #      semantically equivalent.
131    for h in headers.keys():
132        headers[h] = ' '.join(headers[h])
133
134    print('''
135session = requests.Session()
136# TODO: use "data" to supply any parameters to be included in the request
137def sendRequest(session, data=None):
138    method = %s
139    path = %s
140    headers = %s
141    url = "%%s://%%s:%%d%%s" %% (protocol,host,port,path)
142    body = (%s)
143
144    return session.request(method, url, headers=headers, data=body)
145    ''' % (repr(method), repr(path), repr(headers), formatted_body))
146
147    print('''   
148
149def fetch(data):
150    global session
151    ret_val = None
152
153    # TODO: customize code here to retrieve what you need from the response(s)
154    # For information on the response object's interface, see:
155    #   http://docs.python-requests.org/en/latest/api/#requests.Response
156    response = sendRequest(session, data)
157    print(response.headers)
158    print(repr(response.content))
159
160    return ret_val
161
162data = ''
163fetch(data)
[39]164''')
165
166
[40]167
168else:
169    print('''#!/usr/bin/env python3
170
171import sys
172import http.client as httpc
173
174
[4]175# TODO: ensure the host, port, and SSL settings are correct.
176host = %s
177port = %s
178use_ssl = %s
179''' % (repr(host),repr(port),repr(use_ssl)))
180
[40]181    print('''
[39]182# TODO: use "data" to supply any parameters to be included in the request
183def sendRequest(connection, data=None):
[4]184    method = %s
185    path = %s
[14]186    body = (%s)
187   
[4]188    connection.putrequest(method, path)
[40]189    ''' % (repr(method), repr(path), formatted_body))
[4]190
[40]191    for name,values in headers:
192        if len(values) > 1:
193            continuations = ','.join([repr(v) for v in values[1:]])
194            print('''    connection.putheader(%s, %s, %s)''' % (repr(name),repr(values[0]),continuations))
195        else:
196            print('''    connection.putheader(%s, %s)''' % (repr(name),repr(values[0])))
[4]197
[40]198    print('''   
[4]199    if len(body) > 0:
200        connection.putheader('Content-Length', len(body))
201    connection.endheaders()
202    connection.send(body)
203   
204    return connection.getresponse()
205
206
[39]207def newConnection():
208    if use_ssl:
209        return httpc.HTTPSConnection(host, port)
210    else:
211        return httpc.HTTPConnection(host, port)
[4]212
213
[39]214def fetch(data):
215    ret_val = None
216    connection = newConnection()
217
218    # TODO: customize code here to retrieve what you need from the response(s)
219    # For information on the response object's interface, see:
220    #   http://docs.python.org/library/httplib.html#httpresponse-objects
221    response = sendRequest(connection, data)
222    print(response.getheaders())
223    print(repr(response.read()))
224
225    connection.close()
226    return ret_val
227
228data = ''
229fetch(data)
[4]230''')
Note: See TracBrowser for help on using the repository browser.