Package: release.debian.org
Tags: bookworm
User: release.debian....@packages.debian.org
Usertags: pu
Control: affects -1 src:monero
[ Reason ]
Fix security issue that is fixed in unstable/trixie: CVE-2025-26819
[ Impact ]
Two backported upstream patches that are not released yet.
[ Tests ]
Compiled pkg and ran monerod on amd64. No problem seen so far.
[ Checklist ]
[x] *all* changes are documented in the d/changelog
[x] I reviewed all changes and I approve them
[x] attach debdiff against the pkg in stable
[x] the issue is verified as fixed in unstable
[ Changes ]
Backport the two patches for the CVE fix from unstable.
One of them is slightly modified for the older codebase.
diff -Nru monero-0.18.0.0+~0+20200826/debian/changelog
monero-0.18.0.0+~0+20200826/debian/changelog
--- monero-0.18.0.0+~0+20200826/debian/changelog 2022-07-26
20:15:22.000000000 +0000
+++ monero-0.18.0.0+~0+20200826/debian/changelog 2025-02-21
07:52:42.000000000 +0000
@@ -1,3 +1,10 @@
+monero (0.18.0.0+~0+20200826-1+deb12u1) bookworm; urgency=medium
+
+ * Backport CVE-2025-26819 fix
+ * Use fully qualified names for epee (fix compilation fail)
+
+ -- Bastian Germann <b...@debian.org> Fri, 21 Feb 2025 07:52:42 +0000
+
monero (0.18.0.0+~0+20200826-1) unstable; urgency=medium
* New upstream version 0.18.0.0+~0+20200826
diff -Nru monero-0.18.0.0+~0+20200826/debian/patches/1003_tcp_throttling.patch
monero-0.18.0.0+~0+20200826/debian/patches/1003_tcp_throttling.patch
--- monero-0.18.0.0+~0+20200826/debian/patches/1003_tcp_throttling.patch
1970-01-01 00:00:00.000000000 +0000
+++ monero-0.18.0.0+~0+20200826/debian/patches/1003_tcp_throttling.patch
2025-02-18 17:40:17.000000000 +0000
@@ -0,0 +1,357 @@
+Origin: upstream, 7e766e13c3790856fee440dcf8d47dab0bed5ea6
+From: Lee *!* Clagett <c...@leeclagett.com>
+Date: Tue, 27 Aug 2024 14:07:52 -0400
+Subject: Cleanup TCP throttling code (performance) + move connection checks
+
+---
+ .../epee/include/net/abstract_tcp_server2.h | 11 ++++-
+ .../epee/include/net/abstract_tcp_server2.inl | 24 ++++++++---
+ .../include/net/network_throttle-detail.hpp | 4 +-
+ contrib/epee/src/network_throttle-detail.cpp | 28 +++++++++----
+ src/p2p/net_node.h | 6 ++-
+ src/p2p/net_node.inl | 42 +++++++++++--------
+ tests/unit_tests/node_server.cpp | 12 ++++++
+ 7 files changed, 93 insertions(+), 34 deletions(-)
+
+diff --git a/contrib/epee/include/net/abstract_tcp_server2.h
b/contrib/epee/include/net/abstract_tcp_server2.h
+index bc0da66e299..be9999203c6 100644
+--- a/contrib/epee/include/net/abstract_tcp_server2.h
++++ b/contrib/epee/include/net/abstract_tcp_server2.h
+@@ -76,6 +76,13 @@ namespace net_utils
+ protected:
+ virtual ~i_connection_filter(){}
+ };
++
++ struct i_connection_limit
++ {
++ virtual bool is_host_limit(const epee::net_utils::network_address
&address)=0;
++ protected:
++ virtual ~i_connection_limit(){}
++ };
+
+
+ /************************************************************************/
+@@ -260,10 +267,11 @@ 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), stop_signal_sent(false)
++ : connection_basic_shared_state(), t_protocol_handler::config_type(),
pfilter(nullptr), plimit(nullptr), stop_signal_sent(false)
+ {}
+
+ i_connection_filter* pfilter;
++ i_connection_limit* plimit;
+ bool stop_signal_sent;
+ };
+
+@@ -369,6 +377,7 @@ namespace net_utils
+ size_t get_threads_count(){return m_threads_count;}
+
+ void set_connection_filter(i_connection_filter* pfilter);
++ void set_connection_limit(i_connection_limit* plimit);
+
+ 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 d88f1819421..8a3a8299c47 100644
+--- a/contrib/epee/include/net/abstract_tcp_server2.inl
++++ b/contrib/epee/include/net/abstract_tcp_server2.inl
+@@ -328,7 +328,7 @@ namespace net_utils
+ return;
+ }
+ auto self = connection<T>::shared_from_this();
+- if (m_connection_type != e_connection_type_RPC) {
++ if (speed_limit_is_enabled()) {
+ auto calc_duration = []{
+ CRITICAL_REGION_LOCAL(
+ network_throttle_manager_t::m_lock_get_global_throttle_in
+@@ -382,7 +382,7 @@ namespace net_utils
+ m_conn_context.m_max_speed_down,
+ speed
+ );
+- {
++ if (speed_limit_is_enabled()) {
+ CRITICAL_REGION_LOCAL(
+ network_throttle_manager_t::m_lock_get_global_throttle_in
+ );
+@@ -454,7 +454,7 @@ namespace net_utils
+ return;
+ }
+ auto self = connection<T>::shared_from_this();
+- if (m_connection_type != e_connection_type_RPC) {
++ if (speed_limit_is_enabled()) {
+ auto calc_duration = [this]{
+ CRITICAL_REGION_LOCAL(
+ network_throttle_manager_t::m_lock_get_global_throttle_out
+@@ -513,7 +513,7 @@ namespace net_utils
+ m_conn_context.m_max_speed_down,
+ speed
+ );
+- {
++ if (speed_limit_is_enabled()) {
+ CRITICAL_REGION_LOCAL(
+ network_throttle_manager_t::m_lock_get_global_throttle_out
+ );
+@@ -873,6 +873,13 @@ namespace net_utils
+ ).pfilter;
+ if (filter && !filter->is_remote_host_allowed(*real_remote))
+ return false;
++
++ auto *limit = static_cast<shared_state&>(
++ connection_basic::get_state()
++ ).plimit;
++ if (limit && limit->is_host_limit(*real_remote))
++ return false;
++
+ ec_t ec;
+ #if !defined(_WIN32) || !defined(__i686)
+ connection_basic::socket_.next_layer().set_option(
+@@ -1022,7 +1029,7 @@ namespace net_utils
+ template<typename T>
+ bool connection<T>::speed_limit_is_enabled() const
+ {
+- return m_connection_type != e_connection_type_RPC;
++ return m_connection_type == e_connection_type_P2P;
+ }
+
+ template<typename T>
+@@ -1349,6 +1356,13 @@ namespace net_utils
+ }
+
//---------------------------------------------------------------------------------
+ template<class t_protocol_handler>
++ void
boosted_tcp_server<t_protocol_handler>::set_connection_limit(i_connection_limit*
plimit)
++ {
++ assert(m_state != nullptr); // always set in constructor
++ m_state->plimit = plimit;
++ }
++
//---------------------------------------------------------------------------------
++ 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/network_throttle-detail.hpp
b/contrib/epee/include/net/network_throttle-detail.hpp
+index d97cb9d8857..48af0d95b0a 100644
+--- a/contrib/epee/include/net/network_throttle-detail.hpp
++++ b/contrib/epee/include/net/network_throttle-detail.hpp
+@@ -46,13 +46,13 @@ namespace net_utils
+
+
+ class network_throttle : public i_network_throttle {
+- private:
++ public:
+ struct packet_info {
+ size_t m_size; // octets sent. Summary for given
small-window (e.g. for all packaged in 1 second)
+ packet_info();
+ };
+
+-
++ private:
+ network_speed_bps m_target_speed;
+ size_t m_network_add_cost; // estimated add cost of headers
+ size_t m_network_minimal_segment; // estimated minimal cost of
sending 1 byte to round up to
+diff --git a/contrib/epee/src/network_throttle-detail.cpp
b/contrib/epee/src/network_throttle-detail.cpp
+index 5812679abfa..1cf001ffa9c 100644
+--- a/contrib/epee/src/network_throttle-detail.cpp
++++ b/contrib/epee/src/network_throttle-detail.cpp
+@@ -46,7 +46,7 @@
+ #include "misc_log_ex.h"
+ #include <boost/chrono.hpp>
+ #include "misc_language.h"
+-#include <sstream>
++#include <fstream>
+ #include <iomanip>
+ #include <algorithm>
+
+@@ -186,6 +186,23 @@ void network_throttle::handle_trafic_exact(size_t
packet_size)
+ _handle_trafic_exact(packet_size, packet_size);
+ }
+
++namespace
++{
++ struct output_history
++ {
++ const boost::circular_buffer< network_throttle::packet_info >&
history;
++ };
++
++ std::ostream& operator<<(std::ostream& out, const output_history&
source)
++ {
++ out << '[';
++ for (auto sample: source.history)
++ out << sample.m_size << ' ';
++ out << ']';
++ return out;
++ }
++}
++
+ void network_throttle::_handle_trafic_exact(size_t packet_size, size_t
orginal_size)
+ {
+ tick();
+@@ -196,14 +213,11 @@ void network_throttle::_handle_trafic_exact(size_t
packet_size, size_t orginal_s
+ m_total_packets++;
+ m_total_bytes += packet_size;
+
+- std::ostringstream oss; oss << "["; for (auto sample: m_history)
oss << sample.m_size << " "; oss << "]" << std::ends;
+- std::string history_str = oss.str();
+-
+ MTRACE("Throttle " << m_name << ": packet of ~"<<packet_size<<"b " << "
(from "<<orginal_size<<" b)"
+ << " Speed AVG=" << std::setw(4) << ((long int)(cts .average/1024))
<<"[w="<<cts .window<<"]"
+ << " " << std::setw(4) << ((long int)(cts2.average/1024))
<<"[w="<<cts2.window<<"]"
+ <<" / " << " Limit="<< ((long
int)(m_target_speed/1024)) <<" KiB/sec "
+- << " " << history_str
++ << " " << output_history{m_history}
+ );
+ }
+
+@@ -289,8 +303,6 @@ void network_throttle::calculate_times(size_t packet_size,
calculate_times_struc
+ }
+
+ if (dbg) {
+- std::ostringstream oss; oss << "["; for (auto sample:
m_history) oss << sample.m_size << " "; oss << "]" << std::ends;
+- std::string history_str = oss.str();
+ MTRACE((cts.delay > 0 ? "SLEEP" : "")
+ << "dbg " << m_name << ": "
+ << "speed is A=" << std::setw(8) <<cts.average<<" vs "
+@@ -300,7 +312,7 @@ void network_throttle::calculate_times(size_t packet_size,
calculate_times_struc
+ << "E="<< std::setw(8) << E << "
(Enow="<<std::setw(8)<<Enow<<") "
+ << "M=" << std::setw(8) << M <<" W="<< std::setw(8) << cts.window
<< " "
+ << "R=" << std::setw(8) << cts.recomendetDataSize << " Wgood" <<
std::setw(8) << Wgood << " "
+- << "History: " << std::setw(8) << history_str << " "
++ << "History: " << std::setw(8) <<
output_history{m_history} << " "
+ << "m_last_sample_time=" << std::setw(8) <<
m_last_sample_time
+ );
+
+diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h
+index 7b3477e1f7d..0a2fd372be4 100644
+--- a/src/p2p/net_node.h
++++ b/src/p2p/net_node.h
+@@ -124,7 +124,8 @@ namespace nodetool
+ template<class t_payload_net_handler>
+ class node_server: public
epee::levin::levin_commands_handler<p2p_connection_context_t<typename
t_payload_net_handler::connection_context> >,
+ public i_p2p_endpoint<typename
t_payload_net_handler::connection_context>,
+- public epee::net_utils::i_connection_filter
++ public epee::net_utils::i_connection_filter,
++ public epee::net_utils::i_connection_limit
+ {
+ struct by_conn_id{};
+ struct by_peer_id{};
+@@ -350,7 +351,10 @@ namespace nodetool
+ virtual bool add_host_fail(const epee::net_utils::network_address
&address, unsigned int score = 1);
+ //----------------- i_connection_filter
--------------------------------------------------------
+ virtual bool is_remote_host_allowed(const
epee::net_utils::network_address &address, time_t *t = NULL);
++ //----------------- i_connection_limit
---------------------------------------------------------
++ virtual bool is_host_limit(const epee::net_utils::network_address
&address);
+
//-----------------------------------------------------------------------------------------------
++
+ bool parse_peer_from_string(epee::net_utils::network_address& pe, const
std::string& node_addr, uint16_t default_port = 0);
+ bool handle_command_line(
+ const boost::program_options::variables_map& vm
+diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
+index 662e598e8a1..b3ba315e325 100644
+--- a/src/p2p/net_node.inl
++++ b/src/p2p/net_node.inl
+@@ -222,6 +222,26 @@ namespace nodetool
+ // not found in hosts or subnets, allowed
+ return true;
+ }
++
//-----------------------------------------------------------------------------------
++ template<class t_payload_net_handler>
++ bool node_server<t_payload_net_handler>::is_host_limit(const
epee::net_utils::network_address &address)
++ {
++ const network_zone& zone = m_network_zones.at(address.get_zone());
++ if (zone.m_current_number_of_in_peers >=
zone.m_config.m_net_config.max_in_connection_count) // in peers limit
++ {
++ MWARNING("Exceeded max incoming connections, so dropping this one.");
++ return true;
++ }
++
++ if(has_too_many_connections(address))
++ {
++ MWARNING("CONNECTION FROM " << address.host_str() << " REFUSED, too
many connections from the same address");
++ return true;
++ }
++
++ return false;
++ }
++
+
//-----------------------------------------------------------------------------------
+ template<class t_payload_net_handler>
+ bool
node_server<t_payload_net_handler>::block_host(epee::net_utils::network_address
addr, time_t seconds, bool add_only)
+@@ -967,6 +987,7 @@ namespace nodetool
+ std::string ipv6_addr = "";
+ std::string ipv6_port = "";
+ zone.second.m_net_server.set_connection_filter(this);
++ zone.second.m_net_server.set_connection_limit(this);
+ MINFO("Binding (IPv4) on " << zone.second.m_bind_ip << ":" <<
zone.second.m_port);
+ if (!zone.second.m_bind_ipv6_address.empty() && m_use_ipv6)
+ {
+@@ -2543,13 +2564,6 @@ namespace nodetool
+ return 1;
+ }
+
+- if (zone.m_current_number_of_in_peers >=
zone.m_config.m_net_config.max_in_connection_count) // in peers limit
+- {
+- LOG_WARNING_CC(context, "COMMAND_HANDSHAKE came, but already have max
incoming connections, so dropping this one.");
+- drop_connection(context);
+- return 1;
+- }
+-
+ if(!m_payload_handler.process_payload_sync_data(arg.payload_data,
context, true))
+ {
+ LOG_WARNING_CC(context, "COMMAND_HANDSHAKE came, but
process_payload_sync_data returned false, dropping connection.");
+@@ -2559,13 +2573,6 @@ namespace nodetool
+
+ zone.m_notifier.on_handshake_complete(context.m_connection_id,
context.m_is_income);
+
+- if(has_too_many_connections(context.m_remote_address))
+- {
+- LOG_PRINT_CCONTEXT_L1("CONNECTION FROM " <<
context.m_remote_address.host_str() << " REFUSED, too many connections from the
same address");
+- drop_connection(context);
+- return 1;
+- }
+-
+ //associate peer_id with this connection
+ context.peer_id = arg.node_data.peer_id;
+ context.m_in_timedsync = false;
+@@ -2885,15 +2892,16 @@ namespace nodetool
+ if (cntxt.m_is_income && cntxt.m_remote_address.is_same_host(address)) {
+ count++;
+
+- if (count > max_connections) {
++ // the only call location happens BEFORE foreach_connection list is
updated
++ if (count >= max_connections) {
+ return false;
+ }
+ }
+
+ return true;
+ });
+-
+- return count > max_connections;
++ // the only call location happens BEFORE foreach_connection list is
updated
++ return count >= max_connections;
+ }
+
+ template<class t_payload_net_handler>
+diff --git a/tests/unit_tests/node_server.cpp
b/tests/unit_tests/node_server.cpp
+index 39178884c87..cc9434157bb 100644
+--- a/tests/unit_tests/node_server.cpp
++++ b/tests/unit_tests/node_server.cpp
+@@ -224,6 +224,18 @@ TEST(ban, subnet)
+ test_core pr_core;
+ cryptonote::t_cryptonote_protocol_handler<test_core> cprotocol(pr_core,
NULL);
+ Server server(cprotocol);
++ {
++ boost::program_options::options_description opts{};
++ Server::init_options(opts);
++ cryptonote::core::init_options(opts);
++
++ char** args = nullptr;
++ boost::program_options::variables_map vm;
++ boost::program_options::store(
++ boost::program_options::parse_command_line(0, args, opts), vm
++ );
++ server.init(vm);
++ }
+ cprotocol.set_p2p_endpoint(&server);
+
+ ASSERT_TRUE(server.block_subnet(MAKE_IPV4_SUBNET(1,2,3,4,24), 10));
diff -Nru
monero-0.18.0.0+~0+20200826/debian/patches/1004_http_response_limits.patch
monero-0.18.0.0+~0+20200826/debian/patches/1004_http_response_limits.patch
--- monero-0.18.0.0+~0+20200826/debian/patches/1004_http_response_limits.patch
1970-01-01 00:00:00.000000000 +0000
+++ monero-0.18.0.0+~0+20200826/debian/patches/1004_http_response_limits.patch
2025-02-21 07:52:42.000000000 +0000
@@ -0,0 +1,822 @@
+Origin: upstream, ec74ff4a3d3ca38b7912af680209a45fd1701c3d
+From: Lee *!* Clagett <c...@leeclagett.com>
+Date: Tue, 21 Jan 2025 09:56:52 -0500
+Subject: Set response limits on http server connections
+
+---
+ .../epee/include/net/abstract_tcp_server2.h | 11 +-
+ .../epee/include/net/abstract_tcp_server2.inl | 57 ++++-
+ .../epee/include/net/http_protocol_handler.h | 18 +-
+ .../include/net/http_protocol_handler.inl | 37 +++-
+ .../epee/include/net/http_server_impl_base.h | 39 +++-
+ src/cryptonote_config.h | 4 +
+ src/p2p/net_node.cpp | 2 +-
+ src/rpc/core_rpc_server.cpp | 47 +++-
+ src/rpc/core_rpc_server.h | 5 +-
+ src/wallet/wallet_rpc_server.cpp | 27 ++-
+ .../functional_tests/functional_tests_rpc.py | 2 +-
+ tests/unit_tests/CMakeLists.txt | 1 +
+ tests/unit_tests/epee_http_server.cpp | 200 ++++++++++++++++++
+ 13 files changed, 423 insertions(+), 27 deletions(-)
+ create mode 100644 tests/unit_tests/epee_http_server.cpp
+
+diff --git a/contrib/epee/include/net/abstract_tcp_server2.h
b/contrib/epee/include/net/abstract_tcp_server2.h
+index 3f9b95033f1..1e45e68096e 100644
+--- a/contrib/epee/include/net/abstract_tcp_server2.h
++++ b/contrib/epee/include/net/abstract_tcp_server2.h
+@@ -65,6 +65,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
+ {
+@@ -170,6 +171,7 @@ namespace net_utils
+ } read;
+ struct {
+ std::deque<epee::byte_slice> queue;
++ std::size_t total_bytes;
+ bool wait_consume;
+ } write;
+ };
+@@ -268,11 +270,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;
+ };
+
+@@ -380,6 +388,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 53c0d852c9c..39a58d1b261 100644
+--- a/contrib/epee/include/net/abstract_tcp_server2.inl
++++ b/contrib/epee/include/net/abstract_tcp_server2.inl
+@@ -497,10 +497,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 {
+@@ -525,8 +527,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();
+ }
+@@ -670,8 +675,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)
+@@ -729,8 +735,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)
+@@ -757,6 +768,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.
+@@ -774,7 +787,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(
+@@ -783,14 +803,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
+@@ -816,7 +845,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 {
+@@ -826,6 +857,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();
+ }
+ }
+@@ -1369,6 +1401,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 258b07e2c5a..8b73964dd2b 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 f7d2074b2e7..6647d1f15c5 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 024f141b4ca..e2439167462 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;
+ };
+ }
+diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h
+index c32c399ec21..19b6f9ab867 100644
+--- a/src/cryptonote_config.h
++++ b/src/cryptonote_config.h
+@@ -127,6 +127,10 @@
+
+ #define COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT 1000
+ #define COMMAND_RPC_GET_BLOCKS_FAST_MAX_TX_COUNT 20000
++#define DEFAULT_RPC_MAX_CONNECTIONS_PER_PUBLIC_IP 3
++#define DEFAULT_RPC_MAX_CONNECTIONS_PER_PRIVATE_IP 25
++#define DEFAULT_RPC_MAX_CONNECTIONS 100
++#define DEFAULT_RPC_SOFT_LIMIT_SIZE 25 * 1024 * 1024 //
25 MiB
+ #define MAX_RPC_CONTENT_LENGTH 1048576 // 1 MB
+
+ #define P2P_LOCAL_WHITE_PEERLIST_LIMIT 1000
+diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp
+index 5cba55b4b1e..0f758937c79 100644
+--- a/src/p2p/net_node.cpp
++++ b/src/p2p/net_node.cpp
+@@ -169,7 +169,7 @@ namespace nodetool
+ const command_line::arg_descriptor<bool> arg_pad_transactions = {
+ "pad-transactions", "Pad relayed transactions to help defend against
traffic volume analysis", false
+ };
+- const command_line::arg_descriptor<uint32_t> arg_max_connections_per_ip =
{"max-connections-per-ip", "Maximum number of connections allowed from the same
IP address", 1};
++ const command_line::arg_descriptor<uint32_t> arg_max_connections_per_ip =
{"max-connections-per-ip", "Maximum number of p2p connections allowed from the
same IP address", 1};
+
+ boost::optional<std::vector<proxy>>
get_proxies(boost::program_options::variables_map const& vm)
+ {
+diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
+index 426d8b295f6..8a29edfb207 100644
+--- a/src/rpc/core_rpc_server.cpp
++++ b/src/rpc/core_rpc_server.cpp
+@@ -163,6 +163,10 @@ namespace cryptonote
+ command_line::add_arg(desc, arg_rpc_payment_difficulty);
+ command_line::add_arg(desc, arg_rpc_payment_credits);
+ command_line::add_arg(desc, arg_rpc_payment_allow_free_loopback);
++ command_line::add_arg(desc, arg_rpc_max_connections_per_public_ip);
++ command_line::add_arg(desc, arg_rpc_max_connections_per_private_ip);
++ command_line::add_arg(desc, arg_rpc_max_connections);
++ command_line::add_arg(desc, arg_rpc_response_soft_limit);
+ }
+
//------------------------------------------------------------------------------------------------------------------------------
+ core_rpc_server::core_rpc_server(
+@@ -396,11 +400,28 @@ namespace cryptonote
+ }
+ }
+
++ const auto max_connections_public = command_line::get_arg(vm,
arg_rpc_max_connections_per_public_ip);
++ const auto max_connections_private = command_line::get_arg(vm,
arg_rpc_max_connections_per_private_ip);
++ const auto max_connections = command_line::get_arg(vm,
arg_rpc_max_connections);
++
++ if (max_connections < max_connections_public)
++ {
++ MFATAL(arg_rpc_max_connections_per_public_ip.name << " is bigger than "
<< arg_rpc_max_connections.name);
++ return false;
++ }
++ if (max_connections < max_connections_private)
++ {
++ MFATAL(arg_rpc_max_connections_per_private_ip.name << " is bigger than
" << arg_rpc_max_connections.name);
++ return false;
++ }
++
+ auto rng = [](size_t len, uint8_t *ptr){ return crypto::rand(len, ptr); };
+ const bool inited = epee::http_server_impl_base<core_rpc_server,
connection_context>::init(
+ rng, std::move(port), std::move(bind_ip_str),
+ std::move(bind_ipv6_str), std::move(rpc_config->use_ipv6),
std::move(rpc_config->require_ipv4),
+- std::move(rpc_config->access_control_origins), std::move(http_login),
std::move(rpc_config->ssl_options)
++ std::move(rpc_config->access_control_origins), std::move(http_login),
std::move(rpc_config->ssl_options),
++ max_connections_public, max_connections_private, max_connections,
++ command_line::get_arg(vm, arg_rpc_response_soft_limit)
+ );
+
+ m_net_server.get_config_object().m_max_content_length =
MAX_RPC_CONTENT_LENGTH;
+@@ -3885,4 +3906,28 @@ namespace cryptonote
+ , "Allow free access from the loopback address (ie, the local host)"
+ , false
+ };
++
++ const command_line::arg_descriptor<std::size_t>
core_rpc_server::arg_rpc_max_connections_per_public_ip = {
++ "rpc-max-connections-per-public-ip"
++ , "Max RPC connections per public IP permitted"
++ , DEFAULT_RPC_MAX_CONNECTIONS_PER_PUBLIC_IP
++ };
++
++ const command_line::arg_descriptor<std::size_t>
core_rpc_server::arg_rpc_max_connections_per_private_ip = {
++ "rpc-max-connections-per-private-ip"
++ , "Max RPC connections per private and localhost IP permitted"
++ , DEFAULT_RPC_MAX_CONNECTIONS_PER_PRIVATE_IP
++ };
++
++ const command_line::arg_descriptor<std::size_t>
core_rpc_server::arg_rpc_max_connections = {
++ "rpc-max-connections"
++ , "Max RPC connections permitted"
++ , DEFAULT_RPC_MAX_CONNECTIONS
++ };
++
++ const command_line::arg_descriptor<std::size_t>
core_rpc_server::arg_rpc_response_soft_limit = {
++ "rpc-response-soft-limit"
++ , "Max response bytes that can be queued, enforced at next response
attempt"
++ , DEFAULT_RPC_SOFT_LIMIT_SIZE
++ };
+ } // namespace cryptonote
+diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h
+index 6bd0fc25b69..ce87730052d 100644
+--- a/src/rpc/core_rpc_server.h
++++ b/src/rpc/core_rpc_server.h
+@@ -56,7 +56,6 @@ namespace cryptonote
+ {
+ public:
+
+- static const command_line::arg_descriptor<bool> arg_public_node;
+ static const command_line::arg_descriptor<std::string, false, true, 2>
arg_rpc_bind_port;
+ static const command_line::arg_descriptor<std::string>
arg_rpc_restricted_bind_port;
+ static const command_line::arg_descriptor<bool> arg_restricted_rpc;
+@@ -73,6 +72,10 @@ namespace cryptonote
+ static const command_line::arg_descriptor<uint64_t>
arg_rpc_payment_difficulty;
+ static const command_line::arg_descriptor<uint64_t>
arg_rpc_payment_credits;
+ static const command_line::arg_descriptor<bool>
arg_rpc_payment_allow_free_loopback;
++ static const command_line::arg_descriptor<std::size_t>
arg_rpc_max_connections_per_public_ip;
++ static const command_line::arg_descriptor<std::size_t>
arg_rpc_max_connections_per_private_ip;
++ static const command_line::arg_descriptor<std::size_t>
arg_rpc_max_connections;
++ static const command_line::arg_descriptor<std::size_t>
arg_rpc_response_soft_limit;
+
+ typedef epee::net_utils::connection_context_base connection_context;
+
+diff --git a/src/wallet/wallet_rpc_server.cpp
b/src/wallet/wallet_rpc_server.cpp
+index 33437606228..b3d495a3038 100644
+--- a/src/wallet/wallet_rpc_server.cpp
++++ b/src/wallet/wallet_rpc_server.cpp
+@@ -129,6 +129,10 @@ namespace
+ const command_line::arg_descriptor<bool> arg_restricted =
{"restricted-rpc", "Restricts to view-only commands", false};
+ const command_line::arg_descriptor<std::string> arg_wallet_dir =
{"wallet-dir", "Directory for newly created wallets"};
+ const command_line::arg_descriptor<bool> arg_prompt_for_password =
{"prompt-for-password", "Prompts for password when not provided", false};
++ const command_line::arg_descriptor<std::size_t>
arg_rpc_max_connections_per_public_ip = {"rpc-max-connections-per-public-ip",
"Max RPC connections per public IP permitted",
DEFAULT_RPC_MAX_CONNECTIONS_PER_PUBLIC_IP};
++ const command_line::arg_descriptor<std::size_t>
arg_rpc_max_connections_per_private_ip = {"rpc-max-connections-per-private-ip",
"Max RPC connections per private and localhost IP permitted",
DEFAULT_RPC_MAX_CONNECTIONS_PER_PRIVATE_IP};
++ const command_line::arg_descriptor<std::size_t> arg_rpc_max_connections =
{"rpc-max-connections", "Max RPC connections permitted",
DEFAULT_RPC_MAX_CONNECTIONS};
++ const command_line::arg_descriptor<std::size_t> arg_rpc_response_soft_limit
= {"rpc-response-soft-limit", "Max response bytes that can be queued, enforced
at next response attempt", DEFAULT_RPC_SOFT_LIMIT_SIZE};
+
+ constexpr const char default_rpc_username[] = "monero";
+
+@@ -325,13 +329,30 @@ namespace tools
+
+ check_background_mining();
+
++ const auto max_connections_public = command_line::get_arg(vm,
arg_rpc_max_connections_per_public_ip);
++ const auto max_connections_private = command_line::get_arg(vm,
arg_rpc_max_connections_per_private_ip);
++ const auto max_connections = command_line::get_arg(vm,
arg_rpc_max_connections);
++
++ if (max_connections < max_connections_public)
++ {
++ MFATAL(arg_rpc_max_connections_per_public_ip.name << " is bigger than "
<< arg_rpc_max_connections.name);
++ return false;
++ }
++ if (max_connections < max_connections_private)
++ {
++ MFATAL(arg_rpc_max_connections_per_private_ip.name << " is bigger than
" << arg_rpc_max_connections.name);
++ return false;
++ }
++
+ m_net_server.set_threads_prefix("RPC");
+ auto rng = [](size_t len, uint8_t *ptr) { return crypto::rand(len, ptr);
};
+ return epee::http_server_impl_base<wallet_rpc_server,
connection_context>::init(
+ rng, std::move(bind_port), std::move(rpc_config->bind_ip),
+ std::move(rpc_config->bind_ipv6_address),
std::move(rpc_config->use_ipv6), std::move(rpc_config->require_ipv4),
+ std::move(rpc_config->access_control_origins), std::move(http_login),
+- std::move(rpc_config->ssl_options)
++ std::move(rpc_config->ssl_options),
++ max_connections_public, max_connections_private, max_connections,
++ command_line::get_arg(vm, arg_rpc_response_soft_limit)
+ );
+ }
+
//------------------------------------------------------------------------------------------------------------------------------
+@@ -4771,6 +4771,10 @@ int main(int argc, char** argv) {
+ command_line::add_arg(desc_params, arg_wallet_dir);
+ command_line::add_arg(desc_params, arg_prompt_for_password);
+ command_line::add_arg(desc_params, arg_rpc_client_secret_key);
++ command_line::add_arg(desc_params, arg_rpc_max_connections_per_public_ip);
++ command_line::add_arg(desc_params, arg_rpc_max_connections_per_private_ip);
++ command_line::add_arg(desc_params, arg_rpc_max_connections);
++ command_line::add_arg(desc_params, arg_rpc_response_soft_limit);
+
+ daemonizer::init_options(hidden_options, desc_params);
+ desc_params.add(hidden_options);
+diff --git a/tests/functional_tests/functional_tests_rpc.py
b/tests/functional_tests/functional_tests_rpc.py
+index 520ee596105..3881bf39f25 100755
+--- a/tests/functional_tests/functional_tests_rpc.py
++++ b/tests/functional_tests/functional_tests_rpc.py
+@@ -52,7 +52,7 @@
+ FUNCTIONAL_TESTS_DIRECTORY = builddir + "/tests/functional_tests"
+ DIFFICULTY = 10
+
+-monerod_base = [builddir + "/bin/monerod", "--regtest", "--fixed-difficulty",
str(DIFFICULTY), "--no-igd", "--p2p-bind-port", "monerod_p2p_port",
"--rpc-bind-port", "monerod_rpc_port", "--zmq-rpc-bind-port",
"monerod_zmq_port", "--non-interactive", "--disable-dns-checkpoints",
"--check-updates", "disabled", "--rpc-ssl", "disabled", "--data-dir",
"monerod_data_dir", "--log-level", "1"]
++monerod_base = [builddir + "/bin/monerod", "--regtest", "--fixed-difficulty",
str(DIFFICULTY), "--no-igd", "--p2p-bind-port", "monerod_p2p_port",
"--rpc-bind-port", "monerod_rpc_port", "--zmq-rpc-bind-port",
"monerod_zmq_port", "--non-interactive", "--disable-dns-checkpoints",
"--check-updates", "disabled", "--rpc-ssl", "disabled", "--data-dir",
"monerod_data_dir", "--log-level", "1", "--rpc-max-connections-per-private-ip",
"100", "--rpc-max-connections", "100"]
+ monerod_extra = [
+ ["--offline"],
+ ["--rpc-payment-address",
"44SKxxLQw929wRF6BA9paQ1EWFshNnKhXM3qz6Mo3JGDE2YG3xyzVutMStEicxbQGRfrYvAAYxH6Fe8rnD56EaNwUiqhcwR",
"--rpc-payment-difficulty", str(DIFFICULTY), "--rpc-payment-credits", "5000",
"--offline"],
+diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt
+index e329b7506fa..8e9b7a531f9 100644
+--- a/tests/unit_tests/CMakeLists.txt
++++ b/tests/unit_tests/CMakeLists.txt
+@@ -48,6 +48,7 @@ set(unit_tests_sources
+ dns_resolver.cpp
+ epee_boosted_tcp_server.cpp
+ epee_levin_protocol_handler_async.cpp
++ epee_http_server.cpp
+ epee_serialization.cpp
+ epee_utils.cpp
+ expect.cpp
+diff --git a/tests/unit_tests/epee_http_server.cpp
b/tests/unit_tests/epee_http_server.cpp
+new file mode 100644
+index 00000000000..1d3b60d5485
+--- /dev/null
++++ b/tests/unit_tests/epee_http_server.cpp
+@@ -0,0 +1,200 @@
++// Copyright (c) 2014-2024, The Monero Project
++//
++// All rights reserved.
++//
++// Redistribution and use in source and binary forms, with or without
modification, are
++// permitted provided that the following conditions are met:
++//
++// 1. Redistributions of source code must retain the above copyright notice,
this list of
++// conditions and the following disclaimer.
++//
++// 2. Redistributions in binary form must reproduce the above copyright
notice, this list
++// of conditions and the following disclaimer in the documentation and/or
other
++// materials provided with the distribution.
++//
++// 3. Neither the name of the copyright holder nor the names of its
contributors may be
++// used to endorse or promote products derived from this software without
specific
++// prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY
++// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF
++// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL
++// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO,
++// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS
++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT,
++// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF
++// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
++//
++
++#include <atomic>
++#include <boost/asio/connect.hpp>
++#include <boost/asio/ip/tcp.hpp>
++#include <boost/beast/core.hpp>
++#include <boost/beast/http.hpp>
++#include <boost/beast/version.hpp>
++#include "gtest/gtest.h"
++#include "net/http_server_handlers_map2.h"
++#include "net/http_server_impl_base.h"
++#include "storages/portable_storage_template_helper.h"
++
++namespace
++{
++ constexpr const std::size_t payload_size = 26 * 1024 * 1024;
++ constexpr const std::size_t max_private_ips = 25;
++ struct dummy
++ {
++ struct request
++ {
++ BEGIN_KV_SERIALIZE_MAP()
++ END_KV_SERIALIZE_MAP()
++ };
++
++ struct response
++ {
++ BEGIN_KV_SERIALIZE_MAP()
++ KV_SERIALIZE(payload)
++ END_KV_SERIALIZE_MAP()
++
++ std::string payload;
++ };
++ };
++
++ std::string make_payload()
++ {
++ dummy::request body{};
++ const auto body_serialized = epee::serialization::store_t_to_binary(body);
++ return std::string{
++ reinterpret_cast<const char*>(body_serialized.data()),
++ body_serialized.size()
++ };
++ }
++
++ struct http_server : epee::http_server_impl_base<http_server>
++ {
++ using connection_context = epee::net_utils::connection_context_base;
++
++ http_server()
++ : epee::http_server_impl_base<http_server>(),
++ dummy_size(payload_size)
++ {}
++
++ CHAIN_HTTP_TO_MAP2(connection_context); //forward http requests to uri map
++
++ BEGIN_URI_MAP2()
++ MAP_URI_AUTO_BIN2("/dummy", on_dummy, dummy)
++ END_URI_MAP2()
++
++ bool on_dummy(const dummy::request&, dummy::response& res, const
connection_context *ctx = NULL)
++ {
++ res.payload.resize(dummy_size.load(), 'f');
++ return true;
++ }
++
++ std::atomic<std::size_t> dummy_size;
++ };
++} // anonymous
++
++TEST(http_server, response_soft_limit)
++{
++ namespace http = boost::beast::http;
++
++ http_server server{};
++ server.init(nullptr, "8080");
++ server.run(1, false);
++
++ boost::system::error_code error{};
++ boost::asio::io_context context{};
++ boost::asio::ip::tcp::socket stream{context};
++ stream.connect(
++ boost::asio::ip::tcp::endpoint{
++ boost::asio::ip::make_address("127.0.0.1"), 8080
++ },
++ error
++ );
++ EXPECT_FALSE(bool(error));
++
++ http::request<http::string_body> req{http::verb::get, "/dummy", 11};
++ req.set(http::field::host, "127.0.0.1");
++ req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
++ req.body() = make_payload();
++ req.prepare_payload();
++ http::write(stream, req, error);
++ EXPECT_FALSE(bool(error));
++
++ {
++ dummy::response payload{};
++ boost::beast::flat_buffer buffer;
++ http::response<http::basic_string_body<char>> res;
++ http::read(stream, buffer, res, error);
++ EXPECT_FALSE(bool(error));
++ EXPECT_EQ(200u, res.result_int());
++ EXPECT_TRUE(epee::serialization::load_t_from_binary(payload, res.body()));
++ EXPECT_EQ(payload_size, std::count(payload.payload.begin(),
payload.payload.end(), 'f'));
++ }
++
++ while (!error)
++ http::write(stream, req, error);
++ server.send_stop_signal();
++}
++
++TEST(http_server, private_ip_limit)
++{
++ namespace http = boost::beast::http;
++
++ http_server server{};
++ server.dummy_size = 1;
++ server.init(nullptr, "8080");
++ server.run(1, false);
++
++ boost::system::error_code error{};
++ boost::asio::io_context context{};
++
++ http::request<http::string_body> req{http::verb::get, "/dummy", 11};
++ req.set(http::field::host, "127.0.0.1");
++ req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
++ req.body() = make_payload();
++ req.prepare_payload();
++
++ std::vector<boost::asio::ip::tcp::socket> streams{};
++ for (std::size_t i = 0; i < max_private_ips; ++i)
++ {
++ streams.emplace_back(context);
++ streams.back().connect(
++ boost::asio::ip::tcp::endpoint{
++ boost::asio::ip::make_address("127.0.0.1"), 8080
++ },
++ error
++ );
++ http::write(streams.back(), req, error);
++ EXPECT_FALSE(bool(error));
++
++ dummy::response payload{};
++ boost::beast::flat_buffer buffer;
++ http::response<http::basic_string_body<char>> res;
++
++ http::read(streams.back(), buffer, res, error);
++ EXPECT_FALSE(bool(error));
++ }
++
++ boost::asio::ip::tcp::socket stream{context};
++ stream.connect(
++ boost::asio::ip::tcp::endpoint{
++ boost::asio::ip::make_address("127.0.0.1"), 8080
++ },
++ error
++ );
++ bool failed = bool(error);
++ http::write(stream, req, error);
++ failed |= bool(error);
++ {
++ dummy::response payload{};
++ boost::beast::flat_buffer buffer;
++ http::response<http::basic_string_body<char>> res;
++
++ // make sure server ran async_accept code
++ http::read(stream, buffer, res, error);
++ }
++ failed |= bool(error);
++ EXPECT_TRUE(failed);
++}
diff -Nru
monero-0.18.0.0+~0+20200826/debian/patches/1005_fully_qualified_names.patch
monero-0.18.0.0+~0+20200826/debian/patches/1005_fully_qualified_names.patch
--- monero-0.18.0.0+~0+20200826/debian/patches/1005_fully_qualified_names.patch
1970-01-01 00:00:00.000000000 +0000
+++ monero-0.18.0.0+~0+20200826/debian/patches/1005_fully_qualified_names.patch
2025-02-18 17:40:17.000000000 +0000
@@ -0,0 +1,94 @@
+Origin: upstream, a4cb77f9f3e3b37e5f9a87cee53392c8114a1b37
+From: Jeffrey Ryan <jeffreyr...@tutanota.com>
+Date: Sun, 22 May 2022 23:41:41 -0500
+Subject: epee: update 'http_server_handlers_map2.h' macros to use fully
qualified names
+
+quick patch which fixes the issue where if you use some macros from
`http_server_handlers_map2.h` you have to be in the `epee` namespace or it
doesn't compile. Now can remove `using namespace epee;` from header file
`core_rpc_server.h`, which caused a couple of name qualifying mistakes
+---
+ contrib/epee/include/net/http_server_handlers_map2.h | 8 ++++----
+ src/daemon/main.cpp | 2 +-
+ src/daemon/rpc_command_executor.cpp | 2 +-
+ src/rpc/core_rpc_server.h | 4 ----
+ 4 files changed, 6 insertions(+), 10 deletions(-)
+
+diff --git a/contrib/epee/include/net/http_server_handlers_map2.h
b/contrib/epee/include/net/http_server_handlers_map2.h
+index ffb3f3b7ea5..f52e61817f0 100644
+--- a/contrib/epee/include/net/http_server_handlers_map2.h
++++ b/contrib/epee/include/net/http_server_handlers_map2.h
+@@ -71,7 +71,7 @@
+ else if((query_info.m_URI == s_pattern) && (cond)) \
+ { \
+ handled = true; \
+- uint64_t ticks = misc_utils::get_tick_count(); \
++ uint64_t ticks = epee::misc_utils::get_tick_count(); \
+ boost::value_initialized<command_type::request> req; \
+ bool parse_res =
epee::serialization::load_t_from_json(static_cast<command_type::request&>(req),
query_info.m_body); \
+ if (!parse_res) \
+@@ -107,7 +107,7 @@
+ else if(query_info.m_URI == s_pattern) \
+ { \
+ handled = true; \
+- uint64_t ticks = misc_utils::get_tick_count(); \
++ uint64_t ticks = epee::misc_utils::get_tick_count(); \
+ boost::value_initialized<command_type::request> req; \
+ bool parse_res =
epee::serialization::load_t_from_binary(static_cast<command_type::request&>(req),
epee::strspan<uint8_t>(query_info.m_body)); \
+ if (!parse_res) \
+@@ -117,7 +117,7 @@
+ response_info.m_response_comment = "Bad request"; \
+ return true; \
+ } \
+- uint64_t ticks1 = misc_utils::get_tick_count(); \
++ uint64_t ticks1 = epee::misc_utils::get_tick_count(); \
+ boost::value_initialized<command_type::response> resp;\
+ MINFO(m_conn_context << "calling " << s_pattern); \
+ bool res = false; \
+@@ -129,7 +129,7 @@
+ response_info.m_response_comment = "Internal Server Error"; \
+ return true; \
+ } \
+- uint64_t ticks2 = misc_utils::get_tick_count(); \
++ uint64_t ticks2 = epee::misc_utils::get_tick_count(); \
+ epee::byte_slice buffer; \
+
epee::serialization::store_t_to_binary(static_cast<command_type::response&>(resp),
buffer, 64 * 1024); \
+ uint64_t ticks3 = epee::misc_utils::get_tick_count(); \
+diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp
+index 73d9ebce1b3..3d90e0855fc 100644
+--- a/src/daemon/main.cpp
++++ b/src/daemon/main.cpp
+@@ -83,7 +83,7 @@ uint16_t parse_public_rpc_port(const po::variables_map &vm)
+ }
+
+ uint16_t rpc_port;
+- if (!string_tools::get_xtype_from_string(rpc_port, rpc_port_str))
++ if (!epee::string_tools::get_xtype_from_string(rpc_port, rpc_port_str))
+ {
+ throw std::runtime_error("invalid RPC port " + rpc_port_str);
+ }
+diff --git a/src/daemon/rpc_command_executor.cpp
b/src/daemon/rpc_command_executor.cpp
+index b6364ff7702..0d3688c7655 100644
+--- a/src/daemon/rpc_command_executor.cpp
++++ b/src/daemon/rpc_command_executor.cpp
+@@ -1063,7 +1063,7 @@ bool
t_rpc_command_executor::print_transaction(crypto::hash transaction_hash,
+ cryptonote::blobdata blob;
+ std::string source = as_hex.empty() ? pruned_as_hex + prunable_as_hex :
as_hex;
+ bool pruned = !pruned_as_hex.empty() && prunable_as_hex.empty();
+- if (!string_tools::parse_hexstr_to_binbuff(source, blob))
++ if (!epee::string_tools::parse_hexstr_to_binbuff(source, blob))
+ {
+ tools::fail_msg_writer() << "Failed to parse tx to get json format";
+ }
+diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h
+index 0274f4db84b..b87412ca67d 100644
+--- a/src/rpc/core_rpc_server.h
++++ b/src/rpc/core_rpc_server.h
+@@ -47,10 +47,6 @@
+ #undef MONERO_DEFAULT_LOG_CATEGORY
+ #define MONERO_DEFAULT_LOG_CATEGORY "daemon.rpc"
+
+-// yes, epee doesn't properly use its full namespace when calling its
+-// functions from macros. *sigh*
+-using namespace epee;
+-
+ namespace cryptonote
+ {
+ /************************************************************************/
diff -Nru monero-0.18.0.0+~0+20200826/debian/patches/series
monero-0.18.0.0+~0+20200826/debian/patches/series
--- monero-0.18.0.0+~0+20200826/debian/patches/series 2022-07-26
20:15:22.000000000 +0000
+++ monero-0.18.0.0+~0+20200826/debian/patches/series 2025-02-21
07:52:42.000000000 +0000
@@ -1,2 +1,5 @@
+1003_tcp_throttling.patch
+1004_http_response_limits.patch
+1005_fully_qualified_names.patch
2001_system_shared_libs.patch
2002_privacy.patch