INF_H2_NON_IDENTITY_TE,
INF_H2_DATA_OVERRUNS_CL,
INF_H2_DATA_UNDERRUNS_CL,
+ INF_CONNECT_REQUEST_BODY,
+ INF_EARLY_C2S_TRAFFIC_AFTER_CONNECT,
+ INF_200_CONNECT_RESP_WITH_CL,
+ INF_200_CONNECT_RESP_WITH_TE,
+ INF_100_CONNECT_RESP,
INF__MAX_VALUE
};
EVENT_206_WITHOUT_RANGE,
EVENT_VERSION_NOT_UPPERCASE,
EVENT_BAD_HEADER_WHITESPACE,
- EVENT_GZIP_EARLY_END, // 248
+ EVENT_GZIP_EARLY_END,
EVENT_EXCESS_REPEAT_PARAMS,
- EVENT_H2_NON_IDENTITY_TE,
+ EVENT_H2_NON_IDENTITY_TE, // 250
EVENT_H2_DATA_OVERRUNS_CL,
EVENT_H2_DATA_UNDERRUNS_CL,
+ EVENT_CONNECT_REQUEST_BODY,
+ EVENT_EARLY_C2S_TRAFFIC_AFTER_CONNECT,
+ EVENT_200_CONNECT_RESP_WITH_CL,
+ EVENT_200_CONNECT_RESP_WITH_TE,
+ EVENT_100_CONNECT_RESP, // 257
EVENT__MAX_VALUE
};
int32_t num_head_lines[2] = { HttpCommon::STAT_NOT_PRESENT, HttpCommon::STAT_NOT_PRESENT };
bool tcp_close[2] = { false, false };
bool partial_flush[2] = { false, false };
+ uint64_t last_connect_trans_w_early_traffic = 0;
HttpInfractions* infractions[2] = { new HttpInfractions, new HttpInfractions };
HttpEventGen* events[2] = { new HttpEventGen, new HttpEventGen };
// *** Inspector => StreamSplitter (facts about the message section that is coming next)
HttpEnums::SectionType type_expected[2] = { HttpEnums::SEC_REQUEST, HttpEnums::SEC_STATUS };
+ uint64_t last_request_was_connect = false;
// length of the data from Content-Length field
z_stream* compress_stream[2] = { nullptr, nullptr };
uint64_t zero_nine_expected = 0;
return;
}
+ if ((source_id == SRC_SERVER) && request && (request->get_method_id() == METH_CONNECT) &&
+ !session_data->for_http2)
+ {
+ // Successful CONNECT responses (2XX) switch to tunneled traffic immediately following the
+ // header. Transfer-Encoding and Content-Length headers are not allowed in successful
+ // responses by the RFC.
+ if(((session_data->last_connect_trans_w_early_traffic == 0) ||
+ (trans_num > session_data->last_connect_trans_w_early_traffic)) &&
+ ((status_code_num >= 200) && (status_code_num < 300)))
+ {
+ if ((get_header_count(HEAD_TRANSFER_ENCODING) > 0))
+ {
+ add_infraction(INF_200_CONNECT_RESP_WITH_TE);
+ create_event(EVENT_200_CONNECT_RESP_WITH_TE);
+ }
+ if (get_header_count(HEAD_CONTENT_LENGTH) > 0)
+ {
+ add_infraction(INF_200_CONNECT_RESP_WITH_CL);
+ create_event(EVENT_200_CONNECT_RESP_WITH_CL);
+ }
+ // Temporary - Further traffic on this flow is tunneled traffic, and will get sent back
+ // to the wizard to determine the appropriate inspector. For now just abort.
+ session_data->type_expected[source_id] = SEC_ABORT;
+ return;
+ }
+ if ((status_code_num >= 100) && (status_code_num < 200))
+ {
+ add_infraction(INF_100_CONNECT_RESP);
+ create_event(EVENT_100_CONNECT_RESP);
+ }
+ }
+
if ((source_id == SRC_SERVER) &&
((100 <= status_code_num && status_code_num <= 199) || (status_code_num == 204) ||
(status_code_num == 304)))
if (source_id == SRC_CLIENT)
{
HttpModule::increment_peg_counts(PEG_REQUEST_BODY);
+
+ // Message bodies for CONNECT requests have no defined semantics
+ if ((method_id == METH_CONNECT) && !session_data->for_http2)
+ {
+ add_infraction(INF_CONNECT_REQUEST_BODY);
+ create_event(EVENT_CONNECT_REQUEST_BODY);
+ }
}
}
return;
}
+ if (method_id == METH_CONNECT)
+ {
+ session_data->last_request_was_connect = true;
+ }
+
session_data->type_expected[source_id] = SEC_HEADER;
session_data->version_id[source_id] = version_id;
session_data->method_id = method_id;
}
#endif
+ // If the last request was a CONNECT and we have not yet seen the response, this is early C2S
+ // traffic. If there has been a pipeline overflow or underflow we cannot match requests to
+ // responses, so there is no attempt to track early C2S traffic.
+ if ((source_id == SRC_CLIENT) && (type == SEC_REQUEST) && !session_data->for_http2 &&
+ session_data->last_request_was_connect)
+ {
+ const uint64_t last_request_trans_num = session_data->expected_trans_num[SRC_CLIENT] - 1;
+ const bool server_behind_connect =
+ (session_data->expected_trans_num[SRC_SERVER] < last_request_trans_num);
+ const bool server_expecting_connect_status =
+ ((session_data->expected_trans_num[SRC_SERVER] == last_request_trans_num)
+ && (session_data->type_expected[SRC_SERVER] == SEC_STATUS));
+ const bool pipeline_valid = !session_data->pipeline_overflow &&
+ !session_data->pipeline_underflow;
+
+ if ((server_behind_connect || server_expecting_connect_status) && pipeline_valid)
+ {
+ *session_data->get_infractions(source_id) += INF_EARLY_C2S_TRAFFIC_AFTER_CONNECT;
+ session_data->events[source_id]->create_event(EVENT_EARLY_C2S_TRAFFIC_AFTER_CONNECT);
+ session_data->last_connect_trans_w_early_traffic =
+ session_data->expected_trans_num[SRC_CLIENT] - 1;
+ }
+ session_data->last_request_was_connect = false;
+ }
+
assert(!session_data->tcp_close[source_id]);
HttpModule::increment_peg_counts(PEG_SCAN);
"value" },
{ EVENT_H2_DATA_UNDERRUNS_CL, "HTTP/2 message body smaller than Content-Length header "
"value" },
+ { EVENT_CONNECT_REQUEST_BODY, "HTTP CONNECT request with a message body" },
+ { EVENT_EARLY_C2S_TRAFFIC_AFTER_CONNECT, "HTTP client-to-server traffic after CONNECT request "
+ "but before CONNECT response" },
+ { EVENT_200_CONNECT_RESP_WITH_CL, "HTTP CONNECT 2XX response with Content-Length header" },
+ { EVENT_200_CONNECT_RESP_WITH_TE, "HTTP CONNECT 2XX response with Transfer-Encoding header" },
+ { EVENT_100_CONNECT_RESP, "HTTP CONNECT response with 1XX status code" },
{ 0, nullptr }
};