http: body inspection improvement

Improve http_client_body and file_data performance when request and
response body limits are set to high values.
remotes/origin/HEAD
Victor Julien 14 years ago
parent 4354434522
commit d378b76c04

@ -84,7 +84,7 @@ int HtpBodyAppendChunk(HtpTxUserData *htud, HtpBody *body, uint8_t *data, uint32
SCReturnInt(0);
}
if (body->nchunks == 0) {
if (body->first == NULL) {
/* New chunk */
bd = (HtpBodyChunk *)SCMalloc(sizeof(HtpBodyChunk));
if (bd == NULL)
@ -93,7 +93,6 @@ int HtpBodyAppendChunk(HtpTxUserData *htud, HtpBody *body, uint8_t *data, uint32
bd->len = len;
bd->stream_offset = 0;
bd->next = NULL;
bd->id = 0;
bd->data = SCMalloc(len);
if (bd->data == NULL) {
@ -102,7 +101,6 @@ int HtpBodyAppendChunk(HtpTxUserData *htud, HtpBody *body, uint8_t *data, uint32
memcpy(bd->data, data, len);
body->first = body->last = bd;
body->nchunks++;
body->content_len_so_far = len;
} else {
@ -113,7 +111,6 @@ int HtpBodyAppendChunk(HtpTxUserData *htud, HtpBody *body, uint8_t *data, uint32
bd->len = len;
bd->stream_offset = body->content_len_so_far;
bd->next = NULL;
bd->id = body->nchunks + 1;
bd->data = SCMalloc(len);
if (bd->data == NULL) {
@ -123,12 +120,10 @@ int HtpBodyAppendChunk(HtpTxUserData *htud, HtpBody *body, uint8_t *data, uint32
body->last->next = bd;
body->last = bd;
body->nchunks++;
body->content_len_so_far += len;
}
SCLogDebug("Body %p; Chunk id: %"PRIu32", data %p, len %"PRIu32"", body,
bd->id, bd->data, (uint32_t)bd->len);
SCLogDebug("Body %p; data %p, len %"PRIu32, body, bd->data, (uint32_t)bd->len);
SCReturnInt(0);
@ -152,17 +147,15 @@ void HtpBodyPrint(HtpBody *body)
if (SCLogDebugEnabled()||1) {
SCEnter();
if (body->nchunks == 0)
if (body->first == NULL)
return;
HtpBodyChunk *cur = NULL;
SCLogDebug("--- Start body chunks at %p ---", body);
printf("--- Start body chunks at %p ---\n", body);
for (cur = body->first; cur != NULL; cur = cur->next) {
SCLogDebug("Body %p; Chunk id: %"PRIu32", data %p, len %"PRIu32"\n",
body, cur->id, cur->data, (uint32_t)cur->len);
printf("Body %p; Chunk id: %"PRIu32", data %p, len %"PRIu32"\n",
body, cur->id, cur->data, (uint32_t)cur->len);
SCLogDebug("Body %p; data %p, len %"PRIu32, body, cur->data, (uint32_t)cur->len);
printf("Body %p; data %p, len %"PRIu32"\n", body, cur->data, (uint32_t)cur->len);
PrintRawDataFp(stdout, (uint8_t*)cur->data, cur->len);
}
SCLogDebug("--- End body chunks at %p ---", body);
@ -178,13 +171,11 @@ void HtpBodyFree(HtpBody *body)
{
SCEnter();
if (body->nchunks == 0)
if (body->first == NULL)
return;
SCLogDebug("Removing chunks of Body %p; Last Chunk id: %"PRIu32", data %p,"
" len %"PRIu32, body, body->last->id, body->last->data,
(uint32_t)body->last->len);
body->nchunks = 0;
SCLogDebug("Removing chunks of Body %p; data %p, len %"PRIu32, body,
body->last->data, (uint32_t)body->last->len);
HtpBodyChunk *cur = NULL;
HtpBodyChunk *prev = NULL;
@ -211,7 +202,7 @@ void HtpBodyPrune(HtpBody *body)
{
SCEnter();
if (body == NULL || body->nchunks == 0) {
if (body == NULL || body->first == NULL) {
SCReturn;
}
@ -219,9 +210,8 @@ void HtpBodyPrune(HtpBody *body)
SCReturn;
}
SCLogDebug("Pruning chunks of Body %p; Last Chunk id: %"PRIu32", data %p,"
" len %"PRIu32, body, body->last->id, body->last->data,
(uint32_t)body->last->len);
SCLogDebug("Pruning chunks of Body %p; data %p, len %"PRIu32, body,
body->last->data, (uint32_t)body->last->len);
HtpBodyChunk *cur = body->first;
while (cur != NULL) {
@ -231,7 +221,7 @@ void HtpBodyPrune(HtpBody *body)
"body->body_parsed %"PRIu64, cur->stream_offset, cur->len,
cur->stream_offset + cur->len, body->body_parsed);
if ((cur->stream_offset + cur->len) >= body->body_parsed) {
if (cur->stream_offset >= body->body_inspected) {
break;
}
@ -240,9 +230,6 @@ void HtpBodyPrune(HtpBody *body)
body->last = next;
}
if (body->nchunks > 0)
body->nchunks--;
if (cur->data != NULL) {
SCFree(cur->data);
}

@ -1284,7 +1284,7 @@ int HtpRequestBodyHandleMultipart(HtpState *hstate, HtpTxUserData *htud,
/* if we're in the file storage process, deal with that now */
if (htud->flags & HTP_FILENAME_SET) {
if (header_start != NULL || form_end != NULL || htud->flags & HTP_BODY_COMPLETE) {
if (header_start != NULL || form_end != NULL || htud->flags & HTP_REQ_BODY_COMPLETE) {
SCLogDebug("reached the end of the file");
uint8_t *filedata = chunks_buffer;
@ -1297,7 +1297,7 @@ int HtpRequestBodyHandleMultipart(HtpState *hstate, HtpTxUserData *htud,
filedata_len = form_end - filedata;
} else if (form_end != NULL && form_end == header_start) {
filedata_len = form_end - filedata - 2; /* 0d 0a */
} else if (htud->flags & HTP_BODY_COMPLETE) {
} else if (htud->flags & HTP_REQ_BODY_COMPLETE) {
filedata_len = chunks_buffer_len;
flags = FILE_TRUNCATED;
}
@ -1758,14 +1758,14 @@ int HTPCallbackRequestBodyData(htp_tx_data_t *d)
SCLogDebug("POST");
int r = HtpRequestBodySetupMultipart(d, htud);
if (r == 1) {
htud->request_body.type = HTP_BODY_REQUEST_MULTIPART;
htud->request_body_type = HTP_BODY_REQUEST_MULTIPART;
} else if (r == 0) {
htud->request_body.type = HTP_BODY_REQUEST_POST;
htud->request_body_type = HTP_BODY_REQUEST_POST;
SCLogDebug("not multipart");
}
} else if (d->tx->request_method_number == M_PUT) {
if (HtpRequestBodySetupPUT(d, htud) == 0) {
htud->request_body.type = HTP_BODY_REQUEST_PUT;
htud->request_body_type = HTP_BODY_REQUEST_PUT;
}
}
@ -1791,19 +1791,19 @@ int HTPCallbackRequestBodyData(htp_tx_data_t *d)
int r = HtpBodyAppendChunk(htud, &htud->request_body, (uint8_t *)d->data, len);
if (r < 0) {
htud->flags |= HTP_BODY_COMPLETE;
htud->flags |= HTP_REQ_BODY_COMPLETE;
} else if (hstate->request_body_limit > 0 &&
htud->request_body.content_len_so_far >= hstate->request_body_limit)
{
htud->flags |= HTP_BODY_COMPLETE;
htud->flags |= HTP_REQ_BODY_COMPLETE;
} else if (htud->request_body.content_len_so_far == htud->request_body.content_len) {
htud->flags |= HTP_BODY_COMPLETE;
htud->flags |= HTP_REQ_BODY_COMPLETE;
}
uint8_t *chunks_buffer = NULL;
uint32_t chunks_buffer_len = 0;
if (htud->request_body.type == HTP_BODY_REQUEST_MULTIPART) {
if (htud->request_body_type == HTP_BODY_REQUEST_MULTIPART) {
/* multi-part body handling starts here */
if (!(htud->flags & HTP_BOUNDARY_SET)) {
goto end;
@ -1824,9 +1824,9 @@ int HTPCallbackRequestBodyData(htp_tx_data_t *d)
if (chunks_buffer != NULL) {
SCFree(chunks_buffer);
}
} else if (htud->request_body.type == HTP_BODY_REQUEST_POST) {
} else if (htud->request_body_type == HTP_BODY_REQUEST_POST) {
HtpRequestBodyHandlePOST(hstate, htud, d->tx, (uint8_t *)d->data, (uint32_t)d->len);
} else if (htud->request_body.type == HTP_BODY_REQUEST_PUT) {
} else if (htud->request_body_type == HTP_BODY_REQUEST_PUT) {
HtpRequestBodyHandlePUT(hstate, htud, d->tx, (uint8_t *)d->data, (uint32_t)d->len);
}
@ -1868,6 +1868,10 @@ int HTPCallbackResponseBodyData(htp_tx_data_t *d)
memset(htud, 0, sizeof(HtpTxUserData));
htud->operation = HTP_BODY_RESPONSE;
htp_header_t *cl = table_getc(d->tx->response_headers, "content-length");
if (cl != NULL)
htud->response_body.content_len = htp_parse_content_length(cl->value);
/* Set the user data for handling body chunks on this transaction */
htp_tx_set_user_data(d->tx, htud);
}
@ -1890,13 +1894,13 @@ int HTPCallbackResponseBodyData(htp_tx_data_t *d)
int r = HtpBodyAppendChunk(htud, &htud->response_body, (uint8_t *)d->data, len);
if (r < 0) {
htud->flags |= HTP_BODY_COMPLETE;
htud->flags |= HTP_RES_BODY_COMPLETE;
} else if (hstate->response_body_limit > 0 &&
htud->response_body.content_len_so_far >= hstate->response_body_limit)
{
htud->flags |= HTP_BODY_COMPLETE;
htud->flags |= HTP_RES_BODY_COMPLETE;
} else if (htud->response_body.content_len_so_far == htud->response_body.content_len) {
htud->flags |= HTP_BODY_COMPLETE;
htud->flags |= HTP_RES_BODY_COMPLETE;
}
HtpResponseBodyHandle(hstate, htud, d->tx, (uint8_t *)d->data, (uint32_t)d->len);

@ -121,38 +121,37 @@ enum {
matched on some rule */
/** Struct used to hold chunks of a body on a request */
typedef struct HtpBodyChunk_ {
struct HtpBodyChunk_ {
uint8_t *data; /**< Pointer to the data of the chunk */
uint32_t len; /**< Length of the chunk */
uint32_t id; /**< number of chunk of the current body */
struct HtpBodyChunk_ *next; /**< Pointer to the next chunk */
uint64_t stream_offset;
} HtpBodyChunk;
uint32_t len; /**< Length of the chunk */
} __attribute__((__packed__));
typedef struct HtpBodyChunk_ HtpBodyChunk;
/** Struct used to hold all the chunks of a body on a request */
typedef struct HtpBody_ {
HtpBodyChunk *first; /**< Pointer to the first chunk */
HtpBodyChunk *last; /**< Pointer to the last chunk */
uint32_t nchunks; /**< Number of chunks in the current operation */
uint8_t type;
/* Holds the length of the htp request body */
uint64_t content_len;
/* Holds the length of the htp request body seen so far */
uint64_t content_len_so_far;
/* parser tracker */
uint64_t body_parsed;
/* pahole: padding: 3 */
/* inspection tracker */
uint64_t body_inspected;
} HtpBody;
#define HTP_BODY_COMPLETE 0x01 /**< body is complete or limit is reached,
#define HTP_REQ_BODY_COMPLETE 0x01 /**< body is complete or limit is reached,
either way, this is it. */
#define HTP_CONTENTTYPE_SET 0x02 /**< We have the content type */
#define HTP_BOUNDARY_SET 0x04 /**< We have a boundary string */
#define HTP_BOUNDARY_OPEN 0x08 /**< We have a boundary string */
#define HTP_FILENAME_SET 0x10 /**< filename is registered in the flow */
#define HTP_DONTSTORE 0x20 /**< not storing this file */
#define HTP_RES_BODY_COMPLETE 0x02
#define HTP_CONTENTTYPE_SET 0x04 /**< We have the content type */
#define HTP_BOUNDARY_SET 0x08 /**< We have a boundary string */
#define HTP_BOUNDARY_OPEN 0x10 /**< We have a boundary string */
#define HTP_FILENAME_SET 0x20 /**< filename is registered in the flow */
#define HTP_DONTSTORE 0x40 /**< not storing this file */
#define HTP_TX_HAS_FILE 0x01
#define HTP_TX_HAS_FILENAME 0x02 /**< filename is known at this time */
@ -180,6 +179,10 @@ typedef struct HtpTxUserData_ {
uint8_t flags;
int16_t operation;
uint8_t request_body_type;
uint8_t response_body_type;
} HtpTxUserData;
typedef struct HtpState_ {

@ -1,4 +1,4 @@
/* Copyright (C) 2007-2010 Open Information Security Foundation
/* Copyright (C) 2007-2012 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
@ -55,6 +55,9 @@
#include "app-layer-htp.h"
#include "app-layer-protos.h"
#define BODY_SCAN_WINDOW 4096
#define BODY_MINIMAL_SIZE 32768
/**
* \brief Helps buffer request bodies for different transactions and stores them
* away in detection code.
@ -73,10 +76,6 @@ static void DetectEngineBufferHttpClientBodies(DetectEngineCtx *de_ctx,
htp_tx_t *tx = NULL;
int i = 0;
if (det_ctx->hcbd_buffers_list_len > 0) {
SCReturn;
}
if (htp_state == NULL) {
SCLogDebug("no HTTP state");
goto end;
@ -95,23 +94,20 @@ static void DetectEngineBufferHttpClientBodies(DetectEngineCtx *de_ctx,
/* let's get the transaction count. We need this to hold the client body
* buffer for each transaction */
det_ctx->hcbd_buffers_list_len = list_size(htp_state->connp->conn->transactions) - tmp_idx;
size_t txs = list_size(htp_state->connp->conn->transactions) - tmp_idx;
/* no transactions?! cool. get out of here */
if (det_ctx->hcbd_buffers_list_len == 0)
if (txs == 0) {
goto end;
} else if (txs > det_ctx->hcbd_buffers_list_len) {
det_ctx->hcbd = SCRealloc(det_ctx->hcbd, txs * sizeof(HttpReassembledBody));
if (det_ctx->hcbd == NULL) {
goto end;
}
/* assign space to hold buffers. Each per transaction */
det_ctx->hcbd_buffers = SCMalloc(det_ctx->hcbd_buffers_list_len * sizeof(uint8_t *));
if (det_ctx->hcbd_buffers == NULL) {
goto end;
}
memset(det_ctx->hcbd_buffers, 0, det_ctx->hcbd_buffers_list_len * sizeof(uint8_t *));
det_ctx->hcbd_buffers_len = SCMalloc(det_ctx->hcbd_buffers_list_len * sizeof(uint32_t));
if (det_ctx->hcbd_buffers_len == NULL) {
goto end;
memset(det_ctx->hcbd + det_ctx->hcbd_buffers_list_len, 0,
(txs - det_ctx->hcbd_buffers_list_len) * sizeof(HttpReassembledBody));
det_ctx->hcbd_buffers_list_len = txs;
}
memset(det_ctx->hcbd_buffers_len, 0, det_ctx->hcbd_buffers_list_len * sizeof(uint32_t));
idx = AppLayerTransactionGetInspectId(f);
if (idx == -1) {
@ -120,73 +116,106 @@ static void DetectEngineBufferHttpClientBodies(DetectEngineCtx *de_ctx,
int size = (int)list_size(htp_state->connp->conn->transactions);
for (; idx < size; idx++, i++) {
/* already set up */
if (det_ctx->hcbd[i].buffer_len > 0) {
SCLogDebug("set up already");
continue;
}
tx = list_get(htp_state->connp->conn->transactions, idx);
if (tx == NULL)
if (tx == NULL) {
SCLogDebug("no tx");
continue;
}
HtpTxUserData *htud = (HtpTxUserData *)htp_tx_get_user_data(tx);
if (htud == NULL)
if (htud == NULL) {
SCLogDebug("no htud");
continue;
}
HtpBodyChunk *cur = htud->request_body.first;
/* no new data */
if (htud->request_body.body_inspected == htud->request_body.content_len_so_far) {
SCLogDebug("no new data");
continue;
}
if (htud->request_body.nchunks == 0) {
HtpBodyChunk *cur = htud->request_body.first;
if (cur == NULL) {
SCLogDebug("No http chunks to inspect for this transacation");
continue;
} else {
/* no chunks?!! move on to the next transaction */
if (cur == NULL) {
SCLogDebug("No http chunks to inspect");
continue;
}
}
/* in case of chunked transfer encoding, we don't have the length
* of the request body until we see a chunk with length 0. This
* doesn't let us use the request body callback function to
* figure out the end of request body. Instead we do it here. If
* the length is 0, and we have already seen content, it indicates
* chunked transfer. We also check if the parser has truly seen
* the last chunk by checking the progress state for the
* transaction. If we are done parsing all the chunks, we would
* have it set to something other than TX_PROGRESS_REQ_BODY.
* Either ways we should be moving away from buffering in the end
* and running content validation on this buffer type of architecture
* to a stateful inspection, where we can inspect body chunks as and
* when they come */
if (htud->request_body.content_len == 0) {
if ((htud->request_body.content_len_so_far > 0) &&
/* in case of chunked transfer encoding, we don't have the length
* of the request body until we see a chunk with length 0. This
* doesn't let us use the request body callback function to
* figure out the end of request body. Instead we do it here. If
* the length is 0, and we have already seen content, it indicates
* chunked transfer. We also check if the parser has truly seen
* the last chunk by checking the progress state for the
* transaction. If we are done parsing all the chunks, we would
* have it set to something other than TX_PROGRESS_REQ_BODY.
* Either ways we should be moving away from buffering in the end
* and running content validation on this buffer type of architecture
* to a stateful inspection, where we can inspect body chunks as and
* when they come */
if (htud->request_body.content_len == 0) {
if ((htud->request_body.content_len_so_far > 0) &&
tx->progress != TX_PROGRESS_REQ_BODY) {
/* final length of the body */
htud->flags |= HTP_BODY_COMPLETE;
/* final length of the body */
htud->flags |= HTP_REQ_BODY_COMPLETE;
}
}
/* inspect the body if the transfer is complete or we have hit
* our body size limit */
if (htud->request_body.content_len_so_far < BODY_MINIMAL_SIZE &&
!(htud->flags & HTP_REQ_BODY_COMPLETE)) {
SCLogDebug("we still haven't seen the entire request body. "
"Let's defer body inspection till we see the "
"entire body.");
continue;
}
int first = 1;
while (cur != NULL) {
/* see if we can filter out chunks */
if (htud->request_body.body_inspected > 0) {
if (cur->stream_offset < htud->request_body.body_inspected) {
if (htud->request_body.body_inspected - cur->stream_offset > BODY_SCAN_WINDOW) {
cur = cur->next;
continue;
} else {
/* include this one */
}
} else {
/* include this one */
}
}
/* inspect the body if the transfer is complete or we have hit
* our body size limit */
if (!(htud->flags & HTP_BODY_COMPLETE)) {
SCLogDebug("we still haven't seen the entire request body. "
"Let's defer body inspection till we see the "
"entire body.");
continue;
if (first) {
det_ctx->hcbd[i].offset = cur->stream_offset;
first = 0;
}
uint8_t *chunks_buffer = NULL;
int32_t chunks_buffer_len = 0;
while (cur != NULL) {
chunks_buffer_len += cur->len;
if ( (chunks_buffer = SCRealloc(chunks_buffer, chunks_buffer_len)) == NULL) {
/* see if we need to grow the buffer */
if (det_ctx->hcbd[i].buffer == NULL || det_ctx->hcbd[i].buffer_len + cur->len > det_ctx->hcbd[i].buffer_size) {
det_ctx->hcbd[i].buffer_size += cur->len * 2;
if ((det_ctx->hcbd[i].buffer = SCRealloc(det_ctx->hcbd[i].buffer, det_ctx->hcbd[i].buffer_size)) == NULL) {
goto end;
}
memcpy(chunks_buffer + chunks_buffer_len - cur->len, cur->data, cur->len);
cur = cur->next;
}
/* store the buffers. We will need it for further inspection */
det_ctx->hcbd_buffers[i] = chunks_buffer;
det_ctx->hcbd_buffers_len[i] = chunks_buffer_len;
memcpy(det_ctx->hcbd[i].buffer + det_ctx->hcbd[i].buffer_len, cur->data, cur->len);
det_ctx->hcbd[i].buffer_len += cur->len;
} /* else - if (htud->body.nchunks == 0) */
cur = cur->next;
}
/* update inspected tracker */
htud->request_body.body_inspected =
htud->request_body.last->stream_offset +
htud->request_body.last->len;
} /* for (idx = AppLayerTransactionGetInspectId(f); .. */
end:
@ -200,17 +229,17 @@ int DetectEngineRunHttpClientBodyMpm(DetectEngineCtx *de_ctx,
int i;
uint32_t cnt = 0;
/* bail before locking if we have nothing to do */
if (det_ctx->hcbd_buffers_list_len == 0) {
FLOWLOCK_WRLOCK(f);
DetectEngineBufferHttpClientBodies(de_ctx, det_ctx, f, htp_state);
FLOWLOCK_UNLOCK(f);
}
FLOWLOCK_WRLOCK(f);
DetectEngineBufferHttpClientBodies(de_ctx, det_ctx, f, htp_state);
FLOWLOCK_UNLOCK(f);
for (i = 0; i < det_ctx->hcbd_buffers_list_len; i++) {
if (det_ctx->hcbd[i].buffer_len == 0)
continue;
cnt += HttpClientBodyPatternSearch(det_ctx,
det_ctx->hcbd_buffers[i],
det_ctx->hcbd_buffers_len[i],
det_ctx->hcbd[i].buffer,
det_ctx->hcbd[i].buffer_len,
flags);
}
@ -239,18 +268,15 @@ int DetectEngineInspectHttpClientBody(DetectEngineCtx *de_ctx,
int r = 0;
int i = 0;
/* bail before locking if we have nothing to do */
if (det_ctx->hcbd_buffers_list_len == 0) {
FLOWLOCK_WRLOCK(f);
DetectEngineBufferHttpClientBodies(de_ctx, det_ctx, f, alstate);
FLOWLOCK_UNLOCK(f);
}
FLOWLOCK_WRLOCK(f);
DetectEngineBufferHttpClientBodies(de_ctx, det_ctx, f, alstate);
FLOWLOCK_UNLOCK(f);
for (i = 0; i < det_ctx->hcbd_buffers_list_len; i++) {
uint8_t *hcbd_buffer = det_ctx->hcbd_buffers[i];
uint32_t hcbd_buffer_len = det_ctx->hcbd_buffers_len[i];
uint8_t *hcbd_buffer = det_ctx->hcbd[i].buffer;
uint32_t hcbd_buffer_len = det_ctx->hcbd[i].buffer_len;
if (hcbd_buffer == NULL)
if (hcbd_buffer == NULL || hcbd_buffer_len == 0)
continue;
det_ctx->buffer_offset = 0;
@ -262,8 +288,6 @@ int DetectEngineInspectHttpClientBody(DetectEngineCtx *de_ctx,
hcbd_buffer,
hcbd_buffer_len,
DETECT_ENGINE_CONTENT_INSPECTION_MODE_HCBD, NULL);
//r = DoInspectHttpClientBody(de_ctx, det_ctx, s, s->sm_lists[DETECT_SM_LIST_HCBDMATCH],
//hcbd_buffer, hcbd_buffer_len);
if (r == 1) {
break;
}
@ -279,23 +303,10 @@ int DetectEngineInspectHttpClientBody(DetectEngineCtx *de_ctx,
*/
void DetectEngineCleanHCBDBuffers(DetectEngineThreadCtx *det_ctx)
{
if (det_ctx->hcbd_buffers_list_len != 0) {
int i;
for (i = 0; i < det_ctx->hcbd_buffers_list_len; i++) {
if (det_ctx->hcbd_buffers[i] != NULL)
SCFree(det_ctx->hcbd_buffers[i]);
}
if (det_ctx->hcbd_buffers != NULL) {
SCFree(det_ctx->hcbd_buffers);
det_ctx->hcbd_buffers = NULL;
}
if (det_ctx->hcbd_buffers_len != NULL) {
SCFree(det_ctx->hcbd_buffers_len);
det_ctx->hcbd_buffers_len = NULL;
}
det_ctx->hcbd_buffers_list_len = 0;
int i;
for (i = 0; i < det_ctx->hcbd_buffers_list_len; i++) {
det_ctx->hcbd[i].buffer_len = 0;
}
return;
}
@ -1051,14 +1062,12 @@ static int DetectEngineHttpClientBodyTest07(void)
int r = AppLayerParse(NULL, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
if (r != 0) {
printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
result = 0;
goto end;
}
http_state = f.alstate;
if (http_state == NULL) {
printf("no http state: \n");
result = 0;
goto end;
}
@ -1073,7 +1082,6 @@ static int DetectEngineHttpClientBodyTest07(void)
r = AppLayerParse(NULL, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
if (r != 0) {
printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
result = 0;
goto end;
}

@ -56,6 +56,9 @@
#include "app-layer-htp.h"
#include "app-layer-protos.h"
#define BODY_SCAN_WINDOW 4096
#define BODY_MINIMAL_SIZE 32768
/**
* \brief Helps buffer response bodies for different transactions and stores them
* away in detection code.
@ -74,10 +77,6 @@ static void DetectEngineBufferHttpServerBodies(DetectEngineCtx *de_ctx,
htp_tx_t *tx = NULL;
int i = 0;
if (det_ctx->hsbd_buffers_list_len > 0) {
SCReturn;
}
if (htp_state == NULL) {
SCLogDebug("no HTTP state");
goto end;
@ -96,23 +95,20 @@ static void DetectEngineBufferHttpServerBodies(DetectEngineCtx *de_ctx,
/* let's get the transaction count. We need this to hold the server body
* buffer for each transaction */
det_ctx->hsbd_buffers_list_len = list_size(htp_state->connp->conn->transactions) - tmp_idx;
size_t txs = list_size(htp_state->connp->conn->transactions) - tmp_idx;
/* no transactions?! cool. get out of here */
if (det_ctx->hsbd_buffers_list_len == 0)
goto end;
/* assign space to hold buffers. Each per transaction */
det_ctx->hsbd_buffers = SCMalloc(det_ctx->hsbd_buffers_list_len * sizeof(uint8_t *));
if (det_ctx->hsbd_buffers == NULL) {
if (txs == 0) {
goto end;
}
memset(det_ctx->hsbd_buffers, 0, det_ctx->hsbd_buffers_list_len * sizeof(uint8_t *));
} else if (txs > det_ctx->hsbd_buffers_list_len) {
det_ctx->hsbd = SCRealloc(det_ctx->hsbd, txs * sizeof(HttpReassembledBody));
if (det_ctx->hsbd == NULL) {
goto end;
}
det_ctx->hsbd_buffers_len = SCMalloc(det_ctx->hsbd_buffers_list_len * sizeof(uint32_t));
if (det_ctx->hsbd_buffers_len == NULL) {
goto end;
memset(det_ctx->hsbd + det_ctx->hsbd_buffers_list_len, 0,
(txs - det_ctx->hsbd_buffers_list_len) * sizeof(HttpReassembledBody));
det_ctx->hsbd_buffers_list_len = txs;
}
memset(det_ctx->hsbd_buffers_len, 0, det_ctx->hsbd_buffers_list_len * sizeof(uint32_t));
idx = AppLayerTransactionGetInspectId(f);
if (idx == -1) {
@ -121,6 +117,9 @@ static void DetectEngineBufferHttpServerBodies(DetectEngineCtx *de_ctx,
int size = (int)list_size(htp_state->connp->conn->transactions);
for (; idx < size; idx++, i++) {
/* already set up */
if (det_ctx->hsbd[i].buffer_len > 0)
continue;
tx = list_get(htp_state->connp->conn->transactions, idx);
if (tx == NULL)
@ -130,64 +129,89 @@ static void DetectEngineBufferHttpServerBodies(DetectEngineCtx *de_ctx,
if (htud == NULL)
continue;
HtpBodyChunk *cur = htud->response_body.first;
/* no new data */
if (htud->response_body.body_inspected == htud->response_body.content_len_so_far) {
continue;
}
if (htud->response_body.nchunks == 0) {
HtpBodyChunk *cur = htud->response_body.first;
if (cur == NULL) {
SCLogDebug("No http chunks to inspect for this transacation");
continue;
} else {
/* no chunks?!! move on to the next transaction */
if (cur == NULL) {
SCLogDebug("No http chunks to inspect");
continue;
}
}
/* in case of chunked transfer encoding, we don't have the length
* of the response body until we see a chunk with length 0. This
* doesn't let us use the response body callback function to
* figure out the end of response body. Instead we do it here. If
* the length is 0, and we have already seen content, it indicates
* chunked transfer. We also check if the parser has truly seen
* the last chunk by checking the progress state for the
* transaction. If we are done parsing all the chunks, we would
* have it set to something other than TX_PROGRESS_REQ_BODY.
* Either ways we should be moving away from buffering in the end
* and running content validation on this buffer type of architecture
* to a stateful inspection, where we can inspect body chunks as and
* when they come */
if (htud->response_body.content_len == 0) {
if ((htud->response_body.content_len_so_far > 0) &&
/* in case of chunked transfer encoding, we don't have the length
* of the response body until we see a chunk with length 0. This
* doesn't let us use the response body callback function to
* figure out the end of response body. Instead we do it here. If
* the length is 0, and we have already seen content, it indicates
* chunked transfer. We also check if the parser has truly seen
* the last chunk by checking the progress state for the
* transaction. If we are done parsing all the chunks, we would
* have it set to something other than TX_PROGRESS_REQ_BODY.
* Either ways we should be moving away from buffering in the end
* and running content validation on this buffer type of architecture
* to a stateful inspection, where we can inspect body chunks as and
* when they come */
if (htud->response_body.content_len == 0) {
if ((htud->response_body.content_len_so_far > 0) &&
tx->progress != TX_PROGRESS_REQ_BODY) {
/* final length of the body */
htud->flags |= HTP_BODY_COMPLETE;
/* final length of the body */
htud->flags |= HTP_RES_BODY_COMPLETE;
}
}
/* inspect the body if the transfer is complete or we have hit
* our body size limit */
if (htud->response_body.content_len_so_far < BODY_MINIMAL_SIZE &&
!(htud->flags & HTP_RES_BODY_COMPLETE)) {
SCLogDebug("we still haven't seen the entire response body. "
"Let's defer body inspection till we see the "
"entire body.");
continue;
}
//SCLogInfo("now we inspect! %"PRIu64, htud->response_body.content_len_so_far);
int first = 1;
while (cur != NULL) {
/* see if we can filter out chunks */
if (htud->response_body.body_inspected > 0) {
if (cur->stream_offset < htud->response_body.body_inspected) {
if (htud->response_body.body_inspected - cur->stream_offset > BODY_SCAN_WINDOW) {
cur = cur->next;
continue;
} else {
/* include this one */
}
} else {
/* include this one */
}
}
/* inspect the body if the transfer is complete or we have hit
* our body size limit */
if (!(htud->flags & HTP_BODY_COMPLETE)) {
SCLogDebug("we still haven't seen the entire response body. "
"Let's defer body inspection till we see the "
"entire body.");
continue;
if (first) {
det_ctx->hsbd[i].offset = cur->stream_offset;
first = 0;
}
uint8_t *chunks_buffer = NULL;
int32_t chunks_buffer_len = 0;
while (cur != NULL) {
chunks_buffer_len += cur->len;
if ( (chunks_buffer = SCRealloc(chunks_buffer, chunks_buffer_len)) == NULL) {
/* see if we need to grow the buffer */
if (det_ctx->hsbd[i].buffer == NULL || det_ctx->hsbd[i].buffer_len + cur->len > det_ctx->hsbd[i].buffer_size) {
det_ctx->hsbd[i].buffer_size += cur->len * 2;
if ((det_ctx->hsbd[i].buffer = SCRealloc(det_ctx->hsbd[i].buffer, det_ctx->hsbd[i].buffer_size)) == NULL) {
goto end;
}
memcpy(chunks_buffer + chunks_buffer_len - cur->len, cur->data, cur->len);
cur = cur->next;
}
/* store the buffers. We will need it for further inspection */
det_ctx->hsbd_buffers[i] = chunks_buffer;
det_ctx->hsbd_buffers_len[i] = chunks_buffer_len;
memcpy(det_ctx->hsbd[i].buffer + det_ctx->hsbd[i].buffer_len, cur->data, cur->len);
det_ctx->hsbd[i].buffer_len += cur->len;
} /* else - if (htud->body.nchunks == 0) */
cur = cur->next;
}
/* update inspected tracker */
htud->response_body.body_inspected =
htud->response_body.last->stream_offset +
htud->response_body.last->len;
} /* for (idx = AppLayerTransactionGetInspectId(f); .. */
end:
@ -201,17 +225,17 @@ int DetectEngineRunHttpServerBodyMpm(DetectEngineCtx *de_ctx,
int i;
uint32_t cnt = 0;
/* bail before locking if we have nothing to do */
if (det_ctx->hsbd_buffers_list_len == 0) {
FLOWLOCK_WRLOCK(f);
DetectEngineBufferHttpServerBodies(de_ctx, det_ctx, f, htp_state);
FLOWLOCK_UNLOCK(f);
}
FLOWLOCK_WRLOCK(f);
DetectEngineBufferHttpServerBodies(de_ctx, det_ctx, f, htp_state);
FLOWLOCK_UNLOCK(f);
for (i = 0; i < det_ctx->hsbd_buffers_list_len; i++) {
if (det_ctx->hsbd[i].buffer_len == 0)
continue;
cnt += HttpServerBodyPatternSearch(det_ctx,
det_ctx->hsbd_buffers[i],
det_ctx->hsbd_buffers_len[i],
det_ctx->hsbd[i].buffer,
det_ctx->hsbd[i].buffer_len,
flags);
}
@ -240,18 +264,15 @@ int DetectEngineInspectHttpServerBody(DetectEngineCtx *de_ctx,
int r = 0;
int i = 0;
/* bail before locking if we have nothing to do */
if (det_ctx->hsbd_buffers_list_len == 0) {
FLOWLOCK_WRLOCK(f);
DetectEngineBufferHttpServerBodies(de_ctx, det_ctx, f, alstate);
FLOWLOCK_UNLOCK(f);
}
FLOWLOCK_WRLOCK(f);
DetectEngineBufferHttpServerBodies(de_ctx, det_ctx, f, alstate);
FLOWLOCK_UNLOCK(f);
for (i = 0; i < det_ctx->hsbd_buffers_list_len; i++) {
uint8_t *hsbd_buffer = det_ctx->hsbd_buffers[i];
uint32_t hsbd_buffer_len = det_ctx->hsbd_buffers_len[i];
uint8_t *hsbd_buffer = det_ctx->hsbd[i].buffer;
uint32_t hsbd_buffer_len = det_ctx->hsbd[i].buffer_len;
if (hsbd_buffer == NULL)
if (hsbd_buffer == NULL || hsbd_buffer_len == 0)
continue;
det_ctx->buffer_offset = 0;
@ -263,8 +284,6 @@ int DetectEngineInspectHttpServerBody(DetectEngineCtx *de_ctx,
hsbd_buffer,
hsbd_buffer_len,
DETECT_ENGINE_CONTENT_INSPECTION_MODE_HSBD, NULL);
//r = DoInspectHttpServerBody(de_ctx, det_ctx, s, s->sm_lists[DETECT_SM_LIST_HSBDMATCH],
//hsbd_buffer, hsbd_buffer_len);
if (r == 1) {
break;
}
@ -280,23 +299,10 @@ int DetectEngineInspectHttpServerBody(DetectEngineCtx *de_ctx,
*/
void DetectEngineCleanHSBDBuffers(DetectEngineThreadCtx *det_ctx)
{
if (det_ctx->hsbd_buffers_list_len != 0) {
int i;
for (i = 0; i < det_ctx->hsbd_buffers_list_len; i++) {
if (det_ctx->hsbd_buffers[i] != NULL)
SCFree(det_ctx->hsbd_buffers[i]);
}
if (det_ctx->hsbd_buffers != NULL) {
SCFree(det_ctx->hsbd_buffers);
det_ctx->hsbd_buffers = NULL;
}
if (det_ctx->hsbd_buffers_len != NULL) {
SCFree(det_ctx->hsbd_buffers_len);
det_ctx->hsbd_buffers_len = NULL;
}
det_ctx->hsbd_buffers_list_len = 0;
int i;
for (i = 0; i < det_ctx->hsbd_buffers_list_len; i++) {
det_ctx->hsbd[i].buffer_len = 0;
}
return;
}

@ -484,6 +484,25 @@ TmEcode DetectEngineThreadCtxDeinit(ThreadVars *tv, void *data) {
if (det_ctx->bj_values != NULL)
SCFree(det_ctx->bj_values);
if (det_ctx->hsbd != NULL) {
SCLogDebug("det_ctx hsbd %u", det_ctx->hsbd_buffers_list_len);
for (i = 0; i < det_ctx->hsbd_buffers_list_len; i++) {
if (det_ctx->hsbd[i].buffer != NULL)
SCFree(det_ctx->hsbd[i].buffer);
}
SCFree(det_ctx->hsbd);
}
if (det_ctx->hcbd != NULL) {
SCLogDebug("det_ctx hcbd %u", det_ctx->hcbd_buffers_list_len);
for (i = 0; i < det_ctx->hcbd_buffers_list_len; i++) {
if (det_ctx->hcbd[i].buffer != NULL)
SCFree(det_ctx->hcbd[i].buffer);
SCLogDebug("det_ctx->hcbd[i].buffer_size %u", det_ctx->hcbd[i].buffer_size);
}
SCFree(det_ctx->hcbd);
}
SCFree(det_ctx);
return TM_ECODE_OK;

@ -1609,7 +1609,7 @@ static int DetectHttpClientBodyTest15(void) {
HtpTxUserData *htud = (HtpTxUserData *) htp_tx_get_user_data(t1);
HtpBodyChunk *cur = htud->request_body.first;
if (htud->request_body.nchunks == 0) {
if (htud->request_body.first == NULL) {
SCLogDebug("No body data in t1 (it should be removed only when the tx is destroyed): ");
goto end;
}
@ -1622,7 +1622,7 @@ static int DetectHttpClientBodyTest15(void) {
htud = (HtpTxUserData *) htp_tx_get_user_data(t2);
cur = htud->request_body.first;
if (htud->request_body.nchunks == 0) {
if (htud->request_body.first == NULL) {
SCLogDebug("No body data in t1 (it should be removed only when the tx is destroyed): ");
goto end;
}

@ -562,14 +562,12 @@ static int DetectHttpServerBodyTest07(void)
int r = AppLayerParse(NULL, &f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START|STREAM_EOF, http_buf1, http_len1);
if (r != 0) {
printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
result = 0;
goto end;
}
r = AppLayerParse(NULL, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_START, http_buf2, http_len2);
if (r != 0) {
printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
result = 0;
goto end;
}
@ -582,8 +580,8 @@ static int DetectHttpServerBodyTest07(void)
/* do detect */
SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
if (!(PacketAlertCheck(p1, 1))) {
printf("sid 1 didn't match on p1 but should have: ");
if ((PacketAlertCheck(p1, 1))) {
printf("sid 1 matched on chunk2 but should have: ");
goto end;
}
@ -595,8 +593,8 @@ static int DetectHttpServerBodyTest07(void)
/* do detect */
SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
if (PacketAlertCheck(p2, 1)) {
printf("sid 1 matched on p2 but shouldn't have: ");
if (!(PacketAlertCheck(p2, 1))) {
printf("sid 1 didn't match on p2 (chunk3) but should have: ");
goto end;
}
@ -2805,8 +2803,8 @@ static int DetectHttpServerBodyFileDataTest02(void)
/* do detect */
SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
if (!(PacketAlertCheck(p1, 1))) {
printf("sid 1 didn't match on p1 but should have: ");
if (PacketAlertCheck(p1, 1)) {
printf("sid 1 matched on p1 but should have: ");
goto end;
}
@ -2818,8 +2816,8 @@ static int DetectHttpServerBodyFileDataTest02(void)
/* do detect */
SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
if (PacketAlertCheck(p2, 1)) {
printf("sid 1 matched on p2 but shouldn't have: ");
if (!(PacketAlertCheck(p2, 1))) {
printf("sid 1 didn't match on p2 but should have: ");
goto end;
}

@ -3452,7 +3452,7 @@ static int DetectPcreTxBodyChunksTest01(void) {
}
HtpBodyChunk *cur = htud->request_body.first;
if (htud->request_body.nchunks == 0) {
if (htud->request_body.first == NULL) {
SCLogDebug("No body data in t1 (it should be removed only when the tx is destroyed): ");
goto end;
}
@ -3465,7 +3465,7 @@ static int DetectPcreTxBodyChunksTest01(void) {
htud = (HtpTxUserData *) htp_tx_get_user_data(t2);
cur = htud->request_body.first;
if (htud->request_body.nchunks == 0) {
if (htud->request_body.first == NULL) {
SCLogDebug("No body data in t1 (it should be removed only when the tx is destroyed): ");
goto end;
}
@ -3669,7 +3669,7 @@ static int DetectPcreTxBodyChunksTest02(void) {
HtpTxUserData *htud = (HtpTxUserData *) htp_tx_get_user_data(t1);
HtpBodyChunk *cur = htud->request_body.first;
if (htud->request_body.nchunks == 0) {
if (htud->request_body.first == NULL) {
SCLogDebug("No body data in t1 (it should be removed only when the tx is destroyed): ");
goto end;
}
@ -3682,7 +3682,7 @@ static int DetectPcreTxBodyChunksTest02(void) {
htud = (HtpTxUserData *) htp_tx_get_user_data(t2);
cur = htud->request_body.first;
if (htud->request_body.nchunks == 0) {
if (htud->request_body.first == NULL) {
SCLogDebug("No body data in t1 (it should be removed only when the tx is destroyed): ");
goto end;
}

@ -276,14 +276,6 @@ static inline int DoDetectAppLayerUricontentMatch (DetectEngineThreadCtx *det_ct
"%" PRIu32 ")", det_ctx->sgh, det_ctx->sgh->
mpm_uricontent_maxlen, det_ctx->sgh->sig_cnt);
det_ctx->uris++;
if (det_ctx->sgh->mpm_uricontent_maxlen == 1) det_ctx->pkts_uri_searched1++;
else if (det_ctx->sgh->mpm_uricontent_maxlen == 2) det_ctx->pkts_uri_searched2++;
else if (det_ctx->sgh->mpm_uricontent_maxlen == 3) det_ctx->pkts_uri_searched3++;
else if (det_ctx->sgh->mpm_uricontent_maxlen == 4) det_ctx->pkts_uri_searched4++;
else det_ctx->pkts_uri_searched++;
ret += UriPatternSearch(det_ctx, uri, uri_len, flags);
SCLogDebug("post search: cnt %" PRIu32, ret);

@ -1285,7 +1285,6 @@ int SigMatchSignatures(ThreadVars *th_v, DetectEngineCtx *de_ctx, DetectEngineTh
SCLogDebug("pcap_cnt %"PRIu64, p->pcap_cnt);
p->alerts.cnt = 0;
det_ctx->pkts++;
det_ctx->filestore_cnt = 0;
/* No need to perform any detection on this packet, if the the given flag is set.*/
@ -8649,9 +8648,6 @@ int SigTest40NoPayloadInspection02(void) {
else
result &= 1;
if (det_ctx->pkts_searched == 1)
result &= 0;
SigGroupCleanup(de_ctx);
SigCleanSignatures(de_ctx);
DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);

@ -667,6 +667,13 @@ enum {
ENGINE_SGH_MPM_FACTORY_CONTEXT_AUTO
};
typedef struct HttpReassembledBody_ {
uint8_t *buffer;
uint32_t buffer_size; /**< size of the buffer itself */
uint32_t buffer_len; /**< data len in the buffer */
uint64_t offset; /**< data offset */
} HttpReassembledBody;
#define DETECT_FILESTORE_MAX 15
/**
@ -684,17 +691,16 @@ typedef struct DetectionEngineThreadCtx_ {
/* used by pcre match function alone */
uint32_t pcre_match_start_offset;
uint8_t **hcbd_buffers;
uint32_t *hcbd_buffers_len;
uint16_t hcbd_buffers_list_len;
/* counter for the filestore array below -- up here for cache reasons. */
uint16_t filestore_cnt;
uint16_t hhd_buffers_list_len;
uint16_t hcbd_buffers_list_len;
uint16_t hsbd_buffers_list_len;
uint8_t **hsbd_buffers;
uint32_t *hsbd_buffers_len;
HttpReassembledBody *hsbd;
HttpReassembledBody *hcbd;
uint8_t **hhd_buffers;
uint32_t *hhd_buffers_len;
@ -706,6 +712,9 @@ typedef struct DetectionEngineThreadCtx_ {
uint16_t discontinue_matching;
uint16_t flags;
/** ID of the transaction currently being inspected. */
uint16_t tx_id;
/* holds the current recursion depth on content inspection */
int inspection_recursion_counter;
@ -732,24 +741,6 @@ typedef struct DetectionEngineThreadCtx_ {
PatternMatcherQueue pmq;
PatternMatcherQueue smsg_pmq[256];
/** ID of the transaction currently being inspected. */
uint16_t tx_id;
/* counters */
uint32_t pkts;
uint32_t pkts_searched;
uint32_t pkts_searched1;
uint32_t pkts_searched2;
uint32_t pkts_searched3;
uint32_t pkts_searched4;
uint32_t uris;
uint32_t pkts_uri_searched;
uint32_t pkts_uri_searched1;
uint32_t pkts_uri_searched2;
uint32_t pkts_uri_searched3;
uint32_t pkts_uri_searched4;
/** ip only rules ctx */
DetectEngineIPOnlyThreadCtx io_ctx;

Loading…
Cancel
Save