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

Reply via email to