nhttp_stream_splitter.h
nhttp_splitter.cc
nhttp_splitter.h
+ nhttp_infractions.h
)
if (STATIC_INSPECTORS)
nhttp_scratch_pad.h \
nhttp_enum.h \
nhttp_test_manager.cc nhttp_test_manager.h \
-nhttp_field.cc nhttp_field.h
+nhttp_field.cc nhttp_field.h \
+nhttp_infractions.h
if STATIC_INSPECTORS
noinst_LIBRARIES = libnhttp_inspect.a
// All the infractions we might find while parsing and analyzing a message
typedef enum {
- INF_TRUNCATED=0x1, INF_HEADTOOLONG=0x2, INF_BADREQLINE=0x4, INF_BADSTATLINE=0x8, INF_TOOMANYHEADERS=0x10,
- INF_BADHEADER=0x20, INF_BADSTATCODE=0x40, INF_UNKNOWNVERSION=0x80, INF_BADVERSION=0x100, INF_NOSCRATCH=0x200,
- INF_BADHEADERREPS=0x400, INF_BADHEADERDATA=0x800, INF_BADCHUNKSIZE=0x2000,
- INF_BADPHRASE=0x4000, INF_BADURI=0x8000, INF_BADPORT=0x10000, INF_URINEEDNORM=0x20000, INF_URIPERCENTNORMAL=0x40000,
- INF_URIPERCENTASCII=0x80000, INF_URIPERCENTUTF8=0x100000, INF_URIPERCENTUCODE=0x200000, INF_URIPERCENTOTHER=0x400000,
- INF_URIBADCHAR=0x800000, INF_URI8BITCHAR=0x1000000, INF_URIMULTISLASH=0x2000000, INF_URIBACKSLASH=0x4000000,
- INF_URISLASHDOT=0x8000000, INF_URISLASHDOTDOT=0x10000000, INF_URIROOTTRAV=0x20000000 } Infraction;
+ INF_TRUNCATED = 0,
+ INF_HEADTOOLONG,
+ INF_BADREQLINE,
+ INF_BADSTATLINE,
+ INF_TOOMANYHEADERS,
+ INF_BADHEADER,
+ INF_BADSTATCODE,
+ INF_UNKNOWNVERSION,
+ INF_BADVERSION,
+ INF_NOSCRATCH,
+ INF_BADHEADERREPS,
+ INF_BADHEADERDATA,
+ INF_FIXIT_L_UNUSED,
+ INF_BADCHUNKSIZE,
+ INF_BADPHRASE,
+ INF_BADURI,
+ INF_BADPORT,
+ INF_URINEEDNORM,
+ INF_URIPERCENTNORMAL,
+ INF_URIPERCENTASCII,
+ INF_URIPERCENTUTF8,
+ INF_URIPERCENTUCODE,
+ INF_URIPERCENTOTHER,
+ INF_URIBADCHAR,
+ INF_URI8BITCHAR,
+ INF_URIMULTISLASH,
+ INF_URIBACKSLASH,
+ INF_URISLASHDOT,
+ INF_URISLASHDOTDOT,
+ INF_URIROOTTRAV,
+ INF_TOOMUCHLEADINGWS,
+} Infraction;
// Formats for output from a header normalization function
typedef enum { NORM_NULL, NORM_FIELD, NORM_INT64, NORM_ENUM64, NORM_ENUM64LIST } NormFormat;
TRANSCODE_DEFLATE } Transcoding;
typedef enum
-{
+{ // FIXIT-L limit 63 before code changes required
EVENT_ASCII = 1,
EVENT_DOUBLE_DECODE,
EVENT_U_ENCODE,
EVENT_BARE_BYTE,
EVENT_OBSOLETE_1,
- EVENT_UTF_8,
+ EVENT_UTF_8,
EVENT_IIS_UNICODE,
EVENT_MULTI_SLASH,
EVENT_IIS_BACKSLASH,
#include "stream/stream_api.h"
#include "nhttp_splitter.h"
+#include "nhttp_infractions.h"
class NHttpTransaction;
int32_t chunk_buffer_length[2] = { 0, 0 };
bool chunk_buffer_owned[2] = { true, true }; // never actually false and not checked, just here to simplify code
bool zero_chunk[2] = { false, false };
- uint64_t chunk_infractions[2] = { 0, 0 };
+ NHttpInfractions chunk_infractions[2];
// StreamSplitter => Inspector (facts about the most recent message section)
// 0 element refers to client request, 1 element refers to server response
NHttpEnums::SectionType section_type[2] = { NHttpEnums::SEC__NOTCOMPUTE, NHttpEnums::SEC__NOTCOMPUTE };
uint32_t num_excess[2] = { 0, 0 };
bool tcp_close[2] = { false, false };
- uint64_t infractions[2] = { 0, 0 };
+ NHttpInfractions infractions[2];
uint32_t unused_octets_visible[2] = { 0, 0 };
uint32_t header_octets_visible[2] = { 0, 0 };
}
// This method normalizes the header field value for headId.
-int32_t HeaderNormalizer::normalize(const HeaderId head_id, const int count, ScratchPad &scratch_pad, uint64_t &infractions,
- const HeaderId header_name_id[], const Field header_value[], const int32_t num_headers, Field &result_field) const {
+int32_t HeaderNormalizer::normalize(const HeaderId head_id, const int count, ScratchPad &scratch_pad,
+ NHttpInfractions &infractions, const HeaderId header_name_id[], const Field header_value[],
+ const int32_t num_headers, Field &result_field) const {
if (result_field.length != STAT_NOTCOMPUTE) {
return result_field.length;
}
#include "nhttp_scratch_pad.h"
#include "nhttp_field.h"
+#include "nhttp_infractions.h"
//-------------------------------------------------------------------------
// HeaderNormalizer class
constexpr HeaderNormalizer(
NHttpEnums::NormFormat _format,
bool _concatenate_repeats,
- int32_t (*f1)(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void*),
+ int32_t (*f1)(const uint8_t*, int32_t, uint8_t*, NHttpInfractions&, const void*),
const void *f1_arg,
- int32_t (*f2)(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void*),
+ int32_t (*f2)(const uint8_t*, int32_t, uint8_t*, NHttpInfractions&, const void*),
const void *f2_arg,
- int32_t (*f3)(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void*),
+ int32_t (*f3)(const uint8_t*, int32_t, uint8_t*, NHttpInfractions&, const void*),
const void *f3_arg)
: format(_format), concatenate_repeats(_concatenate_repeats), normalizer { f1, f2, f3 },
norm_arg { f1_arg, f2_arg, f3_arg },
num_normalizers((f1 != nullptr) + (f1 != nullptr)*(f2 != nullptr) + (f1 != nullptr)*(f2 != nullptr)*(f3 != nullptr))
{};
- int32_t normalize(const NHttpEnums::HeaderId head_id, const int count, ScratchPad &scratch_pad, uint64_t &infractions,
- const NHttpEnums::HeaderId header_name_id[], const Field header_value[], const int32_t num_headers,
- Field &result_field) const;
+ int32_t normalize(const NHttpEnums::HeaderId head_id, const int count, ScratchPad &scratch_pad,
+ NHttpInfractions &infractions, const NHttpEnums::HeaderId header_name_id[], const Field header_value[],
+ const int32_t num_headers, Field &result_field) const;
NHttpEnums::NormFormat get_format() const { return format; };
private:
const NHttpEnums::NormFormat format;
const bool concatenate_repeats;
- int32_t (* const normalizer[3])(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void*);
+ int32_t (* const normalizer[3])(const uint8_t*, int32_t, uint8_t*, NHttpInfractions&, const void*);
const void * norm_arg[3];
const int num_normalizers;
};
--- /dev/null
+/*
+** Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License Version 2 as
+** published by the Free Software Foundation. You may not use, modify or
+** distribute this program under any other version of the GNU General
+** Public License.
+**
+** 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
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+// nhttp_scratch_pad.h author Tom Peters <thopeter@cisco.com>
+
+#ifndef NHTTP_INFRACTIONS_H
+#define NHTTP_INFRACTIONS_H
+
+#include <assert.h>
+
+//-------------------------------------------------------------------------
+// Infractions class
+//-------------------------------------------------------------------------
+
+class NHttpInfractions {
+public:
+ NHttpInfractions() {};
+ NHttpInfractions(int inf) : infractions(1 << inf) { assert((inf >= 0) && (inf < 64)); };
+ bool found_new(int inf) {
+ assert((inf >= 0) && (inf < 64));
+ const bool ret_val = ((1 << inf) & infractions & ~previous_infractions) != 0;
+ previous_infractions |= (1 << inf) & infractions;
+ return ret_val; };
+ bool none_found() const { return infractions == 0; };
+ NHttpInfractions& operator+=(const NHttpInfractions& rhs) { infractions |= rhs.infractions;
+ previous_infractions |= rhs.previous_infractions; return *this; };
+ friend NHttpInfractions operator+(NHttpInfractions lhs, const NHttpInfractions& rhs) { lhs += rhs; return lhs; };
+ friend bool operator&&(const NHttpInfractions& lhs, const NHttpInfractions& rhs) {
+ return (lhs.infractions & rhs.infractions) != 0; };
+
+ // The following two methods are for convenience of debug and test output only!
+ // The 64-bit implementation will not be big enough forever and this interface cannot be all over the code.
+ uint64_t get_raw() const { return infractions; };
+ uint64_t get_raw_prev() const { return previous_infractions; };
+private:
+ NHttpInfractions(uint64_t infractions_, uint64_t previous_infractions_) : infractions(infractions_),
+ previous_infractions(previous_infractions_) {};
+
+ uint64_t infractions = 0;
+ uint64_t previous_infractions = 0;
+};
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
data.start = msg_text.start;
data.length = msg_text.length;
- if (tcp_close && (body_octets < data_length)) infractions |= INF_TRUNCATED;
+ if (tcp_close && (body_octets < data_length)) infractions += INF_TRUNCATED;
}
void NHttpMsgBody::gen_events() {
}
}
if (bytes_used < msg_text.length) {
- infractions |= INF_TOOMANYHEADERS;
+ infractions += INF_TOOMANYHEADERS;
}
}
header_value[k].length = header_line[k].length - colon - 1;
}
else {
- infractions |= INF_BADHEADER;
+ infractions += INF_BADHEADER;
}
}
}
// Normalize header field name to lower case for matching purposes
uint8_t *lower_name;
if ((lower_name = scratch_pad.request(header_name[index].length)) == nullptr) {
- infractions |= INF_NOSCRATCH;
+ infractions += INF_NOSCRATCH;
header_name_id[index] = HEAD__INSUFMEMORY;
return;
}
}
void NHttpMsgHeadShared::gen_events() {
- if (infractions & INF_TOOMANYHEADERS) create_event(EVENT_MAX_HEADERS);
+ if (infractions && INF_TOOMANYHEADERS) create_event(EVENT_MAX_HEADERS);
}
void NHttpMsgHeadShared::print_headers(FILE *output) {
session_data->type_expected[source_id] = SEC_CLOSED;
session_data->half_reset(source_id);
}
- else if (infractions & disaster_mask) {
+ else if (infractions && disaster_mask) {
session_data->type_expected[source_id] = SEC_ABORT;
session_data->half_reset(source_id);
}
// <method><SP><URI><SP>HTTP/X.Y
if (start_line.start[start_line.length-9] != ' ') {
// space before "HTTP" missing or in wrong place
- infractions |= INF_BADREQLINE;
+ infractions += INF_BADREQLINE;
return;
}
}
if (space >= start_line.length-9) {
// leading space or no space
- infractions |= INF_BADREQLINE;
+ infractions += INF_BADREQLINE;
return;
}
if (method_id == METH__OTHER) create_event(EVENT_UNKNOWN_METHOD);
// URI character encoding events
- if (uri && (uri->get_uri_infractions() & INF_URIPERCENTASCII)) create_event(EVENT_ASCII);
- if (uri && (uri->get_uri_infractions() & INF_URIPERCENTUCODE)) create_event(EVENT_U_ENCODE);
- if (uri && (uri->get_uri_infractions() & INF_URI8BITCHAR)) create_event(EVENT_BARE_BYTE);
- if (uri && (uri->get_uri_infractions() & INF_URIPERCENTUTF8)) create_event(EVENT_UTF_8);
- if (uri && (uri->get_uri_infractions() & INF_URIBADCHAR)) create_event(EVENT_NON_RFC_CHAR);
+ if (uri && (uri->get_uri_infractions() && INF_URIPERCENTASCII)) create_event(EVENT_ASCII);
+ if (uri && (uri->get_uri_infractions() && INF_URIPERCENTUCODE)) create_event(EVENT_U_ENCODE);
+ if (uri && (uri->get_uri_infractions() && INF_URI8BITCHAR)) create_event(EVENT_BARE_BYTE);
+ if (uri && (uri->get_uri_infractions() && INF_URIPERCENTUTF8)) create_event(EVENT_UTF_8);
+ if (uri && (uri->get_uri_infractions() && INF_URIBADCHAR)) create_event(EVENT_NON_RFC_CHAR);
// URI path events
- if (uri && (uri->get_path_infractions() & INF_URIMULTISLASH)) create_event(EVENT_MULTI_SLASH);
- if (uri && (uri->get_path_infractions() & INF_URIBACKSLASH)) create_event(EVENT_IIS_BACKSLASH);
- if (uri && (uri->get_path_infractions() & INF_URISLASHDOT)) create_event(EVENT_SELF_DIR_TRAV);
- if (uri && (uri->get_path_infractions() & INF_URISLASHDOTDOT)) create_event(EVENT_DIR_TRAV);
- if (uri && (uri->get_path_infractions() & INF_URIROOTTRAV)) create_event(EVENT_WEBROOT_DIR);
+ if (uri && (uri->get_path_infractions() && INF_URIMULTISLASH)) create_event(EVENT_MULTI_SLASH);
+ if (uri && (uri->get_path_infractions() && INF_URIBACKSLASH)) create_event(EVENT_IIS_BACKSLASH);
+ if (uri && (uri->get_path_infractions() && INF_URISLASHDOT)) create_event(EVENT_SELF_DIR_TRAV);
+ if (uri && (uri->get_path_infractions() && INF_URISLASHDOTDOT)) create_event(EVENT_DIR_TRAV);
+ if (uri && (uri->get_path_infractions() && INF_URIROOTTRAV)) create_event(EVENT_WEBROOT_DIR);
}
uri->get_norm_fragment().print(output, "Normalized Fragment");
fprintf(output, "URI infractions: overall %" PRIx64 ", format %" PRIx64 ", scheme %" PRIx64 ", host %" PRIx64 ", port %" PRIx64 ", path %"
PRIx64 ", query %" PRIx64 ", fragment %" PRIx64 "\n",
- uri->get_uri_infractions(), uri->get_format_infractions(), uri->get_scheme_infractions(), uri->get_host_infractions(),
- uri->get_port_infractions(), uri->get_path_infractions(), uri->get_query_infractions(), uri->get_fragment_infractions());
+ uri->get_uri_infractions().get_raw(), uri->get_format_infractions().get_raw(),
+ uri->get_scheme_infractions().get_raw(), uri->get_host_infractions().get_raw(),
+ uri->get_port_infractions().get_raw(), uri->get_path_infractions().get_raw(),
+ uri->get_query_infractions().get_raw(), uri->get_fragment_infractions().get_raw());
}
NHttpMsgSection::print_message_wrapup(output);
}
session_data->type_expected[source_id] = SEC_CLOSED;
session_data->half_reset(source_id);
}
- else if (infractions & disaster_mask) {
+ else if (infractions && disaster_mask) {
session_data->type_expected[source_id] = SEC_ABORT;
session_data->half_reset(source_id);
}
}
void NHttpMsgSection::print_message_wrapup(FILE *output) const {
- fprintf(output, "Infractions: %" PRIx64 ", Events: %" PRIx64 ", TCP Close: %s\n", infractions, events_generated,
- tcp_close ? "True" : "False");
+ fprintf(output, "Infractions: %" PRIx64 ", Events: %" PRIx64 ", TCP Close: %s\n", infractions.get_raw(),
+ events_generated, tcp_close ? "True" : "False");
fprintf(output, "Interface to old clients. http_mask = %x.\n", http_mask);
for (int i=0; i < HTTP_BUFFER_MAX; i++) {
if ((1 << i) & http_mask) Field(http_buffer[i].length, http_buffer[i].buf).print(output, http_buffer_name[i]);
#include "nhttp_field.h"
#include "nhttp_flow_data.h"
#include "nhttp_transaction.h"
+#include "nhttp_infractions.h"
//-------------------------------------------------------------------------
// NHttpMsgSection class
const bool tcp_close;
ScratchPad scratch_pad;
- uint64_t infractions;
+ NHttpInfractions infractions;
uint64_t events_generated = 0;
NHttpEnums::VersionId version_id;
NHttpEnums::MethodId method_id;
}
if (version.length != 8) {
version_id = VERS__PROBLEMATIC;
- infractions |= INF_BADVERSION;
+ infractions += INF_BADVERSION;
return;
}
if (memcmp(version.start, "HTTP/", 5) || (version.start[6] != '.')) {
version_id = VERS__PROBLEMATIC;
- infractions |= INF_BADVERSION;
+ infractions += INF_BADVERSION;
}
else if ((version.start[5] == '1') && (version.start[7] == '1')) {
version_id = VERS_1_1;
else if ((version.start[5] >= '0') && (version.start[5] <= '9') &&
(version.start[7] >= '0') && (version.start[7] <= '9')) {
version_id = VERS__OTHER;
- infractions |= INF_UNKNOWNVERSION;
+ infractions += INF_UNKNOWNVERSION;
}
else {
version_id = VERS__PROBLEMATIC;
- infractions |= INF_BADVERSION;
+ infractions += INF_BADVERSION;
}
}
// Eventually we may need to cater to certain format errors, but for now exact match or treat as error.
// HTTP/X.Y<SP>###<SP><text>
if ((start_line.length < 13) || (start_line.start[8] != ' ') || (start_line.start[12] != ' ')) {
- infractions |= INF_BADSTATLINE;
+ infractions += INF_BADSTATLINE;
return;
}
version.start = start_line.start;
for (int32_t k = 0; k < reason_phrase.length; k++) {
if ((reason_phrase.start[k] <= 31) || (reason_phrase.start[k] >= 127)) {
// Illegal character in reason phrase
- infractions |= INF_BADPHRASE;
+ infractions += INF_BADPHRASE;
break;
}
}
if ((status_code.start[0] < '0') || (status_code.start[0] > '9') || (status_code.start[1] < '0') || (status_code.start[1] > '9') ||
(status_code.start[2] < '0') || (status_code.start[2] > '9')) {
- infractions |= INF_BADSTATCODE;
+ infractions += INF_BADSTATCODE;
status_code_num = STAT_PROBLEMATIC;
return;
}
status_code_num = (status_code.start[0] - '0') * 100 + (status_code.start[1] - '0') * 10 + (status_code.start[2] - '0');
if ((status_code_num < 100) || (status_code_num > 599)) {
- infractions |= INF_BADSTATCODE;
+ infractions += INF_BADSTATCODE;
}
}
session_data->type_expected[source_id] = SEC_CLOSED;
session_data->half_reset(source_id);
}
- else if (infractions & disaster_mask) {
+ else if (infractions && disaster_mask) {
session_data->type_expected[source_id] = SEC_ABORT;
session_data->half_reset(source_id);
}
// Collection of stock normalization functions. This will probably grow throughout the life of the software. New functions must follow the standard signature.
// The void* at the end is for any special configuration data the function requires.
-int32_t norm_decimal_integer(const uint8_t* in_buf, int32_t in_length, uint8_t* out_buf, uint64_t& infractions, const void *) {
+int32_t norm_decimal_integer(const uint8_t* in_buf, int32_t in_length, uint8_t* out_buf, NHttpInfractions& infractions, const void *) {
// Limited to 18 decimal digits, not including leading zeros, to fit comfortably into int64_t
int64_t total = 0;
int non_leading_zeros = 0;
int value = in_buf[k] - '0';
if (non_leading_zeros || (value != 0)) non_leading_zeros++;
if (non_leading_zeros > 18) {
- infractions |= INF_BADHEADERDATA;
+ infractions += INF_BADHEADERDATA;
return STAT_PROBLEMATIC;
}
if ((value < 0) || (value > 9)) {
- infractions |= INF_BADHEADERDATA;
+ infractions += INF_BADHEADERDATA;
return STAT_PROBLEMATIC;
}
total = total*10 + value;
}
-int32_t norm_to_lower(const uint8_t* in_buf, int32_t in_length, uint8_t *out_buf, uint64_t&, const void *) {
+int32_t norm_to_lower(const uint8_t* in_buf, int32_t in_length, uint8_t *out_buf, NHttpInfractions&, const void *) {
for (int32_t k=0; k < in_length; k++) {
out_buf[k] = ((in_buf[k] < 'A') || (in_buf[k] > 'Z')) ? in_buf[k] : in_buf[k] - ('A' - 'a');
}
}
-int32_t norm_str_code(const uint8_t* in_buf, int32_t in_length, uint8_t *out_buf, uint64_t&, const void *table) {
+int32_t norm_str_code(const uint8_t* in_buf, int32_t in_length, uint8_t* out_buf, NHttpInfractions&,
+ const void* table) {
((int64_t*)out_buf)[0] = str_to_code(in_buf, in_length, (const StrCode*)table);
return sizeof(int64_t);
}
-int32_t norm_seq_str_code(const uint8_t* in_buf, int32_t in_length, uint8_t *out_buf, uint64_t&, const void *table) {
+int32_t norm_seq_str_code(const uint8_t* in_buf, int32_t in_length, uint8_t *out_buf, NHttpInfractions&,
+ const void *table) {
int32_t num_codes = 0;
const uint8_t* start = in_buf;
while (true) {
}
// Remove all space and tab characters (known as LWS or linear white space in the RFC)
-int32_t norm_remove_lws(const uint8_t* in_buf, int32_t in_length, uint8_t *out_buf, uint64_t&, const void *) {
+int32_t norm_remove_lws(const uint8_t* in_buf, int32_t in_length, uint8_t* out_buf, NHttpInfractions&, const void*) {
int32_t length = 0;
for (int32_t k = 0; k < in_length; k++) {
if ((in_buf[k] != ' ') && (in_buf[k] != '\t')) out_buf[length++] = in_buf[k];
#ifndef NHTTP_NORMALIZERS_H
#define NHTTP_NORMALIZERS_H
-int32_t norm_decimal_integer(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* not_used);
-int32_t norm_to_lower(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* not_used);
-int32_t norm_str_code(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void*);
-int32_t norm_seq_str_code(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void*);
-int32_t norm_remove_lws(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* not_used);
+#include "nhttp_infractions.h"
+
+int32_t norm_decimal_integer(const uint8_t*, int32_t, uint8_t*, NHttpInfractions&, const void* not_used);
+int32_t norm_to_lower(const uint8_t*, int32_t, uint8_t*, NHttpInfractions&, const void* not_used);
+int32_t norm_str_code(const uint8_t*, int32_t, uint8_t*, NHttpInfractions&, const void*);
+int32_t norm_seq_str_code(const uint8_t*, int32_t, uint8_t*, NHttpInfractions&, const void*);
+int32_t norm_remove_lws(const uint8_t*, int32_t, uint8_t*, NHttpInfractions&, const void* not_used);
#endif
num_crlf++;
continue;
}
- else { // FIXIT-M there needs to be an event for this
+ else {
+ infractions += INF_TOOMUCHLEADINGWS;
return SCAN_ABORT;
}
}
#include <assert.h>
#include "nhttp_enum.h"
+#include "nhttp_infractions.h"
//-------------------------------------------------------------------------
// NHttpSplitter class
virtual uint32_t get_num_excess() const { return 0; };
virtual bool get_zero_chunk() const { return false; };
virtual bool partial_ok() const { return true; };
-
+ NHttpInfractions get_infractions() const { return infractions; }
protected:
uint32_t octets_seen = 0; // number of octets processed by previous split() calls that returned NOTFOUND
uint32_t num_crlf = 0;
uint32_t num_flush = 0;
+ NHttpInfractions infractions;
};
class NHttpStartSplitter : public NHttpSplitter {
// Convenience function. All the housekeeping that must be done before we can return FLUSH to stream.
void NHttpStreamSplitter::prepare_flush(NHttpFlowData* session_data, uint32_t* flush_offset, SectionType section_type,
- bool tcp_close, uint64_t infractions, uint32_t num_octets, uint32_t length, uint32_t num_excess,
+ bool tcp_close, const NHttpInfractions& infractions, uint32_t num_octets, uint32_t length, uint32_t num_excess,
bool zero_chunk) {
session_data->section_type[source_id] = section_type;
session_data->num_excess[source_id] = num_excess;
}
if (tcp_close) {
if (splitter->partial_ok()) {
- prepare_flush(session_data, flush_offset, type, true, INF_TRUNCATED, length, length,
- splitter->get_num_excess(), splitter->get_zero_chunk());
+ prepare_flush(session_data, flush_offset, type, true, splitter->get_infractions() += INF_TRUNCATED,
+ length, length, splitter->get_num_excess(), splitter->get_zero_chunk());
delete splitter;
splitter = nullptr;
return StreamSplitter::FLUSH;
}
else {
- prepare_flush(session_data, flush_offset, SEC_DISCARD, true, 0, length, length, 0, false);
+ prepare_flush(session_data, flush_offset, SEC_DISCARD, true, splitter->get_infractions(), length,
+ length, 0, false);
delete splitter;
splitter = nullptr;
return StreamSplitter::FLUSH;
case SCAN_DISCARD:
case SCAN_DISCARD_CONTINUE: {
const uint32_t flush_octets = splitter->get_num_flush();
- prepare_flush(session_data, flush_offset, SEC_DISCARD, tcp_close && (flush_octets >= length), 0,
- flush_octets, length, 0, false);
+ prepare_flush(session_data, flush_offset, SEC_DISCARD, tcp_close && (flush_octets >= length),
+ splitter->get_infractions(), flush_octets, length, 0, false);
if (split_result == SCAN_DISCARD) {
delete splitter;
splitter = nullptr;
}
case SCAN_FOUND: {
const uint32_t flush_octets = splitter->get_num_flush();
- prepare_flush(session_data, flush_offset, type, tcp_close && (flush_octets == length), 0,
- flush_octets, length, splitter->get_num_excess(), splitter->get_zero_chunk());
+ prepare_flush(session_data, flush_offset, type, tcp_close && (flush_octets == length),
+ splitter->get_infractions(), flush_octets, length, splitter->get_num_excess(),
+ splitter->get_zero_chunk());
delete splitter;
splitter = nullptr;
if ((type == SEC_REQUEST) || (type == SEC_STATUS)) {
}
case SEC_BODY: {
prepare_flush(session_data, flush_offset, SEC_BODY,
- tcp_close && (length <= session_data->data_length[source_id]), 0,
+ tcp_close && (length <= session_data->data_length[source_id]), NHttpInfractions(),
session_data->data_length[source_id], length, 0, false);
return StreamSplitter::FLUSH;
}
// zero_chunk == true) flushes accumulated chunks.
// infractions for each aggregated small chunk are accumulated here and then restored before processing
- session_data->chunk_infractions[source_id] |= session_data->infractions[source_id];
+ session_data->chunk_infractions[source_id] += session_data->infractions[source_id];
// FIXIT-M this implementation of the zero-length chunk is temporary until stream can support a zero-
// octet flush.
}
paf_max = DATABLOCKSIZE;
session_data->infractions[source_id] = session_data->chunk_infractions[source_id];
- session_data->chunk_infractions[source_id] = 0;
+ session_data->chunk_infractions[source_id] = NHttpInfractions();
send_to_detection = my_inspector->process(chunk_buffer, total_chunk_len, flow, source_id, true);
if (session_data->zero_chunk[source_id]) {
// zero-length chunk is not visible to inspector. Transition to trailer must be handled here.
unsigned max() override { return NHttpTestManager::use_test_input() ? NHttpEnums::DATABLOCKSIZE : paf_max; };
private:
void prepare_flush(NHttpFlowData* session_data, uint32_t* flush_offset, NHttpEnums::SectionType section_type,
- bool tcp_close, uint64_t infractions, uint32_t num_octets, uint32_t length, uint32_t num_excess,
+ bool tcp_close, const NHttpInfractions& infractions, uint32_t num_octets, uint32_t length, uint32_t num_excess,
bool zero_chunk);
NHttpSplitter* get_splitter(NHttpEnums::SectionType type) const;
abs_path.start = uri.start + k;
}
else {
- format_infractions |= INF_BADURI;
+ format_infractions += INF_BADURI;
uri_type = URI__PROBLEMATIC;
scheme.length = STAT_PROBLEMATIC;
authority.length = STAT_PROBLEMATIC;
// Normalize scheme name to lower case for matching purposes
uint8_t *lower_scheme;
if ((lower_scheme = scratch_pad.request(scheme.length)) == nullptr) {
- scheme_infractions |= INF_NOSCRATCH;
+ scheme_infractions += INF_NOSCRATCH;
scheme_id = SCH__INSUFMEMORY;
return scheme_id;
}
port_value = port_value * 10 + (port.start[k] - '0');
if ((port.start[k] < '0') || (port.start[k] > '9') || (port_value > 65535))
{
- port_infractions |= INF_BADPORT;
+ port_infractions += INF_BADPORT;
port_value = STAT_PROBLEMATIC;
break;
}
}
// We can reuse the raw URI for the normalized URI unless at least one part of the URI has been normalized
- if ((host_infractions == 0) && (path_infractions == 0) && (query_infractions == 0) && (fragment_infractions == 0)) {
+ if ((host_infractions.none_found()) && (path_infractions.none_found()) && (query_infractions.none_found()) &&
+ (fragment_infractions.none_found())) {
legacy_norm.start = uri.start;
legacy_norm.length = uri.length;
return legacy_norm;
#include "nhttp_str_to_code.h"
#include "nhttp_uri_norm.h"
#include "nhttp_field.h"
+#include "nhttp_infractions.h"
//-------------------------------------------------------------------------
// NHttpUri class
const Field& get_query() { parse_abs_path(); return query; };
const Field& get_fragment() { parse_abs_path(); return fragment; };
- uint64_t get_format_infractions() { parse_uri(); return format_infractions; };
- uint64_t get_scheme_infractions() { get_scheme_id(); return scheme_infractions; };
- uint64_t get_host_infractions() { get_norm_host(); return host_infractions; };
- uint64_t get_port_infractions() { get_port_value(); return port_infractions; };
- uint64_t get_path_infractions() { get_norm_path(); return path_infractions; };
- uint64_t get_query_infractions() { get_norm_query(); return query_infractions; };
- uint64_t get_fragment_infractions() { get_norm_fragment(); return fragment_infractions; };
- uint64_t get_uri_infractions() { return get_format_infractions() | get_scheme_infractions() | get_host_infractions() |
- get_port_infractions() | get_path_infractions() | get_query_infractions() | get_fragment_infractions(); };
+ NHttpInfractions get_format_infractions() { parse_uri(); return format_infractions; };
+ NHttpInfractions get_scheme_infractions() { get_scheme_id(); return scheme_infractions; };
+ NHttpInfractions get_host_infractions() { get_norm_host(); return host_infractions; };
+ NHttpInfractions get_port_infractions() { get_port_value(); return port_infractions; };
+ NHttpInfractions get_path_infractions() { get_norm_path(); return path_infractions; };
+ NHttpInfractions get_query_infractions() { get_norm_query(); return query_infractions; };
+ NHttpInfractions get_fragment_infractions() { get_norm_fragment(); return fragment_infractions; };
+ NHttpInfractions get_uri_infractions() { return get_format_infractions() + get_scheme_infractions() + get_host_infractions() +
+ get_port_infractions() + get_path_infractions() + get_query_infractions() + get_fragment_infractions(); };
NHttpEnums::SchemeId get_scheme_id();
const Field& get_norm_host();
Field query;
Field fragment;
- uint64_t format_infractions = 0;
- uint64_t scheme_infractions = 0;
- uint64_t host_infractions = 0;
- uint64_t port_infractions = 0;
- uint64_t path_infractions = 0;
- uint64_t query_infractions = 0;
- uint64_t fragment_infractions = 0;
+ NHttpInfractions format_infractions;
+ NHttpInfractions scheme_infractions;
+ NHttpInfractions host_infractions;
+ NHttpInfractions port_infractions;
+ NHttpInfractions path_infractions;
+ NHttpInfractions query_infractions;
+ NHttpInfractions fragment_infractions;
NHttpEnums::UriType uri_type = NHttpEnums::URI__NOTCOMPUTE;
NHttpEnums::SchemeId scheme_id = NHttpEnums::SCH__NOTCOMPUTE;
using namespace NHttpEnums;
-void UriNormalizer::normalize(const Field &input, Field &result, bool do_path, ScratchPad &scratch_pad, uint64_t &infractions) {
+void UriNormalizer::normalize(const Field &input, Field &result, bool do_path, ScratchPad &scratch_pad,
+ NHttpInfractions &infractions) {
if (result.length != STAT_NOTCOMPUTE) return;
assert (input.length >= 0);
result.length = data_length;
}
-bool UriNormalizer::no_path_check(const uint8_t* in_buf, int32_t in_length, uint64_t& infractions) {
+bool UriNormalizer::no_path_check(const uint8_t* in_buf, int32_t in_length, NHttpInfractions& infractions) {
for (int32_t k = 0; k < in_length; k++) {
if ((uri_char[in_buf[k]] == CHAR_NORMAL) || (uri_char[in_buf[k]] == CHAR_PATH)) continue;
- infractions |= INF_URINEEDNORM;
+ infractions += INF_URINEEDNORM;
return false;
}
return true;
}
-bool UriNormalizer::path_check(const uint8_t* in_buf, int32_t in_length, uint64_t& infractions) {
+bool UriNormalizer::path_check(const uint8_t* in_buf, int32_t in_length, NHttpInfractions& infractions) {
for (int32_t k = 0; k < in_length; k++) {
// FIXIT-P Periods are common and most don't need to be normalized. Need a better test.
if (uri_char[in_buf[k]] == CHAR_NORMAL) continue;
if ((in_buf[k] == '/') && ((k == 0) || (in_buf[k-1] != '/'))) continue;
- infractions |= INF_URINEEDNORM;
+ infractions += INF_URINEEDNORM;
return false;
}
return true;
}
-int32_t UriNormalizer::norm_char_clean(const uint8_t* in_buf, int32_t in_length, uint8_t *out_buf, uint64_t& infractions, const void *) {
+int32_t UriNormalizer::norm_char_clean(const uint8_t* in_buf, int32_t in_length, uint8_t *out_buf,
+ NHttpInfractions& infractions, const void *) {
int32_t length = 0;
for (int32_t k = 0; k < in_length; k++) {
switch (uri_char[in_buf[k]]) {
out_buf[length++] = in_buf[k];
break;
case CHAR_INVALID:
- infractions |= INF_URIBADCHAR;
+ infractions += INF_URIBADCHAR;
out_buf[length++] = in_buf[k];
break;
case CHAR_EIGHTBIT:
- infractions |= INF_URI8BITCHAR;
+ infractions += INF_URI8BITCHAR;
out_buf[length++] = in_buf[k];
break;
case CHAR_PERCENT:
uint8_t value = as_hex[in_buf[k+1]] * 16 + as_hex[in_buf[k+2]];
if (good_percent[value]) {
// Normal % escape of an ASCII special character that is supposed to be escaped
- infractions |= INF_URIPERCENTNORMAL;
+ infractions += INF_URIPERCENTNORMAL;
out_buf[length++] = '%';
}
else {
// Suspicious % escape of an ASCII character that does not need to be escaped
- infractions |= INF_URIPERCENTASCII;
- if (uri_char[value] == CHAR_INVALID) infractions |= INF_URIBADCHAR;
+ infractions += INF_URIPERCENTASCII;
+ if (uri_char[value] == CHAR_INVALID) infractions += INF_URIBADCHAR;
out_buf[length++] = value;
k += 2;
}
}
else {
// UTF-8 decoding not implemented yet
- infractions |= INF_URIPERCENTUTF8;
+ infractions += INF_URIPERCENTUTF8;
out_buf[length++] = '%';
}
}
else if ((k+5 < in_length) && (in_buf[k+1] == 'u') && (as_hex[in_buf[k+2]] != -1) && (as_hex[in_buf[k+3]] != -1)
&& (as_hex[in_buf[k+4]] != -1) && (as_hex[in_buf[k+5]] != -1)) {
// 'u' UTF-16 decoding not implemented yet
- infractions |= INF_URIPERCENTUCODE;
+ infractions += INF_URIPERCENTUCODE;
out_buf[length++] = '%';
}
else {
// Don't recognize it
- infractions |= INF_URIPERCENTOTHER;
+ infractions += INF_URIPERCENTOTHER;
out_buf[length++] = '%';
}
break;
}
// Convert URI backslashes to slashes
-int32_t UriNormalizer::norm_backslash(const uint8_t* in_buf, int32_t in_length, uint8_t *out_buf, uint64_t& infractions, const void *) {
+int32_t UriNormalizer::norm_backslash(const uint8_t* in_buf, int32_t in_length, uint8_t* out_buf,
+ NHttpInfractions& infractions, const void*) {
for (int32_t k = 0; k < in_length; k++) {
if (in_buf[k] != '\\') out_buf[k] = in_buf[k];
else {
out_buf[k] = '/';
- infractions |= INF_URIBACKSLASH;
+ infractions += INF_URIBACKSLASH;
}
}
return in_length;
}
// Caution: worst case output length is one greater than input length
-int32_t UriNormalizer::norm_path_clean(const uint8_t* in_buf, int32_t in_length, uint8_t *out_buf, uint64_t& infractions, const void *) {
+int32_t UriNormalizer::norm_path_clean(const uint8_t* in_buf, int32_t in_length, uint8_t* out_buf,
+ NHttpInfractions& infractions, const void*) {
int32_t length = 0;
// It simplifies the code that handles /./ and /../ to pretend there is an extra '/' after the buffer.
// Avoids making a special case of URIs that end in . or ..
}
// Ignore this slash if it directly follows another slash
else if ((k < in_length) && (length >= 1) && (out_buf[length-1] == '/')) {
- infractions |= INF_URIMULTISLASH;
+ infractions += INF_URIMULTISLASH;
}
// This slash is the end of a /./ pattern, ignore this slash and remove the period from the output
else if ((length >= 2) && (out_buf[length-1] == '.') && (out_buf[length-2] == '/')) {
- infractions |= INF_URISLASHDOT;
+ infractions += INF_URISLASHDOT;
length -= 1;
}
// This slash is the end of a /../ pattern, normalization depends on whether there is a previous directory that
// we can remove
else if ((length >= 3) && (out_buf[length-1] == '.') && (out_buf[length-2] == '.') && (out_buf[length-3] == '/')) {
- infractions |= INF_URISLASHDOTDOT;
+ infractions += INF_URISLASHDOTDOT;
// Traversing above the root of the absolute path. A path of the form /../../../foo/bar/whatever cannot be
// further normalized. Instead of taking away a directory we leave the .. and write out the new slash.
// This code can write out the pretend slash after the end of the buffer. That is intentional so that the
if ( (length == 3) ||
((length >= 6) && (out_buf[length-4] == '.') && (out_buf[length-5] == '.') && (out_buf[length-6] == '/')))
{
- infractions |= INF_URIROOTTRAV;
+ infractions += INF_URIROOTTRAV;
out_buf[length++] = '/';
}
// Remove the previous directory from the output. "/foo/bar/../" becomes "/foo/"
#include "nhttp_scratch_pad.h"
#include "nhttp_field.h"
+#include "nhttp_infractions.h"
class UriNormalizer {
public:
- static void normalize(const Field &input, Field &result, bool do_path, ScratchPad &scratch_pad, uint64_t &infractions);
+ static void normalize(const Field &input, Field &result, bool do_path, ScratchPad &scratch_pad,
+ NHttpInfractions &infractions);
private:
static const NHttpEnums::CharAction uri_char[256];
static const bool good_percent[256];
- static bool no_path_check(const uint8_t* in_buf, int32_t in_length, uint64_t& infractions);
- static bool path_check(const uint8_t* in_buf, int32_t in_length, uint64_t& infractions);
+ static bool no_path_check(const uint8_t* in_buf, int32_t in_length, NHttpInfractions& infractions);
+ static bool path_check(const uint8_t* in_buf, int32_t in_length, NHttpInfractions& infractions);
- static int32_t norm_char_clean(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* not_used);
- static int32_t norm_backslash(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* not_used);
- static int32_t norm_path_clean(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* not_used);
+ static int32_t norm_char_clean(const uint8_t*, int32_t, uint8_t*, NHttpInfractions&, const void* not_used);
+ static int32_t norm_backslash(const uint8_t*, int32_t, uint8_t*, NHttpInfractions&, const void* not_used);
+ static int32_t norm_path_clean(const uint8_t*, int32_t, uint8_t*, NHttpInfractions&, const void* not_used);
};
#endif