You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
suricata/libhtp/htp/htp_connection_parser.c

275 lines
8.0 KiB
C

/*
* LibHTP (http://www.libhtp.org)
* Copyright 2009,2010 Ivan Ristic <ivanr@webkreator.com>
*
* LibHTP is an open source product, released under terms of the General Public Licence
* version 2 (GPLv2). Please refer to the file LICENSE, which contains the complete text
* of the license.
*
* In addition, there is a special exception that allows LibHTP to be freely
* used with any OSI-approved open source licence. Please refer to the file
* LIBHTP_LICENSING_EXCEPTION for the full text of the exception.
*
*/
#include "htp.h"
// NOTE The parser contains a lot of duplicated code. That is on purpose.
//
// Within the request parser alone there are several states in which
// bytes are copied into the line buffer and lines are processed one at a time.
// This code could be made more elegant by adding a new line-reading state along
// with a what-fn-to-invoke-to-handle-line pointer.
//
// Furthermore, the entire request parser is terribly similar to the response parser.
// It is imaginable that a single parser could handle both.
//
// After some thought, I decided not to make any changes (at least not for the time
// being). State-based parsers are sometimes difficult to understand. I remember trying
// to figure one once and I had a hard time following the logic because each function
// was small and did exactly one thing. There was jumping all around. You could probably
// say that it was elegant, but I saw it as difficult to understand, verify and maintain.
//
// Thus, I am keeping this inelegant but quite straightforward parser with duplicated code,
// mostly for the sake of maintenance.
//
// For the time being, anyway. I will review at a later time.
/**
* Clears an existing parser error, if any.
*
* @param connp
*/
void htp_connp_clear_error(htp_connp_t *connp) {
connp->last_error = NULL;
}
/**
* Closes the connection associated with the supplied parser.
*
* @param connp
* @param timestamp
*/
void htp_connp_close(htp_connp_t *connp, htp_time_t timestamp) {
// Update internal information
connp->conn->close_timestamp = timestamp;
connp->in_status = STREAM_STATE_CLOSED;
connp->out_status = STREAM_STATE_CLOSED;
// Call the parsers one last time, which will allow them
// to process the events that depend on stream closure
htp_connp_req_data(connp, timestamp, NULL, 0);
htp_connp_res_data(connp, timestamp, NULL, 0);
}
/**
* Creates a new connection parser using the provided configuration. Because
* the configuration structure is used directly, in a multithreaded environment
* you are not allowed to change the structure, ever. If you have a need to
* change configuration on per-connection basis, make a copy of the configuration
* structure to go along with every connection parser.
*
* @param cfg
* @return A pointer to a newly created htp_connp_t instance.
*/
htp_connp_t *htp_connp_create(htp_cfg_t *cfg) {
htp_connp_t *connp = calloc(1, sizeof (htp_connp_t));
if (connp == NULL) return NULL;
// Use the supplied configuration structure
connp->cfg = cfg;
// Create a new connection object
connp->conn = htp_conn_create(connp);
if (connp->conn == NULL) {
free(connp);
return NULL;
}
connp->in_status = HTP_OK;
// Request parsing
connp->in_line_size = cfg->field_limit_hard;
connp->in_line_len = 0;
connp->in_line = malloc(connp->in_line_size);
if (connp->in_line == NULL) {
htp_conn_destroy(connp->conn);
free(connp);
return NULL;
}
connp->in_header_line_index = -1;
connp->in_state = htp_connp_REQ_IDLE;
// Response parsing
connp->out_line_size = cfg->field_limit_hard;
connp->out_line_len = 0;
connp->out_line = malloc(connp->out_line_size);
if (connp->out_line == NULL) {
free(connp->in_line);
htp_conn_destroy(connp->conn);
free(connp);
return NULL;
}
connp->out_header_line_index = -1;
connp->out_state = htp_connp_RES_IDLE;
connp->in_status = STREAM_STATE_NEW;
connp->out_status = STREAM_STATE_NEW;
return connp;
}
/**
* Creates a new configuration parser, making a copy of the supplied
* configuration structure.
*
* @param cfg
* @return A pointer to a newly created htp_connp_t instance.
*/
htp_connp_t *htp_connp_create_copycfg(htp_cfg_t *cfg) {
htp_connp_t *connp = htp_connp_create(cfg);
if (connp == NULL) return NULL;
connp->cfg = htp_config_copy(cfg);
connp->is_cfg_private = 1;
return connp;
}
/**
* Destroys the connection parser and its data structures, leaving
* the connection data intact.
*
* @param connp
*/
void htp_connp_destroy(htp_connp_t *connp) {
if (connp->out_decompressor != NULL) {
connp->out_decompressor->destroy(connp->out_decompressor);
connp->out_decompressor = NULL;
}
if (connp->in_header_line != NULL) {
if (connp->in_header_line->line != NULL) {
free(connp->in_header_line->line);
}
free(connp->in_header_line);
}
free(connp->in_line);
if (connp->out_header_line != NULL) {
if (connp->out_header_line->line != NULL) {
free(connp->out_header_line->line);
}
free(connp->out_header_line);
}
free(connp->out_line);
// Destroy the configuration structure, but only
// if it is our private copy
if (connp->is_cfg_private) {
htp_config_destroy(connp->cfg);
}
free(connp);
}
/**
* Destroys the connection parser, its data structures, as well
* as the connection and its transactions.
*
* @param connp
*/
void htp_connp_destroy_all(htp_connp_t *connp) {
if (connp->conn == NULL) {
fprintf(stderr, "HTP: htp_connp_destroy_all was invoked, but conn is NULL\n");
return;
}
// Destroy connection
htp_conn_destroy(connp->conn);
connp->conn = NULL;
// Destroy everything else
htp_connp_destroy(connp);
}
/**
* Retrieve the user data associated with this connection parser.
*
* @param connp
* @return User data, or NULL if there isn't any.
*/
void *htp_connp_get_data(htp_connp_t *connp) {
return connp->user_data;
}
/**
* Returns the last error that occured with this connection parser. Do note, however,
* that the value in this field will only be valid immediately after an error condition,
* but it is not guaranteed to remain valid if the parser is invoked again.
*
* @param connp
* @return A pointer to an htp_log_t instance if there is an error, or NULL
* if there isn't.
*/
htp_log_t *htp_connp_get_last_error(htp_connp_t *connp) {
return connp->last_error;
}
/**
* Opens connection.
*
* @param connp
* @param remote_addr Remote address
* @param remote_port Remote port
* @param local_addr Local address
* @param local_port Local port
* @param timestamp
*/
void htp_connp_open(htp_connp_t *connp, const char *remote_addr, int remote_port, const char *local_addr, int local_port, htp_time_t timestamp) {
if ((connp->in_status != STREAM_STATE_NEW) || (connp->out_status != STREAM_STATE_NEW)) {
htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Connection is already open");
return;
}
if (remote_addr != NULL) {
connp->conn->remote_addr = strdup(remote_addr);
if (connp->conn->remote_addr == NULL) return;
}
connp->conn->remote_port = remote_port;
if (local_addr != NULL) {
connp->conn->local_addr = strdup(local_addr);
if (connp->conn->local_addr == NULL) {
if (connp->conn->remote_addr != NULL) {
free(connp->conn->remote_addr);
}
return;
}
}
connp->conn->local_port = local_port;
connp->conn->open_timestamp = timestamp;
connp->in_status = STREAM_STATE_OPEN;
connp->out_status = STREAM_STATE_OPEN;
}
/**
* Associate user data with the supplied parser.
*
* @param connp
* @param user_data
*/
void htp_connp_set_user_data(htp_connp_t *connp, void *user_data) {
connp->user_data = user_data;
}