├── .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 | --------------------------------------------------------------------------------