From: Philippe Antoine Date: Thu, 4 Apr 2019 18:37:29 +0000 (+0200) Subject: http: logs content range X-Git-Tag: suricata-5.0.0-beta1~36 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F3810%2Fhead;p=thirdparty%2Fsuricata.git http: logs content range Fixes #2485 --- diff --git a/src/app-layer-htp-file.c b/src/app-layer-htp-file.c index 2018452ef5..ce5599381e 100644 --- a/src/app-layer-htp-file.c +++ b/src/app-layer-htp-file.c @@ -150,6 +150,111 @@ end: SCReturnInt(retval); } +/** + * Performs parsing of the content-range value + * + * @param[in] rawvalue + * @param[out] range + * + * @return HTP_OK on success, HTP_ERROR on failure. + */ +int HTPParseContentRange(bstr * rawvalue, HtpContentRange *range) +{ + unsigned char *data = bstr_ptr(rawvalue); + size_t len = bstr_len(rawvalue); + size_t pos = 0; + size_t last_pos; + + // skip spaces and units + while (pos < len && data[pos] == ' ') + pos++; + while (pos < len && data[pos] != ' ') + pos++; + while (pos < len && data[pos] == ' ') + pos++; + + // initialize to unseen + range->start = -1; + range->end = -1; + range->size = -1; + + if (pos == len) { + // missing data + return -1; + } + + if (data[pos] == '*') { + // case with size only + if (len < pos + 1 || data[pos+1] != '/') { + range->size = -1; + return -1; + } + pos += 2; + range->size = bstr_util_mem_to_pint(data + pos, len - pos, 10, &last_pos); + } else { + // case with start and end + range->start = bstr_util_mem_to_pint(data + pos, len - pos, 10, &last_pos); + pos += last_pos; + if (len < pos || data[pos] != '-') { + return -1; + } + pos++; + range->end = bstr_util_mem_to_pint(data + pos, len - pos, 10, &last_pos); + pos += last_pos; + if (len < pos || data[pos] != '/') { + return -1; + } + pos++; + if (data[pos] != '*') { + // case with size + range->size = bstr_util_mem_to_pint(data + pos, len - pos, 10, &last_pos); + } + } + + return 0; +} + +/** + * \brief Sets range for a file + * + * \param s http state + * \param rawvalue raw header value + * + * \retval 0 ok + * \retval -1 error + * \retval -2 error parsing + * \retval -3 error negative end in range + */ +int HTPFileSetRange(HtpState *s, bstr *rawvalue) +{ + SCEnter(); + + if (s == NULL) { + SCReturnInt(-1); + } + + FileContainer * files = s->files_tc; + if (files == NULL) { + SCLogDebug("no files in state"); + SCReturnInt(-1); + } + + HtpContentRange crparsed; + if (HTPParseContentRange(rawvalue, &crparsed) != 0) { + SCLogDebug("parsing range failed"); + SCReturnInt(-2); + } + if (crparsed.end <= 0) { + SCLogDebug("negative end in range"); + SCReturnInt(-3); + } + int retval = FileSetRange(files, crparsed.start, crparsed.end); + if (retval == -1) { + SCLogDebug("set range failed"); + } + SCReturnInt(retval); +} + /** * \brief Store a chunk of data in the flow * diff --git a/src/app-layer-htp-file.h b/src/app-layer-htp-file.h index 1f0bfb6055..3e6bdc1fb9 100644 --- a/src/app-layer-htp-file.h +++ b/src/app-layer-htp-file.h @@ -25,7 +25,15 @@ #ifndef __APP_LAYER_HTP_FILE_H__ #define __APP_LAYER_HTP_FILE_H__ +typedef struct HtpContentRange_ { + int64_t start; + int64_t end; + int64_t size; +} HtpContentRange; + int HTPFileOpen(HtpState *, const uint8_t *, uint16_t, const uint8_t *, uint32_t, uint64_t, uint8_t); +int HTPParseContentRange(bstr * rawvalue, HtpContentRange *range); +int HTPFileSetRange(HtpState *, bstr *rawvalue); int HTPFileStoreChunk(HtpState *, const uint8_t *, uint32_t, uint8_t); int HTPFileClose(HtpState *, const uint8_t *, uint32_t, uint8_t, uint8_t); diff --git a/src/app-layer-htp.c b/src/app-layer-htp.c index d01f420fd7..5c7a5ad50d 100644 --- a/src/app-layer-htp.c +++ b/src/app-layer-htp.c @@ -1633,6 +1633,11 @@ static int HtpResponseBodyHandle(HtpState *hstate, HtpTxUserData *htud, htud->tcflags |= HTP_FILENAME_SET; htud->tcflags &= ~HTP_DONTSTORE; } + //set range if present + htp_header_t *h_content_range = htp_table_get_c(tx->response_headers, "content-range"); + if (h_content_range != NULL) { + HTPFileSetRange(hstate, h_content_range->value); + } } } else diff --git a/src/output-json-file.c b/src/output-json-file.c index 09d3510ba4..3283a2f310 100644 --- a/src/output-json-file.c +++ b/src/output-json-file.c @@ -217,6 +217,10 @@ json_t *JsonBuildFileInfoRecord(const Packet *p, const File *ff, json_object_set_new(fjs, "file_id", json_integer(ff->file_store_id)); } json_object_set_new(fjs, "size", json_integer(FileTrackedSize(ff))); + if (ff->end > 0) { + json_object_set_new(fjs, "start", json_integer(ff->start)); + json_object_set_new(fjs, "end", json_integer(ff->end)); + } json_object_set_new(fjs, "tx_id", json_integer(ff->txid)); /* xff header */ diff --git a/src/output-json-http.c b/src/output-json-http.c index f167b27b4c..aebdd72521 100644 --- a/src/output-json-http.c +++ b/src/output-json-http.c @@ -40,6 +40,7 @@ #include "output.h" #include "app-layer-htp.h" +#include "app-layer-htp-file.h" #include "app-layer-htp-xff.h" #include "app-layer.h" #include "app-layer-parser.h" @@ -261,6 +262,26 @@ static void JsonHttpLogJSONBasic(json_t *js, htp_tx_t *tx) *p = '\0'; json_object_set_new(js, "http_content_type", SCJsonString(string)); } + htp_header_t *h_content_range = htp_table_get_c(tx->response_headers, "content-range"); + if (h_content_range != NULL) { + const size_t size = bstr_len(h_content_range->value) * 2 + 1; + char string[size]; + BytesToStringBuffer(bstr_ptr(h_content_range->value), bstr_len(h_content_range->value), string, size); + json_t *crjs = json_object(); + if (crjs != NULL) { + json_object_set_new(crjs, "raw", SCJsonString(string)); + HtpContentRange crparsed; + if (HTPParseContentRange(h_content_range->value, &crparsed) == 0) { + if (crparsed.start >= 0) + json_object_set_new(crjs, "start", json_integer(crparsed.start)); + if (crparsed.end >= 0) + json_object_set_new(crjs, "end", json_integer(crparsed.end)); + if (crparsed.size >= 0) + json_object_set_new(crjs, "size", json_integer(crparsed.size)); + } + json_object_set_new(js, "content_range", crjs); + } + } } } diff --git a/src/util-file.c b/src/util-file.c index d3fd235ab0..f322970e72 100644 --- a/src/util-file.c +++ b/src/util-file.c @@ -738,6 +738,28 @@ int FileAppendGAPById(FileContainer *ffc, uint32_t track_id, SCReturnInt(-1); } +/** + * \brief Sets the offset range for a file. + * + * \param ffc the container + * \param start start offset + * \param end end offset + * + * \retval 0 ok + * \retval -1 error + */ +int FileSetRange(FileContainer *ffc, uint64_t start, uint64_t end) +{ + SCEnter(); + + if (ffc == NULL || ffc->tail == NULL) { + SCReturnInt(-1); + } + ffc->tail->start = start; + ffc->tail->end = end; + SCReturnInt(0); +} + /** * \brief Open a new File * diff --git a/src/util-file.h b/src/util-file.h index 4f67e73d3f..01bff145b2 100644 --- a/src/util-file.h +++ b/src/util-file.h @@ -89,6 +89,8 @@ typedef struct File_ { * flag is set */ uint64_t content_stored; uint64_t size; + uint64_t start; + uint64_t end; uint32_t *sid; /* signature id of a rule that triggered the filestore event */ uint32_t sid_cnt; @@ -169,6 +171,18 @@ int FileAppendDataById(FileContainer *, uint32_t track_id, int FileAppendGAPById(FileContainer *ffc, uint32_t track_id, const uint8_t *data, uint32_t data_len); +/** + * \brief Sets the offset range for a file. + * + * \param ffc the container + * \param start start offset + * \param end end offset + * + * \retval 0 ok + * \retval -1 error + */ +int FileSetRange(FileContainer *, uint64_t start, uint64_t end); + /** * \brief Tag a file for storing *