Coverage Report

Created: 2025-12-31 06:43

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
245k
htp_tx_t *htp_tx_create(htp_connp_t *connp) {
57
245k
    if (connp == NULL) return NULL;
58
59
245k
    htp_tx_t *tx = calloc(1, sizeof (htp_tx_t));
60
245k
    if (tx == NULL) return NULL;
61
62
245k
    tx->connp = connp;
63
245k
    tx->conn = connp->conn;
64
245k
    tx->index = htp_list_size(tx->conn->transactions);
65
245k
    tx->cfg = connp->cfg;
66
245k
    tx->is_config_shared = HTP_CONFIG_SHARED;
67
68
    // Request fields.
69
70
245k
    tx->request_progress = HTP_REQUEST_NOT_STARTED;
71
245k
    tx->request_protocol_number = HTP_PROTOCOL_UNKNOWN;
72
245k
    tx->request_content_length = -1;
73
74
245k
    tx->parsed_uri_raw = htp_uri_alloc();
75
245k
    if (tx->parsed_uri_raw == NULL) {
76
0
        htp_tx_destroy_incomplete(tx);
77
0
        return NULL;
78
0
    }
79
80
245k
    tx->request_headers = htp_table_create(32);
81
245k
    if (tx->request_headers == NULL) {
82
0
        htp_tx_destroy_incomplete(tx);
83
0
        return NULL;
84
0
    }
85
86
245k
    tx->request_params = htp_table_create(32);
87
245k
    if (tx->request_params == NULL) {
88
0
        htp_tx_destroy_incomplete(tx);
89
0
        return NULL;
90
0
    }
91
92
    // Response fields.
93
94
245k
    tx->response_progress = HTP_RESPONSE_NOT_STARTED;
95
245k
    tx->response_status = NULL;
96
245k
    tx->response_status_number = HTP_STATUS_UNKNOWN;
97
245k
    tx->response_protocol_number = HTP_PROTOCOL_UNKNOWN;
98
245k
    tx->response_content_length = -1;
99
100
245k
    tx->response_headers = htp_table_create(32);
101
245k
    if (tx->response_headers == NULL) {
102
0
        htp_tx_destroy_incomplete(tx);
103
0
        return NULL;
104
0
    }
105
106
245k
    htp_list_add(tx->conn->transactions, tx);
107
108
245k
    return tx;
109
245k
}
110
111
68.4k
htp_status_t htp_tx_destroy(htp_tx_t *tx) {
112
68.4k
    if (tx == NULL) return HTP_ERROR;
113
114
68.4k
    if (!htp_tx_is_complete(tx)) return HTP_ERROR;
115
116
68.4k
    htp_tx_destroy_incomplete(tx);
117
118
68.4k
    return HTP_OK;
119
68.4k
}
120
121
245k
void htp_tx_destroy_incomplete(htp_tx_t *tx) {
122
245k
    if (tx == NULL) return;
123
124
    // Disconnect transaction from other structures.
125
245k
    htp_conn_remove_tx(tx->conn, tx);
126
245k
    htp_connp_tx_remove(tx->connp, tx);
127
128
    // Request fields.
129
130
245k
    bstr_free(tx->request_line);
131
245k
    bstr_free(tx->request_method);
132
245k
    bstr_free(tx->request_uri);
133
245k
    bstr_free(tx->request_protocol);
134
245k
    bstr_free(tx->request_content_type);
135
245k
    bstr_free(tx->request_hostname);
136
245k
    htp_uri_free(tx->parsed_uri_raw);
137
245k
    htp_uri_free(tx->parsed_uri);
138
245k
    bstr_free(tx->request_auth_username);
139
245k
    bstr_free(tx->request_auth_password);
140
141
    // Request_headers.
142
245k
    if (tx->request_headers != NULL) {
143
245k
        htp_header_t *h = NULL;
144
481k
        for (size_t i = 0, n = htp_table_size(tx->request_headers); i < n; i++) {
145
236k
            h = htp_table_get_index(tx->request_headers, i, NULL);
146
236k
            bstr_free(h->name);
147
236k
            bstr_free(h->value);
148
236k
            free(h);
149
236k
        }
150
151
245k
        htp_table_destroy(tx->request_headers);
152
245k
    }
153
154
    // Request parsers.
155
156
245k
    htp_urlenp_destroy(tx->request_urlenp_query);
157
245k
    htp_urlenp_destroy(tx->request_urlenp_body);
158
245k
    htp_mpartp_destroy(tx->request_mpartp);
159
160
    // Request parameters.
161
162
245k
    htp_param_t *param = NULL;
163
245k
    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
245k
    htp_table_destroy(tx->request_params);
171
172
    // Request cookies.
173
174
245k
    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
245k
    htp_hook_destroy(tx->hook_request_body_data);
185
186
    // Response fields.
187
188
245k
    bstr_free(tx->response_line);
189
245k
    bstr_free(tx->response_protocol);
190
245k
    bstr_free(tx->response_status);
191
245k
    bstr_free(tx->response_message);
192
245k
    bstr_free(tx->response_content_type);
193
194
    // Destroy response headers.
195
245k
    if (tx->response_headers != NULL) {
196
245k
        htp_header_t *h = NULL;
197
402k
        for (size_t i = 0, n = htp_table_size(tx->response_headers); i < n; i++) {
198
156k
            h = htp_table_get_index(tx->response_headers, i, NULL);
199
156k
            bstr_free(h->name);
200
156k
            bstr_free(h->value);
201
156k
            free(h);
202
156k
        }
203
204
245k
        htp_table_destroy(tx->response_headers);
205
245k
    }
206
207
    // If we're using a private configuration structure, destroy it.
208
245k
    if (tx->is_config_shared == HTP_CONFIG_PRIVATE) {
209
0
        htp_config_destroy(tx->cfg);
210
0
    }
211
212
245k
    free(tx);
213
245k
}
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
108M
void *htp_tx_get_user_data(const htp_tx_t *tx) {
221
108M
    if (tx == NULL) return NULL;
222
108M
    return tx->user_data;
223
108M
}
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
490k
void htp_tx_set_user_data(htp_tx_t *tx, void *user_data) {
240
490k
    if (tx == NULL) return;
241
490k
    tx->user_data = user_data;
242
490k
}
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
229k
int htp_tx_req_has_body(const htp_tx_t *tx) {
275
229k
    if (tx == NULL) return -1;
276
277
229k
    if ((tx->request_transfer_coding == HTP_CODING_IDENTITY) || (tx->request_transfer_coding == HTP_CODING_CHUNKED)) {
278
12.3k
        return 1;
279
12.3k
    }
280
281
217k
    return 0;
282
229k
}
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
178k
static htp_status_t htp_tx_process_request_headers(htp_tx_t *tx) {
362
178k
    if (tx == NULL) return HTP_ERROR;
363
364
    // Determine if we have a request body, and how it is packaged.
365
366
178k
    htp_status_t rc = HTP_OK;
367
368
178k
    if (tx->connp->cfg->request_decompression_enabled) {
369
178k
        tx->request_content_encoding = HTP_COMPRESSION_NONE;
370
178k
        htp_header_t *ce = htp_table_get_c(tx->request_headers, "content-encoding");
371
178k
        if (ce != NULL) {
372
            /* fast paths: regular gzip and friends */
373
4.34k
            if ((bstr_cmp_c_nocasenorzero(ce->value, "gzip") == 0) ||
374
3.00k
                (bstr_cmp_c_nocasenorzero(ce->value, "x-gzip") == 0)) {
375
3.00k
                tx->request_content_encoding = HTP_COMPRESSION_GZIP;
376
3.00k
            } else if ((bstr_cmp_c_nocasenorzero(ce->value, "deflate") == 0) ||
377
946
                       (bstr_cmp_c_nocasenorzero(ce->value, "x-deflate") == 0)) {
378
395
                tx->request_content_encoding = HTP_COMPRESSION_DEFLATE;
379
946
            } 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
4.34k
            if ((tx->request_content_encoding != HTP_COMPRESSION_NONE))
384
3.40k
            {
385
3.40k
                if (tx->connp->req_decompressor != NULL) {
386
2.11k
                    htp_tx_req_destroy_decompressors(tx->connp);
387
2.11k
                }
388
3.40k
                tx->connp->req_decompressor = htp_gzip_decompressor_create(tx->connp, tx->request_content_encoding);
389
3.40k
                if (tx->connp->req_decompressor == NULL)
390
0
                    return HTP_ERROR;
391
392
3.40k
                tx->connp->req_decompressor->callback = htp_tx_req_process_body_data_decompressor_callback;
393
3.40k
            }
394
4.34k
        }
395
178k
    }
396
397
178k
    htp_header_t *cl = htp_table_get_c(tx->request_headers, "content-length");
398
178k
    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
178k
    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.55k
        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
151
            tx->request_transfer_coding = HTP_CODING_INVALID;
411
151
            tx->flags |= HTP_REQUEST_INVALID_T_E;
412
151
            tx->flags |= HTP_REQUEST_INVALID;
413
1.40k
        } 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.40k
            if (tx->request_protocol_number < HTP_PROTOCOL_1_1) {
421
1.15k
                tx->flags |= HTP_REQUEST_INVALID_T_E;
422
1.15k
                tx->flags |= HTP_REQUEST_SMUGGLING;
423
1.15k
            }
424
425
            // If the T-E header is present we are going to use it.
426
1.40k
            tx->request_transfer_coding = HTP_CODING_CHUNKED;
427
428
            // We are still going to check for the presence of C-L.
429
1.40k
            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
22
                tx->flags |= HTP_REQUEST_SMUGGLING;
439
22
            }
440
1.40k
        }
441
176k
    } else if (cl != NULL) {
442
        // Check for a folded C-L header.
443
11.7k
        if (cl->flags & HTP_FIELD_FOLDED) {
444
0
            tx->flags |= HTP_REQUEST_SMUGGLING;
445
0
        }
446
447
        // Check for multiple C-L headers.
448
11.7k
        if (cl->flags & HTP_FIELD_REPEATED) {
449
4.89k
            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.89k
        }
454
455
        // Get the body length.
456
11.7k
        tx->request_content_length = htp_parse_content_length(cl->value, tx->connp);
457
11.7k
        if (tx->request_content_length < 0) {
458
47
            tx->request_transfer_coding = HTP_CODING_INVALID;
459
47
            tx->flags |= HTP_REQUEST_INVALID_C_L;
460
47
            tx->flags |= HTP_REQUEST_INVALID;
461
11.6k
        } else {
462
            // We have a request body of known length.
463
11.6k
            tx->request_transfer_coding = HTP_CODING_IDENTITY;
464
11.6k
        }
465
164k
    } else {
466
        // No body.
467
164k
        tx->request_transfer_coding = HTP_CODING_NO_BODY;
468
164k
    }
469
470
    // If we could not determine the correct body handling,
471
    // consider the request invalid.
472
178k
    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
178k
    if (tx->request_method_number == HTP_M_PUT) {
479
522
        if (htp_tx_req_has_body(tx)) {
480
            // Prepare to treat PUT request body as a file.
481
            
482
29
            tx->connp->put_file = calloc(1, sizeof (htp_file_t));
483
29
            if (tx->connp->put_file == NULL) return HTP_ERROR;
484
485
29
            tx->connp->put_file->fd = -1;
486
29
            tx->connp->put_file->source = HTP_FILE_PUT;
487
493
        } else {
488
            // TODO Warn about PUT request without a body.
489
493
        }
490
522
    }
491
492
    // Determine hostname.
493
494
    // Use the hostname from the URI, when available.   
495
178k
    if (tx->parsed_uri->hostname != NULL) {
496
26.0k
        tx->request_hostname = bstr_dup(tx->parsed_uri->hostname);
497
26.0k
        if (tx->request_hostname == NULL) return HTP_ERROR;
498
26.0k
    }
499
500
178k
    tx->request_port_number = tx->parsed_uri->port_number;
501
502
    // Examine the Host header.
503
504
178k
    htp_header_t *h = htp_table_get_c(tx->request_headers, "host");
505
178k
    if (h == NULL) {
506
        // No host information in the headers.
507
508
        // HTTP/1.1 requires host information in the headers.
509
156k
        if (tx->request_protocol_number >= HTP_PROTOCOL_1_1) {
510
915
            tx->flags |= HTP_HOST_MISSING;
511
915
        }
512
156k
    } else {
513
        // Host information available in the headers.
514
515
21.3k
        bstr *hostname;
516
21.3k
        int port;
517
518
21.3k
        rc = htp_parse_header_hostport(h->value, &hostname, NULL, &port, &(tx->flags));
519
21.3k
        if (rc != HTP_OK) return rc;
520
521
21.3k
        if (hostname != NULL) {
522
            // The host information in the headers is valid.
523
524
            // Is there host information in the URI?
525
19.1k
            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
13.6k
                tx->request_hostname = hostname;
529
13.6k
                tx->request_port_number = port;
530
13.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.53k
                if (bstr_cmp_nocase(hostname, tx->request_hostname) != 0) {                    
536
2.45k
                    tx->flags |= HTP_HOST_AMBIGUOUS;
537
2.45k
                }
538
539
                // Check for different ports.
540
5.53k
                if (((tx->request_port_number != -1)&&(port != -1))&&(tx->request_port_number != port)) {
541
509
                    tx->flags |= HTP_HOST_AMBIGUOUS;
542
509
                }
543
544
5.53k
                bstr_free(hostname);
545
5.53k
            }
546
19.1k
        } else {
547
            // Invalid host information in the headers.
548
549
2.16k
            if (tx->request_hostname != NULL) {
550
                // Raise the flag, even though the host information in the headers is invalid.
551
534
                tx->flags |= HTP_HOST_AMBIGUOUS;
552
534
            }
553
2.16k
        }
554
21.3k
    }
555
556
    // Determine Content-Type.
557
178k
    htp_header_t *ct = htp_table_get_c(tx->request_headers, "content-type");
558
178k
    if (ct != NULL) {
559
9.52k
        rc = htp_parse_ct_header(ct->value, &tx->request_content_type);
560
9.52k
        if (rc != HTP_OK) return rc;
561
9.52k
    }
562
563
    // Parse cookies.
564
178k
    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
178k
    if (tx->connp->cfg->parse_request_auth) {
571
178k
        rc = htp_parse_authorization(tx->connp);
572
178k
        if (rc == HTP_DECLINED) {
573
            // Don't fail the stream if an authorization header is invalid, just set a flag.
574
1.00k
            tx->flags |= HTP_AUTH_INVALID;
575
177k
        } else {
576
177k
            if (rc != HTP_OK) return rc;
577
177k
        }
578
178k
    }
579
580
    // Finalize sending raw header data.
581
178k
    rc = htp_connp_req_receiver_finalize_clear(tx->connp);
582
178k
    if (rc != HTP_OK) return rc;
583
584
    // Run hook REQUEST_HEADERS.
585
178k
    rc = htp_hook_run_all(tx->connp->cfg->hook_request_headers, tx);
586
178k
    if (rc != HTP_OK) return rc;
587
588
    // We still proceed if the request is invalid.
589
590
178k
    return HTP_OK;
591
178k
}
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
734k
htp_status_t htp_tx_req_process_body_data_ex(htp_tx_t *tx, const void *data, size_t len) {
601
734k
    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
734k
    htp_tx_data_t d;
609
734k
    d.tx = tx;
610
734k
    d.data = (unsigned char *) data;
611
734k
    d.len = len;
612
734k
    d.is_last = (data == NULL && len == 0);
613
614
734k
    switch(tx->request_content_encoding) {
615
54.6k
        case HTP_COMPRESSION_UNKNOWN:
616
381k
        case HTP_COMPRESSION_NONE:
617
            // When there's no decompression, request_entity_len.
618
            // is identical to request_message_len.
619
381k
            tx->request_entity_len += d.len;
620
381k
            htp_status_t rc = htp_req_run_hook_body_data(tx->connp, &d);
621
381k
            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
381k
            break;
626
627
381k
        case HTP_COMPRESSION_GZIP:
628
353k
        case HTP_COMPRESSION_DEFLATE:
629
353k
        case HTP_COMPRESSION_LZMA:
630
            // In severe memory stress these could be NULL
631
353k
            if (tx->connp->req_decompressor == NULL)
632
1
                return HTP_ERROR;
633
634
            // Send data buffer to the decompressor.
635
353k
            htp_gzip_decompressor_decompress(tx->connp->req_decompressor, &d);
636
637
353k
            if (data == NULL) {
638
                // Shut down the decompressor, if we used one.
639
1.00k
                htp_tx_req_destroy_decompressors(tx->connp);
640
1.00k
            }
641
353k
            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
734k
    }
650
651
734k
    return HTP_OK;
652
734k
}
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
47.4k
htp_status_t htp_tx_state_response_line(htp_tx_t *tx) {
729
47.4k
    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
47.4k
    if (tx->response_protocol_number == HTP_PROTOCOL_INVALID) {
746
39.1k
        htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0,
747
39.1k
                "Invalid response line: invalid protocol");
748
39.1k
        tx->flags |= HTP_STATUS_LINE_INVALID;
749
39.1k
    }
750
47.4k
    if ((tx->response_status_number == HTP_STATUS_INVALID)
751
28.2k
            || (tx->response_status_number < HTP_VALID_STATUS_MIN)
752
28.2k
            || (tx->response_status_number > HTP_VALID_STATUS_MAX)) {
753
19.2k
        htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0,
754
19.2k
                "Invalid response line: invalid response status %d.",
755
19.2k
                tx->response_status_number);
756
19.2k
        tx->response_status_number = HTP_STATUS_INVALID;
757
19.2k
        tx->flags |= HTP_STATUS_LINE_INVALID;
758
19.2k
    }
759
760
    // Run hook HTP_RESPONSE_LINE
761
47.4k
    htp_status_t rc = htp_hook_run_all(tx->connp->cfg->hook_response_line, tx);
762
47.4k
    if (rc != HTP_OK) return rc;
763
764
47.4k
    return HTP_OK;
765
47.4k
}
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
32.1k
static void htp_tx_res_destroy_decompressors(htp_connp_t *connp) {
824
32.1k
    htp_decompressor_t *comp = connp->out_decompressor;
825
50.4k
    while (comp) {
826
18.2k
        htp_decompressor_t *next = comp->next;
827
18.2k
        htp_gzip_decompressor_destroy(comp);
828
18.2k
        comp = next;
829
18.2k
    }
830
32.1k
    connp->out_decompressor = NULL;
831
32.1k
}
832
833
20.7k
static void htp_tx_req_destroy_decompressors(htp_connp_t *connp) {
834
20.7k
    htp_decompressor_t *comp = connp->req_decompressor;
835
24.1k
    while (comp) {
836
3.40k
        htp_decompressor_t *next = comp->next;
837
3.40k
        htp_gzip_decompressor_destroy(comp);
838
3.40k
        comp = next;
839
3.40k
    }
840
20.7k
    connp->req_decompressor = NULL;
841
20.7k
}
842
843
17.6k
void htp_connp_destroy_decompressors(htp_connp_t *connp) {
844
17.6k
    htp_tx_res_destroy_decompressors(connp);
845
17.6k
    htp_tx_req_destroy_decompressors(connp);
846
17.6k
}
847
848
95.9k
static htp_status_t htp_timer_track(int32_t *time_spent, struct timeval * after, struct timeval *before) {
849
95.9k
    if (after->tv_sec < before->tv_sec) {
850
0
        return HTP_ERROR;
851
95.9k
    } else if (after->tv_sec == before->tv_sec) {
852
95.6k
        if (after->tv_usec < before->tv_usec) {
853
0
            return HTP_ERROR;
854
0
        }
855
95.6k
        *time_spent += after->tv_usec - before->tv_usec;
856
95.6k
    } else {
857
240
        *time_spent += (after->tv_sec - before->tv_sec) * 1000000 + after->tv_usec - before->tv_usec;
858
240
    }
859
95.9k
    return HTP_OK;
860
95.9k
}
861
862
278k
static htp_status_t htp_tx_req_process_body_data_decompressor_callback(htp_tx_data_t *d) {
863
278k
    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
278k
    d->tx->request_entity_len += d->len;
871
872
    // Invoke all callbacks.
873
278k
    htp_status_t rc = htp_req_run_hook_body_data(d->tx->connp, d);
874
278k
    if (rc != HTP_OK) return HTP_ERROR;
875
278k
    d->tx->connp->req_decompressor->nb_callbacks++;
876
278k
    if (d->tx->connp->req_decompressor->nb_callbacks % HTP_COMPRESSION_TIME_FREQ_TEST == 0) {
877
931
        struct timeval after;
878
931
        gettimeofday(&after, NULL);
879
        // sanity check for race condition if system time changed
880
931
        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
931
            d->tx->connp->req_decompressor->time_before = after;
883
931
            if (d->tx->connp->req_decompressor->time_spent > d->tx->connp->cfg->compression_time_limit ) {
884
0
                htp_log(d->tx->connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0,
885
0
                        "Compression bomb: spent %"PRId32" us decompressing",
886
0
                        d->tx->connp->req_decompressor->time_spent);
887
0
                d->tx->connp->req_decompressor->passthrough = 1;
888
0
            }
889
931
        }
890
891
931
    }
892
278k
    if (d->tx->request_entity_len > d->tx->connp->cfg->compression_bomb_limit &&
893
1.42k
        d->tx->request_entity_len > HTP_COMPRESSION_BOMB_RATIO * d->tx->request_message_len) {
894
1.37k
        htp_log(d->tx->connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0,
895
1.37k
                "Compression bomb: decompressed %"PRId64" bytes out of %"PRId64,
896
1.37k
                d->tx->request_entity_len, d->tx->request_message_len);
897
1.37k
        return HTP_ERROR;
898
1.37k
    }
899
900
276k
    return HTP_OK;
901
278k
}
902
903
238k
static htp_status_t htp_tx_res_process_body_data_decompressor_callback(htp_tx_data_t *d) {
904
238k
    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
238k
    d->tx->response_entity_len += d->len;
912
913
    // Invoke all callbacks.
914
238k
    htp_status_t rc = htp_res_run_hook_body_data(d->tx->connp, d);
915
238k
    if (rc != HTP_OK) return HTP_ERROR;
916
238k
    d->tx->connp->out_decompressor->nb_callbacks++;
917
238k
    if (d->tx->connp->out_decompressor->nb_callbacks % HTP_COMPRESSION_TIME_FREQ_TEST == 0) {
918
732
        struct timeval after;
919
732
        gettimeofday(&after, NULL);
920
        // sanity check for race condition if system time changed
921
732
        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
732
            d->tx->connp->out_decompressor->time_before = after;
924
732
            if (d->tx->connp->out_decompressor->time_spent > d->tx->connp->cfg->compression_time_limit ) {
925
378
                htp_log(d->tx->connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0,
926
378
                        "Compression bomb: spent %"PRId32" us decompressing",
927
378
                        d->tx->connp->out_decompressor->time_spent);
928
378
                d->tx->connp->out_decompressor->passthrough = 1;
929
378
            }
930
732
        }
931
932
732
    }
933
238k
    if (d->tx->response_entity_len > d->tx->connp->cfg->compression_bomb_limit &&
934
187k
        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
238k
    return HTP_OK;
942
238k
}
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
768k
htp_status_t htp_tx_res_process_body_data_ex(htp_tx_t *tx, const void *data, size_t len) {
951
768k
    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
768k
    htp_tx_data_t d;
961
962
768k
    d.tx = tx;
963
768k
    d.data = (unsigned char *) data;
964
768k
    d.len = len;
965
768k
    d.is_last = 0;
966
967
    // Keep track of body size before decompression.
968
768k
    tx->response_message_len += d.len;
969
970
768k
    switch (tx->response_content_encoding_processing) {
971
45.3k
        case HTP_COMPRESSION_GZIP:
972
94.8k
        case HTP_COMPRESSION_DEFLATE:
973
94.8k
        case HTP_COMPRESSION_LZMA:
974
            // In severe memory stress these could be NULL
975
94.8k
            if (tx->connp->out_decompressor == NULL) {
976
572
                if (data == NULL) {
977
                    // we were already stopped on a gap finishing CL
978
540
                    return HTP_OK;
979
540
                }
980
32
                return HTP_ERROR;
981
572
            }
982
983
94.2k
            struct timeval after;
984
94.2k
            gettimeofday(&tx->connp->out_decompressor->time_before, NULL);
985
            // Send data buffer to the decompressor.
986
94.2k
            tx->connp->out_decompressor->nb_callbacks=0;
987
94.2k
            htp_gzip_decompressor_decompress(tx->connp->out_decompressor, &d);
988
94.2k
            gettimeofday(&after, NULL);
989
            // sanity check for race condition if system time changed
990
94.2k
            if ( htp_timer_track(&tx->connp->out_decompressor->time_spent, &after, &tx->connp->out_decompressor->time_before) == HTP_OK) {
991
94.2k
                if ( tx->connp->out_decompressor->time_spent > tx->connp->cfg->compression_time_limit ) {
992
793
                    htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0,
993
793
                            "Compression bomb: spent %"PRId32" us decompressing",
994
793
                            tx->connp->out_decompressor->time_spent);
995
793
                    tx->connp->out_decompressor->passthrough = 1;
996
793
                }
997
94.2k
            }
998
999
94.2k
            if (data == NULL) {
1000
                // Shut down the decompressor, if we used one.
1001
6.94k
                htp_tx_res_destroy_decompressors(tx->connp);
1002
6.94k
            }
1003
94.2k
            break;
1004
1005
673k
        case HTP_COMPRESSION_NONE:
1006
            // When there's no decompression, response_entity_len.
1007
            // is identical to response_message_len.
1008
673k
            tx->response_entity_len += d.len;
1009
1010
673k
            htp_status_t rc = htp_res_run_hook_body_data(tx->connp, &d);
1011
673k
            if (rc != HTP_OK) return HTP_ERROR;
1012
673k
            break;
1013
1014
673k
        default:
1015
            // Internal error.
1016
748
            htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0,
1017
748
                    "[Internal Error] Invalid tx->response_content_encoding_processing value: %d",
1018
748
                    tx->response_content_encoding_processing);
1019
748
            return HTP_ERROR;
1020
0
            break;
1021
768k
    }
1022
1023
767k
    return HTP_OK;
1024
768k
}
1025
1026
228k
htp_status_t htp_tx_state_request_complete_partial(htp_tx_t *tx) {
1027
228k
    if (tx == NULL) return HTP_ERROR;
1028
1029
    // Finalize request body.
1030
228k
    if (htp_tx_req_has_body(tx)) {
1031
12.2k
        htp_status_t rc = htp_tx_req_process_body_data_ex(tx, NULL, 0);
1032
12.2k
        if (rc != HTP_OK) return rc;
1033
12.2k
    }
1034
1035
228k
    tx->request_progress = HTP_REQUEST_COMPLETE;
1036
1037
    // Run hook REQUEST_COMPLETE.
1038
228k
    htp_status_t rc = htp_hook_run_all(tx->connp->cfg->hook_request_complete, tx);
1039
228k
    if (rc != HTP_OK) return rc;
1040
228k
    rc = htp_connp_req_receiver_finalize_clear(tx->connp);
1041
228k
    if (rc != HTP_OK) return rc;
1042
1043
    // Clean-up.
1044
228k
    if (tx->connp->put_file != NULL) {
1045
25
        bstr_free(tx->connp->put_file->filename);
1046
25
        free(tx->connp->put_file);
1047
25
        tx->connp->put_file = NULL;
1048
25
    }
1049
1050
228k
    return HTP_OK;
1051
228k
}
1052
1053
228k
htp_status_t htp_tx_state_request_complete(htp_tx_t *tx) {
1054
228k
    if (tx == NULL) return HTP_ERROR;
1055
1056
228k
    if (tx->request_progress != HTP_REQUEST_COMPLETE) {
1057
228k
        htp_status_t rc = htp_tx_state_request_complete_partial(tx);
1058
228k
        if (rc != HTP_OK) return rc;
1059
228k
    }
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
228k
    htp_connp_t *connp = tx->connp;
1065
1066
    // Determine what happens next, and remove this transaction from the parser.
1067
228k
    if (tx->is_protocol_0_9) {
1068
3.13k
        connp->in_state = htp_connp_REQ_IGNORE_DATA_AFTER_HTTP_0_9;
1069
225k
    } else {
1070
225k
        connp->in_state = htp_connp_REQ_IDLE;
1071
225k
    }
1072
1073
    // Check if the entire transaction is complete. This call may
1074
    // destroy the transaction, if auto-destroy is enabled.
1075
228k
    htp_tx_finalize(tx);
1076
1077
    // At this point, tx may no longer be valid.
1078
1079
228k
    connp->in_tx = NULL;
1080
1081
228k
    return HTP_OK;
1082
228k
}
1083
1084
193k
htp_status_t htp_tx_state_request_start(htp_tx_t *tx) {
1085
193k
    if (tx == NULL) return HTP_ERROR;
1086
1087
    // Run hook REQUEST_START.
1088
193k
    htp_status_t rc = htp_hook_run_all(tx->connp->cfg->hook_request_start, tx);
1089
193k
    if (rc != HTP_OK) return rc;
1090
1091
    // Change state into request line parsing.
1092
193k
    tx->connp->in_state = htp_connp_REQ_LINE;
1093
193k
    tx->connp->in_tx->request_progress = HTP_REQUEST_LINE;
1094
1095
193k
    return HTP_OK;
1096
193k
}
1097
1098
180k
htp_status_t htp_tx_state_request_headers(htp_tx_t *tx) {
1099
180k
    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
180k
    if (tx->request_progress > HTP_REQUEST_HEADERS) {
1105
        // Request trailers.
1106
1107
        // Run hook HTP_REQUEST_TRAILER.
1108
2.81k
        htp_status_t rc = htp_hook_run_all(tx->connp->cfg->hook_request_trailer, tx);
1109
2.81k
        if (rc != HTP_OK) return rc;
1110
1111
        // Finalize sending raw header data.
1112
2.81k
        rc = htp_connp_req_receiver_finalize_clear(tx->connp);
1113
2.81k
        if (rc != HTP_OK) return rc;
1114
1115
        // Completed parsing this request; finalize it now.
1116
2.81k
        tx->connp->in_state = htp_connp_REQ_FINALIZE;
1117
178k
    } else if (tx->request_progress >= HTP_REQUEST_LINE) {
1118
        // Request headers.
1119
1120
        // Did this request arrive in multiple data chunks?
1121
178k
        if (tx->connp->in_chunk_count != tx->connp->in_chunk_request_index) {
1122
21.2k
            tx->flags |= HTP_MULTI_PACKET_HEAD;
1123
21.2k
        }
1124
1125
178k
        htp_status_t rc = htp_tx_process_request_headers(tx);
1126
178k
        if (rc != HTP_OK) return rc;
1127
1128
178k
        tx->connp->in_state = htp_connp_REQ_CONNECT_CHECK;
1129
178k
    } 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
180k
    return HTP_OK;
1136
180k
}
1137
1138
187k
htp_status_t htp_tx_state_request_line(htp_tx_t *tx) {
1139
187k
    if (tx == NULL) return HTP_ERROR;
1140
1141
    // Determine how to process the request URI.
1142
1143
187k
    if (tx->request_method_number == HTP_M_CONNECT) {
1144
        // When CONNECT is used, the request URI contains an authority string.
1145
3.44k
        if (htp_parse_uri_hostport(tx->connp, tx->request_uri, tx->parsed_uri_raw) != HTP_OK) {
1146
1
            return HTP_ERROR;
1147
1
        }
1148
183k
    } else {
1149
        // Parse the request URI into htp_tx_t::parsed_uri_raw.
1150
183k
        if (htp_parse_uri(tx->request_uri, &(tx->parsed_uri_raw)) != HTP_OK) {
1151
0
            return HTP_ERROR;
1152
0
        }
1153
183k
    }
1154
1155
    // Build htp_tx_t::parsed_uri, but only if it was not explicitly set already.
1156
187k
    if (tx->parsed_uri == NULL) {
1157
187k
        tx->parsed_uri = htp_uri_alloc();
1158
187k
        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
187k
        if (htp_normalize_parsed_uri(tx, tx->parsed_uri_raw, tx->parsed_uri) != HTP_OK) {
1162
0
            return HTP_ERROR;
1163
0
        }
1164
187k
    }
1165
1166
    // Check parsed_uri hostname.
1167
187k
    if (tx->parsed_uri->hostname != NULL) {
1168
27.0k
        if (htp_validate_hostname(tx->parsed_uri->hostname) == 0) {
1169
21.9k
            tx->flags |= HTP_HOSTU_INVALID;
1170
21.9k
        }
1171
27.0k
    }
1172
1173
    // Run hook REQUEST_URI_NORMALIZE.
1174
187k
    htp_status_t rc = htp_hook_run_all(tx->connp->cfg->hook_request_uri_normalize, tx);
1175
187k
    if (rc != HTP_OK) return rc;
1176
1177
1178
    // Run hook REQUEST_LINE.
1179
187k
    rc = htp_hook_run_all(tx->connp->cfg->hook_request_line, tx);
1180
187k
    if (rc != HTP_OK) return rc;
1181
1182
    // Move on to the next phase.
1183
187k
    tx->connp->in_state = htp_connp_REQ_PROTOCOL;
1184
1185
187k
    return HTP_OK;
1186
187k
}
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
319k
htp_status_t htp_tx_finalize(htp_tx_t *tx) {
1194
319k
    if (tx == NULL) return HTP_ERROR;
1195
1196
319k
    if (!htp_tx_is_complete(tx)) return HTP_OK;
1197
1198
    // Run hook TRANSACTION_COMPLETE.
1199
82.6k
    htp_status_t rc = htp_hook_run_all(tx->connp->cfg->hook_transaction_complete, tx);
1200
82.6k
    if (rc != HTP_OK) return rc;
1201
1202
    // In streaming processing, we destroy the transaction because it will not be needed any more.
1203
82.6k
    if (tx->connp->cfg->tx_auto_destroy) {
1204
0
        htp_tx_destroy(tx);
1205
0
    }
1206
1207
82.6k
    return HTP_OK;
1208
82.6k
}
1209
1210
91.1k
htp_status_t htp_tx_state_response_complete_ex(htp_tx_t *tx, int hybrid_mode) {
1211
91.1k
    if (tx == NULL) return HTP_ERROR;
1212
1213
91.1k
    if (tx->response_progress != HTP_RESPONSE_COMPLETE) {
1214
90.9k
        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
90.9k
        if (tx->response_transfer_coding != HTP_CODING_NO_BODY) {
1218
76.2k
            htp_tx_res_process_body_data_ex(tx, NULL, 0);
1219
76.2k
        }
1220
1221
        // Run hook RESPONSE_COMPLETE.
1222
90.9k
        htp_status_t rc = htp_hook_run_all(tx->connp->cfg->hook_response_complete, tx);
1223
90.9k
        if (rc != HTP_OK) return rc;
1224
1225
        // Clear the data receivers hook if any
1226
90.9k
        rc = htp_connp_res_receiver_finalize_clear(tx->connp);
1227
90.9k
        if (rc != HTP_OK) return rc;
1228
90.9k
    }
1229
1230
91.1k
    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
91.1k
        if ((tx->connp->in_status == HTP_STREAM_DATA_OTHER) && (tx->connp->in_tx == tx->connp->out_tx)) {
1245
112
            return HTP_DATA_OTHER;
1246
112
        }
1247
1248
        // Do we have a signal to yield to inbound processing at
1249
        // the end of the next transaction?
1250
90.9k
        if (tx->connp->out_data_other_at_tx_end) {
1251
            // We do. Let's yield then.
1252
109
            tx->connp->out_data_other_at_tx_end = 0;
1253
109
            return HTP_DATA_OTHER;
1254
109
        }
1255
90.9k
    }
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
90.8k
    htp_connp_t *connp = tx->connp;
1260
1261
    // Finalize the transaction. This may call may destroy the transaction, if auto-destroy is enabled.
1262
90.8k
    htp_status_t rc = htp_tx_finalize(tx);
1263
90.8k
    if (rc != HTP_OK) return rc;
1264
1265
    // Disconnect transaction from the parser.
1266
90.8k
    connp->out_tx = NULL;
1267
1268
90.8k
    connp->out_state = htp_connp_RES_IDLE;
1269
1270
90.8k
    return HTP_OK;
1271
90.8k
}
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
33.3k
{
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
33.3k
    size_t i = 0;
1287
1288
    /* skip leading 'separators' */
1289
36.4k
    while (i < in_len)
1290
36.1k
    {
1291
36.1k
        int match = 0;
1292
104k
        for (const char *s = seps; *s != '\0'; s++) {
1293
71.4k
            if (in[i] == *s) {
1294
3.16k
                match++;
1295
3.16k
                break;
1296
3.16k
            }
1297
71.4k
        }
1298
36.1k
        if (!match)
1299
32.9k
            break;
1300
1301
3.16k
        i++;
1302
3.16k
    }
1303
33.3k
    if (i >= in_len)
1304
370
        return 0;
1305
1306
32.9k
    in += i;
1307
32.9k
    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
732k
    for (i = 0; i < in_len; i++)
1315
724k
    {
1316
2.14M
        for (const char *s = seps; *s != '\0'; s++) {
1317
1.44M
            if (in[i] == *s) {
1318
24.7k
                *ret_tok_ptr = (unsigned char *)in;
1319
24.7k
                *ret_tok_len = i;
1320
24.7k
                return 1;
1321
24.7k
            }
1322
1.44M
        }
1323
724k
    }
1324
1325
8.18k
    *ret_tok_ptr = (unsigned char *)in;
1326
8.18k
    *ret_tok_len = in_len;
1327
8.18k
    return 1;
1328
32.9k
}
1329
1330
43.5k
htp_status_t htp_tx_state_response_headers(htp_tx_t *tx) {
1331
43.5k
    if (tx == NULL) return HTP_ERROR;
1332
1333
    // Check for compression.
1334
1335
    // Determine content encoding.
1336
1337
43.5k
    int ce_multi_comp = 0;
1338
43.5k
    tx->response_content_encoding = HTP_COMPRESSION_NONE;
1339
43.5k
    htp_header_t *ce = htp_table_get_c(tx->response_headers, "content-encoding");
1340
43.5k
    if (ce != NULL) {
1341
        /* fast paths: regular gzip and friends */
1342
16.5k
        if ((bstr_cmp_c_nocasenorzero(ce->value, "gzip") == 0) ||
1343
13.8k
            (bstr_cmp_c_nocasenorzero(ce->value, "x-gzip") == 0)) {
1344
2.68k
            tx->response_content_encoding = HTP_COMPRESSION_GZIP;
1345
13.8k
        } else if ((bstr_cmp_c_nocasenorzero(ce->value, "deflate") == 0) ||
1346
13.3k
                   (bstr_cmp_c_nocasenorzero(ce->value, "x-deflate") == 0)) {
1347
485
            tx->response_content_encoding = HTP_COMPRESSION_DEFLATE;
1348
13.3k
        } else if (bstr_cmp_c_nocasenorzero(ce->value, "lzma") == 0) {
1349
0
            tx->response_content_encoding = HTP_COMPRESSION_LZMA;
1350
13.3k
        } else if (bstr_cmp_c_nocasenorzero(ce->value, "inflate") == 0 ||
1351
13.3k
                   bstr_cmp_c_nocasenorzero(ce->value, "identity") == 0) {
1352
            // ignore
1353
13.3k
        } else {
1354
            /* exceptional cases: enter slow path */
1355
13.3k
            ce_multi_comp = 1;
1356
13.3k
        }
1357
16.5k
    }
1358
1359
    // Configure decompression, if enabled in the configuration.
1360
43.5k
    if (tx->connp->cfg->response_decompression_enabled) {
1361
43.5k
        tx->response_content_encoding_processing = tx->response_content_encoding;
1362
43.5k
    } 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
43.5k
    htp_status_t rc = htp_connp_res_receiver_finalize_clear(tx->connp);
1369
43.5k
    if (rc != HTP_OK) return rc;
1370
1371
    // Run hook RESPONSE_HEADERS.
1372
43.5k
    rc = htp_hook_run_all(tx->connp->cfg->hook_response_headers, tx);
1373
43.5k
    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
43.5k
    if ((tx->response_content_encoding_processing == HTP_COMPRESSION_GZIP) ||
1387
40.8k
        (tx->response_content_encoding_processing == HTP_COMPRESSION_DEFLATE) ||
1388
40.3k
        (tx->response_content_encoding_processing == HTP_COMPRESSION_LZMA) ||
1389
40.3k
         ce_multi_comp)
1390
16.5k
    {
1391
16.5k
        if (tx->connp->out_decompressor != NULL) {
1392
7.58k
            htp_tx_res_destroy_decompressors(tx->connp);
1393
7.58k
        }
1394
1395
        /* normal case */
1396
16.5k
        if (!ce_multi_comp) {
1397
3.16k
            tx->connp->out_decompressor = htp_gzip_decompressor_create(tx->connp, tx->response_content_encoding_processing);
1398
3.16k
            if (tx->connp->out_decompressor == NULL) return HTP_ERROR;
1399
1400
3.16k
            tx->connp->out_decompressor->callback = htp_tx_res_process_body_data_decompressor_callback;
1401
1402
        /* multiple ce value case */
1403
13.3k
        } else {
1404
13.3k
            int layers = 0;
1405
13.3k
            htp_decompressor_t *comp = NULL;
1406
13.3k
            int nblzma = 0;
1407
1408
13.3k
            uint8_t *tok = NULL;
1409
13.3k
            size_t tok_len = 0;
1410
1411
13.3k
            uint8_t *input = bstr_ptr(ce->value);
1412
13.3k
            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
33.4k
            while (input_len > 0 &&
1420
33.3k
                   get_token(input, input_len, ", ", &tok, &tok_len))
1421
32.9k
            {
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
32.9k
                enum htp_content_encoding_t cetype = HTP_COMPRESSION_NONE;
1427
1428
                /* check depth limit (0 means no limit) */
1429
32.9k
                if ((tx->connp->cfg->response_decompression_layer_limit != 0) &&
1430
32.9k
                    ((++layers) > tx->connp->cfg->response_decompression_layer_limit))
1431
7.51k
                {
1432
7.51k
                    htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0,
1433
7.51k
                            "Too many response content encoding layers");
1434
7.51k
                    break;
1435
7.51k
                }
1436
1437
25.4k
                nblzma++;
1438
25.4k
                if (bstr_util_mem_index_of_c_nocase(tok, tok_len, "gzip") != -1) {
1439
6.47k
                    if (!(bstr_util_cmp_mem(tok, tok_len, "gzip", 4) == 0 ||
1440
5.33k
                          bstr_util_cmp_mem(tok, tok_len, "x-gzip", 6) == 0)) {
1441
5.33k
                        htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0,
1442
5.33k
                                "C-E gzip has abnormal value");
1443
5.33k
                    }
1444
6.47k
                    cetype = HTP_COMPRESSION_GZIP;
1445
18.9k
                } else if (bstr_util_mem_index_of_c_nocase(tok, tok_len, "deflate") != -1) {
1446
8.59k
                    if (!(bstr_util_cmp_mem(tok, tok_len, "deflate", 7) == 0 ||
1447
6.91k
                          bstr_util_cmp_mem(tok, tok_len, "x-deflate", 9) == 0)) {
1448
6.91k
                        htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0,
1449
6.91k
                                "C-E deflate has abnormal value");
1450
6.91k
                    }
1451
8.59k
                    cetype = HTP_COMPRESSION_DEFLATE;
1452
10.3k
                } 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
10.3k
                } else if (bstr_util_cmp_mem(tok, tok_len, "inflate", 7) == 0 || bstr_util_cmp_mem(tok, tok_len, "none", 4) == 0) {
1460
81
                    cetype = HTP_COMPRESSION_NONE;
1461
10.2k
                } else {
1462
                    // continue
1463
10.2k
                    htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0,
1464
10.2k
                            "C-E unknown setting");
1465
10.2k
                }
1466
1467
25.4k
                if (cetype != HTP_COMPRESSION_NONE) {
1468
15.0k
                    if (comp == NULL) {
1469
11.7k
                        tx->response_content_encoding_processing = cetype;
1470
11.7k
                        tx->connp->out_decompressor = htp_gzip_decompressor_create(tx->connp, tx->response_content_encoding_processing);
1471
11.7k
                        if (tx->connp->out_decompressor == NULL) {
1472
0
                            return HTP_ERROR;
1473
0
                        }
1474
11.7k
                        tx->connp->out_decompressor->callback = htp_tx_res_process_body_data_decompressor_callback;
1475
11.7k
                        comp = tx->connp->out_decompressor;
1476
11.7k
                    } else {
1477
3.37k
                        comp->next = htp_gzip_decompressor_create(tx->connp, cetype);
1478
3.37k
                        if (comp->next == NULL) {
1479
0
                            return HTP_ERROR;
1480
0
                        }
1481
3.37k
                        comp->next->callback = htp_tx_res_process_body_data_decompressor_callback;
1482
3.37k
                        comp = comp->next;
1483
3.37k
                    }
1484
15.0k
                }
1485
1486
25.4k
                if ((tok_len + 1) >= input_len)
1487
5.30k
                    break;
1488
20.1k
                input += (tok_len + 1);
1489
20.1k
                input_len -= (tok_len + 1);
1490
20.1k
            }
1491
13.3k
        }
1492
27.0k
    } else if (tx->response_content_encoding_processing != HTP_COMPRESSION_NONE) {
1493
0
        return HTP_ERROR;
1494
0
    }
1495
1496
43.5k
    return HTP_OK;
1497
43.5k
}
1498
1499
96.6k
htp_status_t htp_tx_state_response_start(htp_tx_t *tx) {
1500
96.6k
    if (tx == NULL) return HTP_ERROR;
1501
1502
96.6k
    tx->connp->out_tx = tx;
1503
1504
    // Run hook RESPONSE_START.
1505
96.6k
    htp_status_t rc = htp_hook_run_all(tx->connp->cfg->hook_response_start, tx);
1506
96.6k
    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
96.6k
    if (tx->is_protocol_0_9) {
1511
225
        tx->response_transfer_coding = HTP_CODING_IDENTITY;
1512
225
        tx->response_content_encoding_processing = HTP_COMPRESSION_NONE;
1513
225
        tx->response_progress = HTP_RESPONSE_BODY;
1514
225
        tx->connp->out_state = htp_connp_RES_BODY_IDENTITY_STREAM_CLOSE;
1515
225
        tx->connp->out_body_data_left = -1;
1516
96.4k
    } else {
1517
96.4k
        tx->connp->out_state = htp_connp_RES_LINE;
1518
96.4k
        tx->response_progress = HTP_RESPONSE_LINE;
1519
96.4k
    }
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
96.6k
    if (tx->request_method == HTP_M_UNKNOWN && tx->request_uri == NULL && tx->connp->in_state == htp_connp_REQ_LINE) {
1525
8.34k
        htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Request line incomplete");
1526
8.34k
    }
1527
1528
96.6k
    return HTP_OK;
1529
96.6k
}
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
388k
int htp_tx_is_complete(htp_tx_t *tx) {
1554
388k
    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
388k
    if ((tx->request_progress != HTP_REQUEST_COMPLETE) || (tx->response_progress != HTP_RESPONSE_COMPLETE)) {
1560
237k
        return 0;
1561
237k
    } else {
1562
151k
        return 1;
1563
151k
    }
1564
388k
}