// The purpose of validate() is to quickly and efficiently dispose of obviously wrong
// bindings. Passing is no guarantee that the connection is really HTTP, but failing
// makes it clear that it isn't.
- switch (validate(buffer[k]))
+ switch (validate(buffer[k], infractions, events))
{
case V_GOOD:
validated = true;
return SCAN_NOTFOUND;
}
-HttpStartCutter::ValidationResult HttpRequestCutter::validate(uint8_t octet)
+HttpStartCutter::ValidationResult HttpRequestCutter::validate(uint8_t octet, HttpInfractions*,
+ HttpEventGen*)
{
// Request line must begin with a method. There is no list of all possible methods because
// extension is allowed, so there is no absolute way to tell whether something is a method.
return V_TBD;
}
-HttpStartCutter::ValidationResult HttpStatusCutter::validate(uint8_t octet)
+HttpStartCutter::ValidationResult HttpStatusCutter::validate(uint8_t octet,
+ HttpInfractions* infractions, HttpEventGen* events)
{
// Status line must begin "HTTP/"
static const int match_size = 5;
- static const uint8_t match[match_size] = { 'H', 'T', 'T', 'P', '/' };
+ static const uint8_t primary_match[match_size] = { 'H', 'T', 'T', 'P', '/' };
+ static const uint8_t secondary_match[match_size] = { 'h', 't', 't', 'p', '/' };
- if (octet != match[octets_checked++])
- return V_BAD;
- if (octets_checked >= match_size)
+ if (octet != primary_match[octets_checked])
+ {
+ if (octet == secondary_match[octets_checked])
+ {
+ // Lower case is wrong but we can still parse the message
+ *infractions += INF_VERSION_NOT_UPPERCASE;
+ events->create_event(EVENT_VERSION_NOT_UPPERCASE);
+ }
+ else
+ return V_BAD;
+ }
+ if (++octets_checked >= match_size)
return V_GOOD;
return V_TBD;
}
private:
static const int MAX_LEADING_WHITESPACE = 20;
- virtual ValidationResult validate(uint8_t octet) = 0;
+ virtual ValidationResult validate(uint8_t octet, HttpInfractions*, HttpEventGen*) = 0;
bool validated = false;
};
{
private:
uint32_t octets_checked = 0;
- ValidationResult validate(uint8_t octet) override;
+ ValidationResult validate(uint8_t octet, HttpInfractions*, HttpEventGen*) override;
};
class HttpStatusCutter : public HttpStartCutter
{
private:
uint32_t octets_checked = 0;
- ValidationResult validate(uint8_t octet) override;
+ ValidationResult validate(uint8_t octet, HttpInfractions*, HttpEventGen*) override;
};
class HttpHeaderCutter : public HttpCutter
INF_REPEATED_HEADER,
INF_CONTENT_ENCODING_CHUNKED,
INF_206_WITHOUT_RANGE,
+ INF_VERSION_NOT_UPPERCASE,
INF__MAX_VALUE
};
EVENT_REPEATED_HEADER,
EVENT_CONTENT_ENCODING_CHUNKED,
EVENT_206_WITHOUT_RANGE,
+ EVENT_VERSION_NOT_UPPERCASE,
EVENT__MAX_VALUE
};
void HttpMsgRequest::parse_start_line()
{
- // Check the version field
+ // Version field
if ((start_line.length() < 10) || !is_sp_tab[start_line.start()[start_line.length()-9]] ||
- memcmp(start_line.start() + start_line.length() - 8, "HTTP/", 5))
+ memcmp(start_line.start() + start_line.length() - 8, "HTTP/", 5))
{
- if (!handle_zero_nine())
+ // Something is wrong with this message. Check for lower case letters in HTTP-name.
+ if ((start_line.length() >= 10) && is_sp_tab[start_line.start()[start_line.length()-9]] &&
+ http_name_nocase_ok(start_line.start() + start_line.length() - 8))
+ {
+ add_infraction(INF_VERSION_NOT_UPPERCASE);
+ create_event(EVENT_VERSION_NOT_UPPERCASE);
+ }
+ // Check for version 0.9 request.
+ else if (handle_zero_nine())
+ {
+ return;
+ }
+ // Just a plain old bad request
+ else
{
- // Just a plain old bad request
add_infraction(INF_BAD_REQ_LINE);
transaction->get_events(source_id)->generate_misformatted_http(start_line.start(),
start_line.length());
+ return;
}
- return;
}
+ version.set(8, start_line.start() + (start_line.length() - 8));
+ derive_version_id();
+
HttpModule::increment_peg_counts(PEG_REQUEST);
// The splitter guarantees there will be a non-whitespace at octet 1 and a whitespace within
default: HttpModule::increment_peg_counts(PEG_OTHER_METHOD); break;
}
- version.set(8, start_line.start() + (start_line.length() - 8));
- derive_version_id();
-
if (first_end < last_begin)
{
uri = new HttpUri(start_line.start() + first_end + 1, last_begin - first_end - 1,
}
}
+bool HttpMsgRequest::http_name_nocase_ok(const uint8_t* start)
+{
+ return ((start[0] == 'H') || (start[0] == 'h')) &&
+ ((start[1] == 'T') || (start[1] == 't')) &&
+ ((start[2] == 'T') || (start[2] == 't')) &&
+ ((start[3] == 'P') || (start[3] == 'p')) &&
+ (start[4] == '/');
+}
+
bool HttpMsgRequest::handle_zero_nine()
{
// 0.9 request line is supposed to be "GET <URI>\r\n"
static const StrCode method_list[];
void parse_start_line() override;
+ bool http_name_nocase_ok(const uint8_t* start);
bool handle_zero_nine();
Field method;
"values" },
{ EVENT_CONTENT_ENCODING_CHUNKED, "invalid value chunked in Content-Encoding header" },
{ EVENT_206_WITHOUT_RANGE, "206 response sent to a request without a Range header" },
+ { EVENT_VERSION_NOT_UPPERCASE, "'HTTP' in version field not all upper case" },
{ 0, nullptr }
};