/* Copyright (C) 2007-2011 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 * Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * version 2 along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ /** * \file * * \author Victor Julien * \author Gurvinder Singh * \author Pablo Rincon * \author Brian Rectanus * * This file provides a HTTP protocol support for the engine using HTP library. */ #include "suricata-common.h" #include "app-layer-htp.h" #include "app-layer-htp-mem.h" #include "app-layer-htp-body.h" #include "util-streaming-buffer.h" #include "util-print.h" extern StreamingBufferConfig htp_sbcfg; /** * \brief Append a chunk of body to the HtpBody struct * * \param body pointer to the HtpBody holding the list * \param data pointer to the data of the chunk * \param len length of the chunk pointed by data * * \retval 0 ok * \retval -1 error */ int HtpBodyAppendChunk(const HTPCfgDir *hcfg, HtpBody *body, const uint8_t *data, uint32_t len) { SCEnter(); HtpBodyChunk *bd = NULL; if (len == 0 || data == NULL) { SCReturnInt(0); } if (body->sb == NULL) { body->sb = StreamingBufferInit(&htp_sbcfg); if (body->sb == NULL) SCReturnInt(-1); } /* New chunk */ bd = (HtpBodyChunk *)HTPCalloc(1, sizeof(HtpBodyChunk)); if (bd == NULL) { SCReturnInt(-1); } if (StreamingBufferAppend(body->sb, &htp_sbcfg, &bd->sbseg, data, len) != 0) { HTPFree(bd, sizeof(HtpBodyChunk)); SCReturnInt(-1); } if (body->first == NULL) { body->first = body->last = bd; } else { body->last->next = bd; body->last = bd; } body->content_len_so_far += len; SCLogDebug("body %p", body); SCReturnInt(0); } /** * \brief Print the information and chunks of a Body * \param body pointer to the HtpBody holding the list * \retval none */ void HtpBodyPrint(HtpBody *body) { if (SCLogDebugEnabled()||1) { SCEnter(); 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) { const uint8_t *data = NULL; uint32_t data_len = 0; StreamingBufferSegmentGetData(body->sb, &cur->sbseg, &data, &data_len); SCLogDebug("Body %p; data %p, len %"PRIu32, body, data, data_len); printf("Body %p; data %p, len %"PRIu32"\n", body, data, data_len); PrintRawDataFp(stdout, data, data_len); } SCLogDebug("--- End body chunks at %p ---", body); } } /** * \brief Free the information held in the request body * \param body pointer to the HtpBody holding the list * \retval none */ void HtpBodyFree(const HTPCfgDir *hcfg, HtpBody *body) { SCEnter(); SCLogDebug("removing chunks of body %p", body); HtpBodyChunk *cur = NULL; HtpBodyChunk *prev = NULL; prev = body->first; while (prev != NULL) { cur = prev->next; HTPFree(prev, sizeof(HtpBodyChunk)); prev = cur; } body->first = body->last = NULL; StreamingBufferFree(body->sb, &htp_sbcfg); } /** * \brief Free request body chunks that are already fully parsed. * * \param state htp_state, with reference to our config * \param body the body to prune * \param direction STREAM_TOSERVER (request), STREAM_TOCLIENT (response) * * \retval none */ void HtpBodyPrune(HtpState *state, HtpBody *body, int direction) { SCEnter(); if (body == NULL || body->first == NULL) { SCReturn; } if (body->body_parsed == 0) { SCReturn; } const HTPCfgDir *cfg = (direction == STREAM_TOCLIENT) ? &state->cfg->response : &state->cfg->request; uint32_t min_size = cfg->inspect_min_size; uint32_t window = cfg->inspect_window; uint64_t max_window = ((min_size > window) ? min_size : window); uint64_t in_flight = body->content_len_so_far - body->body_inspected; /* Special case. If body_inspected is not being updated, we make sure that * we prune the body. We allow for some extra size/room as we may be called * multiple times on uninspected body chunk additions if a large block of * data was ack'd at once. Want to avoid pruning before inspection. */ if (in_flight > (max_window * 3)) { body->body_inspected = body->content_len_so_far - max_window; } else if (body->body_inspected < max_window) { SCReturn; } uint64_t left_edge = body->body_inspected; if (left_edge <= min_size || left_edge <= window) left_edge = 0; if (left_edge) left_edge -= window; if (left_edge) { SCLogDebug("sliding body to offset %"PRIu64, left_edge); StreamingBufferSlideToOffset(body->sb, &htp_sbcfg, left_edge); } SCLogDebug("pruning chunks of body %p", body); HtpBodyChunk *cur = body->first; while (cur != NULL) { HtpBodyChunk *next = cur->next; SCLogDebug("cur %p", cur); if (!StreamingBufferSegmentIsBeforeWindow(body->sb, &cur->sbseg)) { SCLogDebug("not removed"); break; } body->first = next; if (body->last == cur) { body->last = next; } HTPFree(cur, sizeof(HtpBodyChunk)); cur = next; SCLogDebug("removed"); } SCReturn; }