Coverage Report

Created: 2025-11-16 07:09

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