Coverage Report

Created: 2025-07-11 06:42

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