├── .gitignore
├── README.md
├── htpy.c
├── libhtp-0.5.31.tar.gz
└── setup.py
/.gitignore:
--------------------------------------------------------------------------------
1 | libhtp-0.*/
2 | build/*
3 | *.swp
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | htpy
2 | ====
3 |
4 | Python bindings to libhtp
5 |
6 | Description
7 | ===========
8 | htpy is a python interface to libhtp, which is an HTTP parser library. libhtp
9 | is used by suricata and is complete, permissive and fast enough. ;)
10 |
11 | Using htpy
12 | ==========
13 | The best way to describe how to use htpy is to see it in action. Let's setup an
14 | example where we want to see the Host header and the path part of the URI for a
15 | request. In the example below ''req'' is the HTTP request as a string. How you
16 | get this has nothing to do with htpy. We tend to use pynids and feed the
17 | request data in as we get it. You do not have to buffer the entire request up
18 | and pass it in all at once.
19 |
20 |
21 | import htpy
22 |
23 | def uri_callback(cp):
24 | uri = cp.get_uri()
25 | if 'path' in uri:
26 | print uri['path']
27 | return htpy.HTP_OK
28 |
29 | def request_headers_callback(cp):
30 | host = cp.get_request_header('Host')
31 | if host:
32 | print host
33 | return htpy.HTP_OK
34 |
35 | cp = htpy.init()
36 | cp.register_request_uri_normalize(uri_callback)
37 | cp.register_request_headers(request_headers_callback)
38 | cp.req_data(req)
39 |
40 |
41 | Details
42 | =======
43 | Initialization
44 | --------------
45 | There are two ways to start using htpy. The first is to call htpy.init(), which
46 | returns a connection parser object.
47 |
48 | cp = htpy.init()
49 |
50 |
51 | The second way, using htpy.config() and htpy.connp(), provides more flexibility
52 | though such flexibility is not needed in the common case.
53 |
54 |
55 | cfg = htpy.config()
56 | cp = htpy.connp(cfg)
57 |
58 |
59 | htp.config() returns a configuration object which has various properties that
60 | can be altered before generating a connection parser based upon that
61 | configuration. Once a connection parser has been generated with a given config
62 | it's internal configuration can not be altered.
63 |
64 | Callbacks
65 | ---------
66 | Callbacks are used heavily throughout htpy as a way to act on data as it is
67 | parsed. The basic premise is to pass data into register callbacks for the
68 | states in the parser you are interested in and then pass the data to the
69 | parser. As the data is parsed (and possibly altered) the appropriate callbacks
70 | will be called.
71 |
72 | Return values
73 | -------------
74 | Callback functions must return one of the following values.
75 | * htpy.HTP_OK
76 | * htpy.HTP_ERROR
77 | * htpy.HTP_STOP
78 | * htpy.HTP_DECLINE (XXX: What does this do?)
79 |
80 | ###HTP_STOP vs HTP_ERROR
81 | When libhtp is given the HTP_ERROR return value it sets the parser state to an
82 | error and will refuse to parse any further. This manifests in htpy as an
83 | htpy_error exception. This is considered a fatal error. The difference between
84 | HTP_STOP and HTP_ERROR is that HTP_STOP will raise a htpy_stop exception,
85 | which should be caught in handleStream() and tcp.stop() should be called when
86 | the exception is caught. The reason you can not call tcp.stop() from a callback
87 | is that the tcp object passed into handleStream() is created on the fly each
88 | time handleStream() is called. This means that if you pass that object into a
89 | callback via the cp.set_obj() method you will be passing in an old tcp object
90 | on every subsequent call into handleStream() beyond the first.
91 |
92 | Registering callbacks
93 | ---------------------
94 | Callbacks are registered with the connection parser object. They can be any
95 | callable python block. There are a number of callbacks, which fall into three
96 | categories:
97 |
98 | Regular callbacks:
99 | * request_start: Called as soon as a new transaction is started.
100 | * request_line: Called after the request line has been parsed.
101 | * request_uri_normalize: Called right before the URI is normalized. When a URI
102 | is normalized default fields are filled in if they are not specified. This
103 | hook is useful if you are going to normalize a URI but still want to know
104 | what was provided by the request as opposed to what was filled in by the
105 | parser.
106 | * request_headers: Called right after all the headers have been parsed and
107 | basic sanity checks have passed.
108 | * request_trailer: Called right after all the request headers have been parsed.
109 | * request_complete: Called right after an entire request is parsed.
110 | * response_start: Called as soon as the parser is ready for a response.
111 | * response_line: Called right after the response line has been parsed.
112 | * response_headers: Called right after all the headers have been parsed and
113 | basic sanity checks have passed.
114 | * response_trailer: Called right after all the response headers have been
115 | parsed.
116 | * response_complete: Called right after an entire response is parsed.
117 |
118 | Transaction callbacks:
119 | * request_header_data: Called whenever request header data is available.
120 | * response_header_data: Called whenever response header data is available.
121 | * request_body_data: Called whenever a body data chunk is processed.
122 | * response_body_data: Called whenever response body data is available. Gzip
123 | compressed data should be properly decompressed.
124 | * request_trailer_data: Called whenever request trailer data is available.
125 | * response_trailer_data: Called whenever response trailer data is available.
126 |
127 | Request File Data callback:
128 | * request_file_data: Called whenever file data is found in a request.
129 |
130 | Log callbacks:
131 | * log: Called for every log message that should be handled.
132 |
133 | Registering callbacks
134 | ---------------------
135 | Registering a callback is done with the appropriate method within the
136 | connection parser object. The method for any callback is the name of the
137 | callback pre-pended with "register_". For example, to register a response
138 | callback:
139 |
140 | def response_callback(cp):
141 | print "INSIDE RESPONSE CALLBACK"
142 | return htpy.HTP_OK
143 |
144 | cp = htpy.init()
145 | cp.register_response(response_callback)
146 |
147 |
148 | All registrations take one parameter, the python function to call. The only
149 | exception to this rule is the request_file_data callback. This registration
150 | can take an optional second argument which is used to tell libhtp if it
151 | should write the file data to disk. For example:
152 |
153 | def file_data_callback(data):
154 | print "INSIDE FILE DATA CALLBACK"
155 | return htpy.HTP_OK
156 |
157 | cp = htpy.init()
158 | cp.register_request_file_data(file_data_callback, True)
159 |
160 |
161 | The "True" above is the optional argument used to turn on file carving. If
162 | this argument is not provided then file carving will be left off (the
163 | default state).
164 |
165 | Callback definitions
166 | --------------------
167 | ###Regular callbacks
168 | Regular callbacks are passed one argument:
169 |
170 | * cp: The connection parser.
171 |
172 |
173 | def request_uri_normalize_callback(cp):
174 | uri = cp.get_uri()
175 | if query in uri:
176 | print uri['query']
177 | return htpy.HTP_OK
178 |
179 |
180 | ###Transaction callbacks
181 | Transaction callbacks are passed two arguments:
182 |
183 | * data: The transaction data.
184 | * length: The length of the data.
185 |
186 |
187 | def response_body_data_callback(data, length):
188 | print "Got %i bytes: %s" % (length, data)
189 | return htpy.HTP_OK
190 |
191 |
192 | ###Log callback
193 | Log callbacks are passed three arguments:
194 |
195 | * cp: The connection parser from which this log message was generated.
196 | * msg: A string containing the log message.
197 | * level: An integer. The level of the log message. See documentation about
198 | setting the log_level of a configuration object if desired.
199 |
200 |
201 | def log_callback(cp, msg, level):
202 | # elog['level'] is the same as level
203 | # elog['msg'] is the same as msg
204 | # When level == htpy.LOG_ERROR you can get a dictionary of the error
205 | # by calling get_last_error().
206 | if level == htpy.HTP_LOG_ERROR:
207 | elog = cp.get_last_error()
208 | if elog == None:
209 | return htpy.HTP_ERROR
210 | print "%s:%i - %s (%i)" % (elog['file'], elog['line'], elog['msg'], elog['level'])
211 | else:
212 | print "%i - %s" % (level, msg)
213 | return htpy.HTP_OK
214 |
215 |
216 | ###Request file data callback
217 | Request file data callbacks are passed one argument:
218 |
219 | * data: A dictionary which has the following key/value pairs:
220 | * data: A blob of the data that has been parsed.
221 | * filename: The filename parsed out of the data. This is an optional entry
222 | in the dictionary. If libhtp can not find the filename then it will be
223 | left out of the dictionary.
224 | * tmpname: The filename on disk where the data is being written. This is an
225 | optional entry in the dictionary. If libhtp is not carving the file then
226 | it will be left out of the dictionary.
227 |
228 |
229 | def file_data_callback(data):
230 | print "Wrote %i bytes to %s for %s" % (len(data['data']), data['tmpname'], data['filename'])
231 | return htpy.HTP_OK
232 |
233 | cp = htpy.init()
234 | cp.register_request_file_data(file_data_callback, True)
235 |
236 |
237 | Sending an object to callbacks
238 | ------------------------------
239 | It is possible to pass an arbitrary object to each callback. This object
240 | will be passed to each callback as the last argument. When using this the
241 | definition of each callback must take this into account:
242 |
243 |
244 | x = "FOO"
245 |
246 | def request_uri_normalize_callback(cp, obj):
247 | uri = cp.get_uri()
248 | if query in uri:
249 | print uri['query']
250 | print obj
251 | return htpy.HTP_OK
252 |
253 | cp = htpy.init()
254 | cp.set_obj(x)
255 | cp.register_request_uri_normalize(request_uri_normalize_callback)
256 | cp.req_data(req)
257 |
258 |
259 | It is also possible to remove this object using the ''del_obj()'' method of the
260 | connection parser.
261 |
262 | ###EXCEPTION
263 | The only callback which can not be passed an arbitrary object is the
264 | request_file_data callback. This is due to a limitation in libhtp.
265 |
266 | Sending data to the parser
267 | --------------------------
268 | Sending data to the parser uses one of two functions:
269 |
270 |
271 | cp.req_data(req)
272 | cp.res_data(res)
273 |
274 |
275 | You use ''req_data()'' to send request data as you get it. You use ''res_data''
276 | to send response data as you get it. There is no requirement to pass the entire
277 | request or response data in one shot, you can send data to the parser as you
278 | get it.
279 |
280 | Objects
281 | =======
282 | Config object
283 | -------------
284 | ###Methods
285 | Configuration objects have no methods. All their functionality is exposed as
286 | attributes that can be manipulated.
287 |
288 | ###Attributes
289 | Configuration objects contain the following attributes. In many cases the
290 | value being set is not sanity checked. Using the wrong value can potentially
291 | alter the parser in odd ways.
292 |
293 | * log_level: Used when deciding whether to store or ignore the messages issued
294 | by the parser. If the level of a log message is less than the configuration
295 | log level, the message will be ignored. Htpy provides macros that map
296 | directly to the levels used internally by libhtp.
297 | * htpy.HTP_LOG_ERROR (1)
298 | * htpy.HTP_LOG_WARNING (2)
299 | * htpy.HTP_LOG_NOTICE (3, DEFAULT)
300 | * htpy.HTP_LOG_INFO (4)
301 | * htpy.HTP_LOG_DEBUG (5)
302 | * htpy.HTP_LOG_DEBUG2 (6)
303 | * tx_auto_destroy: Automatically destroy transactions when done.
304 | * response_decompression: Determine whether response bodies are
305 | automatically decompressed. Default value is 1 which is enabled.
306 | To disable automatic decompression set this to 0.
307 |
308 | Connection parser object
309 | ------------------------
310 | ###Methods
311 | * get_request_header(string): Given a header (string) return a string that is
312 | the value of that header.
313 | * get_response_header(string): Given a header (string) return a string that is
314 | the value of that header.
315 | * get_all_request_headers(): Return a dictionary of all request headers. The
316 | key will be the header name and the value will be the header value.
317 | * get_all_response_headers(): Return a dictionary of all response headers. The
318 | key will be the header name and the value will be the header value.
319 | * get_response_status(): Return the status code of the response as an integer.
320 | * get_response_status_string(): Return the status code of the response as a
321 | string.
322 | * get_request_line(): Return the request line with method, path and host in one line.
323 | * get_response_line(): Return the response line with protocol, status code and status message in one line.
324 | * get_request_message_len(): Return the request message length before decompressed and dechunked.
325 | * get_response_message_len(): Return the response message length before decompressed and dechunked.
326 | * get_request_entity_len(): Return the request message length after decompressed and dechunked.
327 | * get_response_entity_len(): Return the request message length after decompressed and dechunked.
328 | * set_obj(object): Pass ''object'' to each callback as the last argument. Using
329 | this without altering the callback definition to account for the new object
330 | will cause htpy to raise an error when calling your callback.
331 | * del_obj(object): Stop passing ''object'' to each callback as the last
332 | argument. XXX: Does it make sense to have this? Removing an object but
333 | still using the callback definition that expects it will cause problems
334 | * req_data(data): Send ''data'' into the parser. The data will be treated as a
335 | request. You do not have to send the entire request at once, you can send it
336 | into the parser as you get it. XXX: Document return value
337 | * req_data_consumed(): Return the number of request bytes consumed by the
338 | parser.
339 | * res_data(data): Send ''data'' into the parser. The data will be treated as a
340 | response. You do not have to send the entire response at once, you can send
341 | it into the parser as you get it. XXX: Document return value
342 | * res_data_consumed(): Return the number of response bytes consumed by the
343 | parser.
344 | * get_last_error(): Return a dictionary of the last log message with level
345 | htpy.HTP_LOG_ERROR. In the case of no error it will return None. The
346 | dictionary is:
347 |
348 | { 'level': int,
349 | 'msg': string,
350 | 'file': string,
351 | 'line': int }
352 |
353 | * clear_error(): Clear the last saved error. XXX: Is this worth keeping? A
354 | new error message will overwrite the old one.
355 | * get_request_protocol(): Return the protocol as a string without version.
356 | * get_request_protocol_number(): Return the protocol number. Prefer using htpy.HTP_PROTOCOL_* constants with output values.
357 | * get_response_protocol(): Return the protocol as a string without version.
358 | * get_response_protocol_number(): Return the protocol number. Prefer using htpy.HTP_PROTOCOL_* constants with output values.
359 | * get_uri(): Return a dictionary of the parsed URI. Depending upon the
360 | normalization setting this dictionary can be the URI as it was parsed or as
361 | it was normalized. In the case of a normalized URI the missing parts wil be
362 | given default values. The dictionary is:
363 |
364 | { 'scheme': string,
365 | 'username': string,
366 | 'password': string,
367 | 'hostname': string,
368 | 'port': string,
369 | 'port_num': int,
370 | 'path': string,
371 | 'query': string,
372 | 'fragment': string, }
373 |
374 | * get_method(): Return the request method as a string (GET, POST, HEAD, etc).
375 |
376 | Each of the callback methods take a callable python function as the argument.
377 | When the callbacks are called are documented elsewhere.
378 | * register_transaction_start(callback)
379 | * register_request_line(callback)
380 | * register_request_uri_normalize(callback)
381 | * register_request_headers(callback)
382 | * register_request_body_data(callback)
383 | * register_request_trailer(callback)
384 | * register_request_complete(callback)
385 | * register_response_start(callback)
386 | * register_response_line(callback)
387 | * register_response_headers(callback)
388 | * register_response_body_data(callback)
389 | * register_response_trailer(callback)
390 | * register_response_complete(callback)
391 | * register_log(callback)
392 |
393 | ###Attributes
394 | The connection parser object contains the config object as a member, but
395 | you should not touch it, ever.
396 |
--------------------------------------------------------------------------------
/htpy.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 The MITRE Corporation. All rights reserved.
3 | *
4 | * Redistribution and use in source and binary forms, with or without
5 | * modification, are permitted provided that the following conditions
6 | * are met:
7 | * 1. Redistributions of source code must retain the above copyright
8 | * notice, this list of conditions and the following disclaimer.
9 | * 2. Redistributions in binary form must reproduce the above copyright
10 | * notice, this list of conditions and the following disclaimer in the
11 | * documentation and/or other materials provided with the distribution.
12 | *
13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 | * SUCH DAMAGE.
24 | */
25 |
26 | #include
27 | #include
28 | #include "../htp_config_auto_gen.h"
29 | #include "htp.h"
30 | #include "htp_private.h"
31 |
32 | #define HTPY_VERSION "0.26"
33 |
34 | static PyObject *htpy_error;
35 | static PyObject *htpy_stop;
36 |
37 | /*
38 | * We set the python connection parser as user_data in the libhtp connection
39 | * parser. Most callbacks are given a way to eventually get to the connection
40 | * parser by doing something like this:
41 | *
42 | * PyObject *obj = (PyObject *) htp_connp_get_user_data(tx->connp);
43 | *
44 | * We store the python objects for the callbacks in the connection parser
45 | * object. Once we have the connection parser using the above snippet
46 | * we can call the appropriate python function by using obj->foo_callback.
47 | *
48 | * The only callback that is not able to get to the connection parser is
49 | * the request_file_data callback. Since we can't get to a connection
50 | * parser from there we are storing the python object as a global.
51 | */
52 | PyObject *request_file_data_callback;
53 |
54 | typedef struct {
55 | PyObject_HEAD
56 | htp_cfg_t *cfg;
57 | } htpy_config;
58 |
59 | static PyObject *htpy_config_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
60 | htpy_config *self;
61 |
62 | self = (htpy_config *) type->tp_alloc(type, 0);
63 |
64 | return (PyObject *) self;
65 | }
66 |
67 | static int htpy_config_init(htpy_config *self, PyObject *args, PyObject *kwds) {
68 | self->cfg = htp_config_create();
69 | if (!self->cfg)
70 | return -1;
71 |
72 | htp_config_set_tx_auto_destroy(self->cfg, 1);
73 |
74 | return 0;
75 | }
76 |
77 | static void htpy_config_dealloc(htpy_config *self) {
78 | htp_config_destroy(self->cfg);
79 | self->ob_type->tp_free((PyObject *) self);
80 | }
81 |
82 | static PyMethodDef htpy_config_methods[] = {
83 | { NULL }
84 | };
85 |
86 | #define CONFIG_GET(ATTR) \
87 | static PyObject *htpy_config_get_##ATTR(htpy_config *self, void *closure) { \
88 | PyObject *ret; \
89 | ret = Py_BuildValue("i", self->cfg->ATTR); \
90 | if (!ret) { \
91 | PyErr_SetString(htpy_error, "Unable to get this attribute."); \
92 | return NULL; \
93 | } \
94 | return(ret); \
95 | }
96 |
97 | CONFIG_GET(log_level)
98 | CONFIG_GET(tx_auto_destroy)
99 |
100 | /**
101 | * To get the response decompression setting there is an int
102 | * in the config called "response_decompression_enabled". Unfortunately
103 | * this doesn't fit with the above macro and so instead the following
104 | * function takes care of getting the value.
105 | */
106 |
107 | static PyObject *htpy_config_get_response_decompression(htpy_config *self, void *closure) {
108 | PyObject *ret;
109 | ret = Py_BuildValue("i", self->cfg->response_decompression_enabled);
110 | if (!ret) {
111 | PyErr_SetString(htpy_error, "Unable to get this attribute.");
112 | return NULL;
113 | }
114 | return(ret);
115 | }
116 |
117 | #define CONFIG_SET(ATTR) \
118 | static int htpy_config_set_##ATTR(htpy_config *self, PyObject *value, void *closure) { \
119 | int v; \
120 | if (!value) { \
121 | PyErr_SetString(htpy_error, "Value may not be None."); \
122 | return -1; \
123 | } \
124 | if (!PyInt_Check(value)) { \
125 | PyErr_SetString(htpy_error, "Attribute must be of type int."); \
126 | return -1; \
127 | } \
128 | v = (int) PyInt_AsLong(value); \
129 | htp_config_set_##ATTR((htp_cfg_t *) self->cfg, v); \
130 | return 0; \
131 | }
132 |
133 | CONFIG_SET(tx_auto_destroy)
134 | CONFIG_SET(response_decompression)
135 |
136 | /*
137 | * Sadly the log level is not exposed like others. Only way to set it
138 | * is to manually set it in the config structure directly.
139 | *
140 | * I'm not checking for values above the maximum log level. If someone
141 | * wants to do something stupid like set it to htpy.HTP_1_1 (101) then
142 | * they can have fun dealing with it.
143 | */
144 | static int htpy_config_set_log_level(htpy_config *self, PyObject *value, void *closure) {
145 | int v;
146 | if (!value) {
147 | PyErr_SetString(htpy_error, "Value may not be None.");
148 | return -1;
149 | }
150 |
151 | if (!PyInt_Check(value)) {
152 | PyErr_SetString(htpy_error, "Attribute must be of type int.");
153 | return -1;
154 | }
155 |
156 | v = (int) PyInt_AsLong(value);
157 | self->cfg->log_level = v;
158 |
159 | return 0;
160 | }
161 |
162 | static PyGetSetDef htpy_config_getseters[] = {
163 | {"log_level",
164 | (getter) htpy_config_get_log_level,
165 | (setter) htpy_config_set_log_level,
166 | "Logs with a level less than this will be ignored.", NULL},
167 | {"tx_auto_destroy",
168 | (getter) htpy_config_get_tx_auto_destroy,
169 | (setter) htpy_config_set_tx_auto_destroy,
170 | "Automatically destroy transactions", NULL},
171 | {"response_decompression",
172 | (getter) htpy_config_get_response_decompression,
173 | (setter) htpy_config_set_response_decompression,
174 | "Enable response decompression", NULL},
175 | {NULL}
176 | };
177 |
178 | static PyMemberDef htpy_config_members[] = {
179 | { NULL }
180 | };
181 |
182 | static PyTypeObject htpy_config_type = {
183 | PyObject_HEAD_INIT(NULL)
184 | 0, /* ob_size */
185 | "htpy.config", /* tp_name */
186 | sizeof(htpy_config), /* tp_basicsize */
187 | 0, /* tp_itemsize */
188 | (destructor) htpy_config_dealloc, /* tp_dealloc */
189 | 0, /* tp_print */
190 | 0, /* tp_getattr */
191 | 0, /* tp_setattr */
192 | 0, /* tp_compare */
193 | 0, /* tp_repr */
194 | 0, /* tp_as_number */
195 | 0, /* tp_as_sequence */
196 | 0, /* tp_as_mapping */
197 | 0, /* tp_hash */
198 | 0, /* tp_call */
199 | 0, /* tp_str */
200 | 0, /* tp_getattro */
201 | 0, /* tp_setattro */
202 | 0, /* tp_as_buffer */
203 | Py_TPFLAGS_DEFAULT, /* tp_flags */
204 | "config object", /* tp_doc */
205 | 0, /* tp_traverse */
206 | 0, /* tp_clear */
207 | 0, /* tp_richcompare */
208 | 0, /* tp_weaklistoffset */
209 | 0, /* tp_iter */
210 | 0, /* tp_iternext */
211 | htpy_config_methods, /* tp_methods */
212 | htpy_config_members, /* tp_members */
213 | htpy_config_getseters, /* tp_getset */
214 | 0, /* tp_base */
215 | 0, /* tp_dict */
216 | 0, /* tp_descr_get */
217 | 0, /* tp_descr_set */
218 | 0, /* tp_dictoffset */
219 | (initproc) htpy_config_init, /* tp_init */
220 | 0, /* tp_alloc */
221 | htpy_config_new, /* tp_new */
222 | };
223 |
224 | typedef struct {
225 | PyObject_HEAD
226 | htp_connp_t *connp;
227 | PyObject *cfg;
228 | PyObject *obj_store;
229 | /* Callbacks */
230 | PyObject *request_start_callback;
231 | PyObject *request_line_callback;
232 | PyObject *request_uri_normalize_callback;
233 | PyObject *request_headers_callback;
234 | PyObject *request_header_data_callback;
235 | PyObject *request_body_data_callback;
236 | PyObject *request_trailer_callback;
237 | PyObject *request_trailer_data_callback;
238 | PyObject *request_complete_callback;
239 | PyObject *response_start_callback;
240 | PyObject *response_line_callback;
241 | PyObject *response_headers_callback;
242 | PyObject *response_header_data_callback;
243 | PyObject *response_body_data_callback;
244 | PyObject *response_trailer_callback;
245 | PyObject *response_trailer_data_callback;
246 | PyObject *response_complete_callback;
247 | PyObject *transaction_complete_callback;
248 | PyObject *log_callback;
249 | } htpy_connp;
250 |
251 | static PyObject *htpy_connp_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
252 | htpy_connp *self;
253 |
254 | self = (htpy_connp *) type->tp_alloc(type, 0);
255 |
256 | return (PyObject *) self;
257 | }
258 |
259 | static void htpy_connp_dealloc(htpy_connp *self) {
260 | /*
261 | * Decrement reference counters and free the underlying
262 | * libhtp backed storage.
263 | */
264 | Py_XDECREF(self->obj_store);
265 | Py_XDECREF(self->cfg);
266 | Py_XDECREF(self->request_start_callback);
267 | Py_XDECREF(self->request_line_callback);
268 | Py_XDECREF(self->request_uri_normalize_callback);
269 | Py_XDECREF(self->request_headers_callback);
270 | Py_XDECREF(self->request_header_data_callback);
271 | Py_XDECREF(self->request_body_data_callback);
272 | Py_XDECREF(self->request_trailer_callback);
273 | Py_XDECREF(self->request_trailer_data_callback);
274 | Py_XDECREF(self->request_complete_callback);
275 | Py_XDECREF(self->response_start_callback);
276 | Py_XDECREF(self->response_line_callback);
277 | Py_XDECREF(self->response_headers_callback);
278 | Py_XDECREF(self->response_header_data_callback);
279 | Py_XDECREF(self->response_body_data_callback);
280 | Py_XDECREF(self->response_trailer_callback);
281 | Py_XDECREF(self->response_trailer_data_callback);
282 | Py_XDECREF(self->response_complete_callback);
283 | Py_XDECREF(self->transaction_complete_callback);
284 | Py_XDECREF(self->log_callback);
285 | htp_connp_destroy_all(self->connp);
286 | self->ob_type->tp_free((PyObject *) self);
287 | }
288 |
289 | static int htpy_connp_init(htpy_connp *self, PyObject *args, PyObject *kwds) {
290 | PyObject *cfg_obj = NULL;
291 |
292 | if (!PyArg_ParseTuple(args, "|O:htpy_connp_init", &cfg_obj))
293 | return -1;
294 |
295 | /*
296 | * If we are not given a config object as an argument create an
297 | * htp_cfg_t and use that.
298 | */
299 | if (!cfg_obj)
300 | cfg_obj = PyObject_CallObject((PyObject *) &htpy_config_type, NULL);
301 | else
302 | Py_XINCREF(cfg_obj);
303 |
304 | if (!cfg_obj) {
305 | PyErr_SetString(htpy_error, "Unable to create config object.");
306 | return -1;
307 | }
308 |
309 | self->cfg = cfg_obj;
310 | self->connp = htp_connp_create(((htpy_config *) cfg_obj)->cfg);
311 |
312 | if (!self->connp) {
313 | PyErr_SetString(htpy_error, "Unable to create connection parser.");
314 | return -1;
315 | }
316 |
317 | htp_connp_set_user_data(self->connp, (void *) self);
318 |
319 | return 0;
320 | }
321 |
322 | /*
323 | * Callback handlers.
324 | *
325 | * Libhtp will call one of these callbacks. This callback will then get the
326 | * connp, convert it into a PyObject and pass that to the real callback.
327 | * It will then convert the returned PyObject to an int and pass that back to
328 | * libhtp.
329 | *
330 | * The callbacks that take a htp_tx_t are defined with CALLBACK_TX. The
331 | * log callback is not defined in a macro because there is only one of it's
332 | * type.
333 | *
334 | * XXX: Add support for removing callbacks?
335 | */
336 | #define CALLBACK(CB) \
337 | int htpy_##CB##_callback(htp_tx_t *tx) { \
338 | PyObject *obj = (PyObject *) htp_connp_get_user_data(tx->connp); \
339 | PyObject *arglist; \
340 | PyObject *res; \
341 | long i; \
342 | if (((htpy_connp *) obj)->obj_store) \
343 | arglist = Py_BuildValue("(OO)", obj, ((htpy_connp *) obj)->obj_store); \
344 | else \
345 | arglist = Py_BuildValue("(O)", obj); \
346 | if (!arglist) \
347 | return HTP_ERROR; \
348 | res = PyObject_CallObject(((htpy_connp *) obj)->CB##_callback, arglist); \
349 | Py_DECREF(arglist); \
350 | if (PyErr_Occurred() != NULL) { \
351 | PyErr_PrintEx(0); \
352 | return HTP_ERROR; \
353 | } \
354 | i = PyInt_AsLong(res); \
355 | Py_DECREF(res); \
356 | return((int) i); \
357 | }
358 |
359 | CALLBACK(request_start)
360 | CALLBACK(request_line)
361 | CALLBACK(request_uri_normalize)
362 | CALLBACK(request_headers)
363 | CALLBACK(request_trailer)
364 | CALLBACK(request_complete)
365 | CALLBACK(response_start)
366 | CALLBACK(response_line)
367 | CALLBACK(response_headers)
368 | CALLBACK(response_trailer)
369 | CALLBACK(response_complete)
370 | CALLBACK(transaction_complete)
371 |
372 | /* These callbacks take a htp_tx_data_t pointer. */
373 | #define CALLBACK_TX(CB) \
374 | int htpy_##CB##_callback(htp_tx_data_t *txd) { \
375 | PyObject *obj = (PyObject *) htp_connp_get_user_data(txd->tx->connp); \
376 | PyObject *arglist; \
377 | PyObject *res; \
378 | long i; \
379 | if (((htpy_connp *) obj)->obj_store) \
380 | arglist = Py_BuildValue("(s#IO)", txd->data, txd->len, txd->len, ((htpy_connp *) obj)->obj_store); \
381 | else \
382 | arglist = Py_BuildValue("(s#I)", txd->data, txd->len, txd->len); \
383 | if (!arglist) \
384 | return HTP_ERROR; \
385 | res = PyObject_CallObject(((htpy_connp *) obj)->CB##_callback, arglist); \
386 | Py_DECREF(arglist); \
387 | if (PyErr_Occurred() != NULL) { \
388 | PyErr_PrintEx(0); \
389 | return HTP_ERROR; \
390 | } \
391 | i = PyInt_AsLong(res); \
392 | Py_DECREF(res); \
393 | return((int) i); \
394 | }
395 |
396 | CALLBACK_TX(request_header_data)
397 | CALLBACK_TX(request_body_data)
398 | CALLBACK_TX(request_trailer_data)
399 | CALLBACK_TX(response_header_data)
400 | CALLBACK_TX(response_body_data)
401 | CALLBACK_TX(response_trailer_data)
402 |
403 | /* Another special case callback. This one takes a htp_file_data_t pointer. */
404 | int htpy_request_file_data_callback(htp_file_data_t *file_data) {
405 | long i;
406 | PyObject *res;
407 | PyObject *arglist;
408 | PyObject *data_key, *data_val;
409 | PyObject *filename_key, *filename_val;
410 | PyObject *tmpname_key, *tmpname_val;
411 | PyObject *dict = PyDict_New();
412 |
413 | if (!dict) {
414 | PyErr_SetString(htpy_error, "Unable to create dictionary.");
415 | return HTP_ERROR;
416 | }
417 |
418 | data_key = Py_BuildValue("s", "data");
419 | data_val = Py_BuildValue("s#", file_data->data, file_data->len);
420 | if (!data_key || !data_val) {
421 | Py_DECREF(dict);
422 | return HTP_ERROR;
423 | }
424 | if (PyDict_SetItem(dict, data_key, data_val) == -1) {
425 | Py_DECREF(dict);
426 | return HTP_ERROR;
427 | }
428 |
429 | if (file_data->file->filename) {
430 | filename_key = Py_BuildValue("s", "filename");
431 | filename_val = Py_BuildValue("s#", bstr_ptr(file_data->file->filename), bstr_len(file_data->file->filename));
432 | if (PyDict_SetItem(dict, filename_key, filename_val) == -1) {
433 | Py_DECREF(dict);
434 | return HTP_ERROR;
435 | }
436 | }
437 |
438 | if (file_data->file->tmpname) {
439 | tmpname_key = Py_BuildValue("s", "tmpname");
440 | tmpname_val = Py_BuildValue("s", file_data->file->tmpname);
441 | if (PyDict_SetItem(dict, tmpname_key, tmpname_val) == -1) {
442 | Py_DECREF(dict);
443 | return HTP_ERROR;
444 | }
445 | }
446 |
447 | arglist = Py_BuildValue("(O)", dict);
448 | if (!arglist)
449 | return HTP_ERROR;
450 |
451 | res = PyObject_CallObject(request_file_data_callback, arglist);
452 | Py_DECREF(arglist);
453 | if (PyErr_Occurred() != NULL) {
454 | PyErr_PrintEx(0);
455 | return HTP_ERROR;
456 | }
457 | i = PyInt_AsLong(res);
458 | Py_DECREF(res);
459 | return((int) i);
460 | }
461 |
462 | int htpy_log_callback(htp_log_t *log) {
463 | PyObject *obj = (PyObject *) htp_connp_get_user_data(log->connp);
464 | PyObject *arglist = NULL;
465 | PyObject *res;
466 | long i;
467 |
468 | if (((htpy_connp *) obj)->obj_store)
469 | arglist = Py_BuildValue("(OsiO)", (htpy_connp *) obj, log->msg, log->level, ((htpy_connp *) obj)->obj_store);
470 | else
471 | arglist = Py_BuildValue("(Osi)", (htpy_connp *) obj, log->msg, log->level);
472 | if (!arglist)
473 | return HTP_ERROR;
474 |
475 | res = PyObject_CallObject(((htpy_connp *) obj)->log_callback, arglist);
476 | Py_DECREF(arglist);
477 | if (PyErr_Occurred() != NULL) {
478 | PyErr_PrintEx(0);
479 | return HTP_ERROR;
480 | }
481 | i = PyInt_AsLong(res);
482 | Py_DECREF(res);
483 | return((int) i);
484 | }
485 |
486 | /* Registering callbacks... */
487 | #define REGISTER_CALLBACK(CB) \
488 | static PyObject *htpy_connp_register_##CB(PyObject *self, PyObject *args) { \
489 | PyObject *res = NULL; \
490 | PyObject *temp; \
491 | if (PyArg_ParseTuple(args, "O:htpy_connp_register_##CB", &temp)) { \
492 | if (!PyCallable_Check(temp)) { \
493 | PyErr_SetString(PyExc_TypeError, "parameter must be callable"); \
494 | return NULL; \
495 | } \
496 | Py_XINCREF(temp); \
497 | Py_XDECREF(((htpy_connp *) self)->CB##_callback); \
498 | ((htpy_connp *) self)->CB##_callback = temp; \
499 | htp_config_register_##CB(((htpy_connp *) self)->connp->cfg, htpy_##CB##_callback); \
500 | Py_INCREF(Py_None); \
501 | res = Py_None; \
502 | } \
503 | return res; \
504 | }
505 |
506 | REGISTER_CALLBACK(request_start)
507 | REGISTER_CALLBACK(request_line)
508 | REGISTER_CALLBACK(request_uri_normalize)
509 | REGISTER_CALLBACK(request_headers)
510 | REGISTER_CALLBACK(request_header_data)
511 | REGISTER_CALLBACK(request_body_data)
512 | REGISTER_CALLBACK(request_trailer)
513 | REGISTER_CALLBACK(request_trailer_data)
514 | REGISTER_CALLBACK(request_complete)
515 | REGISTER_CALLBACK(response_start)
516 | REGISTER_CALLBACK(response_line)
517 | REGISTER_CALLBACK(response_headers)
518 | REGISTER_CALLBACK(response_header_data)
519 | REGISTER_CALLBACK(response_body_data)
520 | REGISTER_CALLBACK(response_trailer)
521 | REGISTER_CALLBACK(response_trailer_data)
522 | REGISTER_CALLBACK(response_complete)
523 | REGISTER_CALLBACK(transaction_complete)
524 | REGISTER_CALLBACK(log)
525 |
526 | static PyObject *htpy_connp_register_request_file_data(PyObject *self, PyObject *args) {
527 | PyObject *res = NULL;
528 | PyObject *temp;
529 | int extract = 0;
530 | if (PyArg_ParseTuple(args, "O|i:htpy_connp_register_request_file_data", &temp, &extract)) {
531 | if (!PyCallable_Check(temp)) {
532 | PyErr_SetString(PyExc_TypeError, "parameter must be callable");
533 | return NULL;
534 | }
535 |
536 | Py_XINCREF(temp);
537 | Py_XDECREF(request_file_data_callback);
538 |
539 | request_file_data_callback = temp;
540 |
541 | if (extract)
542 | ((htpy_connp *) self)->connp->cfg->extract_request_files = 1;
543 |
544 | htp_config_register_multipart_parser(((htpy_connp *) self)->connp->cfg);
545 |
546 | htp_config_register_request_file_data(((htpy_connp *) self)->connp->cfg, htpy_request_file_data_callback);
547 |
548 | Py_INCREF(Py_None);
549 | res = Py_None;
550 | }
551 | return res;
552 | }
553 |
554 | /* Return a header who'se key is the given string. */
555 | #define GET_HEADER(TYPE) \
556 | static PyObject *htpy_connp_get_##TYPE##_header(PyObject *self, PyObject *args) { \
557 | PyObject *ret; \
558 | htp_header_t *hdr; \
559 | PyObject *py_str = NULL; \
560 | htp_tx_t *tx = NULL; \
561 | char *p = NULL; \
562 | if (!PyArg_ParseTuple(args, "S:htpy_connp_get_##TYPE##_header", &py_str)) \
563 | return NULL; \
564 | tx = htp_list_get(((htpy_connp *) self)->connp->conn->transactions, htp_list_size(((htpy_connp *) self)->connp->conn->transactions) - 1); \
565 | if (!tx || !tx->TYPE##_headers) { \
566 | PyErr_SetString(htpy_error, "Missing transaction or headers."); \
567 | return NULL; \
568 | } \
569 | p = PyString_AsString(py_str); \
570 | if (!p) \
571 | return NULL; \
572 | hdr = htp_table_get_c(tx->TYPE##_headers, p); \
573 | if (!hdr) \
574 | Py_RETURN_NONE; \
575 | ret = Py_BuildValue("s#", bstr_ptr(hdr->value), bstr_len(hdr->value)); \
576 | if (!ret) \
577 | return NULL; \
578 | return ret; \
579 | }
580 |
581 | GET_HEADER(request)
582 | GET_HEADER(response)
583 |
584 | /* Return a dictionary of all request or response headers. */
585 | #define GET_ALL_HEADERS(TYPE) \
586 | static PyObject *htpy_connp_get_all_##TYPE##_headers(PyObject *self, PyObject *args) { \
587 | int i; \
588 | size_t n; \
589 | htp_header_t *hdr = NULL; \
590 | PyObject *key, *val; \
591 | htp_tx_t *tx = NULL; \
592 | PyObject *ret = PyDict_New(); \
593 | if (!ret) { \
594 | PyErr_SetString(htpy_error, "Unable to create return dictionary."); \
595 | return NULL; \
596 | } \
597 | tx = htp_list_get(((htpy_connp *) self)->connp->conn->transactions, htp_list_size(((htpy_connp *) self)->connp->conn->transactions) - 1); \
598 | if (!tx || !tx->TYPE##_headers) { \
599 | PyErr_SetString(htpy_error, "Missing transaction or headers."); \
600 | Py_DECREF(ret); \
601 | return NULL; \
602 | } \
603 | for (i = 0, n = htp_table_size(tx->TYPE##_headers); i < n; i++) { \
604 | hdr = htp_table_get_index(tx->TYPE##_headers, i, NULL); \
605 | key = Py_BuildValue("s#", bstr_ptr(hdr->name), bstr_len(hdr->name)); \
606 | val = Py_BuildValue("s#", bstr_ptr(hdr->value), bstr_len(hdr->value)); \
607 | if (!key || !val) { \
608 | Py_DECREF(ret); \
609 | Py_XDECREF(key); \
610 | Py_XDECREF(val); \
611 | return NULL; \
612 | } \
613 | if (PyDict_SetItem(ret, key, val) == -1) { \
614 | Py_DECREF(ret); \
615 | return NULL; \
616 | } \
617 | Py_XDECREF(key); \
618 | Py_XDECREF(val); \
619 | } \
620 | return ret; \
621 | }
622 |
623 | GET_ALL_HEADERS(request)
624 | GET_ALL_HEADERS(response)
625 |
626 | /*
627 | * XXX: Not sure I like mucking around in the transaction to get the method,
628 | * but I'm not sure of a better way.
629 | */
630 | static PyObject *htpy_connp_get_method(PyObject *self, PyObject *args) {
631 | PyObject *ret;
632 | htp_tx_t *tx = NULL;
633 |
634 | tx = htp_list_get(((htpy_connp *) self)->connp->conn->transactions, htp_list_size(((htpy_connp *) self)->connp->conn->transactions) - 1);
635 | if (!tx || !tx->request_method) {
636 | PyErr_SetString(htpy_error, "Missing transaction or request method.");
637 | return NULL;
638 | }
639 |
640 | ret = Py_BuildValue("s#", bstr_ptr(tx->request_method), bstr_len(tx->request_method));
641 |
642 | return ret;
643 | }
644 |
645 | static PyObject *htpy_connp_set_obj(PyObject *self, PyObject *args) {
646 | PyObject *obj;
647 |
648 | if (!PyArg_ParseTuple(args, "O:htpy_connp_set_obj", &obj))
649 | return NULL;
650 |
651 | /*
652 | * Remove a reference to any existing object. This ensures we
653 | * do not leak objects in the case of someone calling this
654 | * multiple times.
655 | */
656 | Py_XDECREF(((htpy_connp *) self)->obj_store);
657 |
658 | Py_XINCREF(obj);
659 | ((htpy_connp *) self)->obj_store = obj;
660 |
661 | Py_RETURN_NONE;
662 | }
663 |
664 | static PyObject *htpy_connp_del_obj(PyObject *self, PyObject *args) {
665 | Py_XDECREF(((htpy_connp *) self)->obj_store);
666 | ((htpy_connp *) self)->obj_store = NULL;
667 |
668 | Py_RETURN_NONE;
669 | }
670 |
671 | /*
672 | * XXX: Not sure I like mucking around in the transaction to get the status,
673 | * but I'm not sure of a better way.
674 | */
675 | static PyObject *htpy_connp_get_response_status_string(PyObject *self, PyObject *args) {
676 | PyObject *ret;
677 | htp_tx_t *tx = NULL;
678 |
679 | tx = htp_list_get(((htpy_connp *) self)->connp->conn->transactions, htp_list_size(((htpy_connp *) self)->connp->conn->transactions) - 1);
680 | if (!tx) {
681 | PyErr_SetString(htpy_error, "Missing transaction.");
682 | return NULL;
683 | }
684 |
685 | ret = Py_BuildValue("s#", bstr_ptr(tx->response_status), bstr_len(tx->response_status));
686 |
687 | return ret;
688 | }
689 |
690 | static PyObject *htpy_connp_get_response_status(PyObject *self, PyObject *args) {
691 | PyObject *ret;
692 | htp_tx_t *tx = NULL;
693 |
694 | tx = htp_list_get(((htpy_connp *) self)->connp->conn->transactions, htp_list_size(((htpy_connp *) self)->connp->conn->transactions) - 1);
695 | if (!tx) {
696 | PyErr_SetString(htpy_error, "Missing transaction.");
697 | return NULL;
698 | }
699 |
700 | ret = Py_BuildValue("i", tx->response_status_number);
701 |
702 | return ret;
703 | }
704 |
705 | static PyObject *htpy_connp_get_response_line(PyObject *self, PyObject *args) {
706 | PyObject *ret;
707 |
708 | if (!((htpy_connp *) self)->connp->out_tx)
709 | Py_RETURN_NONE;
710 |
711 | if (!((htpy_connp *) self)->connp->out_tx->response_line)
712 | Py_RETURN_NONE;
713 |
714 | ret = Py_BuildValue("s#", bstr_ptr(((htpy_connp *) self)->connp->out_tx->response_line), bstr_len(((htpy_connp *) self)->connp->out_tx->response_line));
715 | return ret;
716 | }
717 |
718 | static PyObject *htpy_connp_get_request_line(PyObject *self, PyObject *args) {
719 | PyObject *ret;
720 |
721 | if (!((htpy_connp *) self)->connp->in_tx)
722 | Py_RETURN_NONE;
723 |
724 | if (!((htpy_connp *) self)->connp->in_tx->request_line)
725 | Py_RETURN_NONE;
726 |
727 | ret = Py_BuildValue("s#", bstr_ptr(((htpy_connp *) self)->connp->in_tx->request_line), bstr_len(((htpy_connp *) self)->connp->in_tx->request_line));
728 | return ret;
729 | }
730 |
731 | /* See HTTP 1.1 RFC 4.3 Message Body */
732 |
733 | /*
734 | * The length of the response message-body. In most cases, this value
735 | * will be the same as response_entity_len. The values will be different
736 | * if response compression or chunking were applied. In that case,
737 | * response_message_len contains the length of the response body as it
738 | * has been seen over TCP; response_entity_len contains the length after
739 | * de-chunking and decompression.
740 | */
741 | static PyObject *htpy_connp_get_response_message_length(PyObject *self, PyObject *args) {
742 | PyObject *ret;
743 |
744 | if (!((htpy_connp *) self)->connp->out_tx)
745 | Py_RETURN_NONE;
746 |
747 | if (!((htpy_connp *) self)->connp->out_tx->response_message_len)
748 | Py_RETURN_NONE;
749 |
750 | ret = Py_BuildValue("i", ((htpy_connp *) self)->connp->out_tx->response_message_len);
751 | return ret;
752 | }
753 |
754 | /*
755 | * The length of the request message-body. In most cases, this value
756 | * will be the same as request_entity_len. The values will be different
757 | * if request compression or chunking were applied. In that case,
758 | * request_message_len contains the length of the request body as it
759 | * has been seen over TCP; request_entity_len contains length after
760 | * de-chunking and decompression.
761 | */
762 | static PyObject *htpy_connp_get_request_message_length(PyObject *self, PyObject *args) {
763 | PyObject *ret;
764 |
765 | if (!((htpy_connp *) self)->connp->in_tx)
766 | Py_RETURN_NONE;
767 |
768 | if (!((htpy_connp *) self)->connp->in_tx->request_message_len)
769 | Py_RETURN_NONE;
770 |
771 | ret = Py_BuildValue("i", ((htpy_connp *) self)->connp->in_tx->request_message_len);
772 | return ret;
773 | }
774 |
775 | /*
776 | * The length of the response entity-body. In most cases, this value
777 | * will be the same as response_message_len. The values will be different
778 | * if request compression or chunking were applied. In that case,
779 | * response_message_len contains the length of the response body as it
780 | * has been seen over TCP; response_entity_len contains length after
781 | * de-chunking and decompression.
782 | */
783 | static PyObject *htpy_connp_get_response_entity_length(PyObject *self, PyObject *args) {
784 | PyObject *ret;
785 |
786 | if (!((htpy_connp *) self)->connp->out_tx)
787 | Py_RETURN_NONE;
788 |
789 | if (!((htpy_connp *) self)->connp->out_tx->response_entity_len)
790 | Py_RETURN_NONE;
791 |
792 | ret = Py_BuildValue("i", ((htpy_connp *) self)->connp->out_tx->response_entity_len);
793 | return ret;
794 | }
795 |
796 | /*
797 | * The length of the request entity-body. In most cases, this value
798 | * will be the same as request_message_len. The values will be different
799 | * if request compression or chunking were applied. In that case,
800 | * request_message_len contains the length of the request body as it
801 | * has been seen over TCP; request_entity_len contains length after
802 | * de-chunking and decompression.
803 | */
804 | static PyObject *htpy_connp_get_request_entity_length(PyObject *self, PyObject *args) {
805 | PyObject *ret;
806 |
807 | if (!((htpy_connp *) self)->connp->in_tx)
808 | Py_RETURN_NONE;
809 |
810 | if (!((htpy_connp *) self)->connp->in_tx->request_entity_len)
811 | Py_RETURN_NONE;
812 |
813 | ret = Py_BuildValue("i", ((htpy_connp *) self)->connp->in_tx->request_entity_len);
814 | return ret;
815 | }
816 |
817 | /* These do the actual parsing. */
818 | #define DATA(TYPE) \
819 | static PyObject *htpy_connp_##TYPE##_data(PyObject *self, PyObject *args) { \
820 | const char *data; \
821 | PyObject *ret; \
822 | int x; \
823 | int len; \
824 | if (!PyArg_ParseTuple(args, "s#:htpy_connp_##TYPE##_data", &data, &len)) \
825 | return NULL; \
826 | x = htp_connp_##TYPE##_data(((htpy_connp *) self)->connp, NULL, (unsigned char *) data, len); \
827 | if (x == HTP_STREAM_ERROR) { \
828 | PyErr_SetString(htpy_error, "Stream error."); \
829 | return NULL; \
830 | } \
831 | if (x == HTP_STREAM_STOP) { \
832 | PyErr_SetString(htpy_stop, "Stream stop."); \
833 | return NULL; \
834 | } \
835 | ret = PyInt_FromLong((long) x); \
836 | return(ret); \
837 | }
838 |
839 | DATA(req)
840 | DATA(res)
841 |
842 | #define DATA_CONSUMED(TYPE) \
843 | static PyObject *htpy_connp_##TYPE##_data_consumed(PyObject *self, PyObject *args) { \
844 | PyObject *ret; \
845 | ret = Py_BuildValue("I", htp_connp_##TYPE##_data_consumed(((htpy_connp *) self)->connp)); \
846 | return(ret); \
847 | }
848 |
849 | DATA_CONSUMED(req)
850 | DATA_CONSUMED(res)
851 |
852 | static PyObject *htpy_connp_get_last_error(PyObject *self, PyObject *args) {
853 | htp_log_t *err = NULL;
854 | PyObject *ret;
855 |
856 | err = htp_connp_get_last_error(((htpy_connp *) self)->connp);
857 | if (!err)
858 | Py_RETURN_NONE;
859 |
860 | ret = Py_BuildValue("{sisssssi}", "level", err->level, "msg", err->msg, "file", err->file, "line", err->line);
861 |
862 | return(ret);
863 | }
864 |
865 | static PyObject *htpy_connp_clear_error(PyObject *self, PyObject *args) {
866 | htp_connp_clear_error(((htpy_connp *) self)->connp);
867 | Py_RETURN_NONE;
868 | }
869 |
870 | static PyObject *htpy_connp_get_request_protocol(PyObject *self, PyObject *args) {
871 | PyObject *ret;
872 |
873 | if (!((htpy_connp *) self)->connp->in_tx)
874 | Py_RETURN_NONE;
875 |
876 | if (!((htpy_connp *) self)->connp->in_tx->request_protocol)
877 | Py_RETURN_NONE;
878 |
879 | ret = Py_BuildValue("s#", bstr_ptr(((htpy_connp *) self)->connp->in_tx->request_protocol), bstr_len(((htpy_connp *) self)->connp->in_tx->request_protocol));
880 | return ret;
881 | }
882 |
883 | static PyObject *htpy_connp_get_request_protocol_number(PyObject *self, PyObject *args) {
884 | PyObject *ret;
885 |
886 | if (!((htpy_connp *) self)->connp->in_tx)
887 | Py_RETURN_NONE;
888 |
889 | if (!((htpy_connp *) self)->connp->in_tx->request_protocol_number)
890 | Py_RETURN_NONE;
891 |
892 | ret = Py_BuildValue("i", ((htpy_connp *) self)->connp->in_tx->request_protocol_number);
893 | return ret;
894 | }
895 |
896 | static PyObject *htpy_connp_get_response_protocol(PyObject *self, PyObject *args) {
897 | PyObject *ret;
898 |
899 | if (!((htpy_connp *) self)->connp->out_tx)
900 | Py_RETURN_NONE;
901 |
902 | if (!((htpy_connp *) self)->connp->out_tx->response_protocol)
903 | Py_RETURN_NONE;
904 |
905 | ret = Py_BuildValue("s#", bstr_ptr(((htpy_connp *) self)->connp->out_tx->response_protocol), bstr_len(((htpy_connp *) self)->connp->out_tx->response_protocol));
906 | return ret;
907 | }
908 |
909 | static PyObject *htpy_connp_get_response_protocol_number(PyObject *self, PyObject *args) {
910 | PyObject *ret;
911 |
912 | if (!((htpy_connp *) self)->connp->out_tx)
913 | Py_RETURN_NONE;
914 |
915 | if (!((htpy_connp *) self)->connp->out_tx->response_protocol_number)
916 | Py_RETURN_NONE;
917 |
918 | ret = Py_BuildValue("i", ((htpy_connp *) self)->connp->out_tx->response_protocol_number);
919 | return ret;
920 | }
921 |
922 | static PyObject *htpy_connp_get_uri(PyObject *self, PyObject *args) {
923 | htp_uri_t *uri;
924 | int fail = 0;
925 | PyObject *key, *val;
926 | PyObject *ret = PyDict_New();
927 |
928 | if (!ret) {
929 | PyErr_SetString(htpy_error, "Unable to create new dictionary.");
930 | return NULL;
931 | }
932 |
933 | /* Empty tx? That's odd. */
934 | if (!((htpy_connp *) self)->connp->in_tx)
935 | Py_RETURN_NONE;
936 |
937 | if (!((htpy_connp *) self)->connp->in_tx->parsed_uri)
938 | Py_RETURN_NONE;
939 |
940 | uri = ((htpy_connp *) self)->connp->in_tx->parsed_uri;
941 |
942 | if (uri->scheme) {
943 | key = Py_BuildValue("s", "scheme");
944 | val = Py_BuildValue("s", bstr_util_strdup_to_c(uri->scheme));
945 | if (!key || !val)
946 | fail = 1;
947 | if (PyDict_SetItem(ret, key, val) == -1)
948 | fail = 1;
949 | Py_XDECREF(key);
950 | Py_XDECREF(val);
951 | }
952 |
953 | if (uri->username) {
954 | key = Py_BuildValue("s", "username");
955 | val = Py_BuildValue("s", bstr_util_strdup_to_c(uri->username));
956 | if (!key || !val)
957 | fail = 1;
958 | if (PyDict_SetItem(ret, key, val) == -1)
959 | fail = 1;
960 | Py_XDECREF(key);
961 | Py_XDECREF(val);
962 | }
963 |
964 | if (uri->password) {
965 | key = Py_BuildValue("s", "password");
966 | val = Py_BuildValue("s", bstr_util_strdup_to_c(uri->password));
967 | if (!key || !val)
968 | fail = 1;
969 | if (PyDict_SetItem(ret, key, val) == -1)
970 | fail = 1;
971 | Py_XDECREF(key);
972 | Py_XDECREF(val);
973 | }
974 |
975 | if (uri->hostname) {
976 | key = Py_BuildValue("s", "hostname");
977 | val = Py_BuildValue("s", bstr_util_strdup_to_c(uri->hostname));
978 | if (!key || !val)
979 | fail = 1;
980 | if (PyDict_SetItem(ret, key, val) == -1)
981 | fail = 1;
982 | Py_XDECREF(key);
983 | Py_XDECREF(val);
984 | }
985 |
986 | if (uri->port) {
987 | key = Py_BuildValue("s", "port");
988 | val = Py_BuildValue("s", bstr_util_strdup_to_c(uri->port));
989 | if (!key || !val)
990 | fail = 1;
991 | if (PyDict_SetItem(ret, key, val) == -1)
992 | fail = 1;
993 | Py_XDECREF(key);
994 | Py_XDECREF(val);
995 | }
996 |
997 | if (uri->port_number) {
998 | key = Py_BuildValue("s", "port_number");
999 | val = Py_BuildValue("i", uri->port_number);
1000 | if (!key || !val)
1001 | fail = 1;
1002 | if (PyDict_SetItem(ret, key, val) == -1)
1003 | fail = 1;
1004 | Py_XDECREF(key);
1005 | Py_XDECREF(val);
1006 | }
1007 |
1008 | if (uri->path) {
1009 | key = Py_BuildValue("s", "path");
1010 | val = Py_BuildValue("s", bstr_util_strdup_to_c(uri->path));
1011 | if (!key || !val)
1012 | fail = 1;
1013 | if (PyDict_SetItem(ret, key, val) == -1)
1014 | fail = 1;
1015 | Py_XDECREF(key);
1016 | Py_XDECREF(val);
1017 | }
1018 |
1019 | if (uri->query) {
1020 | key = Py_BuildValue("s", "query");
1021 | val = Py_BuildValue("s", bstr_util_strdup_to_c(uri->query));
1022 | if (!key || !val)
1023 | fail = 1;
1024 | if (PyDict_SetItem(ret, key, val) == -1)
1025 | fail = 1;
1026 | Py_XDECREF(key);
1027 | Py_XDECREF(val);
1028 | }
1029 |
1030 | if (uri->fragment) {
1031 | key = Py_BuildValue("s", "fragment");
1032 | val = Py_BuildValue("s", bstr_util_strdup_to_c(uri->fragment));
1033 | if (!key || !val)
1034 | fail = 1;
1035 | if (PyDict_SetItem(ret, key, val) == -1)
1036 | fail = 1;
1037 | Py_XDECREF(key);
1038 | Py_XDECREF(val);
1039 | }
1040 |
1041 | // Exception should be set by Py_BuildValue or PyDict_SetItem failing.
1042 | if (fail) {
1043 | Py_DECREF(ret);
1044 | return NULL;
1045 | }
1046 |
1047 | return ret;
1048 | }
1049 |
1050 | static PyMethodDef htpy_connp_methods[] = {
1051 | { "get_request_header", htpy_connp_get_request_header, METH_VARARGS,
1052 | "Return a string for the requested header." },
1053 | { "get_response_header", htpy_connp_get_response_header, METH_VARARGS,
1054 | "Return a string for the requested header." },
1055 | { "get_all_request_headers", htpy_connp_get_all_request_headers,
1056 | METH_NOARGS, "Return a dictionary of all request headers." },
1057 | { "get_all_response_headers", htpy_connp_get_all_response_headers,
1058 | METH_NOARGS, "Return a dictionary of all response headers." },
1059 | { "get_response_status", htpy_connp_get_response_status, METH_VARARGS,
1060 | "Return the response status number as an integer." },
1061 | { "get_response_status_string", htpy_connp_get_response_status_string, METH_VARARGS,
1062 | "Return the response status as string." },
1063 | { "get_response_line", htpy_connp_get_response_line, METH_VARARGS,
1064 | "Return the response status line as string." },
1065 | { "get_request_line", htpy_connp_get_request_line, METH_VARARGS,
1066 | "Return the request line as string." },
1067 | { "register_request_start", htpy_connp_register_request_start,
1068 | METH_VARARGS, "Register a hook for start of a request." },
1069 | { "register_request_line", htpy_connp_register_request_line, METH_VARARGS,
1070 | "Register a hook for right after request line has been parsed." },
1071 | { "register_request_uri_normalize",
1072 | htpy_connp_register_request_uri_normalize, METH_VARARGS,
1073 | "Register a hook for right before the URI is normalized." },
1074 | { "get_response_message_length", htpy_connp_get_response_message_length, METH_VARARGS,
1075 | "Return the response message length before decompressed and dechunked." },
1076 | { "get_request_message_length", htpy_connp_get_request_message_length, METH_VARARGS,
1077 | "Return the request message length before decompressed and dechunked." },
1078 | { "get_response_entity_length", htpy_connp_get_response_entity_length, METH_VARARGS,
1079 | "Return the response message length after decomressed and dechunked." },
1080 | { "get_request_entity_length", htpy_connp_get_request_entity_length, METH_VARARGS,
1081 | "Return the request message length after decompressed and dechunked." },
1082 | { "register_request_headers", htpy_connp_register_request_headers,
1083 | METH_VARARGS,
1084 | "Register a hook for right after headers have been parsed and sanity checked." },
1085 | { "register_request_header_data", htpy_connp_register_request_header_data,
1086 | METH_VARARGS,
1087 | "Register a hook for right as headers are being parsed and sanity checked." },
1088 | { "register_request_body_data", htpy_connp_register_request_body_data,
1089 | METH_VARARGS,
1090 | "Register a hook for when a piece of request body data is processed." },
1091 | { "register_request_file_data", htpy_connp_register_request_file_data,
1092 | METH_VARARGS,
1093 | "Register a hook for when a full request body data is processed." },
1094 | { "register_request_trailer", htpy_connp_register_request_trailer,
1095 | METH_VARARGS,
1096 | "Register a hook for request trailer completion." },
1097 | { "register_request_trailer_data", htpy_connp_register_request_trailer_data,
1098 | METH_VARARGS,
1099 | "Register a hook request trialer data." },
1100 | { "register_request_complete", htpy_connp_register_request_complete, METH_VARARGS,
1101 | "Register a callback for when the entire request is parsed." },
1102 | { "register_response_start", htpy_connp_register_response_start,
1103 | METH_VARARGS,
1104 | "Register a hook for as soon as a response is about to start." },
1105 | { "register_response_line", htpy_connp_register_response_line,
1106 | METH_VARARGS,
1107 | "Register a hook for right after response line has been parsed." },
1108 | { "register_response_headers", htpy_connp_register_response_headers,
1109 | METH_VARARGS, "Register a hook for right after headers have been parsed and sanity checked." },
1110 | { "register_response_header_data", htpy_connp_register_response_header_data,
1111 | METH_VARARGS, "Register a hook for right as headers have been parsed and sanity checked." },
1112 | { "register_response_body_data", htpy_connp_register_response_body_data,
1113 | METH_VARARGS,
1114 | "Register a hook for when a piece of response body data is processed. Chunked and gzip'ed data are handled." },
1115 | { "register_response_trailer", htpy_connp_register_response_trailer,
1116 | METH_VARARGS,
1117 | "Register a hook for response trailer completion." },
1118 | { "register_response_trailer_data", htpy_connp_register_response_trailer_data,
1119 | METH_VARARGS,
1120 | "Register a hook for response trailer data." },
1121 | { "register_response_complete", htpy_connp_register_response_complete, METH_VARARGS,
1122 | "Register a hook for right after an entire response has been parsed." },
1123 | { "register_transaction_complete", htpy_connp_register_transaction_complete, METH_VARARGS,
1124 | "Register a hook for right after a transaction has completed." },
1125 | { "register_log", htpy_connp_register_log, METH_VARARGS,
1126 | "Register a callback for when a log message is generated." },
1127 | { "set_obj", htpy_connp_set_obj, METH_VARARGS,
1128 | "Set arbitrary python object to be passed to callbacks." },
1129 | { "del_obj", htpy_connp_del_obj, METH_VARARGS,
1130 | "Remove arbitrary python object being passed to callbacks." },
1131 | { "req_data", htpy_connp_req_data, METH_VARARGS, "Parse a request." },
1132 | { "req_data_consumed", htpy_connp_req_data_consumed, METH_NOARGS,
1133 | "Return amount of data consumed." },
1134 | { "res_data", htpy_connp_res_data, METH_VARARGS, "Parse a response." },
1135 | { "res_data_consumed", htpy_connp_res_data_consumed, METH_NOARGS,
1136 | "Return amount of data consumed." },
1137 | { "get_last_error", htpy_connp_get_last_error, METH_NOARGS,
1138 | "Return a dictionary of the last error for the parser." },
1139 | { "clear_error", htpy_connp_clear_error, METH_NOARGS,
1140 | "Clear last error for the parser." },
1141 | { "get_request_protocol", htpy_connp_get_request_protocol, METH_NOARGS,
1142 | "Return request protocol as a string." },
1143 | { "get_request_protocol_number", htpy_connp_get_request_protocol_number, METH_NOARGS,
1144 | "Return request protocol number." },
1145 | { "get_response_protocol", htpy_connp_get_response_protocol, METH_NOARGS,
1146 | "Return response protocol as a string." },
1147 | { "get_response_protocol_number", htpy_connp_get_response_protocol_number, METH_NOARGS,
1148 | "Return response protocol number." },
1149 | { "get_uri", htpy_connp_get_uri, METH_NOARGS,
1150 | "Return a dictionary of the URI." },
1151 | { "get_method", htpy_connp_get_method, METH_NOARGS,
1152 | "Return the request method as a string." },
1153 | { NULL }
1154 | };
1155 |
1156 | static PyMemberDef htpy_connp_members[] = {
1157 | { "cfg", T_OBJECT_EX, offsetof(htpy_connp, cfg), 0, "Configuration object"},
1158 | { NULL }
1159 | };
1160 |
1161 | static PyTypeObject htpy_connp_type = {
1162 | PyObject_HEAD_INIT(NULL)
1163 | 0, /* ob_size */
1164 | "htpy.connp", /* tp_name */
1165 | sizeof(htpy_connp), /* tp_basicsize */
1166 | 0, /* tp_itemsize */
1167 | (destructor) htpy_connp_dealloc, /* tp_dealloc */
1168 | 0, /* tp_print */
1169 | 0, /* tp_getattr */
1170 | 0, /* tp_setattr */
1171 | 0, /* tp_compare */
1172 | 0, /* tp_repr */
1173 | 0, /* tp_as_number */
1174 | 0, /* tp_as_sequence */
1175 | 0, /* tp_as_mapping */
1176 | 0, /* tp_hash */
1177 | 0, /* tp_call */
1178 | 0, /* tp_str */
1179 | 0, /* tp_getattro */
1180 | 0, /* tp_setattro */
1181 | 0, /* tp_as_buffer */
1182 | Py_TPFLAGS_DEFAULT, /* tp_flags */
1183 | "connp object", /* tp_doc */
1184 | 0, /* tp_traverse */
1185 | 0, /* tp_clear */
1186 | 0, /* tp_richcompare */
1187 | 0, /* tp_weaklistoffset */
1188 | 0, /* tp_iter */
1189 | 0, /* tp_iternext */
1190 | htpy_connp_methods, /* tp_methods */
1191 | htpy_connp_members, /* tp_members */
1192 | 0, /* tp_getset */
1193 | 0, /* tp_base */
1194 | 0, /* tp_dict */
1195 | 0, /* tp_descr_get */
1196 | 0, /* tp_descr_set */
1197 | 0, /* tp_dictoffset */
1198 | (initproc) htpy_connp_init, /* tp_init */
1199 | 0, /* tp_alloc */
1200 | htpy_connp_new, /* tp_new */
1201 | };
1202 |
1203 | static PyObject *htpy_init(PyObject *self, PyObject *args) {
1204 | PyObject *connp;
1205 |
1206 | connp = PyObject_CallObject((PyObject *) &htpy_connp_type, NULL);
1207 |
1208 | return(connp);
1209 | }
1210 |
1211 | static PyMethodDef htpy_methods[] = {
1212 | { "init", htpy_init, METH_VARARGS,
1213 | "Return a parser object with default config." },
1214 | { NULL }
1215 | };
1216 |
1217 | PyMODINIT_FUNC inithtpy(void) {
1218 | PyObject *m;
1219 |
1220 | if (PyType_Ready(&htpy_config_type) < 0 || PyType_Ready(&htpy_connp_type) < 0)
1221 | return;
1222 |
1223 | m = Py_InitModule3("htpy", htpy_methods, "Python interface to libhtp.");
1224 | if (!m)
1225 | return;
1226 |
1227 | htpy_error = PyErr_NewException("htpy.error", NULL, NULL);
1228 | Py_INCREF(htpy_error);
1229 | PyModule_AddObject(m, "error", htpy_error);
1230 |
1231 | htpy_stop = PyErr_NewException("htpy.stop", NULL, NULL);
1232 | Py_INCREF(htpy_stop);
1233 | PyModule_AddObject(m, "stop", htpy_stop);
1234 |
1235 | Py_INCREF(&htpy_config_type);
1236 | PyModule_AddObject(m, "config", (PyObject *) &htpy_config_type);
1237 | Py_INCREF(&htpy_connp_type);
1238 | PyModule_AddObject(m, "connp", (PyObject *) &htpy_connp_type);
1239 |
1240 | PyModule_AddStringMacro(m, HTPY_VERSION);
1241 |
1242 | PyModule_AddIntMacro(m, HTP_ERROR);
1243 | PyModule_AddIntMacro(m, HTP_OK);
1244 | PyModule_AddIntMacro(m, HTP_STOP);
1245 | PyModule_AddIntMacro(m, HTP_DATA);
1246 | PyModule_AddIntMacro(m, HTP_DATA_OTHER);
1247 | PyModule_AddIntMacro(m, HTP_DECLINED);
1248 |
1249 | PyModule_AddIntMacro(m, HTP_PROTOCOL_UNKNOWN);
1250 | PyModule_AddIntMacro(m, HTP_PROTOCOL_0_9);
1251 | PyModule_AddIntMacro(m, HTP_PROTOCOL_1_0);
1252 | PyModule_AddIntMacro(m, HTP_PROTOCOL_1_1);
1253 |
1254 | PyModule_AddIntMacro(m, HTP_COMPRESSION_NONE);
1255 | PyModule_AddIntMacro(m, HTP_COMPRESSION_GZIP);
1256 | PyModule_AddIntMacro(m, HTP_COMPRESSION_DEFLATE);
1257 |
1258 | PyModule_AddIntMacro(m, HTP_LOG_ERROR);
1259 | PyModule_AddIntMacro(m, HTP_LOG_WARNING);
1260 | PyModule_AddIntMacro(m, HTP_LOG_NOTICE);
1261 | PyModule_AddIntMacro(m, HTP_LOG_INFO);
1262 | PyModule_AddIntMacro(m, HTP_LOG_DEBUG);
1263 | PyModule_AddIntMacro(m, HTP_LOG_DEBUG2);
1264 |
1265 | PyModule_AddIntMacro(m, HTP_STREAM_NEW);
1266 | PyModule_AddIntMacro(m, HTP_STREAM_OPEN);
1267 | PyModule_AddIntMacro(m, HTP_STREAM_CLOSED);
1268 | PyModule_AddIntMacro(m, HTP_STREAM_ERROR);
1269 | PyModule_AddIntMacro(m, HTP_STREAM_TUNNEL);
1270 | PyModule_AddIntMacro(m, HTP_STREAM_DATA_OTHER);
1271 | PyModule_AddIntMacro(m, HTP_STREAM_DATA);
1272 | PyModule_AddIntMacro(m, HTP_STREAM_STOP);
1273 | }
1274 |
--------------------------------------------------------------------------------
/libhtp-0.5.31.tar.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MITRECND/htpy/169eba2bf58d3b1c78a1843f53a778316248fa41/libhtp-0.5.31.tar.gz
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python
2 |
3 | from distutils.core import setup, Extension
4 | from distutils.command.build import build
5 | from distutils.spawn import spawn
6 | import os, os.path
7 |
8 | pathjoin = os.path.join
9 |
10 | GITVER = '0.5.31'
11 | PKGNAME = 'libhtp-' + GITVER
12 | PKGTAR = PKGNAME + '.tar.gz'
13 | BUILDDIR = 'libhtp-' + GITVER
14 |
15 | INCLUDE_DIRS = ['/usr/local/include', '/opt/local/include', '/usr/include']
16 | LIBRARY_DIRS = ['/usr/lib', '/usr/local/lib']
17 | EXTRA_OBJECTS = ['-lz']
18 |
19 | class htpyMaker(build):
20 | HTPTAR = PKGTAR
21 | HTPDIR = BUILDDIR
22 | include_dirs = [ HTPDIR, pathjoin(HTPDIR, 'htp') ]
23 | library_dirs = []
24 | extra_objects = [ pathjoin(HTPDIR, 'htp/.libs', 'libhtp.a') ]
25 | libhtp = pathjoin(HTPDIR, 'htp/.libs', 'libhtp.a')
26 | uname = os.uname()[0]
27 | if uname != 'Linux':
28 | EXTRA_OBJECTS.append('-liconv')
29 |
30 | def buildHtp(self):
31 | # extremely crude package builder
32 | try:
33 | os.stat(self.libhtp)
34 | return None # assume already built
35 | except OSError:
36 | pass
37 |
38 | spawn(['tar', '-zxf', self.HTPTAR], search_path = 1)
39 | os.chdir(self.HTPDIR)
40 | spawn([pathjoin('.','autogen.sh')], '-i')
41 | spawn([pathjoin('.','configure'), 'CFLAGS=-fPIC'])
42 | spawn(['make'], search_path = 1)
43 | os.chdir('..')
44 |
45 | def run(self):
46 | self.buildHtp()
47 | build.run(self)
48 |
49 | INCLUDE_DIRS = htpyMaker.include_dirs + INCLUDE_DIRS
50 | EXTRA_OBJECTS = htpyMaker.extra_objects + EXTRA_OBJECTS
51 |
52 | setup (# Distribution meta-data
53 | name = "htpy",
54 | version = "0.26",
55 | description = "python bindings for libhtp",
56 | author = "Wesley Shields",
57 | author_email = "wxs@atarininja.org",
58 | license = "BSD",
59 | long_description = "Python bindings for libhtp",
60 | cmdclass = {'build': htpyMaker},
61 | ext_modules = [Extension("htpy",
62 | sources=["htpy.c"],
63 | include_dirs = INCLUDE_DIRS,
64 | library_dirs = LIBRARY_DIRS,
65 | extra_objects = EXTRA_OBJECTS)],
66 | url = "http://github.com/MITRECND/htpy")
67 |
--------------------------------------------------------------------------------