Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
f1226cb
Fix reject hash read for tx and block rejects.
evoskuil Oct 3, 2026
3a65354
Fix credential parse of passwords containing spaces.
evoskuil Oct 3, 2026
92f373b
Reject an invalid jsonrpc version.
evoskuil Oct 14, 2026
623d966
Fix jsonrpc response parse of null error.
evoskuil Oct 3, 2026
43161e2
Throw on jsonrpc serialization of any_t values.
evoskuil Oct 3, 2026
c0f8ee9
Fix jsonrpc reader exceptions masked by post-parse validation.
evoskuil Oct 3, 2026
86985f6
Gate reject in protocol version 70002.
evoskuil Oct 14, 2026
ed50cd9
Drop a peer that repeats version or verack after the handshake.
evoskuil Oct 14, 2026
2ae2f7d
Preserve network error codes from body readers in body_read.
evoskuil Oct 3, 2026
30fba62
Fix file reader assertion on an unopened file.
evoskuil Oct 3, 2026
fd2ce0b
Fix origin policy for scheme-bearing origins.
evoskuil Oct 3, 2026
3ce50ba
Fix body writes that complete without a buffer reported as bad_stream.
evoskuil Oct 3, 2026
6b9e511
Fix sam forwarded accept re-entering the sam handler.
evoskuil Oct 3, 2026
8ad5a2e
Drop a peer that signals address v2 or wtxid relay before its version.
evoskuil Oct 13, 2026
24cc9bb
Assert the channel is not held on pause.
evoskuil Oct 14, 2026
1cd14a1
Brace macro invocations under conditionals.
evoskuil Oct 13, 2026
cc26123
Correct a stale access marker.
evoskuil Oct 13, 2026
4f5132b
Pass reject to the seed version protocol.
evoskuil Oct 14, 2026
adfb2de
Add service session attachment and a service stop for unrecoverable f…
evoskuil Oct 14, 2026
b1fec4b
Use the unambiguous boolean alias in the null error response test.
evoskuil Oct 14, 2026
94197db
Reset only the hash read failure in reject deserialize.
evoskuil Oct 14, 2026
410fdc5
Type the mock service session pointer.
evoskuil Oct 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/bitcoin/network/config/utilities.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ BCT_API asio::address from_address(const messages::peer::ip_address& address) NO
BCT_API std::string to_normal_host(const std::string& host,
uint16_t default_port) NOEXCEPT;

/// Get ascii lowered host:port form of a url, with default substitution.
BCT_API std::string to_normal_origin(const std::string& origin,
uint16_t default_port) NOEXCEPT;

} // namespace config
} // namespace network
} // namespace libbitcoin
Expand Down
1 change: 1 addition & 0 deletions include/bitcoin/network/error.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ enum error_t : uint8_t
missing_parameter,

// json-rpc error
jsonrpc_invalid_version,
jsonrpc_requires_method,
jsonrpc_v1_requires_params,
jsonrpc_v1_requires_array_params,
Expand Down
28 changes: 28 additions & 0 deletions include/bitcoin/network/net.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ class BCT_API net
/// Resume all P2P connection.
virtual bool resume() NOEXCEPT;

/// Stop all service connections (not resumable).
virtual void stop_services(const code& ec) NOEXCEPT;

/// Properties.
/// -----------------------------------------------------------------------

Expand Down Expand Up @@ -207,6 +210,11 @@ class BCT_API net
return stop_subscriber_.size();
}

virtual size_t service_subscriber_count() const NOEXCEPT
{
return service_subscriber_.size();
}

virtual size_t connect_subscriber_count() const NOEXCEPT
{
return connect_subscriber_.size();
Expand Down Expand Up @@ -241,6 +249,22 @@ class BCT_API net
return session;
}

/// Attach service session to network, caller must start (requires strand).
template <class Session, class Network, typename... Args>
typename Session::ptr attach_service(Network& net, Args&&... args) NOEXCEPT
{
BC_ASSERT_MSG(stranded(), "subscribe_service");
const auto session = attach<Session>(net, std::forward<Args>(args)...);

subscribe_service([=](const code&) NOEXCEPT
{
session->stop();
return false;
}, create_key());

return session;
}

/// Base session (no derived session access).
/// -----------------------------------------------------------------------
friend class session;
Expand Down Expand Up @@ -324,6 +348,9 @@ class BCT_API net
void do_subscribe_close(const stop_handler& handler,
const stop_completer& complete) NOEXCEPT;

code subscribe_service(stop_handler&& handler, object_key key) NOEXCEPT;
void do_stop_services(const code& ec) NOEXCEPT;

// P2P
// ------------------------------------------------------------------------

Expand Down Expand Up @@ -373,6 +400,7 @@ class BCT_API net
channel_ids actives_{};
broadcaster broadcaster_{};
stop_subscriber stop_subscriber_{};
stop_subscriber service_subscriber_{};
channel_subscriber connect_subscriber_{};
std::unordered_set<uint64_t> nonces_{};
};
Expand Down
4 changes: 4 additions & 0 deletions include/bitcoin/network/net/acceptor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ class BCT_API acceptor
// These are protected by strand.
asio::acceptor acceptor_;
bool stopped_{ true };

private:
void do_handle_accept(const code& ec, const socket::ptr& socket,
const socket_handler& handler) NOEXCEPT;
};

} // namespace network
Expand Down
6 changes: 5 additions & 1 deletion include/bitcoin/network/protocols/protocol_version_70002.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class BCT_API protocol_version_70002

/// Construct a version protocol instance using parameterized relay.
protocol_version_70002(const session::ptr& session,
const channel::ptr& channel, bool relay) NOEXCEPT;
const channel::ptr& channel, bool relay, bool reject) NOEXCEPT;

/// Perform the handshake (requires strand), handler invoked on completion.
void shake(result_handler&& handle_event) NOEXCEPT override;
Expand All @@ -52,6 +52,10 @@ class BCT_API protocol_version_70002

virtual bool handle_receive_reject(const code& ec,
const messages::peer::reject::cptr& message) NOEXCEPT;

private:
// This is thread safe.
const bool reject_;
};

} // namespace network
Expand Down
4 changes: 0 additions & 4 deletions include/bitcoin/network/protocols/protocol_version_70014.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,6 @@ class BCT_API protocol_version_70014
const messages::peer::version_acknowledge::cptr& message) NOEXCEPT override;
virtual bool handle_receive_send_compact(const code& ec,
const messages::peer::send_compact::cptr& message) NOEXCEPT;

private:
// This is thread safe.
const bool reject_;
};

} // namespace network
Expand Down
1 change: 1 addition & 0 deletions src/channels/channel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ void channel::stopping(const code& ec) NOEXCEPT
void channel::pause() NOEXCEPT
{
BC_ASSERT(stranded());
BC_ASSERT(!held_);
stop_expiration();
stop_inactivity();

Expand Down
2 changes: 1 addition & 1 deletion src/config/credential.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ std::string credential::to_string() const NOEXCEPT
std::istream& operator>>(std::istream& input, credential& argument) THROWS
{
std::string value{};
input >> value;
std::getline(input, value);

// Methods are optional, so a credential has two or three tokens.
const auto tokens = split(value, ":", true, false);
Expand Down
14 changes: 14 additions & 0 deletions src/config/utilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,20 @@ std::string to_normal_host(const std::string& host,
}
}

std::string to_normal_origin(const std::string& origin,
uint16_t default_port) NOEXCEPT
{
try
{
const url url{ origin };
return endpoint{ url.host(), url.port() }.to_lower(default_port);
}
catch (const std::exception&)
{
return {};
}
}

} // namespace config
} // namespace network
} // namespace libbitcoin
1 change: 1 addition & 0 deletions src/error.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ DEFINE_ERROR_T_MESSAGE_MAP(error)
{ missing_parameter, "missing parameter" },

// json-rpc error
{ jsonrpc_invalid_version, "jsonrpc invalid version" },
{ jsonrpc_requires_method, "jsonrpc requires method" },
{ jsonrpc_v1_requires_params, "jsonrpc v1 requires params" },
{ jsonrpc_v1_requires_array_params, "jsonrpc v1 requires array params" },
Expand Down
26 changes: 18 additions & 8 deletions src/messages/http_body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,23 @@ void body::reader::init(const length_type& length, boost_code& ec) NOEXCEPT
{
ec = to_http_code(http_error_t::end_of_stream);
},
[&](file_reader& read) NOEXCEPT
{
if (!value_.get<file_value>().is_open())
{
ec = to_errc_code(boost_errc_t::bad_file_descriptor);
return;
}

try
{
read.init(length, ec);
}
catch (...)
{
ec = to_http_code(http_error_t::end_of_stream);
}
},
[&](auto& read) NOEXCEPT
{
try
Expand Down Expand Up @@ -132,16 +149,9 @@ body::writer::out_buffer body::writer::get(boost_code& ec) NOEXCEPT
{
[&] (std::monostate&) NOEXCEPT
{
ec = {};
ec = to_http_code(http_error_t::end_of_stream);
return out_buffer{};
},
[&](empty_writer&) NOEXCEPT
{
ec = {};

// Socket body writer requires non-empty buffer to write empty.
return out_buffer{ std::make_pair(asio::const_buffer{}, false) };
},
[&](auto& write) NOEXCEPT
{
try
Expand Down
20 changes: 10 additions & 10 deletions src/messages/peer/detail/reject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,26 +90,26 @@ reject reject::deserialize(uint32_t, reader& source) NOEXCEPT
const auto code = byte_to_reason(source.read_byte());
auto reason = source.read_string(max_reject_message);

const auto get_hash = [&message](reader& source) NOEXCEPT
auto hash = null_hash;
if (source && is_chain(message))
{
if (!source || !is_chain(message))
return null_hash;
const auto position = source.get_read_position();
hash = source.read_hash();

// Some nodes don't follow documented convention of hash for tx/block.
// Reset fault that occurs on hash read to accomodate those slackers.
const auto hash = source.read_hash();
if (!source)
source.set_limit();

return hash;
};
{
source.set_position(position);
hash = null_hash;
}
}

return
{
std::move(message),
code,
std::move(reason),
get_hash(source)
hash
};
}

Expand Down
8 changes: 7 additions & 1 deletion src/messages/rpc/body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,18 +85,24 @@ static void to_request(rpc::request& value, boost_code& ec) NOEXCEPT
{
// Primary exception type for parsing operations.
ec = e.code();
return;
}
catch (...)
{
ec = code{ error::jsonrpc_reader_exception };
return;
}

// Set version default.
if (value.message.jsonrpc == version::undefined)
value.message.jsonrpc = version::v1;

// Post-parse semantic validation.
if (value.message.method.empty())
if (value.message.jsonrpc == version::invalid)
{
ec = code{ error::jsonrpc_invalid_version };
}
else if (value.message.method.empty())
{
ec = code{ error::jsonrpc_requires_method };
}
Expand Down
7 changes: 6 additions & 1 deletion src/messages/rpc/model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,10 @@ DEFINE_JSON_FROM_TAG(value_t)
auto& object = value.as_object();
for (const auto& pair: visit)
object.emplace(pair.first, value_from(pair.second));
},
[&](const any_t&) THROWS
{
throw ostream_exception{ "any_t" };
}
}, instance.value());
}
Expand Down Expand Up @@ -381,7 +385,8 @@ DEFINE_JSON_TO_TAG(response_t)
}

// error
if (const auto it = object.find("error"); it != object.end())
if (const auto it = object.find("error");
it != object.end() && !it->value().is_null())
{
result_t result{};
const auto& error = it->value().as_object();
Expand Down
20 changes: 20 additions & 0 deletions src/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ void net::do_close() NOEXCEPT

// Notify and delete all stop subscribers (all sessions).
stop_subscriber_.stop(error::service_stopped);
service_subscriber_.stop(error::service_stopped);

// Notify and delete subscribers to channel notifications.
connect_subscriber_.stop_default(error::service_stopped);
Expand Down Expand Up @@ -344,6 +345,19 @@ bool net::resume() NOEXCEPT
return true;
}

void net::stop_services(const code& ec) NOEXCEPT
{
boost::asio::post(strand_,
std::bind(&net::do_stop_services, this, ec));
}

void net::do_stop_services(const code& ec) NOEXCEPT
{
BC_ASSERT(stranded());
suspend_services();
service_subscriber_.stop(ec);
}

bool net::suspended() const NOEXCEPT
{
return connect_suspended_.load() || accept_suspended_.load();
Expand Down Expand Up @@ -466,6 +480,12 @@ code net::subscribe_close(stop_handler&& handler, object_key key) NOEXCEPT
return stop_subscriber_.subscribe(std::move(handler), key);
}

code net::subscribe_service(stop_handler&& handler, object_key key) NOEXCEPT
{
BC_ASSERT(stranded());
return service_subscriber_.subscribe(std::move(handler), key);
}

// public
void net::subscribe_close(stop_handler&& handler,
stop_completer&& complete) NOEXCEPT
Expand Down
13 changes: 11 additions & 2 deletions src/net/acceptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ void acceptor::accept(socket_handler&& handler) NOEXCEPT
shared_from_this(), _1, socket, std::move(handler)));
}

// private
// protected
void acceptor::handle_accept(const code& ec, const socket::ptr& socket,
const socket_handler& handler) NOEXCEPT
{
Expand All @@ -173,11 +173,20 @@ void acceptor::handle_accept(const code& ec, const socket::ptr& socket,
if (!stranded())
{
boost::asio::post(strand_,
std::bind(&acceptor::handle_accept,
std::bind(&acceptor::do_handle_accept,
shared_from_this(), ec, socket, handler));
return;
}

do_handle_accept(ec, socket, handler);
}

// private
void acceptor::do_handle_accept(const code& ec, const socket::ptr& socket,
const socket_handler& handler) NOEXCEPT
{
BC_ASSERT(stranded());

if (ec)
{
socket->stop();
Expand Down
Loading
Loading