diff options
| author | selsta <selsta@sent.at> | 2026-05-20 00:24:18 +0200 |
|---|---|---|
| committer | selsta <selsta@sent.at> | 2026-06-21 22:27:37 +0200 |
| commit | d2e4cbcda578152d9640a1aafa97a527f48a1380 (patch) | |
| tree | 4da61b331d76ef05e66db4c252d32a856eea1ccc /contrib | |
| parent | fc46361376a6f3a604df7ae84ddda848d43dd6cc (diff) | |
| download | monzero-core-d2e4cbcda578152d9640a1aafa97a527f48a1380.tar.gz monzero-core-d2e4cbcda578152d9640a1aafa97a527f48a1380.tar.xz monzero-core-d2e4cbcda578152d9640a1aafa97a527f48a1380.zip | |
http: share deterministic header field parser
Diffstat (limited to 'contrib')
| -rw-r--r-- | contrib/epee/include/net/http_base.h | 40 | ||||
| -rw-r--r-- | contrib/epee/include/net/http_client.h | 100 | ||||
| -rw-r--r-- | contrib/epee/include/net/http_protocol_handler.inl | 87 |
3 files changed, 124 insertions, 103 deletions
diff --git a/contrib/epee/include/net/http_base.h b/contrib/epee/include/net/http_base.h index f32fdd9ae..aec0689a5 100644 --- a/contrib/epee/include/net/http_base.h +++ b/contrib/epee/include/net/http_base.h @@ -29,7 +29,7 @@ #pragma once #include "memwipe.h" -#include <boost/utility/string_ref.hpp> +#include <boost/utility/string_view.hpp> #include <string> #include <utility> @@ -70,7 +70,7 @@ namespace net_utils std::string get_value_from_uri_line(const std::string& param_name, const std::string& uri); - static inline void add_field(std::string& out, const boost::string_ref name, const boost::string_ref value) + static inline void add_field(std::string& out, boost::string_view name, boost::string_view value) { out.append(name.data(), name.size()).append(": "); out.append(value.data(), value.size()).append("\r\n"); @@ -80,6 +80,42 @@ namespace net_utils add_field(out, field.first, field.second); } + namespace detail + { + inline bool parse_header_line(boost::string_view line, boost::string_view& name, boost::string_view& value) + { + if(!line.empty() && line.back() == '\r') + line.remove_suffix(1); + if(line.empty()) + return false; + if(line.front() == ' ' || line.front() == '\t') + return false; + + const size_t colon = line.find(':'); + if(colon == boost::string_view::npos || colon == 0) + return false; + + name = line.substr(0, colon); + value = line.substr(colon + 1); + while(!value.empty() && (value.front() == ' ' || value.front() == '\t')) + value.remove_prefix(1); + while(!value.empty() && (value.back() == ' ' || value.back() == '\t')) + value.remove_suffix(1); + + for(char c : name) + { + const bool alnum = (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9'); + const bool allowed = alnum || + c == '!' || c == '#' || c == '$' || c == '%' || c == '&' || + c == '\'' || c == '*' || c == '+' || c == '-' || c == '.' || + c == '^' || c == '_' || c == '`' || c == '|' || c == '~'; + if(!allowed) + return false; + } + + return true; + } + } struct http_header_info { diff --git a/contrib/epee/include/net/http_client.h b/contrib/epee/include/net/http_client.h index 9ce30b620..949629352 100644 --- a/contrib/epee/include/net/http_client.h +++ b/contrib/epee/include/net/http_client.h @@ -414,7 +414,11 @@ namespace net_utils recv_buff.assign(m_header_cache.begin()+pos+4, m_header_cache.end()); m_header_cache.erase(m_header_cache.begin()+pos+4, m_header_cache.end()); - analize_cached_header_and_invoke_state(); + if(!analize_cached_header_and_invoke_state()) + { + m_state = reciev_machine_state_error; + return false; + } if (!on_header(m_response_info)) { MDEBUG("Connection cancelled by on_header"); @@ -647,64 +651,46 @@ namespace net_utils { MTRACE("http_stream_filter::parse_cached_header(*)"); - const char *ptr = m_cache_to_process.c_str(); - while (ptr[0] != '\r' || ptr[1] != '\n') + size_t cur = 0; + while(cur < m_cache_to_process.size()) { - // optional \n - if (*ptr == '\n') - ++ptr; - // an identifier composed of letters or - - const char *key_pos = ptr; - while (isalnum(*ptr) || *ptr == '_' || *ptr == '-') - ++ptr; - const char *key_end = ptr; - // optional space (not in RFC, but in previous code) - if (*ptr == ' ') - ++ptr; - CHECK_AND_ASSERT_MES(*ptr == ':', true, "http_stream_filter::parse_cached_header() invalid header in: " << m_cache_to_process); - ++ptr; - // optional whitespace, but not newlines - line folding is obsolete, let's ignore it - while (isblank(*ptr)) - ++ptr; - const char *value_pos = ptr; - while (*ptr != '\r' && *ptr != '\n') - ++ptr; - const char *value_end = ptr; - // optional trailing whitespace - while (value_end > value_pos && isblank(*(value_end-1))) - --value_end; - if (*ptr == '\r') - ++ptr; - CHECK_AND_ASSERT_MES(*ptr == '\n', true, "http_stream_filter::parse_cached_header() invalid header in: " << m_cache_to_process); - ++ptr; + const size_t line_end = m_cache_to_process.find('\n', cur); + CHECK_AND_ASSERT_MES(line_end != std::string::npos, false, "http_stream_filter::parse_cached_header() invalid header in: " << m_cache_to_process); - const std::string key = std::string(key_pos, key_end - key_pos); - const std::string value = std::string(value_pos, value_end - value_pos); - if (!key.empty()) - { - if (!string_tools::compare_no_case(key, "Connection")) - body_info.m_connection = value; - else if(!string_tools::compare_no_case(key, "Referrer")) - body_info.m_referer = value; - else if(!string_tools::compare_no_case(key, "Content-Length")) - body_info.m_content_length = value; - else if(!string_tools::compare_no_case(key, "Content-Type")) - body_info.m_content_type = value; - else if(!string_tools::compare_no_case(key, "Transfer-Encoding")) - body_info.m_transfer_encoding = value; - else if(!string_tools::compare_no_case(key, "Content-Encoding")) - body_info.m_content_encoding = value; - else if(!string_tools::compare_no_case(key, "Host")) - body_info.m_host = value; - else if(!string_tools::compare_no_case(key, "Cookie")) - body_info.m_cookie = value; - else if(!string_tools::compare_no_case(key, "User-Agent")) - body_info.m_user_agent = value; - else if(!string_tools::compare_no_case(key, "Origin")) - body_info.m_origin = value; - else - body_info.m_etc_fields.emplace_back(key, value); - } + boost::string_view line(m_cache_to_process.data() + cur, line_end - cur); + cur = line_end + 1; + + if(line == "\r" || line.empty()) + break; + + boost::string_view name; + boost::string_view value; + CHECK_AND_ASSERT_MES(detail::parse_header_line(line, name, value), false, "http_stream_filter::parse_cached_header() invalid header in: " << m_cache_to_process); + + std::string key(name.data(), name.size()); + std::string val(value.data(), value.size()); + if (!string_tools::compare_no_case(key, "Connection")) + body_info.m_connection = std::move(val); + else if(!string_tools::compare_no_case(key, "Referer")) + body_info.m_referer = std::move(val); + else if(!string_tools::compare_no_case(key, "Content-Length")) + body_info.m_content_length = std::move(val); + else if(!string_tools::compare_no_case(key, "Content-Type")) + body_info.m_content_type = std::move(val); + else if(!string_tools::compare_no_case(key, "Transfer-Encoding")) + body_info.m_transfer_encoding = std::move(val); + else if(!string_tools::compare_no_case(key, "Content-Encoding")) + body_info.m_content_encoding = std::move(val); + else if(!string_tools::compare_no_case(key, "Host")) + body_info.m_host = std::move(val); + else if(!string_tools::compare_no_case(key, "Cookie")) + body_info.m_cookie = std::move(val); + else if(!string_tools::compare_no_case(key, "User-Agent")) + body_info.m_user_agent = std::move(val); + else if(!string_tools::compare_no_case(key, "Origin")) + body_info.m_origin = std::move(val); + else + body_info.m_etc_fields.emplace_back(std::move(key), std::move(val)); } return true; } diff --git a/contrib/epee/include/net/http_protocol_handler.inl b/contrib/epee/include/net/http_protocol_handler.inl index 6647d1f15..3e46633b7 100644 --- a/contrib/epee/include/net/http_protocol_handler.inl +++ b/contrib/epee/include/net/http_protocol_handler.inl @@ -25,8 +25,9 @@ // -#include <boost/regex.hpp> +#include <boost/algorithm/string/predicate.hpp> #include <boost/lexical_cast.hpp> +#include <boost/regex.hpp> #include "http_protocol_handler.h" #include "reg_exp_definer.h" #include "string_tools.h" @@ -546,54 +547,52 @@ namespace net_utils template<class t_connection_context> bool simple_http_connection_handler<t_connection_context>::parse_cached_header(http_header_info& body_info, const std::string& m_cache_to_process, size_t pos) { - STATIC_REGEXP_EXPR_1(rexp_mach_field, - "\n?((Connection)|(Referer)|(Content-Length)|(Content-Type)|(Transfer-Encoding)|(Content-Encoding)|(Host)|(Cookie)|(User-Agent)|(Origin)" - // 12 3 4 5 6 7 8 9 10 11 - "|([\\w-]+?)) ?: ?((.*?)(\r?\n))[^\t ]", - //11 1213 14 - boost::regex::icase | boost::regex::normal); - - boost::smatch result; - std::string::const_iterator it_current_bound = m_cache_to_process.begin(); - std::string::const_iterator it_end_bound = m_cache_to_process.begin()+pos; - body_info.clear(); + if(pos > m_cache_to_process.size() || pos > HTTP_MAX_HEADER_LEN) + return false; + + size_t cur = 0; - //lookup all fields and fill well-known fields - while( boost::regex_search( it_current_bound, it_end_bound, result, rexp_mach_field, boost::match_default) && result[0].matched) + while(cur < pos) { - const size_t field_val = 14; - const size_t field_etc_name = 12; + const size_t line_end = m_cache_to_process.find('\n', cur); + if(line_end == std::string::npos || line_end >= pos) + break; - int i = 2; //start position = 2 - if(result[i++].matched)//"Connection" - body_info.m_connection = result[field_val]; - else if(result[i++].matched)//"Referer" - body_info.m_referer = result[field_val]; - else if(result[i++].matched)//"Content-Length" - body_info.m_content_length = result[field_val]; - else if(result[i++].matched)//"Content-Type" - body_info.m_content_type = result[field_val]; - else if(result[i++].matched)//"Transfer-Encoding" - body_info.m_transfer_encoding = result[field_val]; - else if(result[i++].matched)//"Content-Encoding" - body_info.m_content_encoding = result[field_val]; - else if(result[i++].matched)//"Host" - body_info.m_host = result[field_val]; - else if(result[i++].matched)//"Cookie" - body_info.m_cookie = result[field_val]; - else if(result[i++].matched)//"User-Agent" - body_info.m_user_agent = result[field_val]; - else if(result[i++].matched)//"Origin" - body_info.m_origin = result[field_val]; - else if(result[i++].matched)//e.t.c (HAVE TO BE MATCHED!) - body_info.m_etc_fields.push_back(std::pair<std::string, std::string>(result[field_etc_name], result[field_val])); - else - { - LOG_ERROR_CC(m_conn_context, "simple_http_connection_handler<t_connection_context>::parse_cached_header() not matched last entry in:" << m_cache_to_process); - } + boost::string_view line(m_cache_to_process.data() + cur, line_end - cur); + cur = line_end + 1; + + // End of header block. + if(line == "\r" || line.empty()) + break; - it_current_bound = result[(int)result.size()-1]. first; + boost::string_view name; + boost::string_view value; + if(!detail::parse_header_line(line, name, value)) + return false; + + if(boost::iequals(name, "Connection")) + body_info.m_connection = std::string(value.data(), value.size()); + else if(boost::iequals(name, "Referer")) + body_info.m_referer = std::string(value.data(), value.size()); + else if(boost::iequals(name, "Content-Length")) + body_info.m_content_length = std::string(value.data(), value.size()); + else if(boost::iequals(name, "Content-Type")) + body_info.m_content_type = std::string(value.data(), value.size()); + else if(boost::iequals(name, "Transfer-Encoding")) + body_info.m_transfer_encoding = std::string(value.data(), value.size()); + else if(boost::iequals(name, "Content-Encoding")) + body_info.m_content_encoding = std::string(value.data(), value.size()); + else if(boost::iequals(name, "Host")) + body_info.m_host = std::string(value.data(), value.size()); + else if(boost::iequals(name, "Cookie")) + body_info.m_cookie = std::string(value.data(), value.size()); + else if(boost::iequals(name, "User-Agent")) + body_info.m_user_agent = std::string(value.data(), value.size()); + else if(boost::iequals(name, "Origin")) + body_info.m_origin = std::string(value.data(), value.size()); + else + body_info.m_etc_fields.push_back(std::make_pair(std::string(name.data(), name.size()), std::string(value.data(), value.size()))); } return true; } |
