Coverage Report

Created: 2026-03-31 07:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/libhtp/htp/htp_transaction.c
Line
Count
Source
1
/***************************************************************************
2
 * Copyright (c) 2009-2010 Open Information Security Foundation
3
 * Copyright (c) 2010-2013 Qualys, Inc.
4
 * All rights reserved.
5
 * 
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions are
8
 * met:
9
 * 
10
 * - Redistributions of source code must retain the above copyright
11
 *   notice, this list of conditions and the following disclaimer.
12
13
 * - Redistributions in binary form must reproduce the above copyright
14
 *   notice, this list of conditions and the following disclaimer in the
15
 *   documentation and/or other materials provided with the distribution.
16
17
 * - Neither the name of the Qualys, Inc. nor the names of its
18
 *   contributors may be used to endorse or promote products derived from
19
 *   this software without specific prior written permission.
20
 * 
21
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
 ***************************************************************************/
33
34
/**
35
 * @file
36
 * @author Ivan Ristic <ivanr@webkreator.com>
37
 */
38
39
#include "htp_config_auto.h"
40
41
#include "htp_private.h"
42
43
static void htp_tx_req_destroy_decompressors(htp_connp_t *connp);
44
static htp_status_t htp_tx_req_process_body_data_decompressor_callback(htp_tx_data_t *d);
45
46
0
static bstr *copy_or_wrap_mem(const void *data, size_t len, enum htp_alloc_strategy_t alloc) {
47
0
    if (data == NULL) return NULL;
48
49
0
    if (alloc == HTP_ALLOC_REUSE) {
50
0
        return bstr_wrap_mem(data, len);
51
0
    } else {
52
0
        return bstr_dup_mem(data, len);
53
0
    }
54
0
}
55
56
272k
htp_tx_t *htp_tx_create(htp_connp_t *connp) {
57
272k
    if (connp == NULL) return NULL;
58
59
272k
    htp_tx_t *tx = calloc(1, sizeof (htp_tx_t));
60
272k
    if (tx == NULL) return NULL;
61
62
272k
    tx->connp = connp;
63
272k
    tx->conn = connp->conn;
64
272k
    tx->index = htp_list_size(tx->conn->transactions);
65
272k
    tx->cfg = connp->cfg;
66
272k
    tx->is_config_shared = HTP_CONFIG_SHARED;
67
68
    // Request fields.
69
70
272k
    tx->request_progress = HTP_REQUEST_NOT_STARTED;
71
272k
    tx->request_protocol_number = HTP_PROTOCOL_UNKNOWN;
72
272k
    tx->request_content_length = -1;
73
74
272k
    tx->parsed_uri_raw = htp_uri_alloc();
75
272k
    if (tx->parsed_uri_raw == NULL) {
76
0
        htp_tx_destroy_incomplete(tx);
77
0
        return NULL;
78
0
    }
79
80
272k
    tx->request_headers = htp_table_create(32);
81
272k
    if (tx->request_headers == NULL) {
82
0
        htp_tx_destroy_incomplete(tx);
83
0
        return NULL;
84
0
    }
85
86
272k
    tx->request_params = htp_table_create(32);
87
272k
    if (tx->request_params == NULL) {
88
0
        htp_tx_destroy_incomplete(tx);
89
0
        return NULL;
90
0
    }
91
92
    // Response fields.
93
94
272k
    tx->response_progress = HTP_RESPONSE_NOT_STARTED;
95
272k
    tx->response_status = NULL;
96
272k
    tx->response_status_number = HTP_STATUS_UNKNOWN;
97
272k
    tx->response_protocol_number = HTP_PROTOCOL_UNKNOWN;
98
272k
    tx->response_content_length = -1;
99
100
272k
    tx->response_headers = htp_table_create(32);
101
272k
    if (tx->response_headers == NULL) {
102
0
        htp_tx_destroy_incomplete(tx);
103
0
        return NULL;
104
0
    }
105
106
272k
    htp_list_add(tx->conn->transactions, tx);
107
108
272k
    return tx;
109
272k
}
110
111
79.0k
htp_status_t htp_tx_destroy(htp_tx_t *tx) {
112
79.0k
    if (tx == NULL) return HTP_ERROR;
113
114
79.0k
    if (!htp_tx_is_complete(tx)) return HTP_ERROR;
115
116
79.0k
    htp_tx_destroy_incomplete(tx);
117
118
79.0k
    return HTP_OK;
119
79.0k
}
120
121
272k
void htp_tx_destroy_incomplete(htp_tx_t *tx) {
122
272k
    if (tx == NULL) return;
123
124
    // Disconnect transaction from other structures.
125
272k
    htp_conn_remove_tx(tx->conn, tx);
126
272k
    htp_connp_tx_remove(tx->connp, tx);
127
128
    // Request fields.
129
130
272k
    bstr_free(tx->request_line);
131
272k
    bstr_free(tx->request_method);
132
272k
    bstr_free(tx->request_uri);
133
272k
    bstr_free(tx->request_protocol);
134
272k
    bstr_free(tx->request_content_type);
135
272k
    bstr_free(tx->request_hostname);
136
272k
    htp_uri_free(tx->parsed_uri_raw);
137
272k
    htp_uri_free(tx->parsed_uri);
138
272k
    bstr_free(tx->request_auth_username);
139
272k
    bstr_free(tx->request_auth_password);
140
141
    // Request_headers.
142
272k
    if (tx->request_headers != NULL) {
143
272k
        htp_header_t *h = NULL;
144
594k
        for (size_t i = 0, n = htp_table_size(tx->request_headers); i < n; i++) {
145
322k
            h = htp_table_get_index(tx->request_headers, i, NULL);
146
322k
            bstr_free(h->name);
147
322k
            bstr_free(h->value);
148
322k
            free(h);
149
322k
        }
150
151
272k
        htp_table_destroy(tx->request_headers);
152
272k
    }
153
154
    // Request parsers.
155
156
272k
    htp_urlenp_destroy(tx->request_urlenp_query);
157
272k
    htp_urlenp_destroy(tx->request_urlenp_body);
158
272k
    htp_mpartp_destroy(tx->request_mpartp);
159
160
    // Request parameters.
161
162
272k
    htp_param_t *param = NULL;
163
272k
    for (size_t i = 0, n = htp_table_size(tx->request_params); i < n; i++) {
164
0
        param = htp_table_get_index(tx->request_params, i, NULL);
165
0
        bstr_free(param->name);
166
0
        bstr_free(param->value);
167
0
        free(param);
168
0
    }
169
170
272k
    htp_table_destroy(tx->request_params);
171
172
    // Request cookies.
173
174
272k
    if (tx->request_cookies != NULL) {
175
0
        bstr *b = NULL;
176
0
        for (size_t i = 0, n = htp_table_size(tx->request_cookies); i < n; i++) {
177
0
            b = htp_table_get_index(tx->request_cookies, i, NULL);
178
0
            bstr_free(b);
179
0
        }
180
181
0
        htp_table_destroy(tx->request_cookies);
182
0
    }
183
184
272k
    htp_hook_destroy(tx->hook_request_body_data);
185
186
    // Response fields.
187
188
272k
    bstr_free(tx->response_line);
189
272k
    bstr_free(tx->response_protocol);
190
272k
    bstr_free(tx->response_status);
191
272k
    bstr_free(tx->response_message);
192
272k
    bstr_free(tx->response_content_type);
193
194
    // Destroy response headers.
195
272k
    if (tx->response_headers != NULL) {
196
272k
        htp_header_t *h = NULL;
197
505k
        for (size_t i = 0, n = htp_table_size(tx->response_headers); i < n; i++) {
198
232k
            h = htp_table_get_index(tx->response_headers, i, NULL);
199
232k
            bstr_free(h->name);
200
232k
            bstr_free(h->value);
201
232k
            free(h);
202
232k
        }
203
204
272k
        htp_table_destroy(tx->response_headers);
205
272k
    }
206
207
    // If we're using a private configuration structure, destroy it.
208
272k
    if (tx->is_config_shared == HTP_CONFIG_PRIVATE) {
209
0
        htp_config_destroy(tx->cfg);
210
0
    }
211
212
272k
    free(tx);
213
272k
}
214
215
0
int htp_tx_get_is_config_shared(const htp_tx_t *tx) {
216
0
    if (tx == NULL) return -1;
217
0
    return tx->is_config_shared;
218
0
}
219
220
129M
void *htp_tx_get_user_data(const htp_tx_t *tx) {
221
129M
    if (tx == NULL) return NULL;
222
129M
    return tx->user_data;
223
129M
}
224
225
0
void htp_tx_set_config(htp_tx_t *tx, htp_cfg_t *cfg, int is_cfg_shared) {
226
0
    if ((tx == NULL) || (cfg == NULL)) return;
227
228
0
    if ((is_cfg_shared != HTP_CONFIG_PRIVATE) && (is_cfg_shared != HTP_CONFIG_SHARED)) return;
229
230
    // If we're using a private configuration, destroy it.
231
0
    if (tx->is_config_shared == HTP_CONFIG_PRIVATE) {
232
0
        htp_config_destroy(tx->cfg);
233
0
    }
234
235
0
    tx->cfg = cfg;
236
0
    tx->is_config_shared = is_cfg_shared;
237
0
}
238
239
544k
void htp_tx_set_user_data(htp_tx_t *tx, void *user_data) {
240
544k
    if (tx == NULL) return;
241
544k
    tx->user_data = user_data;
242
544k
}
243
244
0
htp_status_t htp_tx_req_add_param(htp_tx_t *tx, htp_param_t *param) {
245
0
    if ((tx == NULL) || (param == NULL)) return HTP_ERROR;
246
247
0
    if (tx->cfg->parameter_processor != NULL) {
248
0
        if (tx->cfg->parameter_processor(param) != HTP_OK) return HTP_ERROR;
249
0
    }
250
251
0
    return htp_table_addk(tx->request_params, param->name, param);
252
0
}
253
254
0
htp_param_t *htp_tx_req_get_param(htp_tx_t *tx, const char *name, size_t name_len) {
255
0
    if ((tx == NULL) || (name == NULL)) return NULL;
256
0
    return htp_table_get_mem(tx->request_params, name, name_len);
257
0
}
258
259
0
htp_param_t *htp_tx_req_get_param_ex(htp_tx_t *tx, enum htp_data_source_t source, const char *name, size_t name_len) {
260
0
    if ((tx == NULL) || (name == NULL)) return NULL;
261
262
0
    htp_param_t *p = NULL;
263
264
0
    for (size_t i = 0, n = htp_table_size(tx->request_params); i < n; i++) {
265
0
        p = htp_table_get_index(tx->request_params, i, NULL);
266
0
        if (p->source != source) continue;
267
268
0
        if (bstr_cmp_mem_nocase(p->name, name, name_len) == 0) return p;
269
0
    }
270
271
0
    return NULL;
272
0
}
273
274
254k
int htp_tx_req_has_body(const htp_tx_t *tx) {
275
254k
    if (tx == NULL) return -1;
276
277
254k
    if ((tx->request_transfer_coding == HTP_CODING_IDENTITY) || (tx->request_transfer_coding == HTP_CODING_CHUNKED)) {
278
14.8k
        return 1;
279
14.8k
    }
280
281
240k
    return 0;
282
254k
}
283
284
htp_status_t htp_tx_req_set_header(htp_tx_t *tx, const char *name, size_t name_len,
285
0
        const char *value, size_t value_len, enum htp_alloc_strategy_t alloc) {
286
0
    if ((tx == NULL) || (name == NULL) || (value == NULL)) return HTP_ERROR;
287
288
0
    htp_header_t *h = calloc(1, sizeof (htp_header_t));
289
0
    if (h == NULL) return HTP_ERROR;
290
291
0
    h->name = copy_or_wrap_mem(name, name_len, alloc);
292
0
    if (h->name == NULL) {
293
0
        free(h);
294
0
        return HTP_ERROR;
295
0
    }
296
297
0
    h->value = copy_or_wrap_mem(value, value_len, alloc);
298
0
    if (h->value == NULL) {
299
0
        bstr_free(h->name);
300
0
        free(h);
301
0
        return HTP_ERROR;
302
0
    }
303
304
0
    if (htp_table_add(tx->request_headers, h->name, h) != HTP_OK) {
305
0
        bstr_free(h->name);
306
0
        bstr_free(h->value);
307
0
        free(h);
308
0
        return HTP_ERROR;
309
0
    }
310
311
0
    return HTP_OK;
312
0
}
313
314
0
htp_status_t htp_tx_req_set_method(htp_tx_t *tx, const char *method, size_t method_len, enum htp_alloc_strategy_t alloc) {
315
0
    if ((tx == NULL) || (method == NULL)) return HTP_ERROR;
316
317
0
    tx->request_method = copy_or_wrap_mem(method, method_len, alloc);
318
0
    if (tx->request_method == NULL) return HTP_ERROR;
319
320
0
    return HTP_OK;
321
0
}
322
323
0
void htp_tx_req_set_method_number(htp_tx_t *tx, enum htp_method_t method_number) {
324
0
    if (tx == NULL) return;
325
0
    tx->request_method_number = method_number;
326
0
}
327
328
0
htp_status_t htp_tx_req_set_uri(htp_tx_t *tx, const char *uri, size_t uri_len, enum htp_alloc_strategy_t alloc) {
329
0
    if ((tx == NULL) || (uri == NULL)) return HTP_ERROR;
330
331
0
    tx->request_uri = copy_or_wrap_mem(uri, uri_len, alloc);
332
0
    if (tx->request_uri == NULL) return HTP_ERROR;
333
334
0
    return HTP_OK;
335
0
}
336
337
0
htp_status_t htp_tx_req_set_protocol(htp_tx_t *tx, const char *protocol, size_t protocol_len, enum htp_alloc_strategy_t alloc) {
338
0
    if ((tx == NULL) || (protocol == NULL)) return HTP_ERROR;
339
340
0
    tx->request_protocol = copy_or_wrap_mem(protocol, protocol_len, alloc);
341
0
    if (tx->request_protocol == NULL) return HTP_ERROR;
342
343
0
    return HTP_OK;
344
0
}
345
346
0
void htp_tx_req_set_protocol_number(htp_tx_t *tx, int protocol_number) {
347
0
    if (tx == NULL) return;
348
0
    tx->request_protocol_number = protocol_number;
349
0
}
350
351
0
void htp_tx_req_set_protocol_0_9(htp_tx_t *tx, int is_protocol_0_9) {
352
0
    if (tx == NULL) return;
353
354
0
    if (is_protocol_0_9) {
355
0
        tx->is_protocol_0_9 = 1;
356
0
    } else {
357
0
        tx->is_protocol_0_9 = 0;
358
0
    }
359
0
}
360
361
200k
static htp_status_t htp_tx_process_request_headers(htp_tx_t *tx) {
362
200k
    if (tx == NULL) return HTP_ERROR;
363
364
    // Determine if we have a request body, and how it is packaged.
365
366
200k
    htp_status_t rc = HTP_OK;
367
368
200k
    if (tx->connp->cfg->request_decompression_enabled) {
369
200k
        tx->request_content_encoding = HTP_COMPRESSION_NONE;
370
200k
        htp_header_t *ce = htp_table_get_c(tx->request_headers, "content-encoding");
371
200k
        if (ce != NULL) {
372
            /* fast paths: regular gzip and friends */
373
5.52k
            if ((bstr_cmp_c_nocasenorzero(ce->value, "gzip") == 0) ||
374
3.99k
                (bstr_cmp_c_nocasenorzero(ce->value, "x-gzip") == 0)) {
375
3.99k
                tx->request_content_encoding = HTP_COMPRESSION_GZIP;
376
3.99k
            } else if ((bstr_cmp_c_nocasenorzero(ce->value, "deflate") == 0) ||
377
1.09k
                       (bstr_cmp_c_nocasenorzero(ce->value, "x-deflate") == 0)) {
378
437
                tx->request_content_encoding = HTP_COMPRESSION_DEFLATE;
379
1.09k
            } else if (bstr_cmp_c_nocasenorzero(ce->value, "lzma") == 0) {
380
0
                tx->request_content_encoding = HTP_COMPRESSION_LZMA;
381
0
            }
382
            //ignore other cases such as inflate, ot multiple layers
383
5.52k
            if ((tx->request_content_encoding != HTP_COMPRESSION_NONE))
384
4.43k
            {
385
4.43k
                if (tx->connp->req_decompressor != NULL) {
386
2.67k
                    htp_tx_req_destroy_decompressors(tx->connp);
387
2.67k
                }
388
4.43k
                tx->connp->req_decompressor = htp_gzip_decompressor_create(tx->connp, tx->request_content_encoding);
389
4.43k
                if (tx->connp->req_decompressor == NULL)
390
0
                    return HTP_ERROR;
391
392
4.43k
                tx->connp->req_decompressor->callback = htp_tx_req_process_body_data_decompressor_callback;
393
4.43k
            }
394
5.52k
        }
395
200k
    }
396
397
200k
    htp_header_t *cl = htp_table_get_c(tx->request_headers, "content-length");
398
200k
    htp_header_t *te = htp_table_get_c(tx->request_headers, "transfer-encoding");
399
400
    // Check for the Transfer-Encoding header, which would indicate a chunked request body.
401
200k
    if (te != NULL) {
402
        // Make sure it contains "chunked" only.
403
        // TODO The HTTP/1.1 RFC also allows the T-E header to contain "identity", which
404
        //      presumably should have the same effect as T-E header absence. However, Apache
405
        //      (2.2.22 on Ubuntu 12.04 LTS) instead errors out with "Unknown Transfer-Encoding: identity".
406
        //      And it behaves strangely, too, sending a 501 and proceeding to process the request
407
        //      (e.g., PHP is run), but without the body. It then closes the connection.
408
1.97k
        if (htp_header_has_token(bstr_ptr(te->value), bstr_len(te->value), (unsigned char*) "chunked") != HTP_OK) {
409
            // Invalid T-E header value.
410
142
            tx->request_transfer_coding = HTP_CODING_INVALID;
411
142
            tx->flags |= HTP_REQUEST_INVALID_T_E;
412
142
            tx->flags |= HTP_REQUEST_INVALID;
413
1.83k
        } else {
414
            // Chunked encoding is a HTTP/1.1 feature, so check that an earlier protocol
415
            // version is not used. The flag will also be set if the protocol could not be parsed.
416
            //
417
            // TODO IIS 7.0, for example, would ignore the T-E header when it
418
            //      it is used with a protocol below HTTP 1.1. This should be a
419
            //      personality trait.
420
1.83k
            if (tx->request_protocol_number < HTP_PROTOCOL_1_1) {
421
1.17k
                tx->flags |= HTP_REQUEST_INVALID_T_E;
422
1.17k
                tx->flags |= HTP_REQUEST_SMUGGLING;
423
1.17k
            }
424
425
            // If the T-E header is present we are going to use it.
426
1.83k
            tx->request_transfer_coding = HTP_CODING_CHUNKED;
427
428
            // We are still going to check for the presence of C-L.
429
1.83k
            if (cl != NULL) {
430
                // According to the HTTP/1.1 RFC (section 4.4):
431
                //
432
                // "The Content-Length header field MUST NOT be sent
433
                //  if these two lengths are different (i.e., if a Transfer-Encoding
434
                //  header field is present). If a message is received with both a
435
                //  Transfer-Encoding header field and a Content-Length header field,
436
                //  the latter MUST be ignored."
437
                //
438
91
                tx->flags |= HTP_REQUEST_SMUGGLING;
439
91
            }
440
1.83k
        }
441
198k
    } else if (cl != NULL) {
442
        // Check for a folded C-L header.
443
13.9k
        if (cl->flags & HTP_FIELD_FOLDED) {
444
0
            tx->flags |= HTP_REQUEST_SMUGGLING;
445
0
        }
446
447
        // Check for multiple C-L headers.
448
13.9k
        if (cl->flags & HTP_FIELD_REPEATED) {
449
4.94k
            tx->flags |= HTP_REQUEST_SMUGGLING;
450
            // TODO Personality trait to determine which C-L header to parse.
451
            //      At the moment we're parsing the combination of all instances,
452
            //      which is bound to fail (because it will contain commas).
453
4.94k
        }
454
455
        // Get the body length.
456
13.9k
        tx->request_content_length = htp_parse_content_length(cl->value, tx->connp);
457
13.9k
        if (tx->request_content_length < 0) {
458
45
            tx->request_transfer_coding = HTP_CODING_INVALID;
459
45
            tx->flags |= HTP_REQUEST_INVALID_C_L;
460
45
            tx->flags |= HTP_REQUEST_INVALID;
461
13.8k
        } else {
462
            // We have a request body of known length.
463
13.8k
            tx->request_transfer_coding = HTP_CODING_IDENTITY;
464
13.8k
        }
465
185k
    } else {
466
        // No body.
467
185k
        tx->request_transfer_coding = HTP_CODING_NO_BODY;
468
185k
    }
469
470
    // If we could not determine the correct body handling,
471
    // consider the request invalid.
472
200k
    if (tx->request_transfer_coding == HTP_CODING_UNKNOWN) {
473
0
        tx->request_transfer_coding = HTP_CODING_INVALID;
474
0
        tx->flags |= HTP_REQUEST_INVALID;
475
0
    }
476
477
    // Check for PUT requests, which we need to treat as file uploads.
478
200k
    if (tx->request_method_number == HTP_M_PUT) {
479
439
        if (htp_tx_req_has_body(tx)) {
480
            // Prepare to treat PUT request body as a file.
481
            
482
49
            tx->connp->put_file = calloc(1, sizeof (htp_file_t));
483
49
            if (tx->connp->put_file == NULL) return HTP_ERROR;
484
485
49
            tx->connp->put_file->fd = -1;
486
49
            tx->connp->put_file->source = HTP_FILE_PUT;
487
390
        } else {
488
            // TODO Warn about PUT request without a body.
489
390
        }
490
439
    }
491
492
    // Determine hostname.
493
494
    // Use the hostname from the URI, when available.   
495
200k
    if (tx->parsed_uri->hostname != NULL) {
496
27.7k
        tx->request_hostname = bstr_dup(tx->parsed_uri->hostname);
497
27.7k
        if (tx->request_hostname == NULL) return HTP_ERROR;
498
27.7k
    }
499
500
200k
    tx->request_port_number = tx->parsed_uri->port_number;
501
502
    // Examine the Host header.
503
504
200k
    htp_header_t *h = htp_table_get_c(tx->request_headers, "host");
505
200k
    if (h == NULL) {
506
        // No host information in the headers.
507
508
        // HTTP/1.1 requires host information in the headers.
509
167k
        if (tx->request_protocol_number >= HTP_PROTOCOL_1_1) {
510
1.55k
            tx->flags |= HTP_HOST_MISSING;
511
1.55k
        }
512
167k
    } else {
513
        // Host information available in the headers.
514
515
33.0k
        bstr *hostname;
516
33.0k
        int port;
517
518
33.0k
        rc = htp_parse_header_hostport(h->value, &hostname, NULL, &port, &(tx->flags));
519
33.0k
        if (rc != HTP_OK) return rc;
520
521
33.0k
        if (hostname != NULL) {
522
            // The host information in the headers is valid.
523
524
            // Is there host information in the URI?
525
29.6k
            if (tx->request_hostname == NULL) {
526
                // There is no host information in the URI. Place the
527
                // hostname from the headers into the parsed_uri structure.
528
23.6k
                tx->request_hostname = hostname;
529
23.6k
                tx->request_port_number = port;
530
23.6k
            } else {
531
                // The host information appears in the URI and in the headers. The
532
                // HTTP RFC states that we should ignore the header copy.
533
                
534
                // Check for different hostnames.
535
5.95k
                if (bstr_cmp_nocase(hostname, tx->request_hostname) != 0) {                    
536
2.61k
                    tx->flags |= HTP_HOST_AMBIGUOUS;
537
2.61k
                }
538
539
                // Check for different ports.
540
5.95k
                if (((tx->request_port_number != -1)&&(port != -1))&&(tx->request_port_number != port)) {
541
147
                    tx->flags |= HTP_HOST_AMBIGUOUS;
542
147
                }
543
544
5.95k
                bstr_free(hostname);
545
5.95k
            }
546
29.6k
        } else {
547
            // Invalid host information in the headers.
548
549
3.42k
            if (tx->request_hostname != NULL) {
550
                // Raise the flag, even though the host information in the headers is invalid.
551
757
                tx->flags |= HTP_HOST_AMBIGUOUS;
552
757
            }
553
3.42k
        }
554
33.0k
    }
555
556
    // Determine Content-Type.
557
200k
    htp_header_t *ct = htp_table_get_c(tx->request_headers, "content-type");
558
200k
    if (ct != NULL) {
559
11.2k
        rc = htp_parse_ct_header(ct->value, &tx->request_content_type);
560
11.2k
        if (rc != HTP_OK) return rc;
561
11.2k
    }
562
563
    // Parse cookies.
564
200k
    if (tx->connp->cfg->parse_request_cookies) {
565
0
        rc = htp_parse_cookies_v0(tx->connp);
566
0
        if (rc != HTP_OK) return rc;
567
0
    }
568
569
    // Parse authentication information.
570
200k
    if (tx->connp->cfg->parse_request_auth) {
571
200k
        rc = htp_parse_authorization(tx->connp);
572
200k
        if (rc == HTP_DECLINED) {
573
            // Don't fail the stream if an authorization header is invalid, just set a flag.
574
2.94k
            tx->flags |= HTP_AUTH_INVALID;
575
197k
        } else {
576
197k
            if (rc != HTP_OK) return rc;
577
197k
        }
578
200k
    }
579
580
    // Finalize sending raw header data.
581
200k
    rc = htp_connp_req_receiver_finalize_clear(tx->connp);
582
200k
    if (rc != HTP_OK) return rc;
583
584
    // Run hook REQUEST_HEADERS.
585
200k
    rc = htp_hook_run_all(tx->connp->cfg->hook_request_headers, tx);
586
200k
    if (rc != HTP_OK) return rc;
587
588
    // We still proceed if the request is invalid.
589
590
200k
    return HTP_OK;
591
200k
}
592
593
0
htp_status_t htp_tx_req_process_body_data(htp_tx_t *tx, const void *data, size_t len) {
594
0
    if ((tx == NULL) || (data == NULL)) return HTP_ERROR;
595
0
    if (len == 0) return HTP_OK;
596
597
0
    return htp_tx_req_process_body_data_ex(tx, data, len);
598
0
}
599
600
786k
htp_status_t htp_tx_req_process_body_data_ex(htp_tx_t *tx, const void *data, size_t len) {
601
786k
    if (tx == NULL) return HTP_ERROR;
602
603
    // NULL data is allowed in this private function; it's
604
    // used to indicate the end of request body.
605
606
    // Send data to the callbacks.
607
608
786k
    htp_tx_data_t d;
609
786k
    d.tx = tx;
610
786k
    d.data = (unsigned char *) data;
611
786k
    d.len = len;
612
786k
    d.is_last = (data == NULL && len == 0);
613
614
786k
    switch(tx->request_content_encoding) {
615
67.2k
        case HTP_COMPRESSION_UNKNOWN:
616
475k
        case HTP_COMPRESSION_NONE:
617
            // When there's no decompression, request_entity_len.
618
            // is identical to request_message_len.
619
475k
            tx->request_entity_len += d.len;
620
475k
            htp_status_t rc = htp_req_run_hook_body_data(tx->connp, &d);
621
475k
            if (rc != HTP_OK) {
622
0
                htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Request body data callback returned error (%d)", rc);
623
0
                return HTP_ERROR;
624
0
            }
625
475k
            break;
626
627
475k
        case HTP_COMPRESSION_GZIP:
628
310k
        case HTP_COMPRESSION_DEFLATE:
629
310k
        case HTP_COMPRESSION_LZMA:
630
            // In severe memory stress these could be NULL
631
310k
            if (tx->connp->req_decompressor == NULL)
632
1
                return HTP_ERROR;
633
634
            // Send data buffer to the decompressor.
635
310k
            htp_gzip_decompressor_decompress(tx->connp->req_decompressor, &d);
636
637
310k
            if (data == NULL) {
638
                // Shut down the decompressor, if we used one.
639
1.48k
                htp_tx_req_destroy_decompressors(tx->connp);
640
1.48k
            }
641
310k
            break;
642
643
0
        default:
644
            // Internal error.
645
0
            htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0,
646
0
                    "[Internal Error] Invalid tx->request_content_encoding value: %d",
647
0
                    tx->request_content_encoding);
648
0
            return HTP_ERROR;
649
786k
    }
650
651
786k
    return HTP_OK;
652
786k
}
653
654
0
htp_status_t htp_tx_req_set_headers_clear(htp_tx_t *tx) {
655
0
    if ((tx == NULL) || (tx->request_headers == NULL)) return HTP_ERROR;
656
657
0
    htp_header_t *h = NULL;
658
0
    for (size_t i = 0, n = htp_table_size(tx->request_headers); i < n; i++) {
659
0
        h = htp_table_get_index(tx->request_headers, i, NULL);
660
0
        bstr_free(h->name);
661
0
        bstr_free(h->value);
662
0
        free(h);
663
0
    }
664
665
0
    htp_table_destroy(tx->request_headers);
666
667
0
    tx->request_headers = htp_table_create(32);
668
0
    if (tx->request_headers == NULL) return HTP_ERROR;
669
670
0
    return HTP_OK;
671
0
}
672
673
0
htp_status_t htp_tx_req_set_line(htp_tx_t *tx, const char *line, size_t line_len, enum htp_alloc_strategy_t alloc) {
674
0
    if ((tx == NULL) || (line == NULL) || (line_len == 0)) return HTP_ERROR;
675
676
0
    tx->request_line = copy_or_wrap_mem(line, line_len, alloc);
677
0
    if (tx->request_line == NULL) return HTP_ERROR;
678
679
0
    if (tx->connp->cfg->parse_request_line(tx->connp) != HTP_OK) return HTP_ERROR;
680
681
0
    return HTP_OK;
682
0
}
683
684
0
void htp_tx_req_set_parsed_uri(htp_tx_t *tx, htp_uri_t *parsed_uri) {
685
0
    if ((tx == NULL) || (parsed_uri == NULL)) return;
686
687
0
    if (tx->parsed_uri != NULL) {
688
0
        htp_uri_free(tx->parsed_uri);
689
0
    }
690
691
0
    tx->parsed_uri = parsed_uri;
692
0
}
693
694
0
htp_status_t htp_tx_res_set_status_line(htp_tx_t *tx, const char *line, size_t line_len, enum htp_alloc_strategy_t alloc) {
695
0
    if ((tx == NULL) || (line == NULL) || (line_len == 0)) return HTP_ERROR;
696
697
0
    tx->response_line = copy_or_wrap_mem(line, line_len, alloc);
698
0
    if (tx->response_line == NULL) return HTP_ERROR;
699
700
0
    if (tx->connp->cfg->parse_response_line(tx->connp) != HTP_OK) return HTP_ERROR;
701
702
0
    return HTP_OK;
703
0
}
704
705
0
void htp_tx_res_set_protocol_number(htp_tx_t *tx, int protocol_number) {
706
0
    if (tx == NULL) return;
707
0
    tx->response_protocol_number = protocol_number;
708
0
}
709
710
0
void htp_tx_res_set_status_code(htp_tx_t *tx, int status_code) {
711
0
    if (tx == NULL) return;
712
0
    tx->response_status_number = status_code;
713
0
}
714
715
0
htp_status_t htp_tx_res_set_status_message(htp_tx_t *tx, const char *msg, size_t msg_len, enum htp_alloc_strategy_t alloc) {
716
0
    if ((tx == NULL) || (msg == NULL)) return HTP_ERROR;
717
718
0
    if (tx->response_message != NULL) {
719
0
        bstr_free(tx->response_message);
720
0
    }
721
722
0
    tx->response_message = copy_or_wrap_mem(msg, msg_len, alloc);
723
0
    if (tx->response_message == NULL) return HTP_ERROR;
724
725
0
    return HTP_OK;
726
0
}
727
728
56.5k
htp_status_t htp_tx_state_response_line(htp_tx_t *tx) {
729
56.5k
    if (tx == NULL) return HTP_ERROR;
730
731
    #if 0
732
    // Commented-out until we determine which fields can be
733
    // unavailable in real-life.
734
735
    // Unless we're dealing with HTTP/0.9, check that
736
    // the minimum amount of data has been provided.
737
    if (tx->is_protocol_0_9 != 0) {
738
        if ((tx->response_protocol == NULL) || (tx->response_status_number == -1) || (tx->response_message == NULL)) {
739
            return HTP_ERROR;
740
        }
741
    }
742
    #endif
743
744
    // Is the response line valid?
745
56.5k
    if (tx->response_protocol_number == HTP_PROTOCOL_INVALID) {
746
37.7k
        htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0,
747
37.7k
                "Invalid response line: invalid protocol");
748
37.7k
        tx->flags |= HTP_STATUS_LINE_INVALID;
749
37.7k
    }
750
56.5k
    if ((tx->response_status_number == HTP_STATUS_INVALID)
751
36.3k
            || (tx->response_status_number < HTP_VALID_STATUS_MIN)
752
36.3k
            || (tx->response_status_number > HTP_VALID_STATUS_MAX)) {
753
20.2k
        htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0,
754
20.2k
                "Invalid response line: invalid response status %d.",
755
20.2k
                tx->response_status_number);
756
20.2k
        tx->response_status_number = HTP_STATUS_INVALID;
757
20.2k
        tx->flags |= HTP_STATUS_LINE_INVALID;
758
20.2k
    }
759
760
    // Run hook HTP_RESPONSE_LINE
761
56.5k
    htp_status_t rc = htp_hook_run_all(tx->connp->cfg->hook_response_line, tx);
762
56.5k
    if (rc != HTP_OK) return rc;
763
764
56.5k
    return HTP_OK;
765
56.5k
}
766
767
htp_status_t htp_tx_res_set_header(htp_tx_t *tx, const char *name, size_t name_len,
768
0
        const char *value, size_t value_len, enum htp_alloc_strategy_t alloc) {
769
0
    if ((tx == NULL) || (name == NULL) || (value == NULL)) return HTP_ERROR;
770
771
772
0
    htp_header_t *h = calloc(1, sizeof (htp_header_t));
773
0
    if (h == NULL) return HTP_ERROR;
774
775
0
    h->name = copy_or_wrap_mem(name, name_len, alloc);
776
0
    if (h->name == NULL) {
777
0
        free(h);
778
0
        return HTP_ERROR;
779
0
    }
780
781
0
    h->value = copy_or_wrap_mem(value, value_len, alloc);
782
0
    if (h->value == NULL) {
783
0
        bstr_free(h->name);
784
0
        free(h);
785
0
        return HTP_ERROR;
786
0
    }
787
788
0
    if (htp_table_add(tx->response_headers, h->name, h) != HTP_OK) {
789
0
        bstr_free(h->name);
790
0
        bstr_free(h->value);
791
0
        free(h);
792
0
        return HTP_ERROR;
793
0
    }
794
795
0
    return HTP_OK;
796
0
}
797
798
0
htp_status_t htp_tx_res_set_headers_clear(htp_tx_t *tx) {
799
0
    if ((tx == NULL) || (tx->response_headers == NULL)) return HTP_ERROR;
800
801
0
    htp_header_t *h = NULL;
802
0
    for (size_t i = 0, n = htp_table_size(tx->response_headers); i < n; i++) {
803
0
        h = htp_table_get_index(tx->response_headers, i, NULL);
804
0
        bstr_free(h->name);
805
0
        bstr_free(h->value);
806
0
        free(h);
807
0
    }
808
809
0
    htp_table_destroy(tx->response_headers);
810
811
0
    tx->response_headers = htp_table_create(32);
812
0
    if (tx->response_headers == NULL) return HTP_ERROR;
813
814
0
    return HTP_OK;
815
0
}
816
817
/** \internal
818
 *
819
 * Clean up decompressor(s).
820
 *
821
 * @param[in] tx
822
 */
823
38.5k
static void htp_tx_res_destroy_decompressors(htp_connp_t *connp) {
824
38.5k
    htp_decompressor_t *comp = connp->out_decompressor;
825
55.2k
    while (comp) {
826
16.7k
        htp_decompressor_t *next = comp->next;
827
16.7k
        htp_gzip_decompressor_destroy(comp);
828
16.7k
        comp = next;
829
16.7k
    }
830
38.5k
    connp->out_decompressor = NULL;
831
38.5k
}
832
833
28.4k
static void htp_tx_req_destroy_decompressors(htp_connp_t *connp) {
834
28.4k
    htp_decompressor_t *comp = connp->req_decompressor;
835
32.9k
    while (comp) {
836
4.43k
        htp_decompressor_t *next = comp->next;
837
4.43k
        htp_gzip_decompressor_destroy(comp);
838
4.43k
        comp = next;
839
4.43k
    }
840
28.4k
    connp->req_decompressor = NULL;
841
28.4k
}
842
843
24.3k
void htp_connp_destroy_decompressors(htp_connp_t *connp) {
844
24.3k
    htp_tx_res_destroy_decompressors(connp);
845
24.3k
    htp_tx_req_destroy_decompressors(connp);
846
24.3k
}
847
848
114k
static htp_status_t htp_timer_track(int32_t *time_spent, struct timeval * after, struct timeval *before) {
849
114k
    if (after->tv_sec < before->tv_sec) {
850
0
        return HTP_ERROR;
851
114k
    } else if (after->tv_sec == before->tv_sec) {
852
113k
        if (after->tv_usec < before->tv_usec) {
853
0
            return HTP_ERROR;
854
0
        }
855
113k
        *time_spent += after->tv_usec - before->tv_usec;
856
113k
    } else {
857
287
        *time_spent += (after->tv_sec - before->tv_sec) * 1000000 + after->tv_usec - before->tv_usec;
858
287
    }
859
114k
    return HTP_OK;
860
114k
}
861
862
279k
static htp_status_t htp_tx_req_process_body_data_decompressor_callback(htp_tx_data_t *d) {
863
279k
    if (d == NULL) return HTP_ERROR;
864
865
    #if HTP_DEBUG
866
    fprint_raw_data(stderr, __func__, d->data, d->len);
867
    #endif
868
869
    // Keep track of actual request body length.
870
279k
    d->tx->request_entity_len += d->len;
871
872
    // Invoke all callbacks.
873
279k
    htp_status_t rc = htp_req_run_hook_body_data(d->tx->connp, d);
874
279k
    if (rc != HTP_OK) return HTP_ERROR;
875
279k
    d->tx->connp->req_decompressor->nb_callbacks++;
876
279k
    if (d->tx->connp->req_decompressor->nb_callbacks % HTP_COMPRESSION_TIME_FREQ_TEST == 0) {
877
846
        struct timeval after;
878
846
        gettimeofday(&after, NULL);
879
        // sanity check for race condition if system time changed
880
846
        if ( htp_timer_track(&d->tx->connp->req_decompressor->time_spent, &after, &d->tx->connp->req_decompressor->time_before) == HTP_OK) {
881
            // updates last tracked time
882
846
            d->tx->connp->req_decompressor->time_before = after;
883
846
            if (d->tx->connp->req_decompressor->time_spent > d->tx->connp->cfg->compression_time_limit ) {
884
189
                htp_log(d->tx->connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0,
885
189
                        "Compression bomb: spent %"PRId32" us decompressing",
886
189
                        d->tx->connp->req_decompressor->time_spent);
887
189
                d->tx->connp->req_decompressor->passthrough = 1;
888
189
            }
889
846
        }
890
891
846
    }
892
279k
    if (d->tx->request_entity_len > d->tx->connp->cfg->compression_bomb_limit &&
893
1.97k
        d->tx->request_entity_len > HTP_COMPRESSION_BOMB_RATIO * d->tx->request_message_len) {
894
1.92k
        htp_log(d->tx->connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0,
895
1.92k
                "Compression bomb: decompressed %"PRId64" bytes out of %"PRId64,
896
1.92k
                d->tx->request_entity_len, d->tx->request_message_len);
897
1.92k
        return HTP_ERROR;
898
1.92k
    }
899
900
277k
    return HTP_OK;
901
279k
}
902
903
297k
static htp_status_t htp_tx_res_process_body_data_decompressor_callback(htp_tx_data_t *d) {
904
297k
    if (d == NULL) return HTP_ERROR;
905
906
    #if HTP_DEBUG
907
    fprint_raw_data(stderr, __func__, d->data, d->len);
908
    #endif
909
910
    // Keep track of actual response body length.
911
297k
    d->tx->response_entity_len += d->len;
912
913
    // Invoke all callbacks.
914
297k
    htp_status_t rc = htp_res_run_hook_body_data(d->tx->connp, d);
915
297k
    if (rc != HTP_OK) return HTP_ERROR;
916
297k
    d->tx->connp->out_decompressor->nb_callbacks++;
917
297k
    if (d->tx->connp->out_decompressor->nb_callbacks % HTP_COMPRESSION_TIME_FREQ_TEST == 0) {
918
822
        struct timeval after;
919
822
        gettimeofday(&after, NULL);
920
        // sanity check for race condition if system time changed
921
822
        if ( htp_timer_track(&d->tx->connp->out_decompressor->time_spent, &after, &d->tx->connp->out_decompressor->time_before) == HTP_OK) {
922
            // updates last tracked time
923
822
            d->tx->connp->out_decompressor->time_before = after;
924
822
            if (d->tx->connp->out_decompressor->time_spent > d->tx->connp->cfg->compression_time_limit ) {
925
408
                htp_log(d->tx->connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0,
926
408
                        "Compression bomb: spent %"PRId32" us decompressing",
927
408
                        d->tx->connp->out_decompressor->time_spent);
928
408
                d->tx->connp->out_decompressor->passthrough = 1;
929
408
            }
930
822
        }
931
932
822
    }
933
297k
    if (d->tx->response_entity_len > d->tx->connp->cfg->compression_bomb_limit &&
934
210k
        d->tx->response_entity_len > HTP_COMPRESSION_BOMB_RATIO * d->tx->response_message_len) {
935
0
        htp_log(d->tx->connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0,
936
0
                "Compression bomb: decompressed %"PRId64" bytes out of %"PRId64,
937
0
                d->tx->response_entity_len, d->tx->response_message_len);
938
0
        return HTP_ERROR;
939
0
    }
940
941
297k
    return HTP_OK;
942
297k
}
943
944
0
htp_status_t htp_tx_res_process_body_data(htp_tx_t *tx, const void *data, size_t len) {
945
0
    if ((tx == NULL) || (data == NULL)) return HTP_ERROR;
946
0
    if (len == 0) return HTP_OK;
947
0
    return htp_tx_res_process_body_data_ex(tx, data, len);
948
0
}
949
950
802k
htp_status_t htp_tx_res_process_body_data_ex(htp_tx_t *tx, const void *data, size_t len) {
951
802k
    if (tx == NULL) return HTP_ERROR;
952
953
    // NULL data is allowed in this private function; it's
954
    // used to indicate the end of response body.
955
956
    #ifdef HTP_DEBUG
957
    fprint_raw_data(stderr, __func__, data, len);
958
    #endif
959
960
802k
    htp_tx_data_t d;
961
962
802k
    d.tx = tx;
963
802k
    d.data = (unsigned char *) data;
964
802k
    d.len = len;
965
802k
    d.is_last = 0;
966
967
    // Keep track of body size before decompression.
968
802k
    tx->response_message_len += d.len;
969
970
802k
    switch (tx->response_content_encoding_processing) {
971
80.5k
        case HTP_COMPRESSION_GZIP:
972
113k
        case HTP_COMPRESSION_DEFLATE:
973
113k
        case HTP_COMPRESSION_LZMA:
974
            // In severe memory stress these could be NULL
975
113k
            if (tx->connp->out_decompressor == NULL) {
976
1.55k
                if (data == NULL) {
977
                    // we were already stopped on a gap finishing CL
978
1.52k
                    return HTP_OK;
979
1.52k
                }
980
34
                return HTP_ERROR;
981
1.55k
            }
982
983
112k
            struct timeval after;
984
112k
            gettimeofday(&tx->connp->out_decompressor->time_before, NULL);
985
            // Send data buffer to the decompressor.
986
112k
            tx->connp->out_decompressor->nb_callbacks=0;
987
112k
            htp_gzip_decompressor_decompress(tx->connp->out_decompressor, &d);
988
112k
            gettimeofday(&after, NULL);
989
            // sanity check for race condition if system time changed
990
112k
            if ( htp_timer_track(&tx->connp->out_decompressor->time_spent, &after, &tx->connp->out_decompressor->time_before) == HTP_OK) {
991
112k
                if ( tx->connp->out_decompressor->time_spent > tx->connp->cfg->compression_time_limit ) {
992
131
                    htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0,
993
131
                            "Compression bomb: spent %"PRId32" us decompressing",
994
131
                            tx->connp->out_decompressor->time_spent);
995
131
                    tx->connp->out_decompressor->passthrough = 1;
996
131
                }
997
112k
            }
998
999
112k
            if (data == NULL) {
1000
                // Shut down the decompressor, if we used one.
1001
9.13k
                htp_tx_res_destroy_decompressors(tx->connp);
1002
9.13k
            }
1003
112k
            break;
1004
1005
686k
        case HTP_COMPRESSION_NONE:
1006
            // When there's no decompression, response_entity_len.
1007
            // is identical to response_message_len.
1008
686k
            tx->response_entity_len += d.len;
1009
1010
686k
            htp_status_t rc = htp_res_run_hook_body_data(tx->connp, &d);
1011
686k
            if (rc != HTP_OK) return HTP_ERROR;
1012
686k
            break;
1013
1014
686k
        default:
1015
            // Internal error.
1016
1.48k
            htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0,
1017
1.48k
                    "[Internal Error] Invalid tx->response_content_encoding_processing value: %d",
1018
1.48k
                    tx->response_content_encoding_processing);
1019
1.48k
            return HTP_ERROR;
1020
0
            break;
1021
802k
    }
1022
1023
799k
    return HTP_OK;
1024
802k
}
1025
1026
254k
htp_status_t htp_tx_state_request_complete_partial(htp_tx_t *tx) {
1027
254k
    if (tx == NULL) return HTP_ERROR;
1028
1029
    // Finalize request body.
1030
254k
    if (htp_tx_req_has_body(tx)) {
1031
14.8k
        htp_status_t rc = htp_tx_req_process_body_data_ex(tx, NULL, 0);
1032
14.8k
        if (rc != HTP_OK) return rc;
1033
14.8k
    }
1034
1035
254k
    tx->request_progress = HTP_REQUEST_COMPLETE;
1036
1037
    // Run hook REQUEST_COMPLETE.
1038
254k
    htp_status_t rc = htp_hook_run_all(tx->connp->cfg->hook_request_complete, tx);
1039
254k
    if (rc != HTP_OK) return rc;
1040
254k
    rc = htp_connp_req_receiver_finalize_clear(tx->connp);
1041
254k
    if (rc != HTP_OK) return rc;
1042
1043
    // Clean-up.
1044
254k
    if (tx->connp->put_file != NULL) {
1045
42
        bstr_free(tx->connp->put_file->filename);
1046
42
        free(tx->connp->put_file);
1047
42
        tx->connp->put_file = NULL;
1048
42
    }
1049
1050
254k
    return HTP_OK;
1051
254k
}
1052
1053
254k
htp_status_t htp_tx_state_request_complete(htp_tx_t *tx) {
1054
254k
    if (tx == NULL) return HTP_ERROR;
1055
1056
254k
    if (tx->request_progress != HTP_REQUEST_COMPLETE) {
1057
254k
        htp_status_t rc = htp_tx_state_request_complete_partial(tx);
1058
254k
        if (rc != HTP_OK) return rc;
1059
254k
    }
1060
1061
    // Make a copy of the connection parser pointer, so that
1062
    // we don't have to reference it via tx, which may be
1063
    // destroyed later.
1064
254k
    htp_connp_t *connp = tx->connp;
1065
1066
    // Determine what happens next, and remove this transaction from the parser.
1067
254k
    if (tx->is_protocol_0_9) {
1068
3.82k
        connp->in_state = htp_connp_REQ_IGNORE_DATA_AFTER_HTTP_0_9;
1069
250k
    } else {
1070
250k
        connp->in_state = htp_connp_REQ_IDLE;
1071
250k
    }
1072
1073
    // Check if the entire transaction is complete. This call may
1074
    // destroy the transaction, if auto-destroy is enabled.
1075
254k
    htp_tx_finalize(tx);
1076
1077
    // At this point, tx may no longer be valid.
1078
1079
254k
    connp->in_tx = NULL;
1080
1081
254k
    return HTP_OK;
1082
254k
}
1083
1084
217k
htp_status_t htp_tx_state_request_start(htp_tx_t *tx) {
1085
217k
    if (tx == NULL) return HTP_ERROR;
1086
1087
    // Run hook REQUEST_START.
1088
217k
    htp_status_t rc = htp_hook_run_all(tx->connp->cfg->hook_request_start, tx);
1089
217k
    if (rc != HTP_OK) return rc;
1090
1091
    // Change state into request line parsing.
1092
217k
    tx->connp->in_state = htp_connp_REQ_LINE;
1093
217k
    tx->connp->in_tx->request_progress = HTP_REQUEST_LINE;
1094
1095
217k
    return HTP_OK;
1096
217k
}
1097
1098
204k
htp_status_t htp_tx_state_request_headers(htp_tx_t *tx) {
1099
204k
    if (tx == NULL) return HTP_ERROR;
1100
1101
    // If we're in HTP_REQ_HEADERS that means that this is the
1102
    // first time we're processing headers in a request. Otherwise,
1103
    // we're dealing with trailing headers.
1104
204k
    if (tx->request_progress > HTP_REQUEST_HEADERS) {
1105
        // Request trailers.
1106
1107
        // Run hook HTP_REQUEST_TRAILER.
1108
3.30k
        htp_status_t rc = htp_hook_run_all(tx->connp->cfg->hook_request_trailer, tx);
1109
3.30k
        if (rc != HTP_OK) return rc;
1110
1111
        // Finalize sending raw header data.
1112
3.30k
        rc = htp_connp_req_receiver_finalize_clear(tx->connp);
1113
3.30k
        if (rc != HTP_OK) return rc;
1114
1115
        // Completed parsing this request; finalize it now.
1116
3.30k
        tx->connp->in_state = htp_connp_REQ_FINALIZE;
1117
200k
    } else if (tx->request_progress >= HTP_REQUEST_LINE) {
1118
        // Request headers.
1119
1120
        // Did this request arrive in multiple data chunks?
1121
200k
        if (tx->connp->in_chunk_count != tx->connp->in_chunk_request_index) {
1122
22.8k
            tx->flags |= HTP_MULTI_PACKET_HEAD;
1123
22.8k
        }
1124
1125
200k
        htp_status_t rc = htp_tx_process_request_headers(tx);
1126
200k
        if (rc != HTP_OK) return rc;
1127
1128
200k
        tx->connp->in_state = htp_connp_REQ_CONNECT_CHECK;
1129
200k
    } else {
1130
0
        htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "[Internal Error] Invalid tx progress: %d", tx->request_progress);
1131
1132
0
        return HTP_ERROR;
1133
0
    }
1134
1135
204k
    return HTP_OK;
1136
204k
}
1137
1138
211k
htp_status_t htp_tx_state_request_line(htp_tx_t *tx) {
1139
211k
    if (tx == NULL) return HTP_ERROR;
1140
1141
    // Determine how to process the request URI.
1142
1143
211k
    if (tx->request_method_number == HTP_M_CONNECT) {
1144
        // When CONNECT is used, the request URI contains an authority string.
1145
3.90k
        if (htp_parse_uri_hostport(tx->connp, tx->request_uri, tx->parsed_uri_raw) != HTP_OK) {
1146
1
            return HTP_ERROR;
1147
1
        }
1148
207k
    } else {
1149
        // Parse the request URI into htp_tx_t::parsed_uri_raw.
1150
207k
        if (htp_parse_uri(tx->request_uri, &(tx->parsed_uri_raw)) != HTP_OK) {
1151
0
            return HTP_ERROR;
1152
0
        }
1153
207k
    }
1154
1155
    // Build htp_tx_t::parsed_uri, but only if it was not explicitly set already.
1156
211k
    if (tx->parsed_uri == NULL) {
1157
211k
        tx->parsed_uri = htp_uri_alloc();
1158
211k
        if (tx->parsed_uri == NULL) return HTP_ERROR;
1159
1160
        // Keep the original URI components, but create a copy which we can normalize and use internally.
1161
211k
        if (htp_normalize_parsed_uri(tx, tx->parsed_uri_raw, tx->parsed_uri) != HTP_OK) {
1162
0
            return HTP_ERROR;
1163
0
        }
1164
211k
    }
1165
1166
    // Check parsed_uri hostname.
1167
211k
    if (tx->parsed_uri->hostname != NULL) {
1168
28.6k
        if (htp_validate_hostname(tx->parsed_uri->hostname) == 0) {
1169
22.9k
            tx->flags |= HTP_HOSTU_INVALID;
1170
22.9k
        }
1171
28.6k
    }
1172
1173
    // Run hook REQUEST_URI_NORMALIZE.
1174
211k
    htp_status_t rc = htp_hook_run_all(tx->connp->cfg->hook_request_uri_normalize, tx);
1175
211k
    if (rc != HTP_OK) return rc;
1176
1177
1178
    // Run hook REQUEST_LINE.
1179
211k
    rc = htp_hook_run_all(tx->connp->cfg->hook_request_line, tx);
1180
211k
    if (rc != HTP_OK) return rc;
1181
1182
    // Move on to the next phase.
1183
211k
    tx->connp->in_state = htp_connp_REQ_PROTOCOL;
1184
1185
211k
    return HTP_OK;
1186
211k
}
1187
1188
0
htp_status_t htp_tx_state_response_complete(htp_tx_t *tx) {
1189
0
    if (tx == NULL) return HTP_ERROR;
1190
0
    return htp_tx_state_response_complete_ex(tx, 1 /* hybrid mode */);
1191
0
}
1192
1193
355k
htp_status_t htp_tx_finalize(htp_tx_t *tx) {
1194
355k
    if (tx == NULL) return HTP_ERROR;
1195
1196
355k
    if (!htp_tx_is_complete(tx)) return HTP_OK;
1197
1198
    // Run hook TRANSACTION_COMPLETE.
1199
92.4k
    htp_status_t rc = htp_hook_run_all(tx->connp->cfg->hook_transaction_complete, tx);
1200
92.4k
    if (rc != HTP_OK) return rc;
1201
1202
    // In streaming processing, we destroy the transaction because it will not be needed any more.
1203
92.4k
    if (tx->connp->cfg->tx_auto_destroy) {
1204
0
        htp_tx_destroy(tx);
1205
0
    }
1206
1207
92.4k
    return HTP_OK;
1208
92.4k
}
1209
1210
101k
htp_status_t htp_tx_state_response_complete_ex(htp_tx_t *tx, int hybrid_mode) {
1211
101k
    if (tx == NULL) return HTP_ERROR;
1212
1213
101k
    if (tx->response_progress != HTP_RESPONSE_COMPLETE) {
1214
101k
        tx->response_progress = HTP_RESPONSE_COMPLETE;
1215
1216
        // Run the last RESPONSE_BODY_DATA HOOK, but only if there was a response body present.
1217
101k
        if (tx->response_transfer_coding != HTP_CODING_NO_BODY) {
1218
88.2k
            htp_tx_res_process_body_data_ex(tx, NULL, 0);
1219
88.2k
        }
1220
1221
        // Run hook RESPONSE_COMPLETE.
1222
101k
        htp_status_t rc = htp_hook_run_all(tx->connp->cfg->hook_response_complete, tx);
1223
101k
        if (rc != HTP_OK) return rc;
1224
1225
        // Clear the data receivers hook if any
1226
101k
        rc = htp_connp_res_receiver_finalize_clear(tx->connp);
1227
101k
        if (rc != HTP_OK) return rc;
1228
101k
    }
1229
1230
101k
    if (!hybrid_mode) {
1231
        // Check if the inbound parser is waiting on us. If it is, that means that
1232
        // there might be request data that the inbound parser hasn't consumed yet.
1233
        // If we don't stop parsing we might encounter a response without a request,
1234
        // which is why we want to return straight away before processing any data.
1235
        //
1236
        // This situation will occur any time the parser needs to see the server
1237
        // respond to a particular situation before it can decide how to proceed. For
1238
        // example, when a CONNECT is sent, different paths are used when it is accepted
1239
        // and when it is not accepted.
1240
        //
1241
        // It is not enough to check only in_status here. Because of pipelining, it's possible
1242
        // that many inbound transactions have been processed, and that the parser is
1243
        // waiting on a response that we have not seen yet.
1244
101k
        if ((tx->connp->in_status == HTP_STREAM_DATA_OTHER) && (tx->connp->in_tx == tx->connp->out_tx)) {
1245
129
            return HTP_DATA_OTHER;
1246
129
        }
1247
1248
        // Do we have a signal to yield to inbound processing at
1249
        // the end of the next transaction?
1250
101k
        if (tx->connp->out_data_other_at_tx_end) {
1251
            // We do. Let's yield then.
1252
125
            tx->connp->out_data_other_at_tx_end = 0;
1253
125
            return HTP_DATA_OTHER;
1254
125
        }
1255
101k
    }
1256
1257
    // Make a copy of the connection parser pointer, so that
1258
    // we don't have to reference it via tx, which may be destroyed later.
1259
101k
    htp_connp_t *connp = tx->connp;
1260
1261
    // Finalize the transaction. This may call may destroy the transaction, if auto-destroy is enabled.
1262
101k
    htp_status_t rc = htp_tx_finalize(tx);
1263
101k
    if (rc != HTP_OK) return rc;
1264
1265
    // Disconnect transaction from the parser.
1266
101k
    connp->out_tx = NULL;
1267
1268
101k
    connp->out_state = htp_connp_RES_IDLE;
1269
1270
101k
    return HTP_OK;
1271
101k
}
1272
1273
/**
1274
 *  @internal
1275
 *  @brief split input into tokens separated by "seps"
1276
 *  @param seps nul-terminated string: each character is a separator
1277
 */
1278
static int get_token(const unsigned char *in, size_t in_len, const char *seps,
1279
    unsigned char **ret_tok_ptr, size_t *ret_tok_len)
1280
26.2k
{
1281
    #if HTP_DEBUG
1282
    fprintf(stderr, "INPUT %"PRIuMAX, (uintmax_t)in_len);
1283
    fprint_raw_data(stderr, __func__, in, in_len);
1284
    #endif
1285
1286
26.2k
    size_t i = 0;
1287
1288
    /* skip leading 'separators' */
1289
29.3k
    while (i < in_len)
1290
29.2k
    {
1291
29.2k
        int match = 0;
1292
83.6k
        for (const char *s = seps; *s != '\0'; s++) {
1293
57.5k
            if (in[i] == *s) {
1294
3.09k
                match++;
1295
3.09k
                break;
1296
3.09k
            }
1297
57.5k
        }
1298
29.2k
        if (!match)
1299
26.1k
            break;
1300
1301
3.09k
        i++;
1302
3.09k
    }
1303
26.2k
    if (i >= in_len)
1304
168
        return 0;
1305
1306
26.1k
    in += i;
1307
26.1k
    in_len -= i;
1308
1309
    #if HTP_DEBUG
1310
    fprintf(stderr, "INPUT (POST SEP STRIP) %"PRIuMAX, (uintmax_t)in_len);
1311
    fprint_raw_data(stderr, __func__, in, in_len);
1312
    #endif
1313
1314
606k
    for (i = 0; i < in_len; i++)
1315
598k
    {
1316
1.77M
        for (const char *s = seps; *s != '\0'; s++) {
1317
1.19M
            if (in[i] == *s) {
1318
18.8k
                *ret_tok_ptr = (unsigned char *)in;
1319
18.8k
                *ret_tok_len = i;
1320
18.8k
                return 1;
1321
18.8k
            }
1322
1.19M
        }
1323
598k
    }
1324
1325
7.26k
    *ret_tok_ptr = (unsigned char *)in;
1326
7.26k
    *ret_tok_len = in_len;
1327
7.26k
    return 1;
1328
26.1k
}
1329
1330
50.9k
htp_status_t htp_tx_state_response_headers(htp_tx_t *tx) {
1331
50.9k
    if (tx == NULL) return HTP_ERROR;
1332
1333
    // Check for compression.
1334
1335
    // Determine content encoding.
1336
1337
50.9k
    int ce_multi_comp = 0;
1338
50.9k
    tx->response_content_encoding = HTP_COMPRESSION_NONE;
1339
50.9k
    htp_header_t *ce = htp_table_get_c(tx->response_headers, "content-encoding");
1340
50.9k
    if (ce != NULL) {
1341
        /* fast paths: regular gzip and friends */
1342
16.7k
        if ((bstr_cmp_c_nocasenorzero(ce->value, "gzip") == 0) ||
1343
11.8k
            (bstr_cmp_c_nocasenorzero(ce->value, "x-gzip") == 0)) {
1344
4.85k
            tx->response_content_encoding = HTP_COMPRESSION_GZIP;
1345
11.8k
        } else if ((bstr_cmp_c_nocasenorzero(ce->value, "deflate") == 0) ||
1346
11.4k
                   (bstr_cmp_c_nocasenorzero(ce->value, "x-deflate") == 0)) {
1347
467
            tx->response_content_encoding = HTP_COMPRESSION_DEFLATE;
1348
11.4k
        } else if (bstr_cmp_c_nocasenorzero(ce->value, "lzma") == 0) {
1349
0
            tx->response_content_encoding = HTP_COMPRESSION_LZMA;
1350
11.4k
        } else if (bstr_cmp_c_nocasenorzero(ce->value, "inflate") == 0 ||
1351
11.4k
                   bstr_cmp_c_nocasenorzero(ce->value, "identity") == 0) {
1352
            // ignore
1353
11.4k
        } else {
1354
            /* exceptional cases: enter slow path */
1355
11.4k
            ce_multi_comp = 1;
1356
11.4k
        }
1357
16.7k
    }
1358
1359
    // Configure decompression, if enabled in the configuration.
1360
50.9k
    if (tx->connp->cfg->response_decompression_enabled) {
1361
50.9k
        tx->response_content_encoding_processing = tx->response_content_encoding;
1362
50.9k
    } else {
1363
0
        tx->response_content_encoding_processing = HTP_COMPRESSION_NONE;
1364
0
        ce_multi_comp = 0;
1365
0
    }
1366
1367
    // Finalize sending raw header data.
1368
50.9k
    htp_status_t rc = htp_connp_res_receiver_finalize_clear(tx->connp);
1369
50.9k
    if (rc != HTP_OK) return rc;
1370
1371
    // Run hook RESPONSE_HEADERS.
1372
50.9k
    rc = htp_hook_run_all(tx->connp->cfg->hook_response_headers, tx);
1373
50.9k
    if (rc != HTP_OK) return rc;
1374
1375
    // Initialize the decompression engine as necessary. We can deal with three
1376
    // scenarios:
1377
    //
1378
    // 1. Decompression is enabled, compression indicated in headers, and we decompress.
1379
    //
1380
    // 2. As above, but the user disables decompression by setting response_content_encoding
1381
    //    to COMPRESSION_NONE.
1382
    //
1383
    // 3. Decompression is disabled and we do not attempt to enable it, but the user
1384
    //    forces decompression by setting response_content_encoding to one of the
1385
    //    supported algorithms.
1386
50.9k
    if ((tx->response_content_encoding_processing == HTP_COMPRESSION_GZIP) ||
1387
46.0k
        (tx->response_content_encoding_processing == HTP_COMPRESSION_DEFLATE) ||
1388
45.6k
        (tx->response_content_encoding_processing == HTP_COMPRESSION_LZMA) ||
1389
45.6k
         ce_multi_comp)
1390
16.7k
    {
1391
16.7k
        if (tx->connp->out_decompressor != NULL) {
1392
5.11k
            htp_tx_res_destroy_decompressors(tx->connp);
1393
5.11k
        }
1394
1395
        /* normal case */
1396
16.7k
        if (!ce_multi_comp) {
1397
5.32k
            tx->connp->out_decompressor = htp_gzip_decompressor_create(tx->connp, tx->response_content_encoding_processing);
1398
5.32k
            if (tx->connp->out_decompressor == NULL) return HTP_ERROR;
1399
1400
5.32k
            tx->connp->out_decompressor->callback = htp_tx_res_process_body_data_decompressor_callback;
1401
1402
        /* multiple ce value case */
1403
11.4k
        } else {
1404
11.4k
            int layers = 0;
1405
11.4k
            htp_decompressor_t *comp = NULL;
1406
11.4k
            int nblzma = 0;
1407
1408
11.4k
            uint8_t *tok = NULL;
1409
11.4k
            size_t tok_len = 0;
1410
1411
11.4k
            uint8_t *input = bstr_ptr(ce->value);
1412
11.4k
            size_t input_len = bstr_len(ce->value);
1413
1414
            #if HTP_DEBUG
1415
            fprintf(stderr, "INPUT %"PRIuMAX, (uintmax_t)input_len);
1416
            fprint_raw_data(stderr, __func__, input, input_len);
1417
            #endif
1418
1419
26.7k
            while (input_len > 0 &&
1420
26.2k
                   get_token(input, input_len, ", ", &tok, &tok_len))
1421
26.1k
            {
1422
                #if HTP_DEBUG
1423
                fprintf(stderr, "TOKEN %"PRIuMAX, (uintmax_t)tok_len);
1424
                fprint_raw_data(stderr, __func__, tok, tok_len);
1425
                #endif
1426
26.1k
                enum htp_content_encoding_t cetype = HTP_COMPRESSION_NONE;
1427
1428
                /* check depth limit (0 means no limit) */
1429
26.1k
                if ((tx->connp->cfg->response_decompression_layer_limit != 0) &&
1430
26.1k
                    ((++layers) > tx->connp->cfg->response_decompression_layer_limit))
1431
5.20k
                {
1432
5.20k
                    htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0,
1433
5.20k
                            "Too many response content encoding layers");
1434
5.20k
                    break;
1435
5.20k
                }
1436
1437
20.9k
                nblzma++;
1438
20.9k
                if (bstr_util_mem_index_of_c_nocase(tok, tok_len, "gzip") != -1) {
1439
5.94k
                    if (!(bstr_util_cmp_mem(tok, tok_len, "gzip", 4) == 0 ||
1440
4.84k
                          bstr_util_cmp_mem(tok, tok_len, "x-gzip", 6) == 0)) {
1441
4.84k
                        htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0,
1442
4.84k
                                "C-E gzip has abnormal value");
1443
4.84k
                    }
1444
5.94k
                    cetype = HTP_COMPRESSION_GZIP;
1445
14.9k
                } else if (bstr_util_mem_index_of_c_nocase(tok, tok_len, "deflate") != -1) {
1446
5.42k
                    if (!(bstr_util_cmp_mem(tok, tok_len, "deflate", 7) == 0 ||
1447
5.05k
                          bstr_util_cmp_mem(tok, tok_len, "x-deflate", 9) == 0)) {
1448
5.05k
                        htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0,
1449
5.05k
                                "C-E deflate has abnormal value");
1450
5.05k
                    }
1451
5.42k
                    cetype = HTP_COMPRESSION_DEFLATE;
1452
9.54k
                } else if (bstr_util_cmp_mem(tok, tok_len, "lzma", 4) == 0) {
1453
0
                    cetype = HTP_COMPRESSION_LZMA;
1454
0
                    if (nblzma > tx->connp->cfg->response_lzma_layer_limit) {
1455
0
                        htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0,
1456
0
                                "Compression bomb: multiple encoding with lzma");
1457
0
                        break;
1458
0
                    }
1459
9.54k
                } else if (bstr_util_cmp_mem(tok, tok_len, "inflate", 7) == 0 || bstr_util_cmp_mem(tok, tok_len, "none", 4) == 0) {
1460
415
                    cetype = HTP_COMPRESSION_NONE;
1461
9.13k
                } else {
1462
                    // continue
1463
9.13k
                    htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0,
1464
9.13k
                            "C-E unknown setting");
1465
9.13k
                }
1466
1467
20.9k
                if (cetype != HTP_COMPRESSION_NONE) {
1468
11.3k
                    if (comp == NULL) {
1469
9.30k
                        tx->response_content_encoding_processing = cetype;
1470
9.30k
                        tx->connp->out_decompressor = htp_gzip_decompressor_create(tx->connp, tx->response_content_encoding_processing);
1471
9.30k
                        if (tx->connp->out_decompressor == NULL) {
1472
0
                            return HTP_ERROR;
1473
0
                        }
1474
9.30k
                        tx->connp->out_decompressor->callback = htp_tx_res_process_body_data_decompressor_callback;
1475
9.30k
                        comp = tx->connp->out_decompressor;
1476
9.30k
                    } else {
1477
2.07k
                        comp->next = htp_gzip_decompressor_create(tx->connp, cetype);
1478
2.07k
                        if (comp->next == NULL) {
1479
0
                            return HTP_ERROR;
1480
0
                        }
1481
2.07k
                        comp->next->callback = htp_tx_res_process_body_data_decompressor_callback;
1482
2.07k
                        comp = comp->next;
1483
2.07k
                    }
1484
11.3k
                }
1485
1486
20.9k
                if ((tok_len + 1) >= input_len)
1487
5.63k
                    break;
1488
15.2k
                input += (tok_len + 1);
1489
15.2k
                input_len -= (tok_len + 1);
1490
15.2k
            }
1491
11.4k
        }
1492
34.2k
    } else if (tx->response_content_encoding_processing != HTP_COMPRESSION_NONE) {
1493
0
        return HTP_ERROR;
1494
0
    }
1495
1496
50.9k
    return HTP_OK;
1497
50.9k
}
1498
1499
108k
htp_status_t htp_tx_state_response_start(htp_tx_t *tx) {
1500
108k
    if (tx == NULL) return HTP_ERROR;
1501
1502
108k
    tx->connp->out_tx = tx;
1503
1504
    // Run hook RESPONSE_START.
1505
108k
    htp_status_t rc = htp_hook_run_all(tx->connp->cfg->hook_response_start, tx);
1506
108k
    if (rc != HTP_OK) return rc;
1507
1508
    // Change state into response line parsing, except if we're following
1509
    // a HTTP/0.9 request (no status line or response headers).
1510
108k
    if (tx->is_protocol_0_9) {
1511
571
        tx->response_transfer_coding = HTP_CODING_IDENTITY;
1512
571
        tx->response_content_encoding_processing = HTP_COMPRESSION_NONE;
1513
571
        tx->response_progress = HTP_RESPONSE_BODY;
1514
571
        tx->connp->out_state = htp_connp_RES_BODY_IDENTITY_STREAM_CLOSE;
1515
571
        tx->connp->out_body_data_left = -1;
1516
108k
    } else {
1517
108k
        tx->connp->out_state = htp_connp_RES_LINE;
1518
108k
        tx->response_progress = HTP_RESPONSE_LINE;
1519
108k
    }
1520
1521
    /* If at this point we have no method and no uri and our status
1522
     * is still htp_connp_REQ_LINE, we likely have timed out request
1523
     * or a overly long request */
1524
108k
    if (tx->request_method == HTP_M_UNKNOWN && tx->request_uri == NULL && tx->connp->in_state == htp_connp_REQ_LINE) {
1525
8.39k
        htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Request line incomplete");
1526
8.39k
    }
1527
1528
108k
    return HTP_OK;
1529
108k
}
1530
1531
/**
1532
 * Register callback for the transaction-specific REQUEST_BODY_DATA hook.
1533
 *
1534
 * @param[in] tx
1535
 * @param[in] callback_fn
1536
 */
1537
0
void htp_tx_register_request_body_data(htp_tx_t *tx, int (*callback_fn)(htp_tx_data_t *)) {
1538
0
    if ((tx == NULL) || (callback_fn == NULL)) return;
1539
0
    htp_hook_register(&tx->hook_request_body_data, (htp_callback_fn_t) callback_fn);
1540
0
}
1541
1542
/**
1543
 * Register callback for the transaction-specific RESPONSE_BODY_DATA hook.
1544
 *
1545
 * @param[in] tx
1546
 * @param[in] callback_fn
1547
 */
1548
0
void htp_tx_register_response_body_data(htp_tx_t *tx, int (*callback_fn)(htp_tx_data_t *)) {
1549
0
    if ((tx == NULL) || (callback_fn == NULL)) return;
1550
0
    htp_hook_register(&tx->hook_response_body_data, (htp_callback_fn_t) callback_fn);
1551
0
}
1552
1553
434k
int htp_tx_is_complete(htp_tx_t *tx) {
1554
434k
    if (tx == NULL) return -1;
1555
1556
    // A transaction is considered complete only when both the request and
1557
    // response are complete. (Sometimes a complete response can be seen
1558
    // even while the request is ongoing.)
1559
434k
    if ((tx->request_progress != HTP_REQUEST_COMPLETE) || (tx->response_progress != HTP_RESPONSE_COMPLETE)) {
1560
263k
        return 0;
1561
263k
    } else {
1562
171k
        return 1;
1563
171k
    }
1564
434k
}