source: trunk/bin/bletchley-http2py @ 41

Last change on this file since 41 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
Line 
1#!/usr/bin/env python3
2
3# Requires Python 3+
4
5'''
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.
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
18Copyright (C) 2011-2013 Virtual Security Research, LLC
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
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(
45    'requestfile', type=open, nargs='?', default=sys.stdin, 
46    help='A file containing an HTTP request.  Defaults to stdin if omitted.')
47parser.add_argument(
48    '--requests', action='store_true', help='Generate a script that uses the'
49    ' python-requests module rather than httplib/http.client (experimental).')
50
51args = parser.parse_args()
52input_req = args.requestfile.read()
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'
67port = 80
68use_ssl = False
69protocol = 'http'
70
71headers = []
72for l in header_lines[1:]:
73    if len(l) < 1: 
74        break
75    # Handle header line continuations
76    if l[0] in ' \t':
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', 
91                            'content-length', 'proxy-connection']:
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
99                protocol = 'https'
100        else:
101            host = value
102
103
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''"
107
108
109if args.requests:
110    print('''#!/usr/bin/env python3
111
112import sys
113try:
114    import requests
115except:
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)
164''')
165
166
167
168else:
169    print('''#!/usr/bin/env python3
170
171import sys
172import http.client as httpc
173
174
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
181    print('''
182# TODO: use "data" to supply any parameters to be included in the request
183def sendRequest(connection, data=None):
184    method = %s
185    path = %s
186    body = (%s)
187   
188    connection.putrequest(method, path)
189    ''' % (repr(method), repr(path), formatted_body))
190
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])))
197
198    print('''   
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
207def newConnection():
208    if use_ssl:
209        return httpc.HTTPSConnection(host, port)
210    else:
211        return httpc.HTTPConnection(host, port)
212
213
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)
230''')
Note: See TracBrowser for help on using the repository browser.