aboutsummaryrefslogtreecommitdiff
path: root/contrib
diff options
context:
space:
mode:
authorLee *!* Clagett <code@leeclagett.com>2025-01-21 09:56:52 -0500
committerLee *!* Clagett <code@leeclagett.com>2025-02-14 00:21:05 -0500
commit13ff355cf6081776fd7379080c17246e35c1236d (patch)
tree7efc3ffd2c8288857d428e1eaf5d9615f77b0900 /contrib
parent89fa3ed68ae1a21e71a35ad4c21afaa2baae1987 (diff)
downloadmonzero-core-13ff355cf6081776fd7379080c17246e35c1236d.tar.gz
monzero-core-13ff355cf6081776fd7379080c17246e35c1236d.tar.xz
monzero-core-13ff355cf6081776fd7379080c17246e35c1236d.zip
Set response limits on http server connections
Diffstat (limited to 'contrib')
-rw-r--r--contrib/epee/include/net/abstract_tcp_server2.h11
-rw-r--r--contrib/epee/include/net/abstract_tcp_server2.inl57
-rw-r--r--contrib/epee/include/net/http_protocol_handler.h18
-rw-r--r--contrib/epee/include/net/http_protocol_handler.inl37
-rw-r--r--contrib/epee/include/net/http_server_impl_base.h39
5 files changed, 140 insertions, 22 deletions
diff --git a/contrib/epee/include/net/abstract_tcp_server2.h b/contrib/epee/include/net/abstract_tcp_server2.h
index be9999203..952a6a3cf 100644
--- a/contrib/epee/include/net/abstract_tcp_server2.h
+++ b/contrib/epee/include/net/abstract_tcp_server2.h
@@ -64,6 +64,7 @@
#define MONERO_DEFAULT_LOG_CATEGORY "net"
#define ABSTRACT_SERVER_SEND_QUE_MAX_COUNT 1000
+#define ABSTRACT_SERVER_SEND_QUE_MAX_BYTES_DEFAULT 100 * 1024 * 1024
namespace epee
{
@@ -169,6 +170,7 @@ namespace net_utils
} read;
struct {
std::deque<epee::byte_slice> queue;
+ std::size_t total_bytes;
bool wait_consume;
} write;
};
@@ -267,11 +269,17 @@ namespace net_utils
struct shared_state : connection_basic_shared_state, t_protocol_handler::config_type
{
shared_state()
- : connection_basic_shared_state(), t_protocol_handler::config_type(), pfilter(nullptr), plimit(nullptr), stop_signal_sent(false)
+ : connection_basic_shared_state(),
+ t_protocol_handler::config_type(),
+ pfilter(nullptr),
+ plimit(nullptr),
+ response_soft_limit(ABSTRACT_SERVER_SEND_QUE_MAX_BYTES_DEFAULT),
+ stop_signal_sent(false)
{}
i_connection_filter* pfilter;
i_connection_limit* plimit;
+ std::size_t response_soft_limit;
bool stop_signal_sent;
};
@@ -378,6 +386,7 @@ namespace net_utils
void set_connection_filter(i_connection_filter* pfilter);
void set_connection_limit(i_connection_limit* plimit);
+ void set_response_soft_limit(std::size_t limit);
void set_default_remote(epee::net_utils::network_address remote)
{
diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl
index 8a3a8299c..c3c25825a 100644
--- a/contrib/epee/include/net/abstract_tcp_server2.inl
+++ b/contrib/epee/include/net/abstract_tcp_server2.inl
@@ -498,10 +498,12 @@ namespace net_utils
if (m_state.socket.cancel_write) {
m_state.socket.cancel_write = false;
m_state.data.write.queue.clear();
+ m_state.data.write.total_bytes = 0;
state_status_check();
}
else if (ec.value()) {
m_state.data.write.queue.clear();
+ m_state.data.write.total_bytes = 0;
interrupt();
}
else {
@@ -526,8 +528,11 @@ namespace net_utils
start_timer(get_default_timeout(), true);
}
- assert(bytes_transferred == m_state.data.write.queue.back().size());
+ const std::size_t byte_count = m_state.data.write.queue.back().size();
+ assert(bytes_transferred == byte_count);
m_state.data.write.queue.pop_back();
+ m_state.data.write.total_bytes -=
+ std::min(m_state.data.write.total_bytes, byte_count);
m_state.condition.notify_all();
start_write();
}
@@ -671,8 +676,9 @@ namespace net_utils
return;
if (m_state.timers.throttle.out.wait_expire)
return;
- if (m_state.socket.wait_write)
- return;
+ // \NOTE See on_terminating() comments
+ //if (m_state.socket.wait_write)
+ // return;
if (m_state.socket.wait_shutdown)
return;
if (m_state.protocol.wait_init)
@@ -730,8 +736,13 @@ namespace net_utils
return;
if (m_state.timers.throttle.out.wait_expire)
return;
- if (m_state.socket.wait_write)
- return;
+ // Writes cannot be canceled due to `async_write` being a "composed"
+ // handler. ASIO has new cancellation routines, not available in 1.66, to
+ // handle this situation. The problem is that if cancel is called after an
+ // intermediate handler is queued, the op will not check the cancel flag in
+ // our code, and will instead queue up another write.
+ //if (m_state.socket.wait_write)
+ // return;
if (m_state.socket.wait_shutdown)
return;
if (m_state.protocol.wait_init)
@@ -758,6 +769,8 @@ namespace net_utils
std::lock_guard<std::mutex> guard(m_state.lock);
if (m_state.status != status_t::RUNNING || m_state.socket.wait_handshake)
return false;
+ if (std::numeric_limits<std::size_t>::max() - m_state.data.write.total_bytes < message.size())
+ return false;
// Wait for the write queue to fall below the max. If it doesn't after a
// randomized delay, drop the connection.
@@ -775,7 +788,14 @@ namespace net_utils
std::uniform_int_distribution<>(5000, 6000)(rng)
);
};
- if (m_state.data.write.queue.size() <= ABSTRACT_SERVER_SEND_QUE_MAX_COUNT)
+
+ // The bytes check intentionally does not include incoming message size.
+ // This allows for a soft overflow; a single http response will never fail
+ // this check, but multiple responses could. Clients can avoid this case
+ // by reading the entire response before making another request. P2P
+ // should never hit the MAX_BYTES check (when using default values).
+ if (m_state.data.write.queue.size() <= ABSTRACT_SERVER_SEND_QUE_MAX_COUNT &&
+ m_state.data.write.total_bytes <= static_cast<shared_state&>(connection_basic::get_state()).response_soft_limit)
return true;
m_state.data.write.wait_consume = true;
bool success = m_state.condition.wait_for(
@@ -784,14 +804,23 @@ namespace net_utils
[this]{
return (
m_state.status != status_t::RUNNING ||
- m_state.data.write.queue.size() <=
- ABSTRACT_SERVER_SEND_QUE_MAX_COUNT
+ (
+ m_state.data.write.queue.size() <=
+ ABSTRACT_SERVER_SEND_QUE_MAX_COUNT &&
+ m_state.data.write.total_bytes <=
+ static_cast<shared_state&>(connection_basic::get_state()).response_soft_limit
+ )
);
}
);
m_state.data.write.wait_consume = false;
if (!success) {
- terminate();
+ // synchronize with intermediate writes on `m_strand`
+ auto self = connection<T>::shared_from_this();
+ boost::asio::post(m_strand, [this, self] {
+ std::lock_guard<std::mutex> guard(m_state.lock);
+ terminate();
+ });
return false;
}
else
@@ -817,7 +846,9 @@ namespace net_utils
) {
if (!wait_consume())
return false;
+ const std::size_t byte_count = message.size();
m_state.data.write.queue.emplace_front(std::move(message));
+ m_state.data.write.total_bytes += byte_count;
start_write();
}
else {
@@ -827,6 +858,7 @@ namespace net_utils
m_state.data.write.queue.emplace_front(
message.take_slice(CHUNK_SIZE)
);
+ m_state.data.write.total_bytes += m_state.data.write.queue.front().size();
start_write();
}
}
@@ -1363,6 +1395,13 @@ namespace net_utils
}
//---------------------------------------------------------------------------------
template<class t_protocol_handler>
+ void boosted_tcp_server<t_protocol_handler>::set_response_soft_limit(const std::size_t limit)
+ {
+ assert(m_state != nullptr); // always set in constructor
+ m_state->response_soft_limit = limit;
+ }
+ //---------------------------------------------------------------------------------
+ template<class t_protocol_handler>
bool boosted_tcp_server<t_protocol_handler>::run_server(size_t threads_count, bool wait, const boost::thread::attributes& attrs)
{
TRY_ENTRY();
diff --git a/contrib/epee/include/net/http_protocol_handler.h b/contrib/epee/include/net/http_protocol_handler.h
index 258b07e2c..8b73964dd 100644
--- a/contrib/epee/include/net/http_protocol_handler.h
+++ b/contrib/epee/include/net/http_protocol_handler.h
@@ -32,6 +32,7 @@
#include <boost/optional/optional.hpp>
#include <string>
+#include <unordered_map>
#include "net_utils_base.h"
#include "http_auth.h"
#include "http_base.h"
@@ -54,8 +55,13 @@ namespace net_utils
{
std::string m_folder;
std::vector<std::string> m_access_control_origins;
+ std::unordered_map<std::string, std::size_t> m_connections;
boost::optional<login> m_user;
size_t m_max_content_length{std::numeric_limits<size_t>::max()};
+ std::size_t m_connection_count{0};
+ std::size_t m_max_public_ip_connections{3};
+ std::size_t m_max_private_ip_connections{25};
+ std::size_t m_max_connections{100};
critical_section m_lock;
};
@@ -70,7 +76,7 @@ namespace net_utils
typedef http_server_config config_type;
simple_http_connection_handler(i_service_endpoint* psnd_hndlr, config_type& config, t_connection_context& conn_context);
- virtual ~simple_http_connection_handler(){}
+ virtual ~simple_http_connection_handler();
bool release_protocol()
{
@@ -86,10 +92,7 @@ namespace net_utils
{
return true;
}
- bool after_init_connection()
- {
- return true;
- }
+ bool after_init_connection();
virtual bool handle_recv(const void* ptr, size_t cb);
virtual bool handle_request(const http::http_request_info& query_info, http_response_info& response);
@@ -146,6 +149,7 @@ namespace net_utils
protected:
i_service_endpoint* m_psnd_hndlr;
t_connection_context& m_conn_context;
+ bool m_initialized;
};
template<class t_connection_context>
@@ -212,10 +216,6 @@ namespace net_utils
}
void handle_qued_callback()
{}
- bool after_init_connection()
- {
- return true;
- }
private:
//simple_http_connection_handler::config_type m_stub_config;
diff --git a/contrib/epee/include/net/http_protocol_handler.inl b/contrib/epee/include/net/http_protocol_handler.inl
index f7d2074b2..6647d1f15 100644
--- a/contrib/epee/include/net/http_protocol_handler.inl
+++ b/contrib/epee/include/net/http_protocol_handler.inl
@@ -208,11 +208,46 @@ namespace net_utils
m_newlines(0),
m_bytes_read(0),
m_psnd_hndlr(psnd_hndlr),
- m_conn_context(conn_context)
+ m_conn_context(conn_context),
+ m_initialized(false)
{
}
//--------------------------------------------------------------------------------------------
+ template<class t_connection_context>
+ simple_http_connection_handler<t_connection_context>::~simple_http_connection_handler()
+ {
+ try
+ {
+ if (m_initialized)
+ {
+ CRITICAL_REGION_LOCAL(m_config.m_lock);
+ if (m_config.m_connection_count)
+ --m_config.m_connection_count;
+ auto elem = m_config.m_connections.find(m_conn_context.m_remote_address.host_str());
+ if (elem != m_config.m_connections.end())
+ {
+ if (elem->second == 1 || elem->second == 0)
+ m_config.m_connections.erase(elem);
+ else
+ --(elem->second);
+ }
+ }
+ }
+ catch (...)
+ {}
+ }
+ //--------------------------------------------------------------------------------------------
+ template<class t_connection_context>
+ bool simple_http_connection_handler<t_connection_context>::after_init_connection()
+ {
+ CRITICAL_REGION_LOCAL(m_config.m_lock);
+ ++m_config.m_connections[m_conn_context.m_remote_address.host_str()];
+ ++m_config.m_connection_count;
+ m_initialized = true;
+ return true;
+ }
+ //--------------------------------------------------------------------------------------------
template<class t_connection_context>
bool simple_http_connection_handler<t_connection_context>::set_ready_state()
{
diff --git a/contrib/epee/include/net/http_server_impl_base.h b/contrib/epee/include/net/http_server_impl_base.h
index d88b53c94..7c7b8add2 100644
--- a/contrib/epee/include/net/http_server_impl_base.h
+++ b/contrib/epee/include/net/http_server_impl_base.h
@@ -33,6 +33,7 @@
#include <boost/thread.hpp>
#include <boost/bind/bind.hpp>
+#include "cryptonote_config.h"
#include "net/abstract_tcp_server2.h"
#include "http_protocol_handler.h"
#include "net/http_server_handlers_map2.h"
@@ -44,7 +45,8 @@ namespace epee
{
template<class t_child_class, class t_connection_context = epee::net_utils::connection_context_base>
- class http_server_impl_base: public net_utils::http::i_http_server_handler<t_connection_context>
+ class http_server_impl_base: public net_utils::http::i_http_server_handler<t_connection_context>,
+ net_utils::i_connection_limit
{
public:
@@ -60,8 +62,16 @@ namespace epee
const std::string& bind_ipv6_address = "::", bool use_ipv6 = false, bool require_ipv4 = true,
std::vector<std::string> access_control_origins = std::vector<std::string>(),
boost::optional<net_utils::http::login> user = boost::none,
- net_utils::ssl_options_t ssl_options = net_utils::ssl_support_t::e_ssl_support_autodetect)
+ net_utils::ssl_options_t ssl_options = net_utils::ssl_support_t::e_ssl_support_autodetect,
+ const std::size_t max_public_ip_connections = DEFAULT_RPC_MAX_CONNECTIONS_PER_PUBLIC_IP,
+ const std::size_t max_private_ip_connections = DEFAULT_RPC_MAX_CONNECTIONS_PER_PRIVATE_IP,
+ const std::size_t max_connections = DEFAULT_RPC_MAX_CONNECTIONS,
+ const std::size_t response_soft_limit = DEFAULT_RPC_SOFT_LIMIT_SIZE)
{
+ if (max_connections < max_public_ip_connections)
+ throw std::invalid_argument{"Max public IP connections cannot be more than max connections"};
+ if (max_connections < max_private_ip_connections)
+ throw std::invalid_argument{"Max private IP connections cannot be more than max connections"};
//set self as callback handler
m_net_server.get_config_object().m_phandler = static_cast<t_child_class*>(this);
@@ -75,6 +85,11 @@ namespace epee
m_net_server.get_config_object().m_access_control_origins = std::move(access_control_origins);
m_net_server.get_config_object().m_user = std::move(user);
+ m_net_server.get_config_object().m_max_public_ip_connections = max_public_ip_connections;
+ m_net_server.get_config_object().m_max_private_ip_connections = max_private_ip_connections;
+ m_net_server.get_config_object().m_max_connections = max_connections;
+ m_net_server.set_response_soft_limit(response_soft_limit);
+ m_net_server.set_connection_limit(this);
MGINFO("Binding on " << bind_ip << " (IPv4):" << bind_port);
if (use_ipv6)
@@ -131,6 +146,26 @@ namespace epee
}
protected:
+
+ virtual bool is_host_limit(const net_utils::network_address& na) override final
+ {
+ auto& config = m_net_server.get_config_object();
+ CRITICAL_REGION_LOCAL(config.m_lock);
+ if (config.m_max_connections <= config.m_connection_count)
+ return true;
+
+ const bool is_private = na.is_loopback() || na.is_local();
+ const auto elem = config.m_connections.find(na.host_str());
+ if (elem != config.m_connections.end())
+ {
+ if (is_private)
+ return config.m_max_private_ip_connections <= elem->second;
+ else
+ return config.m_max_public_ip_connections <= elem->second;
+ }
+ return false;
+ }
+
net_utils::boosted_tcp_server<net_utils::http::http_custom_handler<t_connection_context> > m_net_server;
};
}