blob: becbb56c3ceb08e2f61e73f4afaeda944c47e536 [file] [log] [blame]
/** @file
*
* A brief file description
*
* @section license License
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "proxy/hdrs/HeaderValidator.h"
#include "proxy/hdrs/HTTP.h"
bool
HeaderValidator::is_h2_h3_header_valid(const HTTPHdr &hdr, bool is_response, bool is_trailing_header)
{
const MIMEField *field = nullptr;
const char *name = nullptr;
int name_len = 0;
const char *value = nullptr;
int value_len = 0;
MIMEFieldIter iter;
auto method_field = hdr.field_find(PSEUDO_HEADER_METHOD.data(), PSEUDO_HEADER_METHOD.size());
bool has_connect_method = false;
if (method_field) {
int method_len;
const char *method_value = method_field->value_get(&method_len);
has_connect_method = method_len == HTTP_LEN_CONNECT && strncmp(HTTP_METHOD_CONNECT, method_value, HTTP_LEN_CONNECT) == 0;
}
unsigned int expected_pseudo_header_count = is_response ? 1 : has_connect_method ? 2 : 4;
unsigned int pseudo_header_count = 0;
if (is_trailing_header) {
expected_pseudo_header_count = 0;
}
for (auto &field : hdr) {
name = field.name_get(&name_len);
// Pseudo headers must appear before regular headers
if (name_len && name[0] == ':') {
++pseudo_header_count;
if (pseudo_header_count > expected_pseudo_header_count) {
return false;
}
} else if (name_len <= 0) {
return false;
} else {
if (pseudo_header_count != expected_pseudo_header_count) {
return false;
}
}
}
// rfc7540,sec8.1.2.2 and rfc9114,sec4.2: Any message containing
// connection-specific header fields MUST be treated as malformed.
if (hdr.field_find(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION) != nullptr ||
hdr.field_find(MIME_FIELD_KEEP_ALIVE, MIME_LEN_KEEP_ALIVE) != nullptr ||
hdr.field_find(MIME_FIELD_PROXY_CONNECTION, MIME_LEN_PROXY_CONNECTION) != nullptr ||
hdr.field_find(MIME_FIELD_UPGRADE, MIME_LEN_UPGRADE) != nullptr) {
return false;
}
// :path pseudo header MUST NOT empty for http or https URIs
field = hdr.field_find(PSEUDO_HEADER_PATH.data(), PSEUDO_HEADER_PATH.size());
if (field) {
field->value_get(&value_len);
if (value_len == 0) {
return false;
}
}
// when The TE header field is received, it MUST NOT contain any
// value other than "trailers".
field = hdr.field_find(MIME_FIELD_TE, MIME_LEN_TE);
if (field) {
value = field->value_get(&value_len);
if (!(value_len == 8 && memcmp(value, "trailers", 8) == 0)) {
return false;
}
}
if (is_trailing_header) {
// Done with validation for trailing headers, which doesn't have any pseudo headers.
return true;
}
// Check pseudo headers
if (is_response) {
if (hdr.fields_count() >= 1) {
if (hdr.field_find(PSEUDO_HEADER_STATUS.data(), PSEUDO_HEADER_STATUS.size()) == nullptr) {
return false;
}
} else {
// There should at least be :status pseudo header.
return false;
}
} else {
// This is a request.
if (!has_connect_method && hdr.fields_count() >= 4) {
if (hdr.field_find(PSEUDO_HEADER_SCHEME.data(), PSEUDO_HEADER_SCHEME.size()) == nullptr ||
hdr.field_find(PSEUDO_HEADER_METHOD.data(), PSEUDO_HEADER_METHOD.size()) == nullptr ||
hdr.field_find(PSEUDO_HEADER_PATH.data(), PSEUDO_HEADER_PATH.size()) == nullptr ||
hdr.field_find(PSEUDO_HEADER_AUTHORITY.data(), PSEUDO_HEADER_AUTHORITY.size()) == nullptr ||
hdr.field_find(PSEUDO_HEADER_STATUS.data(), PSEUDO_HEADER_STATUS.size()) != nullptr) {
// Decoded header field is invalid
return false;
}
} else if (has_connect_method && hdr.fields_count() >= 2) {
if (hdr.field_find(PSEUDO_HEADER_SCHEME.data(), PSEUDO_HEADER_SCHEME.size()) != nullptr ||
hdr.field_find(PSEUDO_HEADER_METHOD.data(), PSEUDO_HEADER_METHOD.size()) == nullptr ||
hdr.field_find(PSEUDO_HEADER_PATH.data(), PSEUDO_HEADER_PATH.size()) != nullptr ||
hdr.field_find(PSEUDO_HEADER_AUTHORITY.data(), PSEUDO_HEADER_AUTHORITY.size()) == nullptr ||
hdr.field_find(PSEUDO_HEADER_STATUS.data(), PSEUDO_HEADER_STATUS.size()) != nullptr) {
// Decoded header field is invalid
return false;
}
} else {
// Pseudo headers is insufficient
return false;
}
}
return true;
}